4

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?

Adam Sznajder
  • 9,108
  • 4
  • 39
  • 60
  • A pointer in 64 bit code will not be 32 bits. I mean DWORD should not work. – drescherjm Dec 10 '13 at 22:56
  • http://stackoverflow.com/questions/4989928/64-bit-windows-api-what-is-the-size-of-a-c-c-dword – drescherjm Dec 10 '13 at 22:59
  • 1
    IMAGE_NT_HEADERS is no longer accurate. Today there's also IMAGE_NT_HEADERS64. Use IMAGE_FILE_HEADER.Machine to know which one you got. Etcetera. – Hans Passant Dec 10 '13 at 23:47
  • Could you please show me how the code with IMAGE_NT_HEADERS64 should look like? If I simply replace the 32bit structure with 64bit one I don't get the desired results. – Adam Sznajder Dec 11 '13 at 20:52

1 Answers1

5

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.

josh poley
  • 7,236
  • 1
  • 25
  • 25
  • How would look then the code for 64bit DLLs with IMAGE_NT_HEADERS64 in your opinion? I wrote some but it still doesn't work as it should. – Adam Sznajder Dec 11 '13 at 20:33
  • 1
    PIMAGE_NT_HEADERS is a typedef for either PIMAGE_NT_HEADERS32 or PIMAGE_NT_HEADERS64 depending on whether _WIN64 is defined. So how is any of this necessary? – jhoffman0x Mar 19 '16 at 00:12
  • 2
    @jhoffman0x Because you aren't necessarily trying to get information about your own image. The above code lets you analyze either 32-bit or 64-bit headers no matter how you build. – SilverbackNet Apr 23 '16 at 00:05
  • @SilverbackNet Thanks, that makes sense. – jhoffman0x Apr 25 '16 at 15:52