Hooking Jmp Tables
So what this post will cover is a method I've been using for quite a while to hook functions while remaining undetected, It isn't scanned for by Punkbuster and I'm sure is not caught by VAC.. yet. If you have a background with Punkbuster this should be familiar to you. I am posting this because I haven't found too many posts around showing off this method, if any at all.Now, Let's start with an example for Battlefield Play4Free/Refactor 2 engine it's Reset function and Jmp table.
It's Reset Function looks like the following
It's Reset Function looks like the following
Code:
51 PUSH ECX 56 PUSH ESI 8BF1 MOV ESI,ECX 80BE 4A010000 00 CMP BYTE PTR DS:[ESI+14A],0 74 05 JE SHORT RendDX9.0837FE42 B0 01 MOV AL,1 5E POP ESI 59 POP ECX C3 RETN
Amongst the Jmp table at the beginning of the RendDx9.dll is a Jmp to this function that is used to call this function. Now we can find this by using some black magic in the form of a function like the following..
Code:
DWORD FindRelativeJmp( DWORD base, DWORD size, DWORD address ) { for (DWORD end = base + size; base < end; ++base) { if (*reinterpret_cast(base) != 0xE9) continue; if ((address - base) - 4 == *reinterpret_cast (base + 1)) return base; } return NULL;
But it doesn't do much good if we don't know the address of the function, so let's just step along and assume we have a Pattern to search for with a Generic FindPattern function. Under the assumption you're still following (You should be..) here is an example of hooking Play4Free's Reset function..
Code:
DWORD dwResetTrampoline = NULL; DWORD dwResetJmp = NULL; void __stdcall __Reset( void ) { __asm PUSHAD; // TODO: Call OnLostDevice __asm POPAD; ((void (__stdcall *)(void))dwResetTrampoline)(); // Call the original Reset // TODO: Call OnResetDevice } void __stdcall Init( void ) { HMODULE RendDx9 = LoadLibrary(TEXT("RendDx9.dll")); CHAR ResetPattern[] = { '\x51', '\x56', '\x8B', '\xF1', '\x80', '\xBE', '?', '?', '?', '?', '?', '\x74', '\x05', '\xB0', '\x01', '\x5E', '\x59', '\xC3', '\0' }; while (dwResetJmp == NULL) { dwResetJmp = FindPattern(reinterpret_cast(RendDx9), 0x650000, ResetPattern); dwResetJmp = FindRelativeJmp(reinterpret_cast (RendDx9), 0x650000, dwResetJmp); Sleep(100); } dwResetTrampoline = reinterpret_cast ( DetourFunction(reinterpret_cast (dwResetJmp), reinterpret_cast (__Reset)) ); }
What we are doing is..
- Finding the function we want to hook by pattern searching
- Finding a relative jmp to the specified function
- Changing that jmp so it jmps to our function, by use of DetourFunction
- ...
- Profiting
The concept is straight forward and fairly clean.. But there are some problems. For example a game has a Jmp table, but it has x # of functions before it.. This can cause problems because a relative Jmp to our function may exist inside a function which would result in us Hooking the wrong relative Jmp. A game may also not make use of a Jmp table all of the time, this would cause problems because hooks wouldn't be getting "called".
Why should I use this method (For now at least..)?
> Because there aren't very many anti-cheats that can hash a Jmp table correctly without giving false-positives, but this can easily change.
> It's not much messier than your average Detour.
> It's easy to implement.
What is required for this method to be used?
> The game must obviously have a jmp table, and you HAVE to know the location of it.
> There can NOT be any functions etc. that come before the Jmp table otherwise you may end up hooking the wrong thing and cause problems.
> There is guarantee before hand that a Jmp table will be utilized every time the specified function is called, you'll have to investigate each case for yourself.
Hope this was at least a decent read for you and possibly informative for some. If you have any questions I'll do my best to answer them.
0 comentarios:
Publicar un comentario