2020年5月28日 00:12:46
PEToolkit.cpp
#include "headers.h"
// 读取PE文件到内存中,返回读取的字节数;读取失败返回0
DWORD ReadPEFile(LPCSTR lpszFile, LPVOID *pFileBuffer)
{
FILE *pFile = NULL;
DWORD dwFileSize = 0;
pFile = fopen(lpszFile, "rb");
if (pFile == NULL)
{
printf("打开文件失败\n");
return 0;
}
fseek(pFile, 0, SEEK_END);
dwFileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
*pFileBuffer = malloc(dwFileSize);
if (*pFileBuffer == NULL)
{
printf("分配内存失败\n");
fclose(pFile);
return 0;
}
DWORD dwRead = fread(*pFileBuffer, 1, dwFileSize, pFile);
fclose(pFile);
if (dwRead != dwFileSize)
{
printf("文件大小 = %d\t实际写入内存字节 = %d\t写入失败\n", dwFileSize, dwRead);
return 0;
}
if (!IsPEFile(*pFileBuffer, dwRead))
{
printf("不是有效的PE文件\n");
return 0;
}
return dwRead;
}
// 验证是否PE文件
BOOL IsPEFile(LPVOID pFileBuffer, DWORD dwSize)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = \
(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE)
{
printf("不是有效的MZ标志\n");
return FALSE;
}
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标记\n");
return FALSE;
}
return TRUE;
}
// 打印PE头信息
VOID PrintNTHeaders(LPCSTR lpszFile)
{
LPVOID pFileBuffer = NULL;
DWORD dwFileSize = ReadPEFile(lpszFile, &pFileBuffer);
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = \
(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
if (dwFileSize == 0)
{
printf("读取文件失败\n");
return;
}
//打印DOS头
puts("----DOS HEADER----");
printf("e_magic = %x\n", pDosHeader->e_magic);
printf("e_lfanew = %x\n", pDosHeader->e_lfanew);
//打印NT头
printf("----标准PE头----\n");
printf("Machine = %x\n", pPEHeader->Machine);
printf("NumberOfSections = %x\n", pPEHeader->NumberOfSections);
printf("TimeDateStamp = %x\n", pPEHeader->TimeDateStamp);
printf("PointerToSymbolTable = %x\n", pPEHeader->PointerToSymbolTable);
printf("NumberOfSymbols = %x\n", pPEHeader->NumberOfSymbols);
printf("SizeOfOptionalHeader = %x\n", pPEHeader->SizeOfOptionalHeader);
printf("Characteristics = %x\n", pPEHeader->Characteristics);
//可选PE头
printf("----可选PE头----\n");
printf("Magic = %x\n", pOptionHeader->Magic);
printf("MajorLinkerVersion = %x\n", pOptionHeader->MajorLinkerVersion);
printf("MinorLinkerVersion = %x\n", pOptionHeader->MinorLinkerVersion);
printf("SizeOfCode = %x\n", pOptionHeader->SizeOfCode);
printf("SizeOfInitializedData = %x\n", pOptionHeader->SizeOfInitializedData);
printf("SizeOfUninitializedData = %x\n", pOptionHeader->SizeOfUninitializedData);
printf("AddressOfEntryPoint = %x\n", pOptionHeader->AddressOfEntryPoint);
printf("BaseOfCode = %x\n", pOptionHeader->BaseOfCode);
printf("BaseOfData = %x\n", pOptionHeader->BaseOfData);
printf("ImageBase = %x\n", pOptionHeader->ImageBase);
printf("SectionAlignment = %x\n", pOptionHeader->SectionAlignment);
printf("FileAlignment = %x\n", pOptionHeader->FileAlignment);
printf("MajorOperatingSystemVersion = %x\n", pOptionHeader->MajorOperatingSystemVersion);
printf("MinorOperatingSystemVersion = %x\n", pOptionHeader->MinorOperatingSystemVersion);
printf("MajorImageVersion = %x\n", pOptionHeader->MajorImageVersion);
printf("MinorImageVersion = %x\n", pOptionHeader->MinorImageVersion);
printf("MajorSubsystemVersion = %x\n", pOptionHeader->MajorSubsystemVersion);
printf("MinorSubsystemVersion = %x\n", pOptionHeader->MinorSubsystemVersion);
printf("Win32VersionValue = %x\n", pOptionHeader->Win32VersionValue);
printf("SizeOfImage = %x\n", pOptionHeader->SizeOfImage);
printf("SizeOfHeaders = %x\n", pOptionHeader->SizeOfHeaders);
printf("CheckSum = %x\n", pOptionHeader->CheckSum);
printf("Subsystem = %x\n", pOptionHeader->Subsystem);
printf("DllCharacteristics = %x\n", pOptionHeader->DllCharacteristics);
printf("SizeOfStackReserve = %x\n", pOptionHeader->SizeOfStackReserve);
printf("SizeOfStackCommit = %x\n", pOptionHeader->SizeOfStackCommit);
printf("SizeOfHeapReserve = %x\n", pOptionHeader->SizeOfHeapReserve);
printf("SizeOfHeapCommit = %x\n", pOptionHeader->SizeOfHeapCommit);
printf("LoaderFlags = %x\n", pOptionHeader->LoaderFlags);
printf("NumberOfRvaAndSizes = %x\n", pOptionHeader->NumberOfRvaAndSizes);
//打印节表
char sectionName[9];
for (int i = 0; i < pPEHeader->NumberOfSections; i++)
{
memset(sectionName, 0, 9);
memcpy(sectionName, pSectionHeader->Name, 8);
printf("----节表: [%s]----\n", sectionName);
printf("VirtualSize = %x\n", pSectionHeader->Misc.VirtualSize);
printf("VirtualAddress = %x\n", pSectionHeader->VirtualAddress);
printf("SizeOfRawData = %x\n", pSectionHeader->SizeOfRawData);
printf("PointerToRawData = %x\n", pSectionHeader->PointerToRawData);
printf("Characteristics = %x\n", pSectionHeader->Characteristics);
pSectionHeader++;
}
//释放内存
free(pFileBuffer);
}
// 将 FileBuffer 拉伸成 ImageBuffer 并写入到新的缓冲区
// 返回 ImageBuffer 的大小;失败返回0
DWORD CopyFileBufferToImageBuffer(LPVOID pFileBuffer, LPVOID *pImageBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = \
(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
*pImageBuffer = malloc(pOptionHeader->SizeOfImage);
if (*pImageBuffer == NULL)
{
printf("分配内存失败\n");
return 0;
}
memset(*pImageBuffer, 0, pOptionHeader->SizeOfImage);
// 复制DOS头+PE头+可选PE头+节表+文件对齐
memcpy(*pImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders);
// 遍历节表,复制所有节
for (int i = 0; i < pPEHeader->NumberOfSections; i++)
{
memcpy((LPVOID)((DWORD)(*pImageBuffer) + pSectionHeader[i].VirtualAddress), \
(LPVOID)((DWORD)pFileBuffer + pSectionHeader[i].PointerToRawData), \
pSectionHeader[i].SizeOfRawData);
}
return pOptionHeader->SizeOfImage;
}
// 将 ImageBuffer 变成文件对齐的 FileBuffer 写入新的缓冲区
// 返回复制的大小,失败返回0
DWORD CopyImageBufferToFileBuffer(LPVOID pImageBuffer, LPVOID *pNewBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = \
(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
// 最后一个节表
PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pPEHeader->NumberOfSections - 1;
// 计算要复制的字节
// 这一步有BUG,当最后一个节后面还有数据时(多见于控制台程序),丢失数据
DWORD dwFileBufferSize = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
*pNewBuffer = malloc(dwFileBufferSize);
if (*pNewBuffer == NULL)
{
printf("分配内存失败\n");
return 0;
}
memset(*pNewBuffer, 0, dwFileBufferSize);
// 复制DOS头+PE头+可选PE头+节表+文件对齐
memcpy(*pNewBuffer, pImageBuffer, pOptionHeader->SizeOfHeaders);
// 遍历节表,复制文件对齐后的节
for (int i = 0; i < pPEHeader->NumberOfSections; i++)
{
memcpy((LPVOID)((DWORD)(*pNewBuffer) + pSectionHeader[i].PointerToRawData), \
(LPVOID)((DWORD)pImageBuffer + pSectionHeader[i].VirtualAddress), \
pSectionHeader[i].SizeOfRawData);
}
return dwFileBufferSize;
}
// 内存数据写入文件
BOOL MemoryToFile(LPVOID pMemBuffer, DWORD dwSize, LPCSTR lpszFile)
{
FILE *fp = NULL;
fp = fopen(lpszFile, "wb+");
if (fp == NULL)
{
printf("打开文件失败\n");
return FALSE;
}
DWORD dwWritten = fwrite(pMemBuffer, 1, dwSize, fp);
if (dwWritten != dwSize)
{
printf("写入了 %d 字节,不等于 %d\n", dwWritten, dwSize);
fclose(fp);
return FALSE;
}
fclose(fp);
return TRUE;
}
// 向代码节添加MessageBox代码
// 向代码节添加代码不需要担心内存对齐后大小发生变化
// 默认第一个节是代码节,但是这样判断不一定准确,应该遍历节表,根据属性找代码节
BOOL AddCodeToCodeSec(LPCSTR lpszFile, LPCSTR lpszOutFile)
{
BYTE shellcode[] =
{
0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, // push 0 push 0 push 0 push 0
0xE8, 0x00, 0x00, 0x00, 0x00, // call MessageBoxA
0xE9, 0x00, 0x00, 0x00, 0x00 // jmp OEP
};
DWORD dwShellCodeSize = 18;
DWORD dwCodeRva = 0; // 插入的位置RVA
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
DWORD dwFileBufferSize = 0;
DWORD dwImageBufferSize = 0;
DWORD dwNewBufferSize = 0;
// 读取PE到内存中
if ((dwFileBufferSize = ReadPEFile(lpszFile, &pFileBuffer)) == 0)
{
printf("读取失败\n");
return FALSE;
}
// 拉伸成内存映像
dwImageBufferSize = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
if (0 == dwImageBufferSize)
{
free(pFileBuffer);
return FALSE;
}
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4