Home > database >  Manually loading PE imports results error when parsing PIMAGE_IMPORT_DESCRIPTOR
Manually loading PE imports results error when parsing PIMAGE_IMPORT_DESCRIPTOR

Time:07-03

Greetings overflowers,

Almost completed my associates degree in cybersecurity. Trying to manually load imports from a dll that's in byte code. The virtual address and size of PIMAGE_DATA_DIRECTORY shows the correct values. After parsing PIMAGE_IMPORT_DESCRIPTOR, the values don't get parsed correctly.

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: X\n", importDir->VirtualAddress);
    printf("              Size: X\n", importDir->Size);
    printf("OriginalFirstThunk: X\n", iid->OriginalFirstThunk);
    printf("     TimeDateStamp: X\n", iid->TimeDateStamp);
    printf("    ForwarderChain: X\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

CodePudding user response:

In PE Format, the data of each section has two different address. The first is the File Offset in the file. You can use this address to access the section data in the file when the file is not mapped that is, when PE is in disk. The second is the RVA (Relative Virtual Address). This address should be used when the OS mapped the PE file into memory.

In your case, you just copy the Dll to a buffer in memory and don't map the section datas to the specified RVAs. Therefore, you cannot work with RVAs. Instead, you should use the file offsets.

An utility function to convert the RVAs to File Offsets can be written as the following.

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
}

Here is a test program.

#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: X\n", lpImportDirectoryEntry->VirtualAddress);
    printf("              Size: X\n", lpImportDirectoryEntry->Size);
    while (lpImportDescriptor->FirstThunk)
    { 
        char* szDllName = baseAddress   RvaToFileOffset(baseAddress, lpImportDescriptor->Name);
        printf("DLL Name: %s\n", szDllName);
        printf("OriginalFirstThunk: X\n", lpImportDescriptor->OriginalFirstThunk);
        printf("     TimeDateStamp: X\n", lpImportDescriptor->TimeDateStamp);
        printf("    ForwarderChain: X\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
}
  • Related