5.PE文件之节表(IMAGE_SECTION_HEADER)

PE头IMAGE_NT_HEADERS后紧跟着节表(也可以理解为IMAGE_OPTIONAL_HEADER后为节表).它由许多个节表项(IMAGE_SECTION_HEADER)组成,每个节表项记录了PE中与某个特定的节有关的信息,如节的属性、节的大小、节在文件和内存中的起始位置等.节表中节的数量由IMAGE_FILE_HEADER.NumberOfSection来定义.

IMAGE_SECTION_HEADER

结构及成员含义如下:

#define IMAGE_SIZEOF_SECTION_HEADER          40 
#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
  BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];  //节表名称.大小8字节.一般情况下是以"\0"结尾的ASCII码字符串.内容可自定义.
  union {
    DWORD   PhysicalAddress;         //联合体.
    DWORD   VirtualSize;             //节在内存中按照内存对齐前的大小(该值可以不准确).
  } Misc;
  DWORD   VirtualAddress;            //(RVA)节在内存中的偏移地址.加上ImageBase才是在内存中的地址.
  DWORD   SizeOfRawData;             //节在文件中按照文件对齐后的大小.
  DWORD   PointerToRawData;          //(FOA)节在文件中的偏移地址.
  DWORD   PointerToRelocations;      //.OBJ文件中使用,指向重定位表的指针.
  DWORD   PointerToLinenumbers;      //行号表的偏移,提供调试.
  WORD    NumberOfRelocations;       //.OBJ文件中使用,重定位项数目.
  WORD    NumberOfLinenumbers;       //行号表中行号的数量.
  DWORD   Characteristics;           //节的属性.
} IMAGE_SECTION_HEADER, * PIMAGE_SECTION_HEADER;

通过WinHex定位第一个节表(文件中状态,第一个节表位于扩展PE头后):

Name: 节表名称.大小8字节.一般情况下是以"\0"结尾的ASCII码字符串.如果不是以"\0"结尾,系统会截取8个字节的长度进行处理.内容可自定义.

常见名及含义:

.text指令代码
.data初始化的数据
.idata导入表
.rsrc资源数据
.reloc基地址重定位表
.edata导出表
.tlsthread local storage,线程局部存储器
.rdata存放调试目录和说明字符串

Misc.VirtualSize: 节在内存中实际的大小(单位为字节).默认为编译器填充,可以修改不影响程序正常使用.该值可能大于SizeOfRawData.(如果在文件状态下增加shellcode需要注意此值,当它大于文件中节区大小时将会出错).

VirtualAddress: 该值为RVA(Relative Virtual Address)相对虚拟地址,必须是SectionAlignment的整数倍.节在内存中的偏移地址.加上ImageBase才是在内存中的地址.

SizeOfRawData: 节在文件中按照文件对齐后的大小(单位为字节).必须是FileAlignment的整数倍.

PointerToRawData: 该值为FOA(File Offset Address)文件偏移地址,必须是FileAlignment的整数倍.节在文件中的偏移地址.

该节磁盘中与内存中对应地址以及数据如下:

按照对应节表解析,文件中与内存中分布如下:

Characteristics: 节的属性,每一位都有不同含义,如下表所示:

数据位常量符号常量值为1时含义
5IMAGE_SCN_CNT_CODE0x00000020节中包含代码
6IMAGE_SCN_CNT_INITIALIZED_DATA0x00000040节中包含已初始化数据
7IMAGE_SCN_CNT_UNINITIALIZED_DATA0x00000080节中包含未初始化数据
8IMAGE_SCN_LNK_OTHER0x00000100保留供将来使用
25IMAGE_SCN_MEM_DISCARDABLE0x02000000节中的数据在进程开始以后会被丢弃,如.reloc
26IMAGE_SCN_MEM_NOT_CACHED0x04000000节中的数据不会经过缓存
27IMAGE_SCN_MEM_NOT_PAGED0x08000000节中的数据不会被交换到磁盘
28IMAGE_SCN_MEM_SHARED0x10000000表示节中的数据将被不同的进程所共享
29IMAGE_SCN_MEM_EXECUTE0x20000000映射到内存后的页面包含了可执行属性
30IMAGE_SCN_MEM_READ0x40000000映射到内存后的页面包含了可读属性
31IMAGE_SCN_MEM_WRITE0x80000000映射到内存后的页面包含了可写属性

