学习一下滴水三期,代码参考了一些Github
项目以及个人博客,感谢大佬们!
首先判断是否有MZ
标志,符合才会进行DOS
头的解析
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
return 0;
}
第一部分DOS头对应PIMAGE_DOS_HEADER
结构体,其中LONG e_lfanew;
表示PE头相对于文件的偏移,使用pFileBuffer
开始地址加该值即可得到标准PE
头偏移
xxxxxxxxxx
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
DWORD signatureIndex = (DWORD)pFileBuffer + pDosHeader->e_lfanew;
此时得到的signatureIndex
应该指向NT
开头的Signature
标志(DWORD)
xxxxxxxxxx
if (*((PDWORD)signatureIndex) != IMAGE_NT_SIGNATURE)
{
return 0;
}
参考NT
头的定义:从Signature
偏移4字节后是标准PE
头
xxxxxxxxxx
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
}
于是pNTHeader + 4
得到PEHeader
xxxxxxxxxx
pNTHeader = (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头,目前不做关心,之后的文章再分析学习
xxxxxxxxxx
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
下一部分是SECTION_HEADER
头或者说节表,需要找到可选PE头偏移才能定位节表首地址
可选PE头长度记录在PE头的SizeOfOptionalHeader
字段中,以下代码找到节表首地址(结构体数组)
xxxxxxxxxx
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
以上内容在FileBuffer
以及ImageBuffer
中不变,可以直接复制
节表之后的内容,参考海哥的图片
ImageBuffer
的整体长度已经记录在可选头的SizeOfImage
字段,根据该字段分配内存,并使用memset
将内存全部初始化为0,根据可选头的SizeOfHeaders
字段,从开始地址复制(注意这里的pDosHeader
不只复制DOS头,而是指从头开始,DOS头指向pFileBuffer
的首地址,内存复制的时候从这里开始)
xxxxxxxxxx
pTempImageBuffer = malloc(pOptionHeader->SizeOfImage);
if (!pTempImageBuffer)
{
return 0;
}
memset(pTempImageBuffer, 0, pOptionHeader->SizeOfImage);
memcpy(pTempImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
复制一个临时指针用于遍历节表(结构体数组)
xxxxxxxxxx
PIMAGE_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;
完整代码如下
xxxxxxxxxx
DWORD 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头的复制相同,代码如下
xxxxxxxxxx
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);
拿到第一个节之前的所有大小(DOS+PE+可选PE+节表+保留)
xxxxxxxxxx
sizeOfFile = pOptionHeader->SizeOfHeaders;
参考上图,通过每一个节的SizeOfRawData
字段计算出所有节大小(for徐娜换)
xxxxxxxxxx
for (DWORD i = 0; i < pPEHeader->NumberOfSections; i++)
{
sizeOfFile += pSectionHeader[i].SizeOfRawData;
}
根据计算出的总大小分配内存,初始化内存为0,将节之前固定的部分复制
xxxxxxxxxx
pTempNewBuffer = malloc(sizeOfFile);
if (!pTempNewBuffer)
{
return 0;
}
memset(pTempNewBuffer, 0, sizeOfFile);
memcpy(pTempNewBuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
创建临时指针遍历节表
xxxxxxxxxx
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
循环复制,从ImageBuffer
的每个节的VirtualAddress
复制SizeOfRawData
长度的数据,到NewBuffer
从PointerToRawData
开始相同长度
xxxxxxxxxx
for (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;
完整代码如下
xxxxxxxxxx
DWORD 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;
}