PE节表详细分析


目录

  • PE节表详细分析
    • 0x00 前言
    • 0x01 PE节表分析
      • 节表结构
      • 节表数量
      • 节表名字
      • 节表大小
      • 节位置
      • 节表属性
    • 0x02 代码编写

PE节表详细分析 0x00 前言 上一篇文章我们学习了PE结构中的PE头,我之前说过PE文件结构是PE头->节表->每个节,所以我们掌握了节表后也就知道怎么去获取每个节了。(当然后面还有输入表,输出表这些比较重要的东西。这些知识在后面的文章详细介绍。)
0x01 PE节表分析 节表结构
PE节表位于PE头的下面,PE节表中记录了各个节表的起始位置、大小,以及在内存中偏移位置和属性。
其结构体如下:
#define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { BYTEName[IMAGE_SIZEOF_SHORT_NAME]; //*[1]:节名 union { DWORDPhysicalAddress; DWORDVirtualSize; //*[2]:对其前节大小,内存中节大小 } Misc; //联合体 DWORDVirtualAddress; //*[3]:内存中偏移 DWORDSizeOfRawData; //*[4]:文件对其后节大小 DWORDPointerToRawData; //*[5]:文件中节位置 DWORDPointerToRelocations; // DWORDPointerToLinenumbers; // WORDNumberOfRelocations; // WORDNumberOfLinenumbers; // DWORDCharacteristics; //*[6]:节属性的标志 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

PE节表详细分析
文章图片

节表数量
节表数量不在节表中,而是在上一篇文章中已经介绍过,在标准PE头中。标准PE头中有一个成员是NumberOfSections他是节表的数量。
PE节表详细分析
文章图片

节表名字
节表名字是各个节表的第一个成员,首先我们根据PE头大小来确定节表所在位置即DOS头+垃圾数据+NT头,下面在010Editor演示具体数据。
节表中第一个数据是节名,节名一共占用的数据大小是8个字节, 演示中第一个节名是.text
PE节表详细分析
文章图片

节表大小
节表中真实的大小在VirtualAddress中,也就是节名后的4个字节。他所代表的意思是对其前的大小也是在内存中的大小。
而在他后4四节之后的4字节数据,代表的是对其后的大小也是文件中的大小SizeOfRawData
PE节表详细分析
文章图片

节位置
节位置都是在节大小之后,占用大小也是为4字节。VirualAddress后的4字节代表的是内存中节偏移SizeOfRawData后的4字节代表的是文件中节位置
PE节表详细分析
文章图片

在内存中锁定具体的节位置需要内存地址+节偏移得到的地址才是真实的内存节所在的位置,而在文件中的节位置则可以根据地址直接索引到,不需要+文件地址因为文件地址开始就是0。
PE节表详细分析
文章图片

内存地址在可选PE头的ImageBase中,所以我们需要先获取到ImageBase的值这里是0x00400000,所以加上偏移位置就是0x00401000
PE节表详细分析
文章图片

PE节表详细分析
文章图片

一般程序在没有被修改的情况下,默认.text段就是程序用来放代码的地方。所以我们也可以用OD直接载入程序,然后跳到.text段的位置可以看到程序的汇编代码。
(小知识:OD载入程序就是模拟了程序拉伸的过程,程序在内存中的样子。)
PE节表详细分析
文章图片

节表属性
节表中的最后一个数据也是最重要的,他代表了这个节是否可读可写可执行或者是否包含可执行代码、初始化、未初始化的数据。所以一般我们判断一个段是否是代码段就是根据这个属性的值来判断的,因为节表名是可以改的,比如我把随便一个节表名改成.text、.code。那你就觉得他是代码段了吗?
代码段的属性一般是0x60000020,其中这4字节的数据,他每一位都对应下面表格中的数据。下面我们把0x60000020来拆分一下,首先最后2位对应下面的包含可执行代码的数据,然后最高位的6对应下面的(该块可执行)+(该块可读)的值。所以代码段的属性就是:(包括可执行代码)、(可执行)、(可读)
--> 标志(属性块) 常用特征值对照表:<-- [值:00000020h](*包含可执行代码) [值:00000040h](*该块包含已初始化的数据) [值:00000080h](*该块包含未初始化的数据) [值:00000200h][Section contains comments or some other type of information.] [值:00000800h][Section contents will not become part of image.] [值:00001000h][Section contents comdat.] [值:00004000h][Reset speculative exceptions handling bits in the TLB entries for this section.] [值:00008000h][Section content can be accessed relative to GP.] [值:00500000h][Default alignment if no others are specified.] [值:01000000h][Section contains extended relocations.] [值:02000000h][Section can be discarded.] [值:04000000h][Section is not cachable.] [值:08000000h][Section is not pageable.] [值:10000000h](*该块为共享块). [值:20000000h](*该块可执行) [值:40000000h](*该块可读) [值:80000000h](*该块可写)

0x02 代码编写 在最后我们还是用代码来写个工具,之前写过解析PE头的数据了,所以继续解析就是节表的数据了。
节表解析整体思路是:
  • (1)、先得到节数量 NumberOfSections
  • (2)、循环次数=节数量
  • (3)、安装节的结构体来解析每个节的数据
  • (4)、输出相应的数据显示到控制台中