将0x60000020转换为二进制后:0110 0000 0000 0000 0000 0000 0010 0000. 下标5,29,30,为1代表该节属性为IMAGE_SCN_CNT_CODE,IMAGE_SCN_MEM_EXECUTE,IMAGE_SCN_MEM_READ.

代码节的属性一般为60000020h,也就是可执行、可读和节中包含代码;

数据节的属性一般为C0000040h,也就是可读、可写和包含已初始化数据;

常量节的属性一般为40000040h,也就是可读和包含已初始化数据;

代码定位IMAGE_SECTION_HEADER如下:

读取文件代码:

PVOID FileToMem(IN PCHAR szFilePath, OUT LPDWORD dwFileSize)
{
	//打开文件
	FILE* pFile = fopen(szFilePath, "rb");
	if (!pFile)
	{
		printf("FileToMem fopen Fail \r\n");
		return NULL;
	}
 
	//获取文件长度
	fseek(pFile, 0, SEEK_END);			//SEEK_END文件结尾
	DWORD Size = ftell(pFile);
	fseek(pFile, 0, SEEK_SET);			//SEEK_SET文件开头
 
	//申请存储文件数据缓冲区
	PCHAR pFileBuffer = (PCHAR)malloc(Size);
	if (!pFileBuffer)
	{
		printf("FileToMem malloc Fail \r\n");
		fclose(pFile);
		return NULL;
	}
 
	//读取文件数据
	fread(pFileBuffer, Size, 1, pFile);
 
	//判断是否为可执行文件
	if (*(PSHORT)pFileBuffer != IMAGE_DOS_SIGNATURE)
	{
		printf("Error: MZ \r\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}
 
	if (*(PDWORD)(pFileBuffer + *(PDWORD)(pFileBuffer + 0x3C)) != IMAGE_NT_SIGNATURE)
	{
		printf("Error: PE \r\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}
 
	if (dwFileSize)
	{
		*dwFileSize = Size;
	}
 
	fclose(pFile);
 
	return pFileBuffer;
}

读取节表数据代码:

VOID PrintSecHeader()
{
	//读取文件二进制数据
	DWORD dwFileSize = 0;
	PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
	if (!pFileBuffer)
	{
		return;
	}

	PIMAGE_DOS_HEADER        pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
	PIMAGE_NT_HEADERS        pNth = (PIMAGE_NT_HEADERS)(pFileBuffer + pDos->e_lfanew);
	PIMAGE_FILE_HEADER		 pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
	PIMAGE_OPTIONAL_HEADER   pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
	PIMAGE_SECTION_HEADER    pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);

	CHAR szSecName[9] = { 0 };
	for (size_t i = 0; i < pFil->NumberOfSections; i++)
	{
		memset(szSecName, 0, 9);
		memcpy(szSecName, pSec[i].Name, IMAGE_SIZEOF_SHORT_NAME);
		printf("IMAGE_SECTION_HEADER[%d] ->Name [%s] \r\n", i, szSecName);
		printf("IMAGE_SECTION_HEADER[%d] ->VirtualSize [0x%08x] \r\n", i, pSec[i].Misc.VirtualSize);
		printf("IMAGE_SECTION_HEADER[%d] ->VirtualAddress [0x%08x] \r\n", i, pSec[i].VirtualAddress);
		printf("IMAGE_SECTION_HEADER[%d] ->SizeOfRawData [0x%08x] \r\n", i, pSec[i].SizeOfRawData);
		printf("IMAGE_SECTION_HEADER[%d] ->PointerToRawData [0x%08x] \r\n", i, pSec[i].PointerToRawData);
		printf("IMAGE_SECTION_HEADER[%d] ->PointerToRelocations [0x%08x] \r\n", i, pSec[i].PointerToRelocations);
		printf("IMAGE_SECTION_HEADER[%d] ->PointerToLinenumbers [0x%08x] \r\n", i, pSec[i].PointerToLinenumbers);
		printf("IMAGE_SECTION_HEADER[%d] ->NumberOfRelocations [0x%04x] \r\n", i, pSec[i].NumberOfRelocations);
		printf("IMAGE_SECTION_HEADER[%d] ->NumberOfLinenumbers [0x%04x] \r\n", i, pSec[i].NumberOfLinenumbers);
		printf("IMAGE_SECTION_HEADER[%d] ->Characteristics [0x%08x] \r\n", i, pSec[i].Characteristics);

	}
}

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: image_section_header是指PE文件中的一个数据结构,用于描述可执行文件中的一个节(section)的属性和位置信息。它包含了节的名称、大小、起始位置、属性等信息,是PE文件格式中非常重要的一个部分。 ### 回答2: image_section_headerPE文件中的一个数据结构,用于描述每个节区(Section)的属性和信息。它的作用是让操作系统、可执行文件加载器和调试器等工具能够正确地理解和处理PE文件中的各个节区。 image_section_header结构包含多个字段,其中一些重要的字段包括: 1. Name:节区的名称,一般是一个8字节的字符串,用于标识节区的含义,如代码段、数据段等。 2. VirtualSize:虚拟内存中的节区大小,即在内存中占用的空间大小。 3. VirtualAddress:节区在虚拟内存中的起始地址。 4. SizeOfRawData:节区在文件中的大小,即占用的磁盘空间大小。 5. PointerToRawData:节区在文件中的偏移量,指示该节区的数据在文件的什么位置。 6. Characteristics:节区的特征标志,用于描述该节区的属性,如是否可执行、可写、可读等。 通过image_section_header,操作系统可以根据文件中的各个节区的位置和大小来正确加载PE文件的内存布局,保证程序能够正确运行。可执行文件加载器可以根据节区的属性来设置对应的内存区域的访问权限,如只读、读写等。调试器可以根据节区的信息来正确解析可执行文件,定位和调试程序中的错误。 总之,image_section_headerPE文件中重要的数据结构之一,它提供了关于节区的各种属性和信息,让操作系统、可执行文件加载器和调试器能够正确地解析和处理PE文件,保证程序的正确运行和调试。 ### 回答3: image_section_header是一种数据结构,用于描述可执行文件或者DLL文件中的节(section)的属性和信息。 它通常包含以下字段: 1. 节名称(Name):指定该节的名称,如.text、.data等。 2. 节大小(VirtualSize):指定该节在内存中的大小。 3. 节的虚拟地址(VirtualAddress):指定该节在进程的虚拟地址空间中的起始地址。 4. 节的数据地址(PointerToRawData):指定该节在文件中的起始位置的偏移量。 5. 节的数据大小(SizeOfRawData):指定该节在文件中的大小。 6. 节的属性(Characteristics):通常是一个标志位的集合,用于描述该节的特性,如可执行代码、只读数据、已初始化数据等。 image_section_header的作用是让操作系统和加载器能够准确地定位和加载可执行文件或者DLL文件中的节。通过解析image_section_header中的字段,操作系统和加载器可以了解每个节的大小、位置以及属性等信息,从而正确地将节映射到进程的虚拟地址空间中。 在运行时,程序可以通过访问image_section_header中的字段来获取和修改节的属性和信息。例如,可以根据节的名称来定位和修改特定节的内容,也可以通过修改节的属性来改变节的行为,如修改代码节的属性为只读,从而防止恶意代码的注入。 总之,image_section_header是一种重要的数据结构,用于描述可执行文件或DLL文件中的节的属性和信息,为操作系统和加载器提供了必要的信息以正确地加载和执行可执行文件或者DLL文件中的各个节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值