我正在开发一个项目,我想通过在 Windows 上使用 C 将正在运行的 PE(可移植可执行)文件的部分从内存复制到磁盘来创建新的可执行文件。但是,当我尝试运行输出文件时遇到问题,导致“无效访问内存”错误。
这是我的方法:
// current base address
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)hModule;
IMAGE_NT_HEADERS* ntHeaders = (IMAGE_NT_HEADERS*)((BYTE*)hModule + dosHeader->e_lfanew);
IMAGE_FILE_HEADER* fileHeader = &ntHeaders->FileHeader;
IMAGE_OPTIONAL_HEADER* optionalHeader = &ntHeaders->OptionalHeader;
unsigned char* buffer = (unsigned char*)malloc(optionalHeader->SizeOfImage);
memset(buffer, 0, optionalHeader->SizeOfImage);
// Copy the headers
memcpy(buffer, dosHeader, dosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));
// Copy each section
IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)(ntHeaders + 1);
for (int i = 0; i < fileHeader->NumberOfSections; i++) {
memcpy(buffer + sectionHeader[i].VirtualAddress,
(BYTE*)hModule + sectionHeader[i].PointerToRawData,
sectionHeader[i].SizeOfRawData);
}
然后我使用
WriteFile()
API 将其写入磁盘。
PE之后,加载到内存中,它被修改,导入解析,并且
FirstThunk
指向的数据被修改为真实的api地址,当它在磁盘上时必须指向IMAGE_IMPORT_BY_NAME
或等于序数。因此,您必须修复导入的最低限度 - 将 OriginalFirstThunk
的上下文复制到 FirstThunk
。然而搬迁并不是问题。所需要的一切,pinth->OptionalHeader.ImageBase
对应于已经加载并进行重定位的真实图像基础。和窗户自行调整OptionalHeader.ImageBase
到新的正确底座。
导入后PE会出现延迟导入的情况,这个也需要修复,如果已经解决的话。全局变量可以修改,__security_cookie
- 它必须在开始时0x2B992DDFA232
或LdrInitSecurityCookie
失败,等等。我们可以修复几乎所有变量,除了全局变量,我们通常不知道如何修复初始值
演示代码(无范围/错误检查)
#include <delayimp.h>
ULONG GetImageSize(PIMAGE_NT_HEADERS pinth)
{
if (DWORD NumberOfSections = pinth->FileHeader.NumberOfSections)
{
PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinth);
ULONG MaxOffset = 0;
do
{
if (DWORD SizeOfRawData = pish->SizeOfRawData)
{
ULONG u = pish->PointerToRawData + SizeOfRawData;
if (MaxOffset < u)
{
MaxOffset = u;
}
}
} while (pish++, --NumberOfSections);
return MaxOffset;
}
return 0;
}
void copyThunks(PVOID BaseAddress, ULONG OriginalFirstThunk, ULONG FirstThunk)
{
if (OriginalFirstThunk && FirstThunk)
{
union {
PVOID pv;
PULONG_PTR pu;
};
union {
PVOID qv;
PULONG_PTR qu;
};
pv = RtlOffsetToPointer(BaseAddress, OriginalFirstThunk);
qv = RtlOffsetToPointer(BaseAddress, FirstThunk);
ULONGLONG u;
do
{
*qu++ = u = *pu++;
} while (u);
}
}
void FixImport(PVOID BaseAddress)
{
DWORD s;
if (PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)
RtlImageDirectoryEntryToData(BaseAddress, FALSE, IMAGE_DIRECTORY_ENTRY_IMPORT, &s))
{
while (piid->Name)
{
copyThunks(BaseAddress, piid->OriginalFirstThunk, piid->FirstThunk);
piid++;
}
}
}
void FixDelayImport(PVOID BaseAddress)
{
DWORD s;
if (ImgDelayDescr* piid = (ImgDelayDescr*)
RtlImageDirectoryEntryToData(BaseAddress, FALSE, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, &s))
{
while (piid->rvaDLLName)
{
if (dlattrRva & piid->grAttrs)
{
copyThunks(BaseAddress, piid->rvaUnloadIAT, piid->rvaIAT);
}
piid++;
}
}
}
void ImageToLine(PVOID ImageBase, PCWSTR pszFileName)
{
PIMAGE_NT_HEADERS pinth = RtlImageNtHeader(ImageBase);
if (ULONG s = GetImageSize(pinth))
{
if (PVOID BaseAddress = VirtualAlloc(0, s, MEM_COMMIT, PAGE_READWRITE))
{
memcpy(BaseAddress, ImageBase, pinth->OptionalHeader.SizeOfHeaders);
if (DWORD NumberOfSections = pinth->FileHeader.NumberOfSections)
{
PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinth);
do
{
if (DWORD SizeOfRawData = pish->SizeOfRawData)
{
memcpy((PBYTE)BaseAddress + pish->PointerToRawData,
(PBYTE)ImageBase + pish->VirtualAddress,
min(SizeOfRawData, pish->Misc.VirtualSize));
}
} while (pish++, --NumberOfSections);
}
FixImport(BaseAddress);
FixDelayImport(BaseAddress);
ULONG size;
if (PIMAGE_LOAD_CONFIG_DIRECTORY picd = (PIMAGE_LOAD_CONFIG_DIRECTORY)
RtlImageDirectoryEntryToData(BaseAddress, FALSE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &size))
{
if (RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, SecurityCookie) <= size &&
RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, SecurityCookie) <= picd->Size)
{
if (ULONG_PTR* psq = (ULONG_PTR*)RtlAddressInSectionTable(
pinth, BaseAddress, RtlPointerToOffset(ImageBase, picd->SecurityCookie)))
{
*psq = 0x2B992DDFA232;
}
}
}
HANDLE hFile = CreateFileW(pszFileName, FILE_APPEND_DATA, 0, 0, CREATE_ALWAYS, 0, 0);
if (INVALID_HANDLE_VALUE != hFile)
{
WriteFile(hFile, BaseAddress, s, &s, 0);
CloseHandle(hFile);
}
VirtualFree(BaseAddress, 0, MEM_RELEASE);
}
}
}