i wanted to share some ideas and looking for ideas/comments.
Some of you might already have read about Hooking code (Detours,etc).
PeterPan tries to install the Hook in a more generic&easier way.
The old approach
It works like this:
We take CreateFileA as an example. Looking at the disassembly you will see:
Code:
.text:77E48CA4 mov edi, edi .text:77E48CA6 push ebp .text:77E48CA7 mov ebp, esp
Now, if we want to hook that routine, we have to get to our code, this is done by a jmp.
And looking at the jmp instruction we see that it has 5 bytes (0xE9 and 4 bytes for relative jump address)
Now the "mov edi,edi" makes sense! Microsoft has introduced this to pad the Preamble of a function from 3 to 5
bytes, so that we can hook functions more easily (Yes, no joke...).
Ok, so to hook the function we need to do the following:
Manually check the function, see how many bytes we need to save for later use.
Copy the bytes and put at the end a jump to the original function back (not directly to the function beginning, but
to the address that is after our evil jmp)
Put our jmp code at the beginning of the original function.
In the section where we jump to, do some stuff, and then jmp to our saved bytes.
Ok, let's do this at our example:
Code:
.text:77E48CA4 mov edi, edi .text:77E48CA6 push ebp .text:77E48CA7 mov ebp, esp .text:77E48CA9 push [ebp+lpFileName] .text:77E48CAC call sub_77E48C56 .text:77E48CB1 test eax, eax
SavedFunction:
Code:
mov edi,edi push ebp mov ebp,esp jmp 77E48CA9
Code:
.text:77E48CA4 jmp ourEvilCode .text:77E48CA9 push [ebp+lpFileName] .text:77E48CAC call sub_77E48C56 .text:77E48CB1 test eax, eax
Code:
bla bla bla jmp SavedFunction
This works nicely, but what are the downsides?
You have to check every function, for the size you need to save.
For Windows API this is easy, since 99% have the 5 byte preambel...
Let's insert a nop (1 byte length) at the beginning:
Code:
nop mov edi,edi push ebp mov ebp,esp
This will give you a protection fault sooner or later.
The second thing is, if you want to hook more functions, you have to create a ourEvilCode for every function you're
hooking (Because you need to jmp to a different SavedFunction, and probably want to have a different Payload for
every function)
Here's my idea of PeterPan:
* We have a table that gives us the length of an opcode, with this we can analyze how many bytes we have to save,
and where to jmp back in the code
* instead of jmp ourEvilCode, we do a call ourEvilCode. This puts eip onto the stack. with pop eax, we now have the
eip of the calling code, so we can have a lookup table for which Payload we want to execute, and which SavedFunction
we need to call.
Some party of my code (i'm thinking about releasing the whole code, but i need to clean it up a little more first)
It's a proof-of-concept, so there's plenty of room for improvements and cleanups....
------------------ How it's used....
Code:
HMODULE mylo=LoadLibrary("kernel32.dll"); DWORD add=(DWORD)GetProcAddress(mylo,"CreateFileA"); doHook(add,(DWORD)&payload);
Code:
char *jmpcode=(char*)malloc(50); memset(jmpcode,0x90,50); jmpcode[0]=0xE8; // call XX XX XX XX DWORD reljmp; reljmp= (DWORD)&myLoad-(targetFunction+5); memcpy(jmpcode+1,&reljmp,4); DWORD OLD; VirtualProtectEx(GetCurrentProcess(),(void*)targetFunction,50,PAGE_EXECUTE_READWRITE,&OLD); memcpy((void*)targetFunction,jmpcode,copysize-1);
Code:
int copysize=-1; for(int i=0;i<30;i++) { // 30 bytes should be enough p=(char*)(targetFunction+i); DWORD in=(DWORD)*p; in=in & 0xFF; if(step[in]!=-1) //in step[] we have the length of each opcode { i+=step[in]; } else { return -1; //Damn.... unknown opcode, let's fail :( } if(i>=4) { copysize=i+1; break; //Ok, we have at least 5 bytes } }
Code:
__declspec (naked) void myLoad() { __asm { nop // i like to have nops, easier to find code in disassembly :) pop eax // get calling address & save it mov meax,eax push eax // do we have a Payload? call getPay add esp,4 cmp eax,0 je rock_on call eax // Call it... rockon: mov eax,meax // eax=Address of Caller push eax call getBack // Get adress of backjump add esp,4 mov eax,eax // just ignore this line :) jmp eax } }