当前位置:文档之家› PE文件结构

PE文件结构

检验PE文件的有效性<1>首先检验文件头部第一个字的值是否等于IMAGE_DOS_SIGNATURE,是则表示DOS MZ header有效<2>一旦证明文件的Dos header 有效后,就可用e_lfanew来定位PE header<3>比较PE header 的第一个字的值是否等于IMAGE_NT_HEADER,如果前后两个值都匹配. PS.WinHex使用方法1.Alt+G跳到指定位置2.Ctrl+Shift+N放入新文件3.大文件扩容,新建一个扩容大小+1的文件,把这个文件的数据复制后写入整个文件的尾地址.4.文本搜索ctrl+F5.十六进制搜索ctrl+alt+x6.文本显示F77.打开内存alt+F98.进制转换器F89.分析选块F210.计算HASH ctrl+F211.收集文本信息ctrl+F1012.编辑模式F6一.IMAGE_DOS_HEADER<1>位置00H,WORD(2个字节)的e_magic为4D5A,即MZ<2>位置3CH,60,LONG(4个节节)的e_lfanew为64+112=176即B0H,二.IMAGE_NT_HEADERS<1>位置B0H,DWORD(4个字节),PE开始标记,写入50450000,即PE<2>位置B4H,WORD,PE所要求的CPU,对于Intel平台,为4C01<2>位置B6,WORD,PE中段总数,计划有3个段,.text代码段,.rdata只读数据段,.data全局变量数据段,所以值为0300,<3>位置C4,WORD,表示后面的PE文件可选头的占空间大小,即224字节(E0),值为E000<4>位置C6,WORD,表示文件是EXE还是DLL,如果是可执行文件写0200,如果是dll,写0020,<5>位置C8,WORD,表示文件格式,如果是0B01表示.exe,如果是0701表示ROM映像<6>位置D8,DWORD,表示代码入口的RVA地址,要代码写好后才能确定,这里先用AAAAAAAA 代替,后修改为00100000,<7>位置E4,DWORD,表示文件映射到内存的基地址,PE文件优先装载地址,这里值为00004000,<8>位置E8,DWORD,表示段加载后内存中的对齐方式,如果是1000h(即4096),每节的起始地址必须是4096的倍数,若第一节从401000h开始,大小是10个字节,下一个节并不是从401011开始,而是从402000h开始,一般情况下都为4KB.<9>位置EC,DWORD,表示段在文件中对齐方式,一般情况下,程序文件对齐粒度都为200h(512),将此值设为00020000,<10>位置F8,表示子系统主版本号,该子系统版本必定是4.0,那么此处值为04.<11>位置100,表示程序载入内存后占用内存的大小(单位字节),等于所有段的长度之和--PE 结构要占1000h,3个段每个占1000h,总大小为4000h,值为"00400000"<12>位置104,表示所有文件头的长度之和,PE文件头总大小为64+112+4+20+224=424,3个节表的总大小3*40=120,424+120=544 字节,转为十六进制为220h,实际上要占用400h空间,所以值为00040000,<13>位置10C,表示子系统,0300表示控制台程序,0200表示Windows程序,<14>位置124,表示IMAGE_DA TA_DIRECTORY结构数组大小,通常有16个元素,所以值为1000000,<15>位置130,目录表的起始RVA地址,目录表的长度,先填写AAAAAAAAAAAAAAAA,因为要文件对齐,全部添零直到1a7h处.(后改写为1020 0000 3C00 0000<16>位置1A8,表示该节的名称,该节的名字为.text,此值为2E74657874000000,<17>位置1B0,表示该节数据映射到内存后所占字节数,这里是指有效代码所占的字节数,也可以填写26h经过内存对齐后的值,即00100000,<18>位置1B4,表示.text段映射到内存中的起始地址,因为PE头本身结构小于1000h字节,经过内存对齐后便为1000h,那么此值便为00001000,因此此处填写00100000,<19>位置1B8,表.text段在文件中所占的大小,可以填写2600 0000,也可以填写此值经过文件对齐后的值即200h,即00020000,<20>位置1BC,表示.text段在文件中的起始地址,上面经计算PE文件的总长度为400h,实际上也就是.text的起始偏移地址,此值为00040000,<21>位置1CC,表示是代码段,bit5位要置1,含有初始化数据,bit6要置1,可执行bit29置1,所以此处应该填写6000 0020.<22>.text头编写完毕,按照上面方法分别填写.rdata段和.data段,后面的代码用零补齐,直到3ff<23>位置400,表示.text段的程序执行代码,push 0;MessageBoxA的第4个参数,即消息框的风格,这里传入0push 0x403000;第3个参数,消息框的标题字符所在的地址PE头1000h,.text段1000h,.data段1000h,则在后面的.rdata段应该在偏移为3000h处,push 0x403007;第2个参数,消息框的内容所在的地址,"wcc526"占7个字符,push 0;第1个参数,消息框所属窗口句柄0call 40101A;调用MessageBoxA,实际上是跳转到该函数的跳转指令所在地址,因为执行代码起始地址为.text段的起始地址为0x1000,总长度为2+5+5+2+5+2+5=26,转为十六进制是1A, push 0 ;ExitProcess函数的参数,程序退出码,传入0call 401020;调用ExitProcess,实际上是跳转到该函数的跳转指令所在地址,再加6个字节jmp dword ptr[0x402080];跳转到MessageBoxA的真正地址处,通过导入表得到其填充地址为0x2080,这是其RVA值,jmp dword ptr[0x402088];跳转到ExitProcess的真正地址处, 通过导入表得到其填充地址为0x2088,这是其RVA值,转成机器码为push 0 ->6A 00push 0x403000->68 00304000push 0x403007->68 07304000push 0 ->6A 00call 40101A->E8 07 00 00 00push 0->6A 00call 401020->E8 06 00 00 00jmp dword ptr[0x402080]->FF 2580204000jmp dword ptr[0x402088]->FF 2588204000其余部分用00填充,直到600h处.<24>位置61C,指向DLL名字的RVA,由导入函数名称表可知此user32.dll名称所在地址为66A,所以RVA地址为6A20 0000<25>位置630, 指向DLL名字的RVA,由导入函数名称表可知此kernel32.dlol名称所在地址为684,所以RVA地址为8420 0000<24>位置64C,库函数名称地址表,即RVA地址,文件偏移600h对应的RVA是2000h,所以MessageBoxA的地址为65C,所以写5C20 00 0000,<25>位置654, 库函数名称地址表,即RVA地址,文件偏移600h对应的RVA是2000h,所以ExitProcess的地址为676,所以写7620 00 0000导出表结构1.Characteristics 是DWORD 类型,4个字节,可以为零2.TimeDateStampe,DWORD,表示自1970年1月1日至今的秒数,输出表创建时间,可以为零3.MajorVersion,WORD,表示主版本号,可以是零4.MinorVersion,WORD,表示次版本号,可以是零,DWORD,指向模块的真实名称字符串的RVA值,若该值为0x2e0a6,在文件中找到偏移为0x2e0a6的位置,得到Dll.dll,得知导出模块的名字为Dll.dll6.Base,等于所有函数导出序号中最小的值7.NumberOfFunctions,DWORD,表示模块中导出函数/符号个数8.NumberofNames,DWORD,表示通过名字引出的函数/符号的数目,9.AddressofFunctions,DWORD,指向一个地址表,存放的是所有导出函数所在地址的RV A值10.AddressofNames,DWORD,指向一个地址表,存放的是所有导出函数名称字符串所在地址的RVA值.11.AddressOfNameOrdinals,DWORD,指向一个导出序号表,解析导出表的步骤1.找出导出目录,定位到导出表的文件偏移2.查看导出表的第7个成员,得知导出地址成员的个数,这里是43.查看导出表的第9个成员,得到RVA并且将其转成文件偏移,4.查看导出表的第8个成员,得到由函数名导出函数的个数5.查看导出表的第11个成员,得到导出序号表6.查看导出表第10个成员,得出导出表中导出函数名称字符串所在地址的函数名称地址表。

相关主题