PE学习(1) FileBuffer与ImageBuffer转换

学习一下滴水三期,代码参考了一些Github项目以及个人博客,感谢大佬们!

 

从FileBuffer到ImageBuffer

首先判断是否有MZ标志,符合才会进行DOS头的解析

第一部分DOS头对应PIMAGE_DOS_HEADER结构体,其中LONG e_lfanew;表示PE头相对于文件的偏移,使用pFileBuffer开始地址加该值即可得到标准PE头偏移

此时得到的signatureIndex应该指向NT开头的Signature标志(DWORD)

参考NT头的定义:从Signature偏移4字节后是标准PE

于是pNTHeader + 4得到PEHeader

由于#define IMAGE_SIZEOF_FILE_HEADER 20规定了PE文件头首地址偏移20字节即可到达可选PE头,所以通过pPEHeader + IMAGE_SIZEOF_FILE_HEADER得到可选PE头首地址

下图是可选PE头,目前不做关心,之后的文章再分析学习

下一部分是SECTION_HEADER头或者说节表,需要找到可选PE头偏移才能定位节表首地址

可选PE头长度记录在PE头的SizeOfOptionalHeader字段中,以下代码找到节表首地址(结构体数组)

以上内容在FileBuffer以及ImageBuffer中不变,可以直接复制

节表之后的内容,参考海哥的图片

ImageBuffer的整体长度已经记录在可选头的SizeOfImage字段,根据该字段分配内存,并使用memset将内存全部初始化为0,根据可选头的SizeOfHeaders字段,从开始地址复制(注意这里的pDosHeader不只复制DOS头,而是指从头开始,DOS头指向pFileBuffer的首地址,内存复制的时候从这里开始)

复制一个临时指针用于遍历节表(结构体数组)

PE头中已有字段NumberOfSections记录节表数组长度,循环遍历。memcpy函数第一个参数为目标,第二个参数为源,第三个参数为复制长度。从PointerToRawData地址开始复制SizeOfRawData长度到目标VirtualAddress地址开始同样长度

每一份复制之间有很多空出的地方,之前已经初始化为0。复制完一次后pTempSectionHeader结构体指针加一,实际上偏移增加整个结构体大小,正好到达下一节首地址,自动转为PIMAGE_SECTION_HEADER结构

将复制完成的ImageBuffer内存首地址保存到指针pImageBuffer

完整代码如下

 

从ImageBuffer到FileBuffer

对于DOS和PE头的复制相同,代码如下

拿到第一个节之前的所有大小(DOS+PE+可选PE+节表+保留)

参考上图,通过每一个节的SizeOfRawData字段计算出所有节大小(for徐娜换)

根据计算出的总大小分配内存,初始化内存为0,将节之前固定的部分复制

创建临时指针遍历节表

循环复制,从ImageBuffer的每个节的VirtualAddress复制SizeOfRawData长度的数据,到NewBufferPointerToRawData开始相同长度

复制完成并返回

完整代码如下