说明
一些常见的PE操作。。
2021年3月7日
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif // !_CRT_SECURE_NO_WARNINGS
#include "PE.h"
#include <stdio.h>
#include <WINDOWS.H>
#include <STRING.h>
#include <MALLOC.H>
#include "DebugTool.h"
#include "Win32API.h"
// 读取文件到内存中,返回读取的字节数;读取失败返回0
DWORD FileToMemory(LPCSTR lpszFile, LPVOID *pFileBuffer)
{
// 打开文件,权限是读共享
HANDLE hFile = pCreateFileA(lpszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
PrintDebugInfo("打开文件失败\n");
return 0;
}
// 判断文件大小
LARGE_INTEGER nFileSize = {
0 };
pGetFileSizeEx(hFile, &nFileSize);
if (nFileSize.QuadPart >= 0xFFFFFFFFull)
{
PrintDebugInfo("源文件过大,无法处理(%lld)\n", nFileSize.QuadPart);
pCloseHandle(hFile);
return 0;
}
pSetFilePointer(hFile, 0, NULL, FILE_BEGIN);
//PrintDebugInfo("文件大小:%d 字节\n", nFileSize.LowPart);
LPVOID pFile = pVirtualAlloc(NULL, nFileSize.LowPart, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pFile == NULL)
{
PrintDebugInfo("申请内存失败\n");
pCloseHandle(hFile);
return 0;
}
memset(pFile, 0, nFileSize.LowPart);
// 读文件
DWORD dwRead = 0;
BOOL bRet = FALSE;
bRet = pReadFile(hFile, pFile, nFileSize.LowPart, &dwRead, NULL);
if (bRet == FALSE)
{
PrintDebugInfo("读取文件错误:%d\n", GetLastError());
pVirtualFree(pFile, 0, MEM_RELEASE);
pCloseHandle(hFile);
return 0;
}
if (dwRead != nFileSize.LowPart)
{
PrintDebugInfo("期望读取:%d,实际读取:%d\n", nFileSize.LowPart, dwRead);
pVirtualFree(pFile, 0, MEM_RELEASE);
pCloseHandle(hFile);
return 0;
}
pCloseHandle(hFile);
*pFileBuffer = pFile;
return nFileSize.LowPart;
}
// 验证是否是合法的32位PE文件
BOOL Is32PEFile(LPVOID pFileBuffer, DWORD dwSize)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
PrintDebugInfo("不是有效的MZ标志 %x != %x", pDosHeader->e_magic, IMAGE_DOS_SIGNATURE);
return FALSE;
}
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
{
PrintDebugInfo("不是有效的PE标记");
return FALSE;
}
if (pNtHeader->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
{
PrintDebugInfo("不是32位PE程序");
return FALSE;
}
return TRUE;
}
// 将 FileBuffer 拉伸成 ImageBuffer 并写入到新的缓冲区
// 返回 ImageBuffer 的大小;失败返回0
DWORD FileBufferToImageBuffer(LPVOID pFileBuffer, LPVOID *pImageBuffer, DWORD flProtect)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(
(DWORD)pNtHeader +
sizeof(pNtHeader->Signature) +
sizeof(pNtHeader->FileHeader) +
pNtHeader->FileHeader.SizeOfOptionalHeader);
*pImageBuffer = pVirtualAlloc(NULL, pNtHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, flProtect);
if (*pImageBuffer == NULL)
{
PrintDebugInfo("分配内存失败\n");
return 0;
}
memset(*pImageBuffer, 0, pNtHeader->OptionalHeader.SizeOfImage);
// 复制所有头(DOS头,PE头,可选PE头)和节表
memcpy(*pImageBuffer, pFileBuffer, pNtHeader->OptionalHeader.SizeOfHeaders);
// 遍历节表,复制所有节
for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++)
{
memcpy((PBYTE)(*pImageBuffer) + pSectionHeader[i].VirtualAddress,
(PBYTE)pFileBuffer + pSectionHeader[i].PointerToRawData,
pSectionHeader[i].SizeOfRawData);
}
return pNtHeader->OptionalHeader.SizeOfImage;
}
// 将 ImageBuffer 变成文件对齐的 FileBuffer 写入新的缓冲区
// 返回复制的大小,失败返回0
DWORD ImageBufferToFileBuffer(LPVOID pImageBuffer, LPVOID *pFileBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(
(DWORD)pNtHeader +
sizeof(pNtHeader->Signature) +
sizeof(pNtHeader->FileHeader) +
pNtHeader->FileHeader.SizeOfOptionalHeader);
// 最后一个节表
PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pNtHeader->FileHeader.NumberOfSections - 1;
// 计算要复制的字节
// 这一步有BUG,当最后一个节后面还有数据时,这些数据会丢失
DWORD dwFileBufferSize = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
*pFileBuffer = pVirtualAlloc(NULL, dwFileBufferSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (*pFileBuffer == NULL)
{
PrintDebugInfo("分配内存失败\n");
return 0;
}
memset(*pFileBuffer, 0, dwFileBufferSize);
// 复制所有头(DOS头,PE头,可选PE头)和节表
memcpy(*pFileBuffer, pImageBuffer, pNtHeader->OptionalHeader.SizeOfHeaders);
// 遍历节表,复制所有节
for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++)
{
memcpy((PBYTE)(*pFileBuffer) + pSectionHeader[i].PointerToRawData,
(PBYTE)pImageBuffer + pSectionHeader[i].VirtualAddress,
pSectionHeader[i].SizeOfRawData);
}
return dwFileBufferSize;
}
// 内存数据写入文件
BOOL MemoryToFile(LPVOID pMemBuffer, DWORD dwSize, LPCSTR lpszFile)
{
// 打开文件,权限是独占写
HANDLE hFile = pCreateFileA(lpszFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
PrintDebugInfo("打开文件失败\n");
return FALSE;
}
BOOL bRet = FALSE;
DWORD dwWritten = 0;
bRet = pWriteFile(hFile, pMemBuffer, dwSize, &dwWritten, NULL);
if (!bRet)
{
PrintDebugInfo("写文件错误:%d\n", GetLastError());
pCloseHandle(hFile);
return FALSE;
}
pCloseHandle(hFile);
return TRUE;
}
// 计算对齐的函数,如偏移为900,对齐为1000h,返回1000h
DWORD Align(DWORD dwOffset, DWORD dwAlign)
{
// 如果偏移小于对齐,向上取整
if (dwOffset <= dwAlign) return dwAlign;
// 如果偏移大于对齐且不能除尽,向上取整
if (dwOffset % dwAlign)
{
return (dwOffset / dwAlign + 1) * dwAlign;
}
// 如果能除尽,直接返回offset
return dwOffset;
}
// RVA 转 FOA
DWORD RvaToFoa(LPVOID pFileBuffer, DWORD dwRva)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(
(DWORD)pNtHeader +
sizeof(pNtHeader->Signature) +
sizeof(pNtHeader->FileHeader) +
pNtHeader->FileHeader.SizeOfOptionalHeader);
if (dwRva < pNtHeader->OptionalHeader.SizeOfHeaders)
{
return dwRva;
}
// 遍历节表,确定偏移属于哪一个节
for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++)
{
if (dwRva >= pSectionHeader[i].VirtualAddress && \
dwRva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize)
{