c - 解析PIMAGE_IMPORT_DESCRIPTOR时手动加载PE导入结果错误

标签 c import dll dllimport portable-executable

问候溢出者,

我即将完成网络安全副学士学位。尝试从字节码形式的 dll 中手动加载导入。 PIMAGE_DATA_DIRECTORY 的虚拟地址和大小显示正确的值。解析 PIMAGE_IMPORT_DESCRIPTOR 后,这些值无法正确解析。

PIMAGE_NT_HEADERS32 ntHeader = (PIMAGE_NT_HEADERS32)((DWORD)dllFile32 + ((PIMAGE_DOS_HEADER)(DWORD)dllFile32)->e_lfanew);
PIMAGE_DATA_DIRECTORY importDir = &NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];

if (importDir->VirtualAddress > 0 && importDir->Size > sizeof(IMAGE_IMPORT_DESCRIPTOR))
{
    PIMAGE_IMPORT_DESCRIPTOR iid = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)dllFile32 + importDir->VirtualAddress);
    printf("    VirtualAddress: %08X\n", importDir->VirtualAddress);
    printf("              Size: %08X\n", importDir->Size);
    printf("OriginalFirstThunk: %08X\n", iid->OriginalFirstThunk);
    printf("     TimeDateStamp: %08X\n", iid->TimeDateStamp);
    printf("    ForwarderChain: %08X\n", iid->ForwarderChain);
}
    VirtualAddress: 0000507C - CORRECT
              Size: 0000003C - CORRECT
OriginalFirstThunk: 00000000 - INCORRECT
     TimeDateStamp: 56413F2E - INCORRECT
    ForwarderChain: 74737973 - INCORRECT

enter image description here

enter image description here

最佳答案

在PE格式中,每个段的数据有两个不同的地址。第一个是文件中的文件偏移。当文件没有映射时,即PE在磁盘上时,可以使用该地址访问文件中的节数据。第二个是RVA(相对虚拟地址)。当操作系统将PE文件映射到内存时,应该使用这个地址。

在您的情况下,您只需将 Dll 复制到内存中的缓冲区,而不将节数据映射到指定的 RVA。因此,您无法使用 RVA。相反,您应该使用文件偏移量。

RVA转换为文件偏移的实用函数可以编写如下。

UINT RvaToFileOffset(BYTE* baseAddress, UINT rva) {
    PIMAGE_NT_HEADERS lpNtHeaders = ImageNtHeader(baseAddress);
    PIMAGE_FILE_HEADER lpFileHeader = &lpNtHeaders->FileHeader;
    BYTE* lpOptionalHeader = &lpNtHeaders->OptionalHeader;
    PIMAGE_SECTION_HEADER lpSections = (PIMAGE_SECTION_HEADER)(lpOptionalHeader + lpFileHeader->SizeOfOptionalHeader);

    for (int i = 0; i < lpFileHeader->NumberOfSections; ++i)
    {
        IMAGE_SECTION_HEADER section = lpSections[i];
        if (rva >= section.VirtualAddress && rva <= section.VirtualAddress + section.Misc.VirtualSize)
            return section.PointerToRawData + rva - section.VirtualAddress;
    }
    return -1; // Invalid RVA
}

这是一个测试程序。

#include <stdio.h> 
#include <windows.h>
#include <ImageHlp.h>

#pragma comment(lib,"imagehlp.lib")

// Helper function to be used for converting RVAs to File Offsets.
UINT RvaToFileOffset(BYTE * base, UINT rva);

int main(int argc, char* argv[]) {
    PIMAGE_DOS_HEADER lpDosHeader;
    PIMAGE_NT_HEADERS lpNtHeaders;
    PIMAGE_OPTIONAL_HEADER lpOptionalHeader;
    PIMAGE_IMPORT_DESCRIPTOR lpImportDescriptor; 

    if (argc != 2)
    {
        printf("You didn't specified a PE file.\n");
        printf("Usage: ImportsParser.exe <Full path of PE File>\n");
        return -1;
    }
    HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return -1;
    HANDLE hMemoryMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (!hMemoryMap)
        return -2;
    PBYTE baseAddress = (PBYTE)MapViewOfFile(hMemoryMap, FILE_MAP_READ, 0, 0, 0);
    if (!baseAddress)
        return -3;
    lpDosHeader = (PIMAGE_DOS_HEADER)baseAddress;
    lpNtHeaders = (PIMAGE_NT_HEADERS)(baseAddress + lpDosHeader->e_lfanew);
    lpOptionalHeader = (PIMAGE_OPTIONAL_HEADER)(&(lpNtHeaders->OptionalHeader));
    PIMAGE_DATA_DIRECTORY lpImportDirectoryEntry = &lpOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    lpImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(baseAddress +
        RvaToFileOffset(baseAddress, lpImportDirectoryEntry->VirtualAddress));
    
    printf("    VirtualAddress: %08X\n", lpImportDirectoryEntry->VirtualAddress);
    printf("              Size: %08X\n", lpImportDirectoryEntry->Size);
    while (lpImportDescriptor->FirstThunk)
    { 
        char* szDllName = baseAddress + RvaToFileOffset(baseAddress, lpImportDescriptor->Name);
        printf("DLL Name: %s\n", szDllName);
        printf("OriginalFirstThunk: %08X\n", lpImportDescriptor->OriginalFirstThunk);
        printf("     TimeDateStamp: %08X\n", lpImportDescriptor->TimeDateStamp);
        printf("    ForwarderChain: %08X\n", lpImportDescriptor->ForwarderChain); 
        printf("\n");
        lpImportDescriptor++;
    }
    getchar();
    return 0;
}
UINT RvaToFileOffset(BYTE * baseAddress, UINT rva) {
    PIMAGE_NT_HEADERS lpNtHeaders = ImageNtHeader(baseAddress);
    PIMAGE_FILE_HEADER lpFileHeader = &lpNtHeaders->FileHeader;
    BYTE* lpOptionalHeader = &lpNtHeaders->OptionalHeader;
    PIMAGE_SECTION_HEADER lpSections = (PIMAGE_SECTION_HEADER)(lpOptionalHeader + lpFileHeader->SizeOfOptionalHeader);

    for (int i = 0; i < lpFileHeader->NumberOfSections; ++i)
    {
        IMAGE_SECTION_HEADER section = lpSections[i];
        if (rva >= section.VirtualAddress && rva <= section.VirtualAddress + section.Misc.VirtualSize)
            return section.PointerToRawData + rva - section.VirtualAddress;
    }
    return -1; // Invalid RVA
}

关于c - 解析PIMAGE_IMPORT_DESCRIPTOR时手动加载PE导入结果错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72839654/

相关文章:

c++ - 这个独特的字符串函数的执行时间是否从天真的 O(n^2) 方法中减少了?

c - 在 c99 中使用 open_memstream

c - #define TEST_CASE(...) 是什么意思

java - 将项目添加到 Java 构建路径

.net - 是否有可以直接编辑 .NET dll 的工具?

c - 如何仅包含 header 中的定义

javascript - 如何在 node.js 中包含默认模型作为引用名称?

mysql日期显示为0000 :00:00 after importing from CSV

c# - 错误: “Unable to find the DLL xyz.dll!” Visual Studio 2010

visual-studio - 如何在 Visual Studio 中设置 DLL 文件的路径?