上一篇文章我们说到PE头解析,我们这次继续解析节表
先学习一个新的类型【节表中需要用到】
联合体:
在C语言中,变量的定义是分配存储空间的过程。一般的,每个变量都具有其独有的存储空间,那么可不可以在同一个内存空间中存储不同的数据类型(不是同时存储)呢?使用联合体就可以达到这样的目的。
这样定义:
union 联合名
{
成员表
};
联合体和结构体的区别:
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
联合体的特点:
union TestUnion union
{ {
char x; char x;
int y; int y;
}; };TextUnion
这两个有什么区别?
节表的位置就挨着可选PE头的,所以只要从FileBuffer的起始位置加上几个头的大小,就是节表的位置.一个PE文件中,节表的数量可能不一样.各个节表是紧挨着的.以下是Visual C++ 6.0中winnt.h中定义的节表结构:
#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; //该节在ImageBuffer中的偏移
DWORD SizeOfRawData; //该节在文件中对齐后的大小
DWORD PointerToRawData; //该节在在文件中的偏移
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; //该节的属性(比如是否可读,是否可写...)
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
解释:
2.Misc:该节在没有对齐前的真实尺寸(没有文件对齐和内存对齐后),该值可以修改【红色A到B】
3.VirtualAddress :节区在内存中的偏移地址。加上ImageBase才是在内存中的真正地址.【蓝色的部分】
4.SizeOfRawData : 节在文件中对齐后的尺寸.【整个绿色的部分】
5.PointerToRawData :节区在文件中的偏移.【黄色到绿色的部分】
6.Characteristics: 节的属性(可读、可写、可执行)他代表了这个节是否可读、可写、可执行或者是否包含可执行代码、初始化、未初始化的数据。所以一般我们判断一个段是否是代码段就是根据这个属性的值来判断的,因为节表名是可以改的,比如我把随便一个节表名改成.text、.code。那你就觉得他是代码段了吗?】
--> 标志(属性块) 常用特征值对照表:<--
[值: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](*该块可写)
作业:编写一个解析节表的程序
那么既然要解析肯定要知道他的位置和个数---->
确定节表的数量:由NumberOfSections决定
位置:内存开始地址(FileBuffer是从零开始,ImageBuffer从imagebase开始)+ifanew偏移+4(DWORD Signature也就PE标识)+20(标准PE头的大小)+sizeofoptionalheader
注:DOS头和NT头之间有一段垃圾数据,所以不能直接(DOS+NT+FILE+OPTIONAL)
#include "stdafx.h"
#include
#include
#include
#include
LPVOID ReadPE() //读取文件函数
{
FILE *pFile=NULL;
DWORD size=0;
LPVOID pFileBuffer=NULL;
//open
pFile=fopen("C://WINDOWS//system32//notepad.exe","rb");
if(!pFile)
{
printf("打开文件失败\n");
return 0;
}
//size
fseek(pFile,0,SEEK_END);
size=ftell(pFile);
fseek(pFile,0,SEEK_SET);
if(!size)
{
printf("文件大小获取失败\n");
fclose(pFile);
return 0;
}
//分配内存
pFileBuffer=malloc(size);
if(!pFileBuffer)
{
printf("分配内存失败\n");
fclose(pFile);
return 0;
}
//写数据
size_t n=fread(pFileBuffer,size,1,pFile);
if(!n)
{
printf("文件写入失败\n");
free(pFileBuffer);
fclose(pFile);
return 0;
}
fclose(pFile);
return pFileBuffer;
}
int main(int argc, char* argv[])
{
LPVOID pFileBuffer;
PIMAGE_DOS_HEADER pDosHeader=NULL;
PIMAGE_NT_HEADERS pNTHeader=NULL;
PIMAGE_FILE_HEADER pPEHeader=NULL;
PIMAGE_OPTIONAL_HEADER pOptionalHeader=NULL;
PIMAGE_SECTION_HEADER pSectionHeader=NULL;
WORD NumberOfSection=0; //定义节的数量
DWORD Lfanew=0; //定义偏移
WORD SizeOfOptionalHeader=0; //定义可选头的大小
//调用文件读取函数
pFileBuffer=ReadPE();
if(!pFileBuffer)
{
printf("读取失败\n");
return 0;
}
//节表遍历
//确定节表的个数
//确定节表位置
pDosHeader=(PIMAGE_DOS_HEADER)pFileBuffer;
Lfanew=pDosHeader->e_lfanew; //得到偏移量
pNTHeader=(PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader=(PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4);
NumberOfSection=pPEHeader->NumberOfSections; //得到节的数量
SizeOfOptionalHeader=pPEHeader->SizeOfOptionalHeader;
pSectionHeader=(PIMAGE_SECTION_HEADER)((DWORD)pFileBuffer+Lfanew+4+20+SizeOfOptionalHeader); //得到节表的起始位置
for(NumberOfSection;NumberOfSection>0;NumberOfSection--,pSectionHeader++)//循环的次数和NumberOfSection对应,pSectionHeader为节表结构体指针,所以++加的是节表头结构体大小
{
printf("Name:%x\n",pSectionHeader->Name);
printf("Misc:%x\n",pSectionHeader->Misc);
printf("VirtualAddress:%x\n",pSectionHeader->VirtualAddress);
printf("SizeOfRawData:%x\n",pSectionHeader->SizeOfRawData);
printf("PointerToRawData:%x\n",pSectionHeader->PointerToRawData);
printf("Characteristics:%x\n",pSectionHeader->Characteristics);
printf("----------------------------------------------------\n");
}
free(pFileBuffer);
return 0;
}
效果如下: