如何查看一个用C++编出来的exe程序的源代码
除非开发人员把源代码给你看,否则是做不到的。
理由如下:
C是高级语言,而高级语言的编译的过程是,首先将高级语言转化成中间代码,然后再转化成汇编,最后用汇编器转化成相应的机器二进制码,也就是通常windows下的exe文件。由于汇编和机器码的可以说几乎是一一对应的,所以由exe反汇编成汇编代码是可行的,再进一步就不行了。因为高级语言在结构上就和汇编不一样,比如说递归调用,在汇编里肯定会转化为堆栈的非递归实现的。顺着翻译可以,逆翻译却是做不到的。
另外,高级语言自由度远比汇编高,一样的汇编代码完全可以用面貌全不相同的高级语言分别编译出来,所以高级语言和汇编代码并不是一一对应的。
有兴趣可以看一看编译原理的书籍。
如有疑问,欢迎追问。
代码,程序员用开发工具所支持的语言写出来的源文件,是一组由字符、符号或信号码元以离散形式表示信息的明确的规则体系。
1、作用不同
源代码主要功用有如下2种作用:生成目标代码,即计算机可以识别的代码。对软件进行说明,即对软件的编写进行说明。
计算机程序为一组计算机能识别和执行的指令,运行于电子计算机上,满足人们某种需求的信息化工具。
2、目标不同
计算机程序以某些程序设计语言编写,运行于某种目标结构体系上。计算机源代码最终目的为将人类可读文本翻译成为计算机可执行的二进制指令,这种过程叫编译,它由通过编译器完成。
3、特点不同
为了使计算机程序得以运行,计算机需要加载代码,同时也要加载数据。从计算机的底层来说,这是由高级语言(例如Java,C/C++,C#等)代码转译成机器语言而被CPU所理解,进行加载。
如果在一个符合大多数的计算机上,操作系统例如Windows、Linux等,加载并执行很多的程序,在这种情况下,每一个程序是一个单独的映射,并不是计算机上的所有可执行程序。
源代码作为软件的特殊部分,可能被包含在一个或多个文件中。一个程序不必用同一种格式的源代码书写。例如,一个程序如果有C语言库的支持,那么就可以用C语言;而另一部分为了达到比较高的运行效率,则可以用汇编语言编写。
较为复杂的软件,一般需要数十种甚至上百种的源代码的参与。为了降低种复杂度,必须引入一种可以描述各个源代码之间联系,并且如何正确编译的系统。在这样的背景下,修订控制系统(RCS)诞生了,并成为研发者对代码修订的必备工具之一。
还有另外一种组合:源代码的编写和编译分 别在 不同的平台上实现,专业术语叫做软件移植。
-程序
-代码
那得看是什么文件,如果是二进制文件就没有办法看,如果是文本文件可以用记事本打开看,有些二进制文件可以用反编译器反编译后看源文件,反编译器可以从网上下载
源代码(也称源程序),是指一系列人类可读的计算机语言指令。在现代程序语言中,源代码可以是以书籍或者磁带的形式出现,但最为常用的格式是文本文件,这种典型格式的目的是为了编译出计算机程序。
源代码作为软件的特殊部分,可能被包含在一个或多个文件中。一个程序不必用同一种格式的源代码书写。例如,一个程序如果有C语言库的支持,那么就可以用C语言;而另一部分为了达到比较高的运行效率,则可以用汇编语言编写。较为复杂的软件,一般需要数十种甚至上百种的源代码的参与。为了降低种复杂度,必须引入一种可以描述各个源代码之间联系,并且如何正确编译的系统。在这样的背景下,修订控制系统(RCS)诞生了,并成为研发者对代码修订的必备工具之一。还有另外一种组合:源代码的编写和编译分别在不同的平台上实现,专业术语叫做软件移植。
标 题: MFC逆向初级研究(1)
作 者: 北斗之摇光
时 间: 2007-03-15 17:14
链 接: http://bbspediycom/showthreadphpt=41087
详细信息:
文章标题: MFC逆向初级研究(1)
文章作者: 北斗之摇光
作者邮箱: hardlywhen@hotmailcom
下载地址: 自己搜索下载
作者声明: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
详细过程
引言
本文主要针对微软的VC++60中使用MFC产生的EXE文件的逆向研究,我曾经使用微软的Visual Studio 2005编译了一
个EXE文件,通过IDA反汇编以后发现该文件与VC++60产生的文件还是有所区别,因此特别在此声明一下。文中主要使用了I
DA pro 50和在看雪(wwwpediycom)下载的OllyICE作为工具对目标文件进行反汇编。在此也感谢看雪论坛的各位的无私奉
献,在研究过程的中的困难多通过各位的帖子得到了帮助。
逆向的关键
我认为逆向的关键主要是要弄明白目标文件的算法和实现过程,在Window操作系统下,软件的实现过程就体现在其
对Window消息的处理,而软件的算法则包含在处理的具体过程中。对于通过SDK编写的"传统"的Windows应用程序基本都具备
几个共同的特征:WinMain函数、WinProc函数、窗口注册、消息循环。对于这类目标文件的分析主要集中的WinProc的分析上
,WinProc的函数地址获得一般是通过窗口注册函数中的参数获得。(由于我对于这类文件没有具体逆向过,所以只是大概的
说说,有不对的地方请各位不要客气,尽管拍砖)
而使用MFC(Microsoft Function Class)顾名思义,该类库主要封装了大部分的Windows API函数所以在代码中看
不到原本的SDK编程中的消息循环、窗口过程函数等等东西,所有这些封装在相应的mfcxxdll中,让程序员能够专著与处理
过程与算法。这种做法于逆向而言有好处也有坏处:
坏处就是加大了对于MFC产生的EXE文件的逆向难度,让许多的和我一样的菜鸟迷失在汇编代码中找不找北了,基本主要就靠
猜测实现过程中用到了那些函数,然后对文件导入表的函数下断点来寻找我们所需要的处理过程;
好处就是这样的做法使得EXE文件中主要都是目标程序的Window消息处理流程以及算法,而且dll中的大部分函数的功能都能
在MSDN中查到。如果能够通过对目标文件的分析得到这个Window消息处理流程和算法架构,基本上我们就可以重写整个软件;
要做到上面的目标,首先我们要对MFC有所了解,推荐没有基础的兄弟们读读候俊杰的《深入浅出MFC》。该书在逆向过
程中完全可以作为一本参考书,让你能通过源代码了解实现过程,网上有很多该书的电子版下载。
一个逆向MFC产生的EXE文件的例子
下面我们就通过一个具体的例子来学习一下如何从目标文件中挖到我们需要的东西。首先我们来产生一个需要的EXE文件。
在此我假定各位对MFC有过一定的使用经验,毕竟逆向分析才是本文的重点。
1.产生例子所需要的目标文件:
我们通过VC++60的向导来产生一个名为ReverseMFC的工程,这个工程的设置情况如下:
Application type of fff:
Dialog-Based Application targeting:
Win32
Classes to be created:
Application: CFffApp in ReverseMFCh and ReverseMFCcpp
Dialog: CFffDlg in ReverseMFCDlgh and ReverseMFCDlgcpp
Features:
+ Uses shared DLL implementation (MFC42DLL)
+ Localizable text in:
中文[中国]
直接编译以后就能够运行,为了确定我们是否正确的分析的整个目标文件,在该对话框中加入一个我们自定义的按钮如
下,对于该按钮的处理函数如下设定为:
AfxMessageBox("I find it!",MB_OK);编译后就得到了我们需要的目标文件。
现在我们得到了所需要的目标文件,在IDA中载入该文件。在此我们最好是产生Release版本的EXE文件,毕竟所有的发
布软件都是Release版本的。
2.具体分析
在IDA中按Ctrl+S找到rdata段,该段主要存储了目标文件的类运行时创建信息、MessageMap信息、MessageEntry信息、
虚函数表、RTTI数据(如果编译选项中选择了支持RTTI的话)。
在到达rdata段后我们可以看到这样的代码,对数据进行格式转换后可以得到如下图所示的数据。
rdata:004021C0 ; 屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯
rdata:004021C0
rdata:004021C0 ; Segment type: Pure data
rdata:004021C0 ; Segment permissions: Read
rdata:004021C0 _rdata segment para public 'DATA' use32
rdata:004021C0 assume cs:_rdata
rdata:004021C0 ;org 4021C0h
rdata:004021C0 off_4021C0 dd offset sub_401000 ; DATA XREF: sub_401010o
rdata:004021C4 dd offset dword_4021C8
rdata:004021C8 dword_4021C8 dd 111h ; DATA XREF: rdata:004021C4o
rdata:004021CC dd 0
rdata:004021D0 dd 0E146h
rdata:004021D4 dd 0E146h
rdata:004021D8 dd 0Ch
rdata:004021DC dd offset CWinApp::OnHelp(void)
rdata:004021E0 dd 0
rdata:004021E4 dd 0
rdata:004021E8 dd 0
rdata:004021EC dd 0
rdata:004021F0 dd 0
rdata:004021F4 dd 0
rdata:004021F8 off_4021F8 dd offset CWinApp::GetRuntimeClass(void)
rdata:004021F8 ; DATA XREF: unknown_libname_1-56o
rdata:004021FC dd offset sub_401040
rdata:00402200 dd offset nullsub_2
rdata:00402204 dd offset nullsub_3
rdata:00402208 dd offset nullsub_2
rdata:0040220C dd offset CCmdTarget::OnCmdMsg(uint,int,void ,AFX_CMDHANDLERINFO )
其中的off_4021C0就是一个MessageMap数据;dword_4021C8就是MessageMap所指的MessageEntry数据;off_4021F8就是一个
类的虚函数表的开始位置。那么具体这些数据时那个类的相关数据呢?如此判断的依据是什么?
首先我们知道MessageEntry是的数据结构定义如下,而且以6个0表示整个数组的结束。
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
因此我们有理由假设"dword_4021C8就是MessageMap所指的MessageEntry数据"。
而MessageMap数据结构定义如下:
struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP (PASCAL pfnGetBaseMap)();
#else
const AFX_MSGMAP pBaseMap;
#endif
const AFX_MSGMAP_ENTRY lpEntries;
};
off_4021C0的两个数据中第二个数据恰恰就是我们前面假设为MessageEntry的指针,跟入其第一个数据,我们看到如下的代
码:
text:00401000 ; S U B R O U T I N E
text:00401000
text:00401000
text:00401000 sub_401000 proc near ; DATA XREF: rdata:off_4021C0o
text:00401000 mov eax, ds:AFX_MSGMAP const CWinApp::messageMap
text:00401005 retn
text:00401005
text:00401005 sub_401000 endp
恰恰是一个返回基类的MessageMap的函数。因此我们也同样有理由假设"off_4021C0就是一个MessageMap数据"。
对于虚函数表的假设是如何被证明呢?首先我们要知道关于虚函数表的一点知识:虚函数表由虚函数的地址组成,表中函数
地址的顺序和它们第一次出现的顺序(即在类定义的顺序)一致。若有重载的函数,则替换掉基类函数的地址。通过这个我
们可以知道MFC中虚函数表中的函数顺序必然是先按照CObject->CCmdtarget->。。。。这个类继承顺序中的虚函数顺序来处
理虚函数表中的函数顺序的。只要证明这个我们"假设的虚函数"中的函数顺序与上面提到的知识相符合则有理由说明我们的
假设成立。
首先来看CObject中虚函数的顺序,在查看CObject的声明文件后得到了这个类的虚函数顺序:
virtual CRuntimeClass GetRuntimeClass() const;
virtual ~CObject(); // virtual destructors are necessary
virtual void Serialize(CArchive& ar);
#if defined(_DEBUG) || defined(_AFXDLL)
// Diagnostic Support
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
再来查看CCmdtarget的虚函数顺序,在查看CObject的声明文件后得到了这个类的虚函数顺序:
DECLARE_DYNAMIC(CCmdTarget);
virtual BOOL OnCmdMsg(UINT nID, int nCode, void pExtra,
AFX_CMDHANDLERINFO pHandlerInfo);
#ifndef _AFX_NO_OLE_SUPPORT
// called when last OLE reference is released
virtual void OnFinalRelease();
#endif
#ifndef _AFX_NO_OLE_SUPPORT
// called before dispatching to an automation handler function
virtual BOOL IsInvokeAllowed(DISPID dispid);
virtual BOOL GetDispatchIID(IID pIID);
virtual UINT GetTypeInfoCount();
virtual CTypeLibCache GetTypeLibCache();
virtual HRESULT GetTypeLib(LCID lcid, LPTYPELIB ppTypeLib);
之所以还要列出"DECLARE_DYNAMIC(CCmdTarget);"是因为这个宏的定义如下:
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass PASCAL _GetBaseClass(); \
public: \
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass GetRuntimeClass() const; \
这个virtual CRuntimeClass GetRuntimeClass() const; 覆盖掉了一开始的CObject的相对应函数。依次按照类的顺序对
照下来,就可以知道该表确实是虚函数表。同时,对应的GetMessageMap虚函数的位置上跟入后,可以得到如下代码:
text:00401010 ; S U B R O U T I N E
text:00401010
text:00401010
text:00401010 sub_401010 proc near ; DATA XREF: rdata:00402228o
text:00401010 mov eax, offset off_4021C0
text:00401015 retn
text:00401015
text:00401015 sub_401010 endp
恰恰是返回了我们之前假设的MessageMap的地址。
--------------------------------------------------------------------------------
版权声明: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
那什么是家谱,族谱?怎么制作?
谱,又称家乘、宗谱、公谱、祖谱、谱书、家牒、族牒。它是记录家族组织活动的档案材料。福建的族谱兴于宋代,盛于明清之后。修谱的动机是“溯渊源,分疏戚,序尊卑。”(安溪《谢氏总谱》卷首《联谱序》)族谱的形式有装订成册的(古为线装,今与一般印刷物相同),也有图表式的。许多家族把编纂族谱作为后代子孙的一项义务写进族规,重修族谱年限不等。闽北浦城的季氏和李氏家族,均规定族谱30年一修。房氏则规定“宗谱有贤子孙或十年、三十年一修,则存没葬地,时日不爽。先儒云,一世不修谱为不孝,宜知之。”(清·光绪《闽浦房氏族谱》)类似的族规,使修谱用家族法的形式永久化了。
族谱所载内容详略不一,通常除记载全族的户口、婚配和血缘关系外,还有全族的坟墓、族田族产、祠庙等的方位、数量及管理使用办法、家族的规约训诫、修谱凡例义则、各类合同契约文书等。一些较详的族谱,还记载有家族历代的重大事件、与外界的纠纷、可嘉奖的人物传记、科举出仕、名人传记以及义行节烈等。 族谱的核心内容是记载家族的世系源流、血缘系统。以防血缘关系紊乱而导致家族瓦解。连城《新泉张氏族谱》卷首说得明白:“亲疏派别得谱则溯其源,上下分支得谱则穷其本,即荡析代变,而皆有所考焉。”为达到显示血统的目的,各地族谱均有认中国先朝名人为远祖的习俗,这种附会的谱风,虽能使其家系倍增光彩,但往往导致家史失真和对后代误导。安溪湖头的李氏宗谱,认先祖为道教创始人李耳,直系则“出陇西,为唐高祖李渊公之苗裔。”南靖庙兜郭姓,认始祖为周文王之弟“虢叔”(“虢”与“郭”音近),并由虢叔是黄帝的二十七世裔孙,郭子仪是虢叔的第六十世孙、黄帝的八十七世裔孙,而推论自己的宗族嫡出于中唐名将郭子仪。(《南靖文史资料》第二期89页《庙兜郭姓的历史渊源》何柳枝)这些记载,易使族人深信不疑。其他的如刘姓必溯祖为刘邦,肖姓多探源为萧何,都是这种攀强名流、附会望族的修谱心态之反映。 族谱中,家族迁居(开基)始祖之下的代系排列严格分明,不容混淆。这往往是族谱中最具史实价值的部分。福建的许多家族,都实行名字排行制(古称“昭穆”),俗称“排辈份”。即在同一辈份的族人中名或字须用某个统一规定的单字起头,再与其他单字结合成名或字,以示区别。如某一父辈生三子,儿辈名按“永”字排列,分别称“永志”、“永仁”、“永贵”。如此,在族谱中一看“永”字排行便可知其为兄弟或堂兄弟辈份。已去世者,则在其名上写上谥称并加上“公”字,以示区别,沙县一带习俗,收养子若无族长和六亲认可,不可上谱排字辈,否则会被骂为“出透的人”而遭岐视。排辈份除少数由祖、父辈临时决定外,大多是按先祖早已选定的排行用字。南靖县双峰村《丘氏族谱》载:从其二十一世始,标定的昭穆用字是:“文章 ,诗礼传家。创垂显奕,继述藏嘉。光前荣耀,世德作裘。仁亲义祖,燕翼贻谋。桂芳兰茂,日新月盛。思皇多佑,福禄来成。庆余善积,谱泽绵延。宗风丕振,亿万斯年。”(《南靖文史资料》第二期86页《双峰丘氏的历史渊源》林明波)目前已传至“垂”、“显”字辈,即第三十至三十一世。这种按族谱排辈份的方法,使农村常可见到这样的俗象,由于世系分支发展速度不一,一些年届七旬的老翁,要向三岁稚童称“叔”或“伯”。永定俗谓:“白头哥,坐地叔”(即对平辈的白头老翁仅呼“哥”,而尚在襁褓中的叔辈,即使上年纪老者也要唤其为叔)。为了区别同辈中的年龄次序,“排辈份”之外还要“排号”。永泰县同安乡张氏族谱规定,族人添丁按先后次序排号,日常称呼常舍其名而直呼其号,如同辈中是第20个出生的,就直呼“二十”以代名字。这一习俗在当地一些年高者中至今仍颇为流行。 同一姓氏的不同分支家族,还经常利用族谱中的血缘世系排行记载,进行“联谱”活动。安溪谢氏家族,曾于民国年间合数十宗支进行大联谱。辈份分明后,各宗支子孙公议决定排行班次悉归画一,修纂总谱,旨在扩大家族势力。谢氏家族裔孙谢维峻在《联谱序》中称:“联之以谱,化弱小为强大,转柔软为坚刚,众志成城。”此外还有,当族谱分支过多时另分几个房派修谱(房谱和族谱通称家谱)。如一房人外迁,其房谱分出后就成为新的族谱。 由于族谱为同一姓氏家族承继的依据,故一般只记男丁。少数族谱也将族中所谓“贞妇烈女”记载入乘。族谱有的依不同对象规定用笔着色的。据《岛居三录》载:“泉郡向修族谱,世系图所牵连之线,有红有黑,体例甚严。红线者,一本至亲,虽远房承宗亦然;黑线者,乞养异姓,即显宦不能免,此亦春秋诛心之法也。”有功名成就或能光宗耀祖之人,族谱中用红笔记入以示表彰,过去秀才或秀才以上的读书人也用朱笔入谱。现在一些新修族谱仍沿此习。寿宁《刘氏总谱》中,凡上大学的族人,名下都划上红线,所书的姓名用谱名(即按族谱排行而起的名字),抱养子用黑线注明(连城一带是用蓝线)。修谱和查阅族谱也很有讲究。旧时对祖谱保管十分重视,须慎重保存,定期曝晒,认真缮修。霞浦一带,谱系分正谱、副谱(草谱)。副谱可以查阅,而正谱修好后,要入箱上锁,将开锁的钥匙丢入祖祠神龛,以示此谱交祖先收存,以后禁开锁。若遇副谱丢失,有事要查谱时,要先做“牲福”,祭请祖先同意,方可开锁。 旧时一些宗族修谱,每逢初一、十五日要祭祀祖宗。族谱告成后,还要造灵厝、做功德,请和尚法师观灯;观灯时造3座大灵厝(纸糊房子),供已超度的神魂居住,另设一些黑龛,供无子孙后代及未超度的神魂居住。福建省连城一带宗族,每年正月要拜“代图”(即写在布上的族谱)。行仪时,本族男丁都要到场,先是“新丁上图”(即新添男丁的名字入谱),上图之家要给执笔者一个红包,并烧香燃炮,用三牲祭祖。若逢大祭,要用整头猪羊置于下厅的支架上;继而是鸣锣击鼓放鞭炮;最后会餐,座位严格按辈份排列。族人围坐喝酒谈心,气氛很好。连城人把正月“拜代图”的宗族聚会称为“闹花灯”。 用Excel如何制作家谱建议以Word编辑,用插入“组织结构图”编写家谱。
当然,你也可以下载专门的家谱软件,如《族谱家谱制作编辑软件》或《族脉家谱》编写。 族谱家谱制作的9大步骤一、拟定修家谱计划
1、版本:精装、平装、光碟、网络。 2、时间:完成期限、工作时程。 3、预算:经费、人员。 4、范围:同宗、合族、家族。 5、工具:图表、问卷、电脑。 二、组织章程 组织委员、修谱大纲、责任分配、发凡起例。 三、筹集基金 一人负担、众人分摊、家族劝募、早请补助、公开销售组织内容大纲,制作世系图表、填写行实履历、撰写名人家传、考订姓氏渊源、记录迁徙过程、著录文艺著作、附录照片图版、其它次要内容。 四、资料分析 前修旧谱、相说族谱、族谱研究资料、研究与过滤资料、请教族谱专家。 五、寻找史料 家谱文献、世系资料、出生、过世登记证、户籍、除户登记资料、神龛祖先牌位、家庙晋主名册、祭祀公业章程、名册,墓碑文字、坟葬座落方位,讣文、疏文、功德薄,长辈口述记录,家传、年谱、纪念集,老照片、录音、录影资料,古文书、契约、证书,正史、方志,人名录、同学录,旧报纸、电话薄。 六、编辑内容 组织内容大纲、制作世图表、填写行实履历、撰写名人家传、考订姓氏渊源、记录迁徙过程、著录文艺 著作、附录照片图版、其它次要内容。 七、审定内容 专家审稿、多次校稿、最后完稿。 说明:最好有学者专家参与审订,审稿时一定要仔细,可以慢一些但要保持精准。 八、印刷出版 黑白或彩色、纸本或光碟、选定印刷厂、印制若干份、设家族网站。 九、领谱典藏 办祭谱活动,族人领族谱,寄存图书馆。 如何把纸质家谱族谱做成电子版?把纸质家谱做成电子版,就得找这样的软件。或找会做网站的专业人员帮你做。非专业人士在没有软件的情况下应该是做不成的。
如何制作家谱图表家谱,又称族谱、家乘、祖谱、宗谱等。一种以表谱形式,记载一个以血缘关系为主体的家族世系繁衍和重要人物事迹的特殊图书体裁。
用PS图像处理软件制作家谱图表步骤/方法: 1、首先打开PS软件,然后选择菜单栏上的文件选项,选择下拉列表的新建文件。 2、弹出新建对话框,给新建的文档设置一个宽度和高度 3、设置好宽度和高度之后单击确定按钮,新建一个新的文档。 4、选择工具栏上的矩形选框工具,也可以选择椭圆工具,根据自己喜欢的形状来选择,这里我选择矩形选框工具。 5、然后在文档上拖动鼠标,拉进一个长方形的选区。 6、然后新建一个新的图层,选择软件右下角的新建小图标,新建一个新的图层 7、新建完图层之后为图层填充颜色,填充你自己喜欢的颜色,选择软件左侧的工具栏,单击颜色按钮,弹出的窗口选择你喜欢的颜色。 8、选择好颜色之后单击确定按钮即可,然后按下键盘上的ALT+Backspace退格键为刚才新建的图层填充颜色。 9、按键盘上的CTRL+D键,取消选择选区。 10、然后选择工具栏上的文字工具,选择直排文字工具,为刚填充颜色图案上面填写上名字。 11、选择完之后我们选择颜色图标,把颜色更换为白色,然后在文档上面输入文字。 12、如果觉得文字太小的话,可以在菜单栏顶部设置文字的大小,接下来就是要现点竖线,选择左侧工具栏上的自定义形状工具里面的直线工具。 13、然后在文档拉动,由于刚更换颜色为白色,现在将颜色更换为红色,拉动之后按键盘上的CTRL+CENTER然后再按CTRL+D取消选择。 14、“爷爷”已经制作好了,接下来制作“爸爸”,依次这样制作下来,大家发挥自己的想象力,颜色之类都可以更改,包抱直线等。再请参考: 怎样制作家谱图表_家谱吧_百度贴吧 ://tiebabaidu/p/3634444285 有谁知道用“WORD”制作传统式家谱(族谱)?
用文本框(双击文本框,设置文本框颜色为无线条色)就可以制作了。中间的连接线用自选图形里面的 连接符。
先画出来一个文本框,然后双击设置好格式(双击 文本框--颜色与线条,改成无线条色,然后 点文本框(顶部 倒数第2组),设置4个边距均为0)。然后第一个框框就弄好了。 选中这个文本框,ctrl +鼠标左键,拖拽到适当的位置(比如最上面是 家族的族长,下面的第二层,是第二辈分的人,按要求摆放位置即可。这样所有的文本框均是同样的尺寸,同样的格式。 弄好了之后 ,再添加连接线。 每一页都是这样的编辑方式。可以根据大概情况,提前 弄出多页面来。 制作家谱用什么软件好传承家谱程序是专门做树形家谱的程序,而且输出为WORD文档,也可以图形输出。 输出树形谱系为TXT文件,可以输出任意大小的树形谱系,避免了版面上的限制。 可以在百度搜索,最新版本为520
如何制作家谱?可以考虑用Microsoft Office Visio进行绘制。使用方法和word类似
在word中可以考虑采用“组织机构图”的绘制方法 家谱(23)族谱(13)
这是一个简单的程序,会自动计算提子,但不会数目。其它的运行一次估计就差不多会用了。稍微写了点注释。
#include<stdioh>
#include<stdlibh>
char board[21][21];
char move[5][2]=,,,,};
void initBoard();//初始化棋盘
void showBoard();//输出棋盘
char set(int x,int y,char color);//下子
void process(int xx,int yy);//计算提子
int main()
{
FILE fptr=NULL;
char pufile[256]=;
char op;
int s;
int x,y,r;
char color;
char win;
int cnt;
start:
s=8;
while(s!=1 && s!=2)
{
printf("选择模式:\n1---下棋\n2---看棋谱\n0---退出\n");
printf("下棋模式下,下子请输入s x y(x,y为位置),认输输入g,和棋输入h\n选择:");
scanf("%d",&s);
if(s==0) return 0;
//Egg1
if(s==10) printf("Programmer: swordlance :)\n");
//Egg1 end
}
getchar();
printf("输入棋谱路径:");
gets(pufile);
if(s==1) fptr=fopen(pufile,"w");
else fptr=fopen(pufile,"r");
if(!fptr)
{
printf("文件无法打开(创建)!\n");
system("PAUSE");
return -1;
}
initBoard();
cnt=0;
color='B';
while(op!='g')
{
system("CLS");
showBoard();
printf("(第%d手)",++cnt);
if(s==1)
{
printf("%c 方:",color);
scanf("%c",&op);
//printf("[%c]",op);
if(op=='s')
{
scanf("%d %d",&x,&y);
getchar();
if(set(x,y,color)!=0)
{
printf("该处不能落子!\n");
cnt--;
system("PAUSE");
}
else
{
process(x,y);
fprintf(fptr,"%d %d\n",x,y);
if(color=='B') color='W';
else color='B';
}
}
else if(op=='g')
{
printf("%c 方认输。\n",color);
if(color=='B') fprintf(fptr,"0 1\n");
else fprintf(fptr,"0 -1\n");
fflush(fptr);
fclose(fptr);
system("PAUSE");
goto start;
}
else if(op=='h')
{
printf("和棋。\n");
fprintf(fptr,"0 0\n");
fflush(fptr);
fclose(fptr);
system("PAUSE");
goto start;
}
else
{
printf("参数错误,下子请输入s x y(x,y为位置),认输输入 g,和棋输入h");
cnt--;
system("PAUSE");
}
}
else
{
fscanf(fptr,"%d %d",&x,&y);
if(x==0)
{
if(y>0) printf("W 方胜!\n");
else if(y<0) printf("B 方胜!\n");
else printf("和棋!\n");
system("PAUSE");
goto start;
}
else
{
printf("%c 方落子(%d,%d)\n",color,x,y);
set(x,y,color);
process(x,y);
if(color=='B') color='W';
else color='B';
}
system("PAUSE");
}
}
system("PAUSE");
return 0;
}
void initBoard()
{
int i,j;
board[0][0]='O';
for(i=1;i<=19;i++) board[0][i]='-';
board[0][20]='O';
for(i=1;i<=19;i++)
{
board[i][0]='|';
for(j=1;j<=19;j++) board[i][j]='+';
board[i][20]='|';
}
board[20][0]='O';
for(i=1;i<=19;i++) board[20][i]='-';
board[20][20]='O';
board[4][4]=board[4][10]=board[4][16]=
board[10][4]=board[10][10]=board[10][16]=
board[16][4]=board[16][10]=board[16][16]='';
}
void showBoard()
{
int i,j;
for(i=0;i<=20;i++)
{
for(j=0;j<=20;j++)
{
printf("%c",board[i][j]);
}
printf("\n");
}
}
char set(int x,int y,char color)
{
if(board[x][y]=='W' || board[x][y]=='B') return -1;//不能落子
else board[x][y]=color;
return 0;
}
//计算提子
void process(int xx,int yy)
{
char his[21][21]=;//记录算过的棋子以节约效率
char Q[400][2]=;//某一片棋
int e;//Q的长度。
char mcolor;//这片棋的颜色
char ecolor;//另一种颜色
int QI=0;//气数
int i,j,k,l,m;
int x,y;
for(m=0;m<5;m++)
{
i=xx+move[m][0];//为了能够完成打劫,先算别人再算自己
j=yy+move[m][1];
if(his[i][j]==0 && (board[i][j]=='W' || board[i][j]=='B')) //该位置有子开始算气
{
QI=0;
his[i][j]=1;
mcolor=board[i][j];
ecolor=(board[i][j]=='W''B':'W');
//printf("m=%c e=%c\n",mcolor,ecolor);
Q[0][0]=i;
Q[0][1]=j;
e=1;
for(k=0;k<e;k++)
{
for(l=0;l<4;l++)
{
x=Q[k][0]+move[l][0];
y=Q[k][1]+move[l][1];
//printf("x=%d y=%d\n",x,y);
//system("PAUSE");
if(x>0 && y>0 && x<20 && y<20 && his[x][y]==0)
{
if(board[x][y]==mcolor)//己方,长气
{
Q[e][0]=x;
Q[e][1]=y;
e++;
his[x][y]=1;
}
else
{
if(board[x][y]=='+') QI++; //空地,加气,忽略重复计算
}
}
}
}
//printf("QI=%d\n",QI);
//system("PAUSE");
if(!QI)//死棋,提子
{
for(k=0;k<e;k++)
{
board[Q[k][0]][Q[k][1]]='+';
his[Q[k][0]][Q[k][1]]=0;
}
}
}
}
}
如何查看一个用C++编出来的exe程序的源代码
本文2023-09-22 06:33:39发表“资讯”栏目。
本文链接:https://www.lezaizhuan.com/article/37607.html