How to hook any API function in kernel32.dll
with simple exports of your own DLL
Advanced
Advanced
5 September 1998
by PNA
papers
+HCU papers
Courtesy of Fravia's page of reverse engineering
 
fra_00xx
980906
PNA
0010
AD
PC
There's no need to present you PNA, whose essays have always been admired, inter alia, by myself, Quine, Razzia and Greythorne.
PNA is a great Fravia who has always had the capacity and the cleverness to choose VERY IMPORTANT fields for his essays. You'll be able to read on my site his
Ulead PhotoImpact Trial 3.01 -29 August 1997
("Protections" spitting the name of the calling dll and of the calling function)
and
W32Dasm Version 8.0 Save re-enabling by PNA - 31 August 1997
(How to get our dialogs and our routines inside our targets)

. Well a good question, from my own reversing point of view, would be: "PNA, d'you have only towards end August some time to write your beautiful essays?"

This said, I agree totally with PNA: if there will be some good contributions, additions and -more generally- some sound work on this, Troja will indeed be ours!

There is a crack, a crack in everything That's how the light gets in
Rating
( )Beginner ( )Intermediate (x)Advanced ( )Expert


How to hook any API function in kernel32.dll
with simple exports of your own DLL
Written by PNA


Introduction

To accomplish this it was necessary to modify the kernel32.dll of course. Despite all shortcomings I decided to patch the file itself. I coded a patcher that uses PE file format infos to find free space for code and to determine the addresses of the functions to hook. The only things the patcher takes for granted are the sectionnames _FREQASM, .text and .edata in the kernel32.dll. This will change in future versions as there are better ways to retrieve the needed information.

But the patcher is not the objective of this essay. I will rather discuss the results of the patching process as they are far more interesting. If you are interested in the files and sources or if you want to put them on your site please contact me at PNA@starwarsfan.com

As an example I hooked GetLocalTime and GetSystemTime by writing a DLL that exports 2 functions, one for every original one. If they are called they identify the calling module's filename, compare it with entries in an ini-file and if the names match, the functions return a fake SYSTEMTIME struct also specified in the ini-file. Otherwise they execute the original functions. This makes patching time trials (almost) obsolete.



Tools required



Target's URL/FTP

Your Kernel

Essay

First of all, I'll give you an overview of my approach:
1. The patcher will install a bypass-routine common to all hooked functions at a free spot in the .text section. It serves as an interface to your DLL.
2. The patcher will install a header-code for each exported hook-function in your fake.dll at a free spot in _FREQASM. The header-code will jump to the bypass-routine.
3. The AddressOfFunctions pointers in .edata will be modified to point to the new header-code.

Now to the details:

Let's dig into the bypass-routine:

		
:BFFB95C0 EB0A                    jmp BFFB95CC
:BFFB95C2 6661      <- "fa        popa		
:BFFB95C4 6B652E64  <- ke.d       imul esp, dword ptr [ebp+2E],00000064
:BFFB95C8 6C        <- l          insb
:BFFB95C9 6C        <- l"         insb
:BFFB95CA 0000      		  add byte ptr [eax], al

:BFFB95CC 50                      push eax
:BFFB95CD 68C295FBBF              push BFFB95C2 *)

* Reference To: KERNEL32.GetModuleHandleA
                                  |
:BFFB95D2 E8E6DFFBFF              call BFF775BD *)
:BFFB95D7 85C0                    test eax, eax
:BFFB95D9 750E                    jne BFFB95E9
:BFFB95DB 68C295FBBF              push BFFB95C2 *)

* Reference To: KERNEL32.LoadLibraryA
                                  |
:BFFB95E0 E892DFFBFF              call BFF77577 *)
:BFFB95E5 85C0                    test eax, eax
:BFFB95E7 7413                    je BFFB95FC
:BFFB95E9 50                      push eax

* Reference To: KERNEL32.GetProcAddress
                                  |
:BFFB95EA E86DD7FBFF              call BFF76D5C *)
:BFFB95EF 85C0                    test eax, eax
:BFFB95F1 7402                    je BFFB95F5
:BFFB95F3 FFD0                    call eax

:BFFB95F5 61                      popad		**)
:BFFB95F6 C3                      ret

:BFFB95F7 61                      popad		***)
:BFFB95F8 032424                  add esp, dword ptr [esp]
:BFFB95FB C3                      ret

:BFFB95FC 83C404                  add esp, 00000004
:BFFB95FF C3                      ret

<- The string "fake.dll" is stored there. This DLL contains the
hook-functions.
*) These addresses are calculated by the patcher
**) The call eax returns to this address if nothing is to be hooked
***) The call eax returns to this address (the stack is modified by
     fake.dll) if the function was hooked

The bypass-routine gets input from the functions header-code:
Exported fn(): GetLocalTime - Ord:0157h
:BFF77FF0 687D71F7BF              push BFF7717D *)
:BFF77FF5 60                      pushad
:BFF77FF6 B801000000              mov eax, 00000001
:BFF77FFB E9C0150400              jmp BFFB95C0  *)

This header-code is the new entry point of GetLocalTime in this case. First it puts the original address of GetLocalTime on the stack. Then all registers are pushed and the ordinal number of the corresponding hook-function is stored in eax. Next it jumps to the bypass-routine.

The bypass-routine itself does nothing special. It tries to retrieve the ProcAddress of the hook-function specified by the ordinal. I suppose you are familiar with GetModuleHandle, LoadLibrary and GetProcAddress. Otherwise refer to your Win32 help.

After BFFB95F3: CALL EAX the real action takes place in fake.dll. I coded a source code generator for all this, so you don't have to copy and paste yourself to death. Let's have a closer look (it does not necessarily have to be in C):


