PDA

View Full Version : New API hooking tool


Proger
2013-05-21, 22:07
Hi folks,

Now that it's complete I don't know where to start, it's really huge. Anyway let's try a small example. There's an oldy RPG from 2000s called "Avaton (http://vndb.org/v3036)"; I'll use it as a test subject (it's about 120 MiB).

My tool - ApiHook (http://proger.i-forge.net/ApiHook_-_Win32_API_hooking_tool/7wP) - allows you to execute custom scripts attached to any exported function of a DLL loaded into a process. In other words it's like a debugger where you set breakpoints with conditions but here you can run full-blown scripts instead of some simple conditions. You can dump call stack, dump memory segments, log stuff to console, etc.

Among other things it's very useful in finding initial call points when you're dealing with a new engine.

ApiHook has several strong points and one of them is that it can use both IAT table patching and prologue rewriting (the default) to attach its own handler to subject. IAT is fast but unreliable (e.g. it won't work for dynamic binding with LoadLibrary/GetProcAddress) and it only affects particular module (unless you patch every process module). Prologue patching is unavoidable - first instructions of target function body are overwritten to point to ApiHook's handler so you can't call the hooked function bypassing ApiHook itself. It's a technique used by rootkits and low-level apps like antiviruses.

Let's say we want to see what files does Avaron access via CreateFileA. Here's what you need to do:

Download ApiHook (http://proger.i-forge.net/ApiHook_-_Win32_API_hooking_tool/7wP#download) and extract it anywhere
Create hook script - let's call it "avaron.oo" (you can use .txt too if you wish). It's a plain text file, we'll fill it below
Open command prompt at ah.exe (it doesn't matter but usually is more convenient - no typing of long paths)
Now type this command (but don't run it yet): ah.exe launch <avaron.exe> avaron.oo - or it can be shortened to ah l <avaron> avaron (l = launch, .exe and .oo/.txt are added automatically)


Now it's set up. We need to fill in the hook script (docs (http://proger.i-forge.net/ApiHook_-_Win32_API_hooking_tool/7wP#script)) - it specifies what ApiHook should hook and what to do once a trapped call happens. Script file has a basic INI-like format:

[CreateFileA]
log :fn

That's it. Now as soon as CreateFileA is called we'll see a line like CreateFileA: save0000.dat in the console, log or both.

Now you can run the command line above. (Note: you should have DEP disabled!) The game title opens and... nothing happens. You will see something like this:
http://i.imgur.com/rY1sJBB.png

Now that's strange, isn't it? But let's try to Load a saved game (don't close ApiHook) - it's the top clickable menu item. You should immediately see new lines appearing in the console as the game lists empty slots:

Your choice...
21:44: CreateFileA: save0000.dat
* CreateFileA: save0001.dat
* CreateFileA: save0002.dat
* CreateFileA: save0003.dat
* CreateFileA: save0004.dat
* CreateFileA: save0005.dat
* CreateFileA: save0006.dat
* CreateFileA: save0007.dat
* CreateFileA: save0008.dat
* CreateFileA: save0009.dat

As we see ApiHook works and listens for CreateFileA calls. If so - why we didn't see them, the game at least had to read title image from somewhere?

This is because most of the engine isn't contained in avaron.exe but rather in uty32dll.dll (see it in the game's folder?). By default ApiHook only triggers on calls originating from the process itself (the EXE) but not its modules (DLLs) - since it uses prologue rewriting even calls from system libraries like kernel32.dll will be intercepted and output and believe me that's a huge stream - you'll see it in a moment.

So we need to hook uty32dll.dll instead of/in addition to avaron.exe. Close the game and ApiHook (you can type t in its console and press Enter) and change the command line so it looks like this: ah l <avaron> avaron --module=uty.

--module can be * to hook everything including system DLLs or it can be full or part module name to which hooks should be attached. We could type path\...\uty32dll.dll, uty32dll or any other unique file name portion like uty, which we did.

Now run it again and this time you will see much more activity:

...
* CreateFileA: win_up.bmp
* CreateFileA: graphic.md
* CreateFileA: gwaku_up.bmp
* CreateFileA: graphic.md
* CreateFileA: ewaku.bmp
* CreateFileA: graphic.md
* CreateFileA: swaku.bmp
* CreateFileA: graphic.md
* CreateFileA: t.bmp
* CreateFileA: graphic.md
* CreateFileA: t.bmp
* CreateFileA: graphic.md

Aha! graphic.md and others including some non-existent files like t.bmp. Great. Now if you try to Load a game you'll notice that there are no new lines in the console - unless you have used --module=*.

This is very basic usage. You can do a lot more but I'll keep this first post short and if anyone is interested - feel free to drop a comment. ApiHook is yet in early beta stage so there are glitches.

You can find a more appealing teaser script under the spoiler.


[CreateFileA]
save fn
. save <H2FN ^ x fmt cat> ^fn
. if ^ 4294967295 NEQ
. log {gi ^fn} (h = ^)

[CreateFileA]
. if ^ 4294967295 EQU
. log {ri ^fn}

[SetFilePointer]
save h, pos
save fn H2FN :h x fmt cat load
. if ^ 4294967295 NEQ
* if ^fn 'graphic.md' EQU
. log ^fn -> <^pos X fmt>

[ReadFile]
save fn H2FN :h x fmt cat load
* if ^fn 'graphic.md' EQU
log ^fn (:size B by :EIP:x)

You will likely get a crash because of intensive calls to ReadFile so change the command line to this:

ah l <avaron.exe> avaron.oo --module=uty --lib-logs=lib.log --detach=w

When you run it there will be a delay before game shows title screen due to heavy use of these functions. After a few seconds you will see it though and the log will be about 350 KiB large. In particular, its last lines will be like:

* CreateFileA: win_up.bmp
* [usr] CreateFileA: graphic.md (h = 388)
* [usr] SetFilePointer: graphic.md -> 4E4A000
* [usr] ReadFile: graphic.md (1380 B by 10004334)
* [usr] CreateFileA: gwaku_up.bmp
* [usr] CreateFileA: graphic.md (h = 400)
* [usr] SetFilePointer: graphic.md -> 23CC000
* [usr] ReadFile: graphic.md (68848 B by 10004334)

Better in color:
http://i.imgur.com/iOj3R8Y.png

Red entries mark non-existent files, green ones - successful calls to CreateFileA (see ColorConsole syntax (http://proger.i-forge.net/ColorConsole_Delphi_unit/7Sk#features) for {ri ...}). As we see the game first tries to use an uncompressed file outside of archive, then opens the archive (graphic.md (h = 388)), seeks to, say, 4E4A000 and then reads 1380 bytes - and that by 10004334 is the caller's address that we can follow in OllyDbg or IDA (here it belongs to uty32dll.dll).

Knowing the caller is great but if you check it you'll see it's exported function GetMDDataAllocRead that doesn't do anything except reading part of the archive. Isn't very helpful, right?

No worries. We can find out the call chain by dumping stack (http://proger.i-forge.net/ApiHook_-_Win32_API_hooking_tool/7wP#a-stack) at the moment of the call. For this we will need to hook GetMDDataAllocRead itself by adding it to Catalog.ini (docs (http://proger.i-forge.net/ApiHook_-_Win32_API_hooking_tool/7wP#api-cat)) - right, it's not a system API function but ApiHook works on any export function from any library, 3rd party or not. (In fact it can technically hook even internal program's subroutines using the same technique but it's not implemented - it has been implemented in v0.85.)

To keep it short I ill only show you the final result:

* [usr] GetMDDataAllocRead: Stack trace: 0078F9E4 [u][SELF], 100031C0 [uty32dll], 0109712B, 00000000, 100031C0 , 012EB7E0, 000002CA, 00000000, 00000000, 7C900000 [u][ntdll], ...


Brackets tell us to which loaded module this memory address belongs to. So we see that it's called by 100031C0 which belongs to LZFileOpen2 export which upon examination in IDA appears to be only called by LZFileOpen export - and most interestingly it also calls SlideDecode on this data. Now we have pinned-point the decompression function - in just 5 minutes and without complex debugging. Great result.


That's it for now. Hopefully it is useful. Let me know what you think.