[Source] The jump hook [series part 1]

Posts 114 of 14 · Page 1 of 1
[Source] The jump hook [series part 1]
The last time we discussed a very basic hook that would only work inside our own application. Today I'm going to show you the basic and universal hook called the jump hook. With the jump hook you can hook functions regardless of size, location, arguments and whether or not it was originally built in your project or not. The only downside to this hook is that most AC programs detect them quite easily. If there's a jump instruction at a location there shouldn't be one: you're caught.

To follow along:

*Assembler knowledge is a must if you don't want to only leech the code
*A debugger or disassembler
*C++/C or any language that can create dll files and console executables

Forward note:

The EIP (Extended Instruction Pointer) points to the instruction that is currently executed. Once the instruction is executed the EPI is advanced to the next instruction. This is how to processor keeps track of where it's working.

Understanding jumps

Jump instruction prototype:

Code:
JMP IMM32                // imminent operand 32 bit size
JMP MEM32               // Memory operand 32 bit zie
JMP REG32                // 32 bit register (eax, ecx, ebx, edx, esp, ebp, esi, edi)

All offsets are relative to the jump location, remember this!


Example: 

401000:    JMP 4             // jumps to an address 4 bytes from here
...
401004:                        // Execution will continue here.
To understand the jump hook we must first understand how the jump instruction works. When the processor encounters a jump instruction it adds the operand (IMM32, MEM32, REG32) to where the EIP is currently pointing. Consider:







Code:
Note: The operands to all instructions are in little-endian, this means that you have to read the number(two digits) from right to left.

eg: 00 10 is actually 10 00 and 10 00 00 00 is actually 00 00 00 10 

E9 10 00 00 00
As you can see the processor adds the offset bytes (see red bold code above, 0x10) to the EIP register which points to the jump instruction being executed.

If you have any questions about how the JMP instruction works, you can post them below

The next step

Now that we know how the jump instruction works, we can advance to the next problem: How do we redirect the Sleep() function to our hook?

The answer is simple, we must write our jump code to a place were we are 100% sure it's going to be executed if we do that, every call to Sleep() will end in our Hook() >=D

The best place (where you are 100% sure) is of course the start of the Sleep() function, since every call to Sleep() will start there.

Every function has only 1 entry point, and all calls will start at this point

Now that we know where to place our hook, we can start writing code!

Create two projects, one console project named target and one dll project named hook

Target:

Add a file named main.cpp to your project and copy this into it:

Code:
#include <iostream>
#include <windows.h>

int main(){

printf("I'm going to sleep for a long while\n");           // to show us it's working

	while(1){
	
		Sleep(1000);                     // this sleep will get executed many times

	}

return 0;

}
Hook:

Add a file named main.cpp to your project and copy this into it:

Code:
#include <windows.h>
#include <iostream>
#include <string.h>

void MainThread();

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  fdwReason, LPVOID lpReserved ){    // main() function inside the dll

	if( fdwReason == DLL_PROCESS_ATTACH){
       CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&MainThread, NULL, NULL, NULL);
	   return TRUE;
	}

    return TRUE;
}

 
void __stdcall SleepHook(DWORD Timout){								// our hook
	MessageBox(NULL, "Not sleeping!", "SCHiM", MB_OK);       // display a messagebox instead of sleeping
	return;
}                                                            // ret