void FakeGetLocalTimeFake(DWORD dummy)
{
	DWORD NumberOfArgs=1;
	DWORD stack;
	DWORD *ret,val;
1:	if (GetLocalTimeFlag) return;
	GetLocalTimeFlag=TRUE;
	ret=&val;
2:	stack=(DWORD)&dummy-4;
3:	if (GetLocalTimeAction(stack+0x2c,ret))
	{
4:		if (ret!=NULL) {
			*(PULONG)(stack+0x20)= *ret;
			ret=NULL;
		}
5:		*(PULONG)(stack+0x28+4*(NumberOfArgs))= *(PULONG)(stack+0x28);
6:		*(PULONG)(stack+0x24)=NumberOfArgs*4+4;
7:		*(PULONG)stack+=2;
	}
	GetLocalTimeFlag=FALSE;
}

BOOL GetLocalTimeAction(DWORD param,PULONG result)
{
....
}

This structure is common to all hook-functions. The function's name is the original name enclosed by the word Fake. This is required because the patcher has to distinguish the hook-functions from other exported functions. Note that this is case-sensitive. C calling convention has to be used i.e. the stack is cleaned by the CALLING function.

Next the NumberOfArgs variable must be adjusted to the number of arguments the original function (here GetLocalTime) expects. Then change the name (here GetLocalTimeAction) in 3: to a selfwritten function that can do anything you want.

That function expects two arguments. The first one points to the first parameter of the original function. So *(cast)param is the first parameter, *(cast)(param+4) the second one and so on (STDCALL). If the original function returns a value it must be assigned to *result. Otherwise result must be NULL. XXXAction has to return TRUE if it hooks the function and FALSE if the original function is to be called. A little example:


BOOL GetLocalTimeAction(DWORD param,PULONG result)
{
	char name[MAX_PATH];
	pentries p;
	LPSYSTEMTIME st;
	result=NULL;
	if (phead==NULL) initialize();//initialize linked list of programs to
fool
	GetModuleFileName(NULL,name,MAX_PATH);//get name of calling module
	p=phead;
	while ((p!=NULL)&&(stricmp(p->name,name))) p=p->next;
	if (p!=NULL)
	{
		st=*(LPSYSTEMTIME*)param;//if names match, modify SYSTEMTIME
		memcpy(st,&(p->time),16);
		return TRUE;
	}
	return FALSE;
}

I still owe you the explanation of the FakeXXXFake function. The dummy argument is only used to retrieve the stackpointer in 2:. If XXXAction returns FALSE, the function returns to **) in the bypass-routine. There a POPAD is executed to restore all registers and the RET brings us to the original function's address we pushed at the very beginning of our header-code.

The XXXFlag (1:) is global and enables you to call XXX (GetLocalTime in this case) from within the corresponding XXXAction without blowing up the stack. The original function will be called then. This is essential if you only want to modify few values of a complex operation you cannot simulate.

If XXXAction returns TRUE, at 5: the return address of the caller to GetLocalTime is copied to Parameter n (see picture below). This is done because our bypass has to clean the parameters (API-Functions are STDCALL) but we still need the return address. In line 6: we store the number of bytes to be cleaned from the stack by the bypass-routine in Adr of orig. Function as we don't need it anymore. At 7: we add 2 to the return address of the bypass so it will continue at ***) after return. If we must return a value we modify EAX in 4:. Back at ***) in our bypass a POPAD is executed then the ADD ESP,[ESP] cleans the stack excluding parameter n, where the RetAdr of orig. Caller was copied to, so that the following RET brings us there. Done!

+-----------------------+
| Parameter n		|
+-----------------------+
|			|

|			|
+-----------------------+
|Parameter 1		|
+-----------------------+- $2c  <- offsets to stack variable in
FakeXXXFake
|RetAdr of orig. Caller |
+-----------------------+- $28
|Adr of orig. Function	|
+-----------------------+- $24
|EAX			|  $20
|ECX			|
|EDX			|
|EBX			|
|ESP			|
|EBP			|
|ESI			|
|EDI			| 
+-----------------------+- $04
|RetAdr of bypass	|
+-----------------------+- $00

To sum it up, you don't have to deal with all that to apply this technique. You can create or add functions to a fake.dll with my source code generator.

Final Notes

There are still many things to improve. First I will change the patcher not to rely on the section names. This is quite easy as it only has to scan the section headers for the executable flag and use these sections for bypass- and header-code. The .edata section can be found in the DataDirectory of the OptionalHeader. These are some steps to apply this method on any DLL, not only the kernel32.dll.

For now you can only hook STDCALL or Pascal functions, i.e. all functions that clean their stack themselves. As far as I know all API functions in kernel32.dll are STDCALL.

I don't know if it works under NT. It actually should. I am running the patched kernel for two weeks now and tried 6 different time trials. Neither did any of these change behavior after the trial period, nor did I experience any anomalies concerning performance or stability of the OS. Nevertheless I don't recommend hooking very frequently called functions.

The main idea of all this is to build a common interface for hooking API functions. You control the gateway between an application and the Windoze OS by simply writing a DLL. This empowers you to implement generic cracks like the time trial crack briefly introduced here. More power you cannot get at application level. My aim is to have a collection of fake.dlls written by many different crackers, dealing with the root of a problem, not solving an instance of it. Troja will be ours!


PNA

Ob Duh

Ob duh doesn't apply here: this is not low level application cracking, this is higher OS reversing.

You are deep inside Fravia's page of reverse engineering, choose your way out:

papers
Back to Papers

redhomepage redlinks redsearch_forms red+ORC redstudents' essays redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_Fravia
redIs reverse engineering legal?