学习一下滴水三期,代码参考了一些Github项目以及个人博客,感谢大佬们!
首先判断是否有MZ标志,符合才会进行DOS头的解析
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE){ return 0;}第一部分DOS头对应PIMAGE_DOS_HEADER结构体,其中LONG e_lfanew;表示PE头相对于文件的偏移,使用pFileBuffer开始地址加该值即可得到标准PE头偏移

xxxxxxxxxxpDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;DWORD signatureIndex = (DWORD)pFileBuffer + pDosHeader->e_lfanew;此时得到的signatureIndex应该指向NT开头的Signature标志(DWORD)

xxxxxxxxxxif (*((PDWORD)signatureIndex) != IMAGE_NT_SIGNATURE){ return 0;}参考NT头的定义:从Signature偏移4字节后是标准PE头
xxxxxxxxxxtypedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader;}于是pNTHeader + 4得到PEHeader

xxxxxxxxxxpNTHeader = (PIMAGE_NT_HEADERS)signatureIndex;pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);由于#define IMAGE_SIZEOF_FILE_HEADER 20规定了PE文件头首地址偏移20字节即可到达可选PE头,所以通过pPEHeader + IMAGE_SIZEOF_FILE_HEADER得到可选PE头首地址
下图是可选PE头,目前不做关心,之后的文章再分析学习

xxxxxxxxxxpOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);下一部分是SECTION_HEADER头或者说节表,需要找到可选PE头偏移才能定位节表首地址
可选PE头长度记录在PE头的SizeOfOptionalHeader字段中,以下代码找到节表首地址(结构体数组)
xxxxxxxxxxpSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
以上内容在FileBuffer以及ImageBuffer中不变,可以直接复制
节表之后的内容,参考海哥的图片

