Gepostet am: Jul 18, 2012 12:49:52 AM
most articles out there which deal with hooking only tell you how to intercept calls to external DLLs. this is pretty useless when you want to hook inside the executable itself... so here's a quick way to do it.
grab my modified copy of the excellent chrom library from my googlecode repo (originally from http://code.google.com/p/chrom-lib/, licensed GPL)
open my project
adjust the hardcoded logfile names (dllmain.cpp and chrom.cpp are littered with them)
take a look at how the hooks were done in chrom.cpp... copy and edit as needed to add your own hooks.
build your DLL
to inject the dll in the process while it's being run in the IDA debugger:
get idainject from http://newgre.net/idainject and install the .plw file into IDAs plugin folder
via edit->plugins->idainject, install the dll to be injected at process start
launch the debugger
i use the hooks to replace the broken printf/debug message handling, because an executable with windows doesn't have stdout/in/err on windows... so i hooked the functions and cloned the output to various files.
here i'll show how to prototype the function "int _open(const char* path, int access, int mode)" from the c++ runtime library cw3220.dll:
create an instance of a chrom hook object: Hook es_open;
prototype your function and the original function: int open_h(const char* path, int access, int mode); typedef int (*open_orig)(const char*,int,int);
create a pointer to the original function: open_orig open_ptr=NULL;
add the initialization code to create_hooks: es_open.Initialize("_open", "cw3220.dll", open_h); es_open.Start();
make your hook function:
create a variable to hold the return value of the original function: int ret;
create a string buffer large enough to hold your desired log message: char buffer[1024];
reset the original function (chrom plainly overwrote the first six bytes with a JMP without taking care of anything!): es_open.Reset();
get the pointer to the original function: open_ptr=(open_orig)es_open.original_function;
prepare your message: sprintf(buffer,"call to open, path=<%s>, access=%x, mode=%x\n",path,access,mode);
write it to the log: write_log("c:\\sierra\\es2\\intercept.log",buffer);
call the original function: ret=open_ptr(path,access,mode);
replace the hook code: es_open.Place_Hook();
increment the sequence counter to maintain continuity across the different logfiles: seq++;
finally, return the result of the original function: return ret;
hooking a fixed location in memory (e.g. debug_print at 0x46A834) is different in:
step 4: use the initializer i added to chrom for fixed locations: es_debug.Initialize(0x46A834, debug_pr_h); es_debug.Start();
this will not work with the original chrom.h! use my modified version!
let's take a look at the debug-print function at 0x46A834 before the hook has been set:
CODE:0046A834 push ebp
CODE:0046A835 mov ebp, esp
CODE:0046A837 add esp, 0FFFFFF00h ; Add
CODE:0046A83D lea eax, [ebp+arglist] ; Load Effective Address
or, written in opcodes, 55 8B EC 81 C4 00 FF FF FF 8D 45 14
everything ok there. now, let's see what happens after es_debug.Place_Hook() here... (set a breakpoint at 0x00407CC1, this is always the first call to debug_print in the command flow, and run the debugger)
now, the 0x46A834 looks totally different...
CODE:0046A834 jmp near ptr unk_5B8910A0 ; Jump
CODE:0046A835 db 67h
CODE:0046A835 push 0FFC35B42h
CODE:0046A837 inc edx ; Increment by 1
CODE:0046A83D lea eax, [ebp+arglist] ; Load Effective Address
or, written in opcodes, E9 67 68 42 5B C3 FF FF FF 8D 45 14
the first six bytes, 55 8B EC 81 C4 00, have been overwritten by E9 67 68 42 5B C3 - a relative JMP (opcode 0xE9) to current-position (0x46A834 + 0x5B426867) + 5 = 0x5B89109B + 5 = 0x5B8910A0.
the addition of 5, when calculating the address by hand, is because EIP (the instruction pointer) would be changed to 0x46A839 after reading the instruction, and then add 0x5B426867 to it. remember that this opcode is total 5 bytes in length.
nice to see is how the mov and add instructions either vanish (mov) or become totally corrupted (add). now, step into the jmp... and land in the memory regions of the hookdll:
hookdll.dll:5B8910A0 ; void __cdecl debug_pr_h(unsigned __int16 a, unsigned __int16 b, char *format)
hookdll.dll:5B8910A0 j_?debug_pr_h@@YAXGGPADZZ proc near ; DATA XREF: create_hooks(void)+1E.o
hookdll.dll:5B8910A0 jmp ?debug_pr_h@@YAXGGPADZZ ; debug_pr_h(ushort,ushort,char *,...)
hookdll.dll:5B8910A0 j_?debug_pr_h@@YAXGGPADZZ endp
follow this jmp... and land in the debug_pr_h function itself.
remember the first thing we did back in coding? yep, we reset the original function... es_debug.Reset();... and we see it right in the code, after the stack-checking code generated by MSVC.
and you can already guess what will be at 0x46A834 after stepping over the call instruction? correct:
CODE:0046A834 push ebp
CODE:0046A835 mov ebp, esp
CODE:0046A837 add esp, 0FFFFFF00h ; Add
CODE:0046A83D lea eax, [ebp+arglist] ; Load Effective Address
now, let the debug_pr_h function do its work... until it arrives at hookdll.dll:5B8917A3 call j_?Place_Hook@Hook@@QAEHXZ
and once again, we have the jump back at 0x46A834:
CODE:0046A834 jmp near ptr unk_5B8910A0 ; Jump
CODE:0046A835 db 67h
CODE:0046A835 push 0FFC35B42h
CODE:0046A837 inc edx ; Increment by 1
CODE:0046A83D lea eax, [ebp+arglist] ; Load Effective Address
the advantage of this "dirty" method is that you don't have to respect what is at the begin of the function. even if the function is just one byte long (plain RETN), you can hook it.
the disadvantage is that it isn't multithread-safe: a thread may have EIP at the code you're overwriting... boom.