with simple exports of your own DLL |
Advanced | |
+HCU papers |
||
fra_00xx 980906 PNA 0010 AD PC |
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! |
|
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.
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 hookedThe 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.
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