ImageBuffer的整体长度已经记录在可选头的SizeOfImage字段,根据该字段分配内存,并使用memset将内存全部初始化为0,根据可选头的SizeOfHeaders字段,从开始地址复制(注意这里的pDosHeader不只复制DOS头,而是指从头开始,DOS头指向pFileBuffer的首地址,内存复制的时候从这里开始)
xxxxxxxxxxpTempImageBuffer = malloc(pOptionHeader->SizeOfImage);if (!pTempImageBuffer){ return 0;}memset(pTempImageBuffer, 0, pOptionHeader->SizeOfImage);memcpy(pTempImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders);复制一个临时指针用于遍历节表(结构体数组)
xxxxxxxxxxPIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;PE头中已有字段NumberOfSections记录节表数组长度,循环遍历。memcpy函数第一个参数为目标,第二个参数为源,第三个参数为复制长度。从PointerToRawData地址开始复制SizeOfRawData长度到目标VirtualAddress地址开始同样长度
xfor (int i = 0; i < pPEHeader->NumberOfSections; i++){
memcpy((void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress), (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData), pTempSectionHeader->SizeOfRawData);
pTempSectionHeader++;}每一份复制之间有很多空出的地方,之前已经初始化为0。复制完一次后pTempSectionHeader结构体指针加一,实际上偏移增加整个结构体大小,正好到达下一节首地址,自动转为PIMAGE_SECTION_HEADER结构
将复制完成的ImageBuffer内存首地址保存到指针pImageBuffer中
xxxxxxxxxx*pImageBuffer = pTempImageBuffer;pTempImageBuffer = NULL;完整代码如下
xxxxxxxxxxDWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* pImageBuffer){ PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_FILE_HEADER pPEHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; PIMAGE_SECTION_HEADER pSectionHeader = NULL; LPVOID pTempImageBuffer = NULL; if (pFileBuffer == NULL) { return 0; } if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) { return 0; } pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; DWORD signatureIndex = (DWORD)pFileBuffer + pDosHeader->e_lfanew; if (*((PDWORD)signatureIndex) != IMAGE_NT_SIGNATURE) { return 0; } pNTHeader = (PIMAGE_NT_HEADERS)signatureIndex; pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); pTempImageBuffer = malloc(pOptionHeader->SizeOfImage); if (!pTempImageBuffer) { return 0; } memset(pTempImageBuffer, 0, pOptionHeader->SizeOfImage); memcpy(pTempImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders); PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader; for (int i = 0; i < pPEHeader->NumberOfSections; i++) { memcpy((void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress), (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData), pTempSectionHeader->SizeOfRawData);
pTempSectionHeader++; } *pImageBuffer = pTempImageBuffer; pTempImageBuffer = NULL; return pOptionHeader->SizeOfImage;}
对于DOS和PE头的复制相同,代码如下
xxxxxxxxxxPIMAGE_DOS_HEADER pDosHeader = NULL;PIMAGE_NT_HEADERS pNTHeader = NULL;PIMAGE_FILE_HEADER pPEHeader = NULL;PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;PIMAGE_SECTION_HEADER pSectionHeader = NULL;
if (pImageBuffer == NULL){ return 0;}if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE){ return 0;}pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;DWORD signatureIndex = (DWORD)pImageBuffer + pDosHeader->e_lfanew;if (*((PDWORD)signatureIndex) != IMAGE_NT_SIGNATURE){ return 0;}pNTHeader = (PIMAGE_NT_HEADERS)signatureIndex;pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);拿到第一个节之前的所有大小(DOS+PE+可选PE+节表+保留)
xxxxxxxxxxsizeOfFile = pOptionHeader->SizeOfHeaders;参考上图,通过每一个节的SizeOfRawData字段计算出所有节大小(for徐娜换)
xxxxxxxxxxfor (DWORD i = 0; i < pPEHeader->NumberOfSections; i++){ sizeOfFile += pSectionHeader[i].SizeOfRawData;}根据计算出的总大小分配内存,初始化内存为0,将节之前固定的部分复制
xxxxxxxxxxpTempNewBuffer = malloc(sizeOfFile);if (!pTempNewBuffer){ return 0;}memset(pTempNewBuffer, 0, sizeOfFile);memcpy(pTempNewBuffer, pDosHeader, pOptionHeader->SizeOfHeaders);创建临时指针遍历节表
xxxxxxxxxxPIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;循环复制,从ImageBuffer的每个节的VirtualAddress复制SizeOfRawData长度的数据,到NewBuffer从PointerToRawData开始相同长度
xxxxxxxxxxfor (int j = 0; j < pPEHeader->NumberOfSections; j++, pTempSectionHeader++){ memcpy((PDWORD)((DWORD)pTempNewBuffer + pTempSectionHeader->PointerToRawData), (PDWORD)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress), pTempSectionHeader->SizeOfRawData);}复制完成并返回
xxxxxxxxxx*pNewBuffer = pTempNewBuffer;pTempNewBuffer = NULL;完整代码如下
xxxxxxxxxxDWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer, OUT LPVOID* pNewBuffer){ PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_FILE_HEADER pPEHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; PIMAGE_SECTION_HEADER pSectionHeader = NULL;
if (pImageBuffer == NULL) { return 0; } if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE) { return 0; } pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer; DWORD signatureIndex = (DWORD)pImageBuffer + pDosHeader->e_lfanew; if (*((PDWORD)signatureIndex) != IMAGE_NT_SIGNATURE) { return 0; } pNTHeader = (PIMAGE_NT_HEADERS)signatureIndex; pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
LPVOID pTempNewBuffer = NULL; DWORD sizeOfFile = 0; DWORD numberOfSection = 0;
sizeOfFile = pOptionHeader->SizeOfHeaders;
for (DWORD i = 0; i < pPEHeader->NumberOfSections; i++) { sizeOfFile += pSectionHeader[i].SizeOfRawData; }
pTempNewBuffer = malloc(sizeOfFile); if (!pTempNewBuffer) { return 0; } memset(pTempNewBuffer, 0, sizeOfFile); memcpy(pTempNewBuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader; for (int j = 0; j < pPEHeader->NumberOfSections; j++, pTempSectionHeader++) { memcpy((PDWORD)((DWORD)pTempNewBuffer + pTempSectionHeader->PointerToRawData), (PDWORD)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress), pTempSectionHeader->SizeOfRawData); }
*pNewBuffer = pTempNewBuffer; pTempNewBuffer = NULL; return sizeOfFile;}