Mike's Appz
  Site Navigation

Projects

Win32 Import table enumeration and API hooking

Download source code
Download application

It seems most people find this topic interesting and of course i'm no exception. When I first heard about API hooking it seemed like I would never quite figure it out since there weren't many references to the import table and how it relates to this topic. If there was a mention of the import table, it was pie in the sky with really long uncommented sections of code that did who knows what. A person can't work like that.

So what is API hooking exaclty? To put it really simply, it's the process of intercepting calls that are made to imported functions supplied by another module(exe or dll loaded in the same address space as the executable), or even the same module. Why would would anybody want to intercept a function? To change the behaviour or simply to monitor the use of that functions. What I am about to cover in this article? How API hooking relates to the import tables and how exactly to pull it off. I won't go into too much detail about the import tables but I will describe enough to be able to experiment with the code I have provided. There is however another important piece of the puzzle that I totally neglect here since it is described in detail in a million other articles, which I will provide shortly. That is how to inject your code into somebody else's program. After all there isn't much use for API hooking if you already have the source code for the program that uses the imported functions. If you require that information a good place to start is to learn about windows hooks. The book Visual C++ Windows Shell Programming by Dino Esposito is an excellent introduction to this topic. He teaches it in a fun way, for example he shows how windows hooks can be used to modify the look and behaviour of the windows GUI, and he doesn't assume 500 years of prior programming expierence. It's an old book, but definitely worth the every cent. A bit closer to home is the article API hooking revelead by Ivo Ivanov in which he shows how windows hooks relate to API hooking as well as providing a few other methods of code injection. It can be found at www.codeguru.com/Cpp/W-P/system/misc/article.php/c5667. Injecting your code is the first half of the battle, where most articles end, this is where mine begins.

The part that I found missing in just about every API hooking article is... How the hell do I actually intercept that function call?

There is more than one way to do this, as with everything, but the method I will describe involves modifying the import tables of the executable so that function calls to the exported functions are routed to custom defined functions. The information given here is composed from two main sources, Jeffrey Richter's book Programming Applications for Microsoft Windows, and Matt Pietrek's MSDN article An In-Depth Look into the Win32 Portable Executable File Format.

First we need to understand what is the import table? and how do we locate it? The import table is a section of an executable that holds some type of references to functions that are used by the executable but are provided either by the operating system or were created by other vendors. These functions are known as imported functions and are located in other modules(exes or dlls loaded in the same address space as the executable). To be more precise, the import table is an array of data structures that reference these imported functions. At each index into this array various attributes about the imported function can be retrieved, such as the function's name, which is in the form of an ASCII string, or the function's ordinal value, which acts as a subtitute for the function name. The ordinal value in some cases can also be used to retrieve the name of the imported function. Some functions however do not have a name at all, and are purely referenced by this value. I think it's safe to say that every imported function has an accociated ordinal value, but not every function has an accociated string. Before describing how to access these data structures it is important be aware that there are two main types import tables. Firstly the most common one is the Import Address Table(IAT) which lists mandatory imports required for basic program operations such as program startup and memory management. The second most common import table is the Delay Import Table which allows the programmer some flexibility when referencing the imported functions, such as exception handling should something go wrong while accessing the imported function. The other difference between the two occurs during runtime. In the case of the IAT, when the program is launched, one of the first tasks taken is to simplify access to the imported functions by locating the memory addresses where the functions reside. Once the address for a given function has been located, the corresponding data structure in the IAT is updated with this information which is later used for future access to the imported function. We will be overwriting this memory address with the address of our own function so that future calls to the imported function are actually calls to our custom defined function. The Delay Import Table differs as the memory address for the given function is only resolved when the function is required. Hence the name Delay. I know for me that when I first heard about the Delay Import Table I had the misconseption that using LoadLibrary() and GetProcAddress() resulted in entries being created in the Delay Import Table, which is not true. I have seen some programs that go as far as removing all but the bare essential entries from the IAT and call the remaining functions purely through the use of GetProcAddress(), which makes for some serious software protection.

