学破解 *二* PE格式之IMAGE_NT_HEADERS
2824 点击·0 回帖
![]() | ![]() | |
![]() | 这个IMAGE_NT_HEADERS其实就是PE相关结构的映像头,NT据我揣测应该是New Technology的缩写,区分于DOS WIN9X的新技术,您老要是非觉得是NTR什么的也没关系。 IMAGE_NT_HEADERS的结构是这个样子的 IMAGE_NT_HEADERS STRUCT { +0h Dword Signature +4h IMAGE_FILE_HEADER FileHeader +18h IMAGE_OPTIONAL_HEADER32 OptionalHeader } IMAGE_NT_HEADERS ENDS 其中包含两个子结构体,和一个标志。 其中Signature字段被设置成00004550h ,ASCII码为PE00 ,标志着PE头文件的开始。上一篇中DOS头结构体中的e_lfanew正是指向这里。 IMAGE_FILE_HEADER这个结构是这样的 typedef struct _IMAGE_FILE_HEADER { +04h word Machine; // 运行平台 +06h word NumberOfSections; // 文件的区块数目 +08h Dword TimeDateStamp; // 文件创建日期和时间 +0Ch Dword PointerToSymbolTable; // 指向符号表(主要用于调试) +10h Dword NumberOfSymbols; // 符号表中符号个数(同上) +14h word SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32 结构大小 +16h word Characteristics; // 文件属性 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; Machine 代表了cpu的类型 ,定义在windows.h中 #define IMAGE_FILE_MACHINE_UNKNOWN 0 #define IMAGE_FILE_MACHINE_I386 0x014c // intel 386. #define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian #define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian #define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian #define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2 #define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP #define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian #define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian #define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian #define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian #define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian #define IMAGE_FILE_MACHINE_THUMB 0x01c2 #define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64 #define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS #define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS #define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS #define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64 #define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 NumberOfSection 代表区块的数目,区块表紧跟在IMAGE_NT_HEADERS后面,区块表大概是一个链表结构,链表长度由NumberOfSection的数值决定。 TimeDataStamp 表明文件的创建时间 SizeOfOptionalHeader 是IMAGE_NT_HEADERS的另一个子结构IMAGE_OPTIONAL_HEADER的大小,32位的PE文件这个值一般是00E0,64位的PE文件一般是00F0 Characteristics 代表文件的属性EXE文件一般是0100h DLL文件一般是210Eh,多种属性可以用或运算同时拥有。 #define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // 重定位信息被移除 #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // 文件可执行 #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // 行号被移除 #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // 符号被移除 #define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Agressively trim working set #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // 程序能处理大于2G的地址 #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed. #define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32位机器 #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // .dbg文件的调试信息被移除 #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // 如果在移动介质中,拷到交换文件中运行 #define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // 如果在网络中,拷到交换文件中运行 #define IMAGE_FILE_SYSTEM 0x1000 // 系统文件 #define IMAGE_FILE_DLL 0x2000 // 文件是一个dll #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // 文件只能运行在单处理器上 #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed. 下面来编写一个程序来显示一下这个结构体 #include "windows.h" //PE的那一套结构体大多都是定义在这个头文件里的 #include "stdio.h" int main(int argc, char* argv[]) { FILE *p; LONG e_lfanew; //指向IMAGE_NT_HEADERS32结构在文件中的偏移 IMAGE_FILE_HEADER myfileheader; p = fopen("test.exe","r+b"); if(p == NULL)return -1; fseek(p,0x3c,SEEK_SET); fread(;e_lfanew,4,1,p); fseek(p,e_lfanew+4,SEEK_SET); //指向IMAGE_FILE_HEADER结构的偏移PE的标志位是Dword占4字节 fread(;myfileheader,sizeof(myfileheader),1,p); printf("IMAGE_FILE_HEADER结构:\n"); printf("Machine : %04X\n",myfileheader.Machine); printf("NumberOfSections : %04X\n",myfileheader.NumberOfSections); printf("TimeDateStamp : %08X\n",myfileheader.TimeDateStamp); printf("PointerToSymbolTable : %08X\n",myfileheader.PointerToSymbolTable); printf("NumberOfSymbols : %08X\n",myfileheader.NumberOfSymbols); printf("SizeOfOptionalHeader : %04X\n",myfileheader.SizeOfOptionalHeader); printf("Characteristics : %04X\n",myfileheader.Characteristics); getch(); return 0; } 总的来说IMAGE_FILE_HEADER是记录文件的各种信息的 下面说一下IMAGE_OPTIONAL_HEADER结构,他是一个可选结构,是对IMAGE_FILE_HEADER的一个补充,当然很多情况下它是必须的。 typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // +18h word Magic; // 标志字, ROM 映像(0107h),普通可执行文件(010Bh) +1Ah BYTE MajorLinkerVersion; // 链接程序的主版本号 +1Bh BYTE MinorLinkerVersion; // 链接程序的次版本号 +1Ch Dword SizeOfCode; // 所有含代码的节的总大小 +20h Dword SizeOfInitializedData; // 所有含已初始化数据的节的总大小 +24h Dword SizeOfUninitializedData; // 所有含未初始化数据的节的大小 +28h Dword AddressOfEntryPoint; // 程序执行入口RVA +2Ch Dword BaSEOfCode; // 代码的区块的起始RVA +30h Dword BaSEOfData; // 数据的区块的起始RVA // // NT additional fields. 以下是属于NT结构增加的领域。 // +34h Dword ImageBase; // 程序的首选装载地址 +38h Dword SectionAlignment; // 内存中的区块的对齐大小 +3Ch Dword FileAlignment; // 文件中的区块的对齐大小 +40h word MajorOperatingSystemVersion; // 要求操作系统最低版本号的主版本号 +42h word MinorOperatingSystemVersion; // 要求操作系统最低版本号的副版本号 +44h word MajorImageVersion; // 可运行于操作系统的主版本号 +46h word MinorImageVersion; // 可运行于操作系统的次版本号 +48h word MajorSubsystemVersion; // 要求最低子系统版本的主版本号 +4Ah word MinorSubsystemVersion; // 要求最低子系统版本的次版本号 +4Ch Dword Win32VersionValue; // 莫须有字段,不被病毒利用的话一般为0 +50h Dword SizeOfImage; // 映像装入内存后的总尺寸 +54h Dword SizeOfHeaders; // 所有头+ 区块表的尺寸大小 +58h Dword CheckSum; // 映像的校检和 +5Ch word Subsystem; // 可执行文件期望的子系统 +5Eh word DllCharacteristics; // DllMain()函数何时被调用,默认为0 +60h Dword SizeOfStackReserve; // 初始化时的栈大小 +64h Dword SizeOfStackCommit; // 初始化时实际提交的栈大小 +68h Dword SizeOfHeapReserve; // 初始化时保留的堆大小 +6Ch Dword SizeOfHeapCommit; // 初始化时实际提交的堆大小 +70h Dword LoaderFlags; // 与调试有关,默认为0 +74h Dword NumberOfRvaAndSizes; // 下边数据目录的项数,这个字段自Windows NT 发布以来 // 一直是16 +78h IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 数据目录表 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 这个结构东西各种多啊...非常蛋疼,不过很多都是optional的,可以不用管他们,重要的就那么几个。 AddressOfEntryPoint 指出文件执行时的入口地址,是一个RVA地址,就是大家常说的OEP,常常各种寻找的OEP,如果你有什么代码要先于程序主体执行,只需要将这个入口指向这段代码就行啦~ ImageBase 指向文件的优先装入地址,一般情况EXE文件不需要重定位,DLL文件可能需要重定位。 SectionAlignment 和FileAlignment 确定了内存中的节对齐单位和在磁盘中的节对齐单位。 DataDirectory 成员是一个比较牛逼的成员,它由16个IMAGE_DATA_DIRCTORY结构组成,用来定义多种不通用处的数据块。 这个结构体的内容很简单 typedef struct _IMAGE_DATA_DIRECTORY { Dword VirtualAddress; 相对虚拟地址 Dword Size; 大小 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 只有相对虚拟地址和大小两个成员。 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; IMAGE_NUMBEROF_DIRECTORY_ENTRIES 的值代表了数据块的用途 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor 最后把这些结构.读出来看一看。 #include "windows.h" #include "stdio.h" int main(int argc, char* argv[]) { FILE *p; unsigned long Signature; IMAGE_FILE_HEADER myfileheader; IMAGE_DOS_HEADER myDOSheader; IMAGE_OPTIONAL_HEADER myoptionalheader; p = fopen("test.exe","r+b"); if(p == NULL)return -1; fread(;myDOSheader,sizeof(myDOSheader),1,p); fseek(p,myDOSheader.e_lfanew,SEEK_SET); fread(;Signature,sizeof(Signature),1,p); fseek(p,myDOSheader.e_lfanew+sizeof(Signature),SEEK_SET);//指向IMAGE_FILE_HEADER结构的偏移 fread(;myfileheader,sizeof(myfileheader),1,p); fseek(p,myDOSheader.e_lfanew+sizeof(Signature)+sizeof(myfileheader),SEEK_SET); fread(;myoptionalheader,sizeof(myoptionalheader),1,p); printf("%X\n",myDOSheader.e_lfanew); printf("Signature : %04X\n",Signature); printf("IMAGE_FILE_HEADER结构:\n"); printf("Machine : %04X\n",myfileheader.Machine); printf("IMAGE_OPTIONALHEADER_HEADER结构:\n"); printf("Magic : %04X\n",myoptionalheader.Magic); 由于这个IMAGE_OPENTIONAL_HEADER结构成员太多,我就不都打出来了,Magic的值一般为010bH,由此可以判顿读取出的结构是否正确。 | |
![]() | ![]() |