void DoHook(DWORD* Address, DWORD* Hook){   

	/* 
	This time it's not as easy as in part 1 where we could just replace one pointer to point at our hook.
        Here we need to replace 5 bytes with our jump code, E9 ** ** ** ** (where ** = offset bytes)
	Luckily can just place them at the start of the Sleep() function so that we're sure they get executed
	To do that we first need to change the memory protection so that we can write to it (we can't by default)
	*/

	DWORD OldProt;     //                        -5 is there because the offset bytes of the jump instruction are relative to the address of the jump instruction, so the 5 bytes (length of the jump instruction) must be subtracted
	DWORD HookOffset = (DWORD)Hook-(DWORD)Address-5;             // calculate the offset bytes and store the result into hookoffset

	VirtualProtect((void*) Address, 40, 0x40, &OldProt);       // change the memory protection and give us write access.


	char* CharPointer = (char*) Address;    // sizeof(char) == 1, 0E9h == 1, we need a char for the size

	*CharPointer = '\xE9';                  // *CharPointer(Sleep()) now equals E9h (the jump opcode)
											// we now need to place the operands						
	CharPointer++;                          // the size problem again, incrementing Address (Address++) would result in the pointer advancing 4 bytes

	Address = (DWORD*)CharPointer;		    // Advance the pointer with 1 byte to where the offset bytes need to come

	*Address = 	HookOffset;                 // the correct offset 

	VirtualProtect((void*) Address, 40, 0x40, &OldProt);      // change the memory protection back to the old values (it doesn't really matter if we do this or not, but it's just proper)

return;                       // the hook is set!
}


void MainThread(){		// our main function to work with

DWORD* AddressOfSleep	= (DWORD*) GetProcAddress(GetModuleHandle("Kernel32.dll"), "Sleep");          // get the address of the Sleep() function through the address of kernel32.dll

if( AddressOfSleep == NULL){  // oops
	MessageBox(NULL, "Could not find the address of Sleep()!", "SCHiM", MB_OK);
	return;
}

DoHook( AddressOfSleep, (DWORD*)&SleepHook); // hook it! >;)


return;

}
Compile/build the projects in release mode or something like it, as long as it's not debug mode

As usual the code is commented to help you understand it, I suggest you read it before compiling/continuing

If you have any problems with this code or if you don't understand something, post and I'll take a look


To test this code you run Target.exe and then you inject hook.dll into it. If all is well you should see a MessageBox saying: "Not sleeping!"

Reentry

If you've successfully tested the code above, you can continue here for there is yet another problem to be solved. In the code above we only show a MessageBox in our hook before returning. What if we instead want to change the timeout value of sleep and then resume the normal execution flow of the program executing Sleep()?

We've replaced 5 bytes at the start of the Sleep() function, so we can't just call Sleep() nor can we call Sleep()+5 (skipping the jump) for we have overwritten critical prologue code, without this prologue code the program will crash if you try to execute Sleep().

This problem is the problem of reentry in all it's glory: You can't know exactly what prologue code you've overwritten so you can't hardcode a solution, nor can you just jump back-in and you can't call it directly either.

The more absolute readers probably see the way out by now, and there's only one good solution to this problem: we have to save the bytes at the start of the function and execute them before we jump back into the Sleep() function.

But that isn't the end of our problems, there's one last thing I kept back as to not discourage you at the start of this tutorial. Remember you need a debugger or a disassembler? Well here's why:

That prologue code I talked about with the Sleep() function? That's also in your Hook(), and in your Main() and in every other function you ever made or used. At the start of a function the prologue code does two things: It pushes the original base pointer to the stack and then moves the current stack pointer to the base pointer. There's also a matching Epilogue code undoing what the prologue code did, it moves the base pointer to the stack pointer, and then pops the previous base pointer from the stack. This Epilogue code is executed right before a Return; statement.

To keep it simple I'll only tell you about the foremost reason why this is a problem to us: At the end of a function when we Return; the processor thinks that the stack pointer is pointing to the return address. So it can properly return the execution to where it came from. However we're not going to return we're going to jump back into the Sleep() function therefore the epilogue will never be executed. Because of that the stack pointer is NOT pointing at the return address. The function will fail when it's ready to return because it finds that our prologue code is where it's return address should be.

Luckily we can hardcode our own epilogue code because it will not change from hook to hook (we only have one hook function, remember?)

Here's how my compiler implements the prologue code:

Code:
10001049   55               PUSH EBP
1000104A   8BEC             MOV EBP,ESP
                                   push ebx
                                   push esi
                                   push edi
