Question

I'm analyzing 32bit and 64bit DLLs. I would like to find out what are the exported functions' addresses. I already dealt with 32bit DLLs but the same code doesn't work with 64bit modules.

    DWORD address = (*module)->getImageBaseAddress();
    DWORD headerAddress = address + ((PIMAGE_DOS_HEADER)address)->e_lfanew;
    PIMAGE_NT_HEADERS header = (PIMAGE_NT_HEADERS)headerAddress;
    PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY)(address + header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    PVOID names = (BYTE *)address + exports->AddressOfNames;
    PVOID moduleFunctions = (BYTE *)address + exports->AddressOfFunctions;
    std::cout << "Characteristics: " << exports->Characteristics << endl;
    std::cout << "TimeDateStamp: " << exports->TimeDateStamp << endl;
    std::cout << "Major version: " << exports->MajorVersion << endl;
    std::cout << "Minor version: " << exports->MinorVersion << endl;
    std::cout << "Name: " << exports->Name << endl;
    std::cout << "Base: " << exports->Base << endl;
    std::cout << "Number of fun: " << exports->NumberOfFunctions << endl;
    std::cout << "Number of names: " << exports->NumberOfNames << endl;

    for (int i = 0; i < exports->NumberOfFunctions; i++) {
        std::cout << std::string((char*)((BYTE *)address + ((DWORD *)names)[i])) << " @ " << ((DWORD *)moduleFunctions)[i] << endl;
    }

The first output lines look fine (TimeDateStamp has proper value, function names are properly resolved etc.). Unfortunately when I compare my functions' image base offsets with those given by IDA after DLLs file analysis the results differ. E.g. for the first module I get the offset equal to 11d0b where due to IDA no valid instruction starts at this address (imageBase + 0x11d0b).

Is my method of getting the function addresses in 64bit DLLs correct? Why do I get different results? Why everything works fine with 32 bit modules?

Was it helpful?

Solution

The optional header struct is a different size if you are in a 64bit binary. Look at the PE COFF spec for the official reference.

You will need to key off of the "Magic Number" in the optional header (the first field) to identify which format of the structure to use.

Edit: Adding the code snippet which shows choosing the proper header format:

char* address = (*module)->getImageBaseAddress();
char* headerAddress = address + ((PIMAGE_DOS_HEADER)address)->e_lfanew;
PIMAGE_NT_HEADERS32 header32 = (PIMAGE_NT_HEADERS32)headerAddress;
PIMAGE_NT_HEADERS64 header64 = (PIMAGE_NT_HEADERS64)headerAddress;
PIMAGE_EXPORT_DIRECTORY exports = NULL;

if (header32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) // PE32
    exports = (PIMAGE_EXPORT_DIRECTORY)(address + header32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
else if (header32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) // PE32+
    exports = (PIMAGE_EXPORT_DIRECTORY)(address + header64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
else
    return 0;

Also note that if you are trying to do this from an image loaded manually from a file (e.g. a raw mapping of the file), you will need to convert the RVA into a file offset by looking up the memory address in the section listing to identify the file pointer location.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top