是否可以将有效的PE文件从内存写入磁盘?

问题描述 投票:0回答:1

我正在开发一个项目,我想通过在 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 将其写入磁盘。

c windows winapi portable-executable
1个回答
0
投票

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);
        }
    }
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.