手写PE文件(不借助编译器,用十六进制数进行编写)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

⼿写PE⽂件(不借助编译器,⽤⼗六进制数进⾏编写)
这⼏天在看科锐钱⽼师有关PE⽂件的视频,感觉通过⼿写PE⽂件确实能增加对PE⽂件的了解,对结构也能更加熟悉,所以⾃⼰实操了下。

由于平时分析时,都是借⽤CFF等PE⽂件分析⼯具,对很多结构⾥⾯的成员的具体含义也不清楚,⼿写下来还是有点费劲的,写完之后对PE⽂件也清楚了很多。

主要⽤到的⼯具有WinHex(⽤来主要编写),OD(主要看加载到内存后是否符合⾃⼰预期,以及调试功能),CFF⼩辣椒(在整体结构,起码是头的数据框架完全出来前,最好先别⽤,⾃⼰去查下各⾃的成员所代表的含义)
记住⼤概的间隔,可以快速得到PE⽂件的相关数据:
当⽂件内容以16字节排列开时:
DOS头:0x40=64字节长度,占4⾏空间数据;前2字节的e_magic成员"MZ"标记代表其是DOS可执⾏⽂件,为第4排往回数4字节数据
e_lfanew成员,为距离NT头的偏移,两者相加得到NT头位置。

NT头:0xF8=248字节长度,占15⾏半空间数据;
1.NT头的PE标记,占4字节
2.⽂件头:占0x14=20字节,占1⾏4个数据;第⼀个word数据代表运⾏平台(CPU类型0x14C为X86、0x200为Intel Itanium、0x8664为
X64)、第⼆个word代表着该PE⽂件的节表个数,中间为参考下数据,可填CC,倒数第⼆个word为可选PE头的⼤⼩,最后⼀个word为该⽂件的属性(1固定基址 2可执⾏⽂件 4删除COFF⾏号 8删除COFF符号表 0x20处理2G以上的地址 100⽀持32位 200⽆调试信息 400映射到可执⾏⽂件 800映射到⽹络 1000映射系统⽂件 2000 DLL⽂件 4000仅在单处理器运⾏属性值由上述⼗六进制数进⾏&处理得到)
3.可选⽂件头:共占0xE0=224字节,占14⾏数据;其中可选PE头分别由0x60=96字节的普通成员和0x78=120的数组数据还有8字节的0组成
3.1可选⽂件头成员:0x60=96字节,第⼀个word数据为标记字(0x0107为ROM 映像,0x010B为普通可执⾏⽂件)、第5字节的dword为程序的代码长度、
第⼆⾏的第⼀个dword为OEP(RVA),即原始⼊⼝点位置、最后⼀个dword为程序加载的⽬标基址
第三⾏前2个dword分别分内存对齐⼤⼩和⽂件对齐⼤⼩
第四⾏第⼀个word为要求最低⼦系统版本的主版本号(如下图)、倒数第⼆个dword为程序的映射⼤⼩、倒数第⼀个dw ord为程序的头部⼤⼩(各种头+节表的总⼤⼩)
第五⾏第5字节的word为可执⾏⽂件期望的⼦系统(1驱动程序2窗⼝程序3控制台程序(DLL)等..)、后⼀个word为Dllmain( )函数被调⽤的时机(默认为0)、
后⾯接的4个dword(跨越到第六⾏)分别为栈保留、栈提交,堆保留,堆提交的值 (保0留即为有多少(银⾏有多少钱),提交即为⽤多少(你取多少钱))
3.2数据结构:占0x80=128字节,共有16个元素,每个元素占8字节长度数据(先地址(RVA)后长度),其中最后⼀项为未使⽤元素,可设为CC(由于这⾥为⼿写PE,没有太多功能,所以只填写了导⼊表的位置,以便系统能找到调⽤函数的位置)即可
节表:占0x28=40字节,2⾏半的数据;前8字节为节名(⾮必须)、最后16字节的4个dword分别为内存⼤⼩、内存起点、⽂件⼤⼩、⽂件起点,数据末尾的4字节为节属性
以上,⽂件头(可选PE头->SizeOfHeaders)全部编写完,占0x138(图⽚上的是因为DOS头最后的成员为F8,最⼩为40),对齐后,⽂件头独占⼀页空间(此时为0x200对齐)
接下来在下⼀页的空间中填充信息,记得区分⽂件中的页和内存中的页的⼤⼩
导⼊表张⼀⾏半,前4字节为INT表地址,当INT表为0时,导⼊表以IAT(第⼆⾏第1个dword)为线索寻找函数地址,注意,这⾥的地址,全是RVA,⽽且是IAT表的位置,表⾥⾯再存放着相应的函数信息。

第⼀⾏最后⼀个dword存放的为DLL名字的地址;由于这⾥只调⽤了MessageBoxA函数,所以导⼊表只有⼀个(后接24字节的0),IAT也只有⼀项(0x1040 后接4字节0结尾)
第260⾏为实际代码⽚段,也是OEP所指的位置(260位于⽂件第⼆页的60偏移,所以OEP要在内存的第⼆页60偏移,即0x1060),代码为将0压栈,将⽂本内容的地址⼊栈,标题地址⼊栈,0⼊栈,调⽤MessageBoxA函数(这⾥为FF15 00401050,因为IAT表地址为1050,期望的加载地址⼜为400000),ret(C3) 整个程序完毕,在⽤调试器调试时,可以跳转到IAT地址看看是否导⼊成功,再到OEP看下调⽤函数地址是否准确,⾄于标题和⽂本,可以⽤调试器转成⼗六进制再输⼊。

最后再上⼀个实际⼿写的PE⽂件的内容
运⾏结果:。

相关文档
最新文档