admin 管理员组文章数量: 887019
这章学习的主要内容是PE结构以及用C语言编写PE结构解析器。
首先学习PE结构:
E_lfanew:文件的偏移0x3C(第60字节)处是是一个4字节的PE文件签名的偏移地址。
参数的末尾也就是文件的偏移0x3C(第60字节)
//注释掉的不需要重点分析
1、DOS头:共40H(64字节)
struct _IMAGE_DOS_HEADER{
0X00 WORD e_magic; //※Magic DOS signature MZ(4Dh 5Ah):MZ标记:用于标记是否是可执行文件
//0X02 WORD e_cblp; //Bytes on last page of file
//0X04 WORD e_cp; //Pages in file
//0X06 WORD e_crlc; //Relocations
//0X08 WORD e_cparhdr; //Size of header in paragraphs
//0X0A WORD e_minalloc; //Minimun extra paragraphs needs
//0X0C WORD e_maxalloc; //Maximun extra paragraphs needs
//0X0E WORD e_ss; //intial(relative)SS value
//0X10 WORD e_sp; //intial SP value
//0X12 WORD e_csum; //Checksum
//0X14 WORD e_ip; //intial IP value
//0X16 WORD e_cs; //intial(relative)CS value
//0X18 WORD e_lfarlc; //File Address of relocation table
//0X1A WORD e_ovno; //Overlay number
//0x1C WORD e_res[4]; //Reserved words
//0x24 WORD e_oemid; //OEM identifier(for e_oeminfo)
//0x26 WORD e_oeminfo; //OEM information;e_oemid specific
//0x28 WORD e_res2[10]; //Reserved words
0x3C DWORD e_lfanew; //※Offset to start of PE header:定位PE文件,PE头相对于文件的偏移量
};
64字节(共4行)的DOS头,第一个成员2个字节是可执行文件的标志信息;最后一个成员4字节是PE头的偏移地址为00000100H,我们可以根据00000100H来获取PE头的地址。
2、PE头:
//NT头
//pNTHeader = dosHeader + dosHeader->e_lfanew;
struct _IMAGE_NT_HEADERS{
0x00 DWORD Signature; //PE文件标识:ASCII的"PE\0\0"
0x04 _IMAGE_FILE_HEADER FileHeader;//0x04是偏移量。标准PE头
0x18 _IMAGE_OPTIONAL_HEADER OptionalHeader;//0x18是偏移量,可变PE头
};
NT头的第一个成员是”PE\0\0”
//标准PE头:最基础的文件信息,共20字节
struct _IMAGE_FILE_HEADER{
0x00 WORD Machine; //※程序执行的CPU平台:0X0:任何平台,0X14C:intel i386及后续处理器,标识CPU的数字,也就是机器数
0x02 WORD NumberOfSections; //※节的数目。Windows加载器限制节的最大数目为96。
0x04 DWORD TimeDateStamp; //时间戳:它指出文件何时被创建。 //0x08 DWORD PointerToSymbolTable; //COFF符号表格的偏移位置。此字段只对COFF除错信息有用
//0x0c DWORD NumberOfSymbols; //COFF符号表格中的符号个数。该值和上一个值在release版本的程序里为0
//0x10 WORD SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER结构的大小(字节数):32位默认E0H,64位默认F0H(可修改)
0x12 WORD Characteristics; //※描述文件属性,eg:
//单属性(只有1bit为1):#define IMAGE_FILE_DLL 0x2000 //File is a DLL.
//组合属性(多个bit为1,单属性或运算):0X010F 可执行文件
};
首先四字节是NT第一个签名成员“PE\0\0”。接着便是2字节的CPU平台信息:014C,即X86平台(其它平台可见下图):
然后是区块数量0008H即8个块;
第三个成员为时间戳:56BCE029H(即十进制1455218729),转换成北京时间为2016-02-12 03:25:29(时间戳在线转换);
第四个和第五个成员各占4字节且均为0;
第六个成员占2字节为默认值E0H(即可选PE头的大小为224字节,other.exe中为X64默认值F0H);
最后一个属性成员占2字节为“818F”,该成员是按bit位来看的,属性值也是多个属性的组合(或运算)。
//可选PE头
struct _IMAGE_OPTIONAL_HEADER{
0x00 WORD Magic; //※幻数(魔数),0x0107:ROM image,0x010B:32位PE,0X020B:64位PE
//0x02 BYTE MajorLinkerVersion; //连接器主版本号
//0x03 BYTE MinorLinkerVersion; //连接器副版本号
0x04 DWORD SizeOfCode; //所有代码段的总和大小,注意:必须是FileAlignment的整数倍,存在但没用
0x08 DWORD SizeOfInitializedData; //已经初始化数据的大小,注意:必须是FileAlignment的整数倍,存在但没用
0x0c DWORD SizeOfUninitializedData; //未经初始化数据的大小,注意:必须是FileAlignment的整数倍,存在但没用
0x10 DWORD AddressOfEntryPoint; //※程序入口地址OEP,这是一个RVA(Relative Virtual Address),通常会落在.textsection,此字段对于DLLs/EXEs都适用。
0x14 DWORD BaseOfCode; //代码段起始地址(代码基址),(代码的开始和程序无必然联系)
0x18 DWORD BaseOfData; //数据段起始地址(数据基址)
0x1c DWORD ImageBase; //※内存镜像基址(默认装入起始地址),默认为4000H
0x20 DWORD SectionAlignment; //※内存对齐:一旦映像到内存中,每一个section保证从一个「此值之倍数」的虚拟地址开始
0x24 DWORD FileAlignment; //※文件对齐:最初是200H,现在是1000H
//0x28 WORD MajorOperatingSystemVersion; //所需操作系统主版本号
//0x2a WORD MinorOperatingSystemVersion; //所需操作系统副版本号
//0x2c WORD MajorImageVersion; //自定义主版本号,使用连接器的参数设置,eg:LINK /VERSION:2.0 myobj.obj
//0x2e WORD MinorImageVersion; //自定义副版本号,使用连接器的参数设置
//0x30 WORD MajorSubsystemVersion; //所需子系统主版本号,典型数值4.0(Windows 4.0/即Windows 95)
//0x32 WORD MinorSubsystemVersion; //所需子系统副版本号
//0x34 DWORD Win32VersionValue; //总是0
0x38 DWORD SizeOfImage; //※PE文件在内存中映像总大小,sizeof(ImageBuffer),SectionAlignment的倍数
0x3c DWORD SizeOfHeaders; //※DOS头(64B)+PE标记(4B)+标准PE头(20B)+可选PE头+节表的总大小,按照文件对齐(FileAlignment的倍数)
0x40 DWORD CheckSum; //PE文件CRC校验和,判断文件是否被修改
//0x44 WORD Subsystem; //用户界面使用的子系统类型
//0x46 WORD DllCharacteristics; //总是0
0x48 DWORD SizeOfStackReserve; //默认线程初始化栈的保留大小
0x4c DWORD SizeOfStackCommit; //初始化时实际提交的线程栈大小
0x50 DWORD SizeOfHeapReserve; //默认保留给初始化的process heap的虚拟内存大小
0x54 DWORD SizeOfHeapCommit; //初始化时实际提交的process heap大小
//0x58 DWORD LoaderFlags; //总是0
0x5c DWORD NumberOfRvaAndSizes; //目录项数目:总为0X00000010H(16)
0x60 _IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
};
第1个成员(Magic,2Byte):幻数010B,表示该文件为32位PE,其它情况如下:
第4个成员(SizeOfCode,4Byte):代码段总大小为000FF800H;
第5个成员(SizeOfInitializedData,4Byte):已初始化数据大小为00039400H;
第6个成员(SizeOfUninitializedData,4Byte):未初始化数据大小为0,即均已初始化;
第7个成员(AddressOfEntryPoint,4Byte):程序入口地址OEP=0010056CH;
第8个成员(BaseOfCode,4Byte):代码段基址 = 00001000H;
第9个成员(BaseOfData,4Byte):数据段基址 = 00101000H;
第10个成员(ImageBase,4Byte):内存镜像基址 = 00400000H,这是一个默认的值;
第11个成员(SectionAlignment,4Byte):内存对齐 = 00001000H,即4096字节;
第12个成员(FileAlignment,4Byte):文件对齐 = 00000200H,即512字节(文件对齐和内存对齐的目的是提高效率);
第20个成员(SizeOfImage,4字节):PE映像在内存中总大小 = 00145000H,是SectionAlignment的325倍(整数倍);
第21个成员(SizeOfHeaders,4字节):所有头+节表总大小 = 00000400H;
第22个成员(CheckSum,4字节):PE文件CRC校验和 = 0014173FH
第25个成员(SizeOfStackReserve,4字节):为线程初始栈保留虚拟内存的默认值 = 00100000H = 1MB;
第26个成员(SizeOfStackCommit,4字节):为线程的初始栈提交的实际虚拟内存大小 = 00004000H = 16KB;
第27个成员(SizeOfHeapReserve,4字节):为进程的初始堆保留虚拟内存的默认值 = 00100000H = 1MB;
第28个成员(SizeOfHeapCommit,4字节):为进程的初始堆提交的实际虚拟内存大小 = 00001000H = 4KB;
第30个成员(NumberOfRvaAndSizes,4字节):目录项总数默认 = 00000010H = 16个。
1、Section Table结构解析:
Section Table(节表)是记录PE文件中各个节的详细信息的集合,其每个成员是struct _IMAGE_SECTION_HEADER结构体,即节表是一个结构体数组来维护,属于线性结构。而节表的相对起始位置为:紧接着可选PE表。即:DOS头 + 中间空闲及垃圾数据 + NT头(三部分:4字节签名+标准PE头20字节+可选PE头)。
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER{
0X00 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //节(段)的名字.text/.data/.rdata/.cmd等。
//由于长度固定8字节,所以可以没有\0结束符,因此不能用char *直接打印
0X08 union{
DWORD PhysicalAddress; //物理地址
DWORD VirtualSize; //虚拟大小
}Misc;//存储的是该节在没有对齐前的真实尺寸,可改,不一定准确(可干掉)
0X0C DWORD VirtualAddress; //块的RVA,相对虚拟地址
0X10 DWORD SizeOfRawData; //该节在文件对齐后的尺寸大小(FileAlignment的整数倍)
0X14 DWORD PointerToRawData; //节区在文件中的偏移量
//0X18 DWORD PointerToRelocations; //重定位偏移(obj中使用)
//0X1C DWORD PointerToLinenumbers; //行号表偏移(调试用)
//0X20 WORD NumberOfRelocations; //重定位项目数(obj中使用)
//0X22 WORD NumberOfLinenumbers; //行号表中行号的数目
0X24 DWORD Characteristics; //节属性(按bit位设置属性)
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
#define IMAGE_SIZEOF_SECTION_HEADER 40
一个结构体大小为40字节(IMAGE_SIZEOF_SECTION_HEADER),因此节表的大小为节表元素个数乘以结构体大小为:_IMAGE_FILE_HEADER.NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER。而整个头大小(包含节表)为:
SizeOfHeaders = SectionTableStart + SizeOfSectionTable =
_IMAGE_DOS_HEADER.e_lfanew +
IMAGE_SIZEOF_SIGNATURE +
IMAGE_SIZEOF_FILE_HEADER +
_IMAGE_FILE_HEADER.SizeOfOptionalHeader +
(_IMAGE_FILE_HEADER.NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER)
SectionTableStart = 100H + 4H + 14H + E0H = 0000 01F8H。即节表起始偏移地址为:0000 01F8H
我们在标准PE头**_IMAGE_FILE_HEADER中获取的NumberOfSections** 大小为08H,所以总共有八个节(Sections),节表中也有8个结构体元素,每个占用40字节。 SizeOfHeaders = SectionTableStart + SizeOfSectionTable = 0000 01F8H + (40*8)(D) = 0000 01F8H + 140H = 338H。
版权声明:本文标题:网络安全学习笔记(6) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1726378977h948575.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论