Now I can answer the next quiestion, how do we locate it? Microsoft has provided an easy to use function that does exactly that. ImageDirectoryEntryToData(). The only parameter of interest is the third which allows you to specify which sections of the executable file you are interested in retrieving. For our purposes supplying either IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT or IMAGE_DIRECTORY_ENTRY_IMPORT will return a pointer to the Delay Import Table or the IAT respectively. The pointer returned when requesting the Delay Import Table is actually a pointer to an ImgDelayDescr struct defined in delayimp.h and subsequently implemented in DelayHlp.cpp. Taking a look at these two files does help tremendously in understanding how the structure is used. When requesting the IAT from ImageDirectoryEntryToData(), the pointer returned is a pointer to an IMAGE_IMPORT_DESCRIPTOR struct defined in winnt.h. Taking a look at the structure's decleration in conjunction with Matt Pietrek's article mentioned earlier(take a look at the diagrams he provides in part2 of his article) it becomes clear how this structure is used and also the similarities between both the Delay Import Table and the IAT. It's not necessary to read his article right away, but it does make things a whole lot clearer. Trust me. You might even learn something.

Once we have located the import table we can proceed to the original problem of routing function calls to custom defined functions. This is where a programming example best explains the entire process. The links at the top of the page provide the full source code and a test application. There are two parts to the program, a dll that provides a custom defined function and a exe whose import table is to modified.

CustomAPILibrary.dll

This is just a regular win32 dll project, nothing fancy. The only things to note here is the additional function defined in CustomAPILibrary.cpp, and the definition file CustomAPILibrary.def.

CustomAPILibrary.cpp

//This function will serve as the replament function for MessageBoxW(). ie: calls to MessageBoxW that are made by the exe
//will be routed to this function.
//It is imported to note that the parameters passed to this function are identical to the parameters of the original function.

INT APIENTRY CustomMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
LPTSTR Text = __TEXT("CustomMessageBoxW called!");
return MessageBoxW(hWnd, Text, lpCaption, uType);
}


CustomAPILibrary.def
//The definition file ensures that CustomMessageBoxW is compiled as an exported function for this dll. The '@2' is the
//ordinal value accociated with the function.

LIBRARY "CustomAPILibrary"

EXPORTS

CustomMessageBoxW @2


TestApp.exe

This is where all the magic happens. First lets look at the import enumeration functions. One for the IAT and one for the Delay Import Table respectively.

CEnumWinPE.h
//This function performs enumeration for the IAT of any module specified by ModBaseAddress. EnumModImpProc is user
//defined callback function that is called each time an import function is enumerated. The additional parameter is passed
//to EnumModImpProc each time it is called and is purely for personal use, it is not mandatory.

ENUM_RET EnumIAT(HMODULE ModBaseAddress, ENUMMODIMPPROC EnumModImpProc, LONG_PTR Additional);

//This function is exactly the same as EnumIAT except that it performs enumeration of the Delay Import Table.
ENUM_RET EnumDelayIAT(HMODULE ModBaseAddress, ENUMMODIMPPROC EnumModImpProc, LONG_PTR Additional);

//Every time an imported function is enumerated, EnumModImpProc will recieve a pointer to a IMPORTENTRY struct which
//provides information about the enumerated import function.

typedef struct IMPORTENTRY
{
//Specifies if this IMPORTENTRY contains data from the delayload IAT
BOOL IsDelayLoad;
//Name of the module that houses the function
LPSTR ImportModName;
//Name of the imported function. In some cases this member is NULL if the function name could not be located.
LPSTR ImportFuncName;
//Ordinal/Hint of the imported function
INT Ordinal;
//Pointer in the import table that holds the actual address where the function resides. This is what we are interested in.
LONG_PTR RefAddr;
}*PIMPORTENTRY;

Looking at the IMPORTENTRY struct, our main objective is to locate the desired imported function and overwrite the actual function address pointed to by RefAddr with the address of our custom defined function. That way each time the imported function is called, our function will be called instead, and from there we can take any action we desire.