vector vsection_header; //解析节表 for (size_t i = 0; i < IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections; i++) { IMAGE_SECTION_HEADER_2 aa; fread(&aa, sizeof(aa), 1, fp); vsection_header.push_back(aa); } //打印节中的数据 printf("---------------节表数据----------------\n"); cout << "节数量:" << IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections << endl; for (size_t i = 0; i < IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections; i++) { printf("--> %s段信息 <--\n", vsection_header[i].Name); SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED); printf("*[内存中段大小]:0x%x\n", vsection_header[i].Misc.VirtualSize); SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_BLUE); printf("*[内存中偏移]:0x%x\n", vsection_header[i].VirtualAddress); SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED); printf("*[文件中段大小]:0x%x\n", vsection_header[i].SizeOfRawData); SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_BLUE); printf("*[文件中偏移]:0x%x\n", vsection_header[i].PointerToRawData); SetConsoleTextAttribute(handle, 0x07); printf("[OBJ重定位偏移]:0x%x\n", vsection_header[i].PointerToRelocations); printf("[OBJ重定位项数目]:0x%x\n", vsection_header[i].NumberOfRelocations); printf("[行号表偏移]:0x%x\n", vsection_header[i].PointerToLinenumbers); printf("[行号表中的数目]:0x%x\n", vsection_header[i].NumberOfLinenumbers); SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED); printf("*[标志|属性]:0x%x ", vsection_header[i].Characteristics); //区段的属性 DWORD l_Charctieristics = (BYTE)((DWORD)(vsection_header[i].Characteristics) & 0xFF); DWORD h_Charctieristics = (BYTE)(((DWORD)(vsection_header[i].Characteristics) >> 24) & 0xFF); vector l_flag; vector h_flag; //低位 l_flag.push_back((l_Charctieristics >> 7) ? 3 : 0); l_flag.push_back((l_Charctieristics >> 6) & 1 ? 2 : 0); l_flag.push_back((l_Charctieristics >> 5) & 1 ? 1 : 0); //高位 h_flag.push_back((h_Charctieristics >> 7) ? 7 : 0); h_flag.push_back((h_Charctieristics >> 6) & 1 ? 6 : 0); h_flag.push_back((h_Charctieristics >> 5) & 1 ? 5 : 0); h_flag.push_back((h_Charctieristics >> 4) & 1 ? 4 : 0); //包含数据情况 SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN); for (vector::iterator iter = l_flag.begin(); iter != l_flag.end(); iter++) { switch (*iter) { case 1: cout << "(包含可执行代码),"; break; case 2: cout << "(包含已初始化数据),"; break; case 3: cout << "(包含未初始化数据),"; break; default: break; } } //可读写执行情况 for (vector::iterator iter = h_flag.begin(); iter != h_flag.end(); iter++) { switch (*iter) { case 4: cout << "(共享),"; break; case 5: cout << "(可执行),"; break; case 6: cout << "(可读),"; break; case 7: cout << "(可写),"; break; default: break; } } cout << endl << endl; ; SetConsoleTextAttribute(handle, 0x07); } SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN); printf("--> 标志(属性块) 常用特征值对照表:<--\n"); printf("[值:00000020h](*包含可执行代码)\n"); //IMAGE_SCN_CNT_CODE printf("[值:00000040h](*该块包含已初始化的数据)\n"); //IMAGE_SCN_CNT_INITIALIZED_DATA printf("[值:00000080h](*该块包含未初始化的数据)\n"); //IMAGE_SCN_CNT_UNINITIALIZED_DATA printf("[值:00000200h][Section contains comments or some other type of information.]\n"); //IMAGE_SCN_LNK_INFO printf("[值:00000800h][Section contents will not become part of image.]\n"); //IMAGE_SCN_LNK_REMOVE printf("[值:00001000h][Section contents comdat.]\n"); //IMAGE_SCN_LNK_COMDAT printf("[值:00004000h][Reset speculative exceptions handling bits in the TLB entries for this section.]\n"); //IMAGE_SCN_NO_DEFER_SPEC_EXC printf("[值:00008000h][Section content can be accessed relative to GP.]\n"); // IMAGE_SCN_GPREL printf("[值:00500000h][Default alignment if no others are specified.]\n"); //IMAGE_SCN_ALIGN_16BYTES printf("[值:01000000h][Section contains extended relocations.]\n"); //IMAGE_SCN_LNK_NRELOC_OVFL printf("[值:02000000h][Section can be discarded.]\n"); //IMAGE_SCN_MEM_DISCARDABLE printf("[值:04000000h][Section is not cachable.]\n"); //IMAGE_SCN_MEM_NOT_CACHED printf("[值:08000000h][Section is not pageable.]\n"); //IMAGE_SCN_MEM_NOT_PAGED printf("[值:10000000h](*该块为共享块).\n"); //IMAGE_SCN_MEM_SHARED printf("[值:20000000h](*该块可执行)\n"); //IMAGE_SCN_MEM_EXECUTE printf("[值:40000000h](*该块可读)\n"); //IMAGE_SCN_MEM_READ printf("[值:80000000h](*该块可写)\n\n"); // IMAGE_SCN_MEM_WRITE SetConsoleTextAttribute(handle, 0x07); //IMAGE_SCN_MEM_WRITE

运行结果:
PE节表详细分析
文章图片

最后欢迎加群:
【PE节表详细分析】PE节表详细分析
文章图片

    推荐阅读