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.添加一个新的节表,节表中的内容依次修改