My epilogue code looks like this:

Code:
                                   pop edi
                                   pop esi
                                   pop ebx
100010A2   8BE5             MOV ESP,EBP
100010A4   5D               POP EBP
You'll have to find how your compiler implements pro/epi code but this is how it generally looks.

If you have any questions about this section, you should try playing around in a debugger a bit with epilogue/prologue code before asking, because I have the feeling a lot of people won't get this.

Now that we know what to do with the prologue and epilogue code, lets get back to the source codes!!

The final step

hook:

Just c+p this over the code that is in main.cpp previously

The green code is specific to my environment, yours may differ. Instructions on how to find your won epilogue code can be found in the "Reentry" section

Code:
#include <windows.h>
#include <iostream>
#include <string.h>

void MainThread();

DWORD* gPrologPointer;						// this pointer will point at the prologue code of Sleep()

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  fdwReason, LPVOID lpReserved ){    // main() function inside the dll

	if( fdwReason == DLL_PROCESS_ATTACH){
       CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&MainThread, NULL, NULL, NULL);
	   return TRUE;
	}

    return TRUE;
}

 
void __stdcall SleepHook(DWORD Timout){								// our hook
	MessageBox(NULL, "Not sleeping!", "SCHiM", MB_OK);       // display a messagebox instead of sleeping

	Timout = 5000;                     // make it sleep longer 5 seconds instead of 1

	__asm{

		pop edi
		pop esi
		pop ebx
		mov esp, ebp    // MY epilogue code, to match MY prologue code, note that this is suited to MY environment, yours may differ
                pop ebp
		mov eax, gPrologPointer
		jmp eax                       // jump to Sleep's prologue code
	}


	return;
}                                                            // ret

void DoHook(DWORD* Address, DWORD* Hook){   

	/* 
	This time it's not so easy as in part 1 where we could just replace one pointer.
    Now we need to replace 5 bytes with our jump code, E9 ** ** ** ** (where ** = offset bytes)
	Luckely can just place them at the start of the Sleep() function so that we're sure they get executed
	To do that we first need to change the memory protection so that we can write to it. 

	We also need to save the prologue code at the start of the Sleep() function and execute them after our own Epilogue code.
	Luckily I've done all this for you, and the only thing you have to do is C+P :p
	*/

	DWORD OldProt;
	DWORD* OffsetPointer;
	char* CharPointer;                                          

	gPrologPointer = (DWORD*) new char[10];                   // we only need 10 bytes, sizeof(char) = 1
	
	CharPointer = (char*) gPrologPointer;                     // size control again

	memcpy((void*)CharPointer, (void*)Address, 5);            // copy the porlogue code of sleep to our buffer

	CharPointer[5] = '\xE9';                                  // this code will jump from the prologue code to the Sleep() function

	CharPointer += 6;                                         // advance the pointer

	OffsetPointer = (DWORD*)CharPointer;                      // Offsetpointer now points at where to offset bytes need to come 
 

	DWORD JumpTwoOffset = (DWORD)Address-(DWORD)OffsetPointer+1; // no -5 this time, because we want to skip the jump on the other end( jump back into the sleep() not in an endless loop)
	
	*OffsetPointer = JumpTwoOffset;                           // fix this jump

     VirtualProtect((void*)gPrologPointer, 40, 0x40, &OldProt);     // we need to be able to execute the prologue code



	     //                        -5 is there because the offset at the jump instruction is relative to the address of the jump instruction, so the 5 bytes (length of the jump instruction) must be substracted
	DWORD HookOffset = (DWORD)Hook-(DWORD)Address-5;             // calculate the offset bytes and store the result into hookoffset

	VirtualProtect((void*) Address, 40, 0x40, &OldProt);       // change the memory protection and give us write access.


	CharPointer = (char*) Address;    // sizeof(char) == 1, 0E9h == 1, we need a char for the size

	*CharPointer = '\xE9';                  // *CharPointer(Sleep()) now equals E9h (the jump opcode)
											// we now need to place the operands						
	CharPointer++;                          // the size problem again, incrementing Address (Address++) would result in the pointer advancing 4 bytes

	Address = (DWORD*)CharPointer;		    // Advance the pointer with 1 byte to where the offset bytes need to come

	*Address = 	HookOffset;                 // the correct offset 

	VirtualProtect((void*) Address, 40, 0x40, &OldProt);      // change the memory protection back to the old values (it doesn't really matter if we do this or not, but it's just proper)

return;                       // the hook is set!
}