Before calling any of the import table enumeration functions the callback must be setup. You will notice from the next code segment that the callback function recieves two parameters. The most important being a pointer to a IMPORTENTRY struct. As you will see later the Additional parameter is the module handle of our dll that provides the custom defined function.

TestAppDlg.cpp

//Before calling EnumIAT we need to setup our call back function
ENUM_RET EnumIAT_EnumModImpProc(PIMPORTENTRY ImportEntry, LONG_PTR Additional)
{

LPSTR OriginalFuncName = "MessageBoxW";
LPSTR ReplacementFuncName = "CustomMessageBoxW";

//Remember that every time an import function is enumerated, this function is called.
//Compare the currently enumerated import function name to "MessageBoxW"
if(lstrcmpiA(ImportEntry->ImportFuncName, OriginalFuncName) == 0)
{
//If we have a match
//Get the memory address of our replacement function ie: The memory location that it resides at.
FARPROC ReplacementFuncAddr =
GetProcAddress(HMODULE(Additional), ReplacementFuncName);
//If GetProcAddress() failed
if(ReplacementFuncAddr == NULL)
//return failure and stop the enumeration process.
return ENUM_FALSE;

//Get the pointer to the memory location in the IAT that holds the address of MessageBoxW
FARPROC *OriginalFuncAddrPtr = (FARPROC*)ImportEntry->RefAddr;

//This is the important piece of code that actually replaces the entry in the IAT so that future calls to MessageBoxW() are
//routed to the replacement function CustomMessageBoxW(). The
result is that CustomMessageBoxW() intercepts calls
//to MessageBoxW()
.

//Change the protection on this region of the IAT so that we may modify it.
DWORD OldProtection;
if( !VirtualProtect(OriginalFuncAddrPtr, sizeof(FARPROC*), PAGE_READWRITE,
&OldProtection) )
//If VirtualProtect() failed, return failure and stop the enumeration process.
return ENUM_FALSE;

//Overwrite the address of MessageBoxW with the address of CustomMessageBoxW
*OriginalFuncAddrPtr = ReplacementFuncAddr;

//Restore the protection to the modified IAT region
DWORD Unused;
VirtualProtect(OriginalFuncAddrPtr, sizeof(FARPROC*), OldProtection,
&Unused);
//We have now finished with the import table
//Stop the enumeration process.
return ENUM_BREAK;
}
//Continue the enumeration process.
return ENUM_TRUE;
}

With everything setup we can finally call EnumIAT.

TestAppDlg.cpp
//This is the onclick handler for the "Replace MessageBoxW" button
void CTestAppDlg::OnBnClickedRepmsgboxw()
{
LPTSTR DllName = __TEXT("CustomAPILibrary.dll");
HMODULE LibHandle = NULL;

//Get the handle to our dll that exports the replacement function for MessageBoxW
LibHandle = GetModuleHandleW(DllName);
//If the dll is not currently loaded
if(LibHandle == NULL)
{
//Load the dll now
LibHandle = LoadLibrary(DllName);
//If LoadLibrary() failed
if(LibHandle == NULL)
return;
}

//Now we place the call to EnumIAT()
//Enumerate the IAT for this executable file, passing LibHandle as the additional parameter that will be passed to
// EnumIAT_EnumModImpProc()

if(!EnumIAT(GetModuleHandle(NULL), EnumIAT_EnumModImpProc, (LONG_PTR)LibHandle))
return;

//Get a handle to this button.
CWnd *RepMsgBoxWPtr = GetDlgItem(IDC_REPMSGBOXW);
//Disable this button
RepMsgBoxWPtr->EnableWindow(FALSE);
}

Easy huh. Stepping through the EnumIAT() function with the debugger will reveal all the real secrets. The functions are well documented so there is no way to get lost. If while compiling you get a "unresolved external symbol" error it is probably because the use of ImageDirectoryEntryToData() requires the application to be linked with Dbghelp.lib. See the MSDN documentation for details.

Currently I am working on a debugging tool that simplifies the API hooking process among others things. I have made the program available in the downloads section even though it is not fully completed yet.

 

Hosted by www.Geocities.ws

1