On the road of hiding… PEB, PE format handling and DLL loading homemade APIs – part 2

Here I go on with my discussion about the custom APIs I’m writing. As the GetProcAddress seems to work, I think it’s time to write a post about it. It hasn’t been difficult to implement this, but not so easy at the same time (nothing compared to the on-going LoadLibrary anyway). In doing this I had to document on the Portable Executable (from now on called PE) format used by Microsoft to describe executables, object code and DLLs. As far as I remember there is only one really good article on this, written in the nineties by Matt Pietrek, called “Peering Inside the PE: A Tour of the Win32 Portable Executable File Format“. This guide should be printed by whoever is going to approach to analyze the executables or making some nasty stuff with it; it’s a really ground breaking guide I advice (without that, I could forget to write those APIs). Explaining all the necessary stuff to understand the PE format would be too long to be posted here, so I’ll assume you’ve read (or at least faking about doin’ it) Matt’s article.

Anyway, the original GetProcAddress has a simple prototype:

FARPROC WINAPI GetProcAddress(HMODULE hModule, LPCSTR lpProcName);

It accepts an hModule parameter pointing to the handle retrieved by GetModuleHandle or LoadLibrary and the name of the function/variable looked for. The lpProcName can be a number too and in this case the function is retrieved by its ordinal value.

OK. Fire it up. First thing, I modified the prototype because I really want no string inside my application, so I pass an hash value of the name of the procedure; I added a new parameter for the ordinal importing, just to not get confused between hash values and ordinal numbers. The hash function I’ve chosen and modified a very simple one from this site, called Shift-Add-XOR hash. I modified this function to be case-insensitive, for some reasons I am going to explain later:

unsigned int hash_uppercase(const char *string)
    unsigned int hash = 0;
    char *p = (char *)string;

    while (*p != NULL)
        hash ^= (hash << 5) + (hash >> 2) + ((*p >= 'a' && *p <= 'z') ? *p - 0x20 : *p);

    return hash;

There are four main structures (and some other secondary ones) used in this article (and in the following ones in this series): IMAGE_DOS_HEADER, IMAGE_NT_HEADERS, IMAGE_OPTIONAL_HEADER and IMAGE_EXPORT_DIRECTORYR. The first structure I used is the IMAGE_DOS_HEADER:

typedef struct _IMAGE_DOS_HEADER
    WORD e_magic;
    WORD e_cblp;
    WORD e_cp;
    WORD e_crlc;
    WORD e_cparhdr;
    WORD e_minalloc;
    WORD e_maxalloc;
    WORD e_ss;
    WORD e_sp;
    WORD e_csum;
    WORD e_ip;
    WORD e_cs;
    WORD e_lfarlc;
    WORD e_ovno;
    WORD e_res[4];
    WORD e_oemid;
    WORD e_oeminfo;
    WORD e_res2[10];
    LONG e_lfanew;

The hModule points to this structure indeed. First of all, a good check that hModule points to a valid PE image should be done. That’s why I check that dosHeader->e_magic == IMAGE_DOS_SIGNATURE (which is the “MZ” signature). If the test is passed, it’s a good point for the image. Next step is obtaining the IMAGE_NT_HEADERS structure by adding the offset specified in the e_lfanew to the dosHeader pointer, as specified by Matt (all the addresses specified in the PE file are relative addresses to the base address where the module has been loaded):

pNTHeader = (PIMAGE_NT_HEADERS)((LONG)dosHeader + dosHeader->e_lfanew);

The structure obtained is:

typedef struct _IMAGE_NT_HEADERS
    DWORD Signature;