void MainThread(){		// our main function to work with

DWORD* AddressOfSleep	= (DWORD*) GetProcAddress(GetModuleHandle("Kernel32.dll"), "Sleep");          // get the address of the Sleep() function through the address of kernel32.dll

if( AddressOfSleep == NULL){  // oops
	MessageBox(NULL, "Could not find the address of Sleep()!", "SCHiM", MB_OK);
	return;
}

DoHook( AddressOfSleep, (DWORD*)&SleepHook); // hook it! >;)


return;

}
Overview

Because this is a daunting scheme I'll post an overview of what's going on here:



This hasty scrawling of mine should be enough to show you what we're doing here.

Now to address a question that will be poping up in some of you:

You may have noticed some hackers doing things like this:

Code:
Hook(foo, bar){

foobar = foo;
bar();
...
...
...
Return oFoo(foo, bar); 
}
What they are doing here is this: They are calling the original (the real) function in the hooked function, and return the return value to the caller.
This may look simpler and cleaner too you and it probably is, however it's not better because this is more easily detected then our function (which cleans the stack as though it has never been there). If the AC developers have even a slight idea of what they are doing they can check if the all came from the right function (by using the return address). If they see your hook's return address: you're caught!

Ca is not that good though, so can try this, I knew a few games were this was detected from day 1 however.

Any questions can be asked as long as they are not too stupid

-SCHiM
in the c++ code, why do you have __stdcall? isn't c++ stdcall by defualt?
Fan-fucking-tactic. I'm on my iPhone at the moment so I can't see the code properly, but the actual content seemed really well explained. Thanks SCHiM
Just wanted to say that your tutorials seems awesome so far. I played around with C++ for 3-4 years now and yet I've never really learned how hooks work simply because every tutorial I looked at was so complex and advanced. Just by reading this I was able to follow and understand most of the things you did even though I haven't had a chance to try it out yet. But I will and I think that this time I will finally learn how to do it

You explain what you're doing very well, keep that up and I really hope to see more tutorials on this!
dude... you are a-freaking-mazing...
This is good good shit rite here chef-boy-rd yah feel me!
Wow thanks guys, It's very nice to hear that you like it

@kibbles18

I believe it is, however for some strange reason my hook crashes if I try to hook sleep with a function not declared as __stdcall, I thought that it couldn't hurt, so that's how it is.
i know i need to go get more ASM and C++ knowledge because even with how well explained this is, i still don't get it lol but still great
Yeah. Good Job i understand only the 40% of this but anyway GJ!
Quote Originally Posted by coogle007 View Post
Yeah. Good Job i understand only the 40% of this but anyway GJ!
That's a start at least
Quote Originally Posted by .::SCHiM::. View Post
That's a start at least
may I add you SCHiM?
@.::SCHiM::.
Quote Originally Posted by SrNooB View Post
may I add you SCHiM?
@.::SCHiM::.
With what? Sure you can add me on mpgh.
Nice tutorial Shim, I belive once people grasp the basic concept it actually very easy too hook functions directly or indirectly using inline assembly by means of code caves... Anyway excellent job. Also im pretty sure the _Stdcall derives from C code meaning the "_" part. I am not a C or C++ coder so I don't really know, but your tutorial is good for any programming language as it explains the basic concept and methods very well.
Posts 114 of 14 · Page 1 of 1
This thread is closed for replies.

Similar Threads

Tags for this Thread

None

Need help?