1. 节简介
PE文件中所有节的属性都被定义在节表中,节表由一系列的IMAGE_SECTION_HEADER
结构排列而成,每个结构用来描述一个节,结构的排列顺序和它们描述的节在文件中的排列顺序是一致的。全部有效结构的最后以一个空的IMAGE_SECTION_HEADER
结构作为结束,所以节表中IMAGE_SECTION_HEADER
结构数量等于节的数量加一。节表总是被存放在紧接在PE文件头的地方。另外,节表中IMAGE_SECTION_HEADER
结构的总数总是由PE文件头 IMAGE_NT_HEADERS
结构中的FileHeader.NumberOfSections
字段来指定的。
2.1 节表的结构
#define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
2.2解表字段详解
1.Name 8个字节 一般情况下是以"\0"结尾的ASCII吗字符串来标识的名称,内容可以自定义. 注意:该名称并不遵守必须以"\0"结尾的规律,如果不是以"\0"结尾,系统会截取8个字节的长度进行处理. 2.Misc 双字 是该节在没有对齐前的真实尺寸,该值可以不准确。 3.VirtualAddress 节区在内存中的偏移地址。加上ImageBase才是在内存中的真正地址. 4.SizeOfRawData 节在文件中对齐后的尺寸. 5.PointerToRawData 节区在文件中的偏移. 6.PointerToRelocations 在obj文件中使用 对exe无意义 7.PointerToLinenumbers 行号表的位置 调试的时候使用 8.NumberOfRelocations 在obj文件中使用 ,对exe无意义 9.NumberOfLinenumbers 行号表中行号的数量 调试的时候使用 10.Characteristics 节的属性
以上的字段决定了PE文件从硬盘中映射到内存中的映射关系。如下图所示(本图来自滴水逆向教程2015-3-13课堂讲义):
2.3 读取节表的内容c实现
// shellcode_inject.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include<Windows.h> #include<stdlib.h> void * readFile(char * filepath){ //printf(filepath); int filesize=0; FILE *filep=NULL; char *buffer=NULL; filep = fopen(filepath,"rb"); if (filep == NULL){ printf("打开文件失败!"); return NULL; } fseek(filep,0,SEEK_END); filesize = ftell(filep); fseek(filep,0,SEEK_SET); //printf("filesize is %d\n",filesize); buffer = (char *)malloc(sizeof(char)*filesize); if(buffer == NULL){ printf("内存分配失败!"); fclose(filep); return NULL; } memset(buffer,0,filesize); size_t n = fread(buffer,1,filesize,filep); if(!n){ printf("读取数据失败!"); fclose(filep); free(buffer); return NULL; } fclose(filep); return buffer; } void readPEHeader(char *filebuffer){ //声明所要用到的变量 PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; IMAGE_FILE_HEADER PEHeader; PIMAGE_OPTIONAL_HEADER64 pOptionHeader; PIMAGE_SECTION_HEADER pSectionHeader = NULL; //获取DOS头 printf("*********DOS头输出:**********\n"); pDosHeader = (PIMAGE_DOS_HEADER)filebuffer; printf("MZ标志:%x\n",(*pDosHeader).e_magic); printf("NT头相对于文件初始位置的偏移:%x\n",(*pDosHeader).e_lfanew); printf("*********DOS头输出:**********\n"); //获取NP头 printf("*********NT头输出:**********\n"); pNTHeader = PIMAGE_NT_HEADERS(filebuffer + (*pDosHeader).e_lfanew); printf("PE标记:%x\n",(*pNTHeader).Signature); printf("*********NT头输出:**********\n"); //获取标准PE头 printf("*********标准PE头输出:**********\n"); PEHeader = (*pNTHeader).FileHeader; printf("Machine:%x\n",PEHeader.Machine); printf("节的数量为:0x%x\n",PEHeader.NumberOfSections); printf("可选PE头的大小:0x%x\n",PEHeader.SizeOfOptionalHeader); printf("*********标准PE头输出:**********\n"); //获取可选PE头,我这里用的是64位的notepad.exe所以使用这个PIMAGE_OPTIONAL_HEADER64 pOptionHeader = PIMAGE_OPTIONAL_HEADER64(filebuffer + (*pDosHeader).e_lfanew + sizeof((*pNTHeader).Signature)+sizeof((*pNTHeader).FileHeader)); printf("*********可选头输出:**********\n"); printf("Magic:0x%x\n",(*pOptionHeader).Magic); printf("*********可选PE头输出:**********\n"); //free(filebuffer);//释放内存 //获取节表内容 int numOfSection = PEHeader.NumberOfSections; printf("节表的数量为:0x%x\n",numOfSection); pSectionHeader = (PIMAGE_SECTION_HEADER)(filebuffer+(*pDosHeader).e_lfanew+sizeof((*pNTHeader).Signature)+sizeof((*pNTHeader).FileHeader)+PEHeader.SizeOfOptionalHeader); printf("节表的偏移为:0x%x\n",(*pDosHeader).e_lfanew+sizeof((*pNTHeader).Signature)+sizeof((*pNTHeader).FileHeader)+PEHeader.SizeOfOptionalHeader); printf("第一个节表的名称为:%s\n",(*pSectionHeader).Name); } int main(int argc, char* argv[]) { char *filepath = "C:\\Users\\crls\\Desktop\\VC6.0green\\MyProjects\\PEReader\\Debug\\notepad.exe"; char *filebuffer = (char *)readFile(filepath); readPEHeader(filebuffer); return 0; }
2.在PE文件中新增一个节
为了在PE文件中新增一个节需要进行以下几步的操作:
- 1.判断节表后方的剩余空间是否足够添加一个节表
- 2.修改节表数量
- 3.修改sizeofimage
- 4.添加一个新的节表,节表中的内容依次修改