I’m not posting the IMAGE_FILE_HEADER structure for now, as it is not used here, but, here’s the IMAGE_OPTIONAL_HEADER:

    WORD Magic;
    BYTE MajorLinkerVersion;
    BYTE MinorLinkerVersion;
    DWORD SizeOfCode;
    DWORD SizeOfInitializedData;
    DWORD SizeOfUninitializedData;
    DWORD AddressOfEntryPoint;
    DWORD BaseOfCode;
    DWORD BaseOfData;
    DWORD ImageBase;
    DWORD SectionAlignment;
    DWORD FileAlignment;
    WORD MajorOperatingSystemVersion;
    WORD MinorOperatingSystemVersion;
    WORD MajorImageVersion;
    WORD MinorImageVersion;
    WORD MajorSubsystemVersion;
    WORD MinorSubsystemVersion;
    DWORD Win32VersionValue;
    DWORD SizeOfImage;
    DWORD SizeOfHeaders;
    DWORD CheckSum;
    WORD Subsystem;
    WORD DllCharacteristics;
    DWORD SizeOfStackReserve;
    DWORD SizeOfStackCommit;
    DWORD SizeOfHeapReserve;
    DWORD SizeOfHeapCommit;
    DWORD LoaderFlags;
    DWORD NumberOfRvaAndSizes;

Another good check is made by ensuring that pNTHeader->OptionalHeader.NumberOfRvaAndSizes == 16, as it seems that this value is always set to 16 by the current tools. I recall you that for a full explanation of all those fields, give a look at Matt’s article.
Next step is accessing to the export table: this can be done by using the DataDirectory array: the interested element is located at IMAGE_DIRECTORY_ENTRY_EXPORT. A check on the size different from 0 should be done (DataDirectory elements have just two fields: VirtualAddress and Size). By adding the VirtualSize to the hModule value, I get a structure (another one, phew) called IMAGE_EXPORT_DIRECTORY:

    DWORD Characteristics;
    DWORD TimeDateStamp;
    WORD MajorVersion;
    WORD MinorVersion;
    DWORD Name;
    DWORD Base;
    DWORD NumberOfFunctions;
    DWORD NumberOfNames;
    DWORD AddressOfFunctions;
    DWORD AddressOfNames;
    DWORD AddressOfNameOrdinals;

Here there is a structure full of information:

  • NumberOfFunctions: the number of functions exported by the module
  • AddressOfNames: the (DWORD *) array containing the addresses of the string in which the name of the function is stored
  • AddressOfFunctions: the (PDWORD *) array containing address of the procedure
  • AddressOfNameOrdinals: the (WORD *) array containing the index able to translate from the ordinal number of the function to the index for the AddressOfFunctions array

So, the search for the function can be performed by comparing the hash of the name of the function (addressOfNames[i]) with the desired one and, if it matches, the FARPROC address of the function can be retrieved by searching into addressOfFunctions[addressOfNameOrdinals[i]]. So easy, huh? With ordinals is even easier, as I don’t have to scan the whole array: just taking my FARPROC address from addressOfFunctions[ordinal]. So easy, huh? No, it’s the hell not. There is a very nice feature introduced by Microsoft called “forwarded functions” which deserved all the head-banging on the desk I had; quoting Matt’s article:

Forwarding involves one DLL sending on references to one of its functions to another DLL. For example, in Windows NT, NTDLL.DLL appears to forward some of its exported functions to KERNEL32.DLL. An application may think it’s calling a function in NTDLL.DLL, but it actually ends up calling into KERNEL32.DLL.

This has been a real headache for me, but the solution wasn’t so difficult indeed. First thing: how can I recognize a forwarded function? Well, there’s a trick for this. As in the table there is the relative address of the exported function/variable, if this relative address falls into the export section, then the symbol is forwarded. At the desired address I won’t find the code of the function: there will be a string instead following the pattern name_of_the_dll.name_of_the_function. This string tells in which DLL is located the real symbol (well, I hope that the destination isn’t a forwarded function too) and the name of the symbol. In the end the process should be repeated with this data.

If you were wondering “where the hell is the source code for this ?“, don’t worry. I am going to release it at the one of this series, when it will be more stable (and working). Feel free to comment and criticize (positively and/or negatively) this article in a way I can write better posts in the future. Catch ya later 🙂


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s