Wednesday, February 13, 2008

Detours and Code Hooking

This post is going to concern writing your own detour function to hook into a target process' code. The first thing we need to go over is what a detour is. A detour is a patch written to a target location that switches control from the target process and gives that control to code you have written. The example function we are going to write is going to be in C++. Open a new source file in your IDE of choice (I use Visual C++ 2008). First we need to write up a prototype for our function. We should start by figuring out what parameters our function will take. It will need to know where to apply the patch, where to redirect program control to, and what type of detour to write (a one-way jump detour, or a function call that will automatically return control to the appropriate location). We should also implement padding. A situation could easily arise where there isn't a good spot for the detour to go, and will end up only partially overwriting a piece of code in the target process causing corruption. The most common technique is to fill the remaining space with NOP, or No Operation instructions. Another alternative is a short jump, which will skip over the remaining code fragment. The short jump approach will only work if you have 3 or more excess bytes to work with, because two bytes will need to be allocated for the jump instruction and you will need one more because a jump of zero bytes is illegal. I chose to implement NOP padding, and so that is what the article will use in the example. However, once you get more experienced you may wish to look at other alternatives. For NOP padding, our detour function will need to take how many NOP's to write as a parameter. If padding is not needed, we can simply pass zero and it won't add any. The addresses for the patch location and our hook location should both be double-words. We can use a boolean for the flag to decide the hook type (since for this example, we're only going to be using two types). The data-type for the number of NOPs can be any integral type, but I chose to implement it as a double-word for the sake of simplicity. Although, a byte would be more than enough, as I've never seen a detour require padding for a couple hundred bytes or more. With all that in mind, our prototype will look something like this:
void PlaceDetour(DWORD dwAddressToPatch, DWORD dwDetourAddress, DWORD dwPadSize, BOOL bFlag);

The very first thing we will need to do when defining our function is remove the virtual memory protection for the code we want to patch. We will assume we are already within the target process' address space (working from within an injected DLL), and therefore we will need to place a call to the VirtualProtect Win32 API function. It's implemented within Kernel32.dll, but in C++ we only need to include the appropriate header, 'windows.h'. VirtualProtect takes four parameters. The first is a pointer to the address we want to remove the protection from. We can simply pass the address and cast it as type LPVOID, and the API will never know the difference. The second parameter is the number of bytes from the base address to remove the protection from. The detour will require five bytes dedicated to it (four for the DWORD operand, and 1 for the instruction that takes that operand as it's parameter), and an additional byte for each NOP instruction we wish to write. Therefore we can use the expression (5+dwPadSize) to get the proper value. The third parameter is the new protection type. We will pass the constant 'PAGE_EXECUTE_READWRITE' which is defined in 'windows.h' as 0x40. The final parameter will be a pointer to an integer where the function can put the old protection type for temporary storage so that we can easily restore it when we are cleaning up. Before you make the function call, remember to create a variable for the final parameter. My example will use a DWORD type variable called 'dwOldProtect'. Keep in mind that instead of passing the variable as a value, we will instead want to pass a pointer to the variable. We will use the address-of operator (&) for this task. Note that this step isn't necessary on versions of Windows that pre-date Windows NT (95, 98) because at that time virtual memory protection hadn't yet been implemented and the functions do not exist within the Kernel32 library. To maintain compatibility with both systems kernels you should implement a system for checking the version of Windows being run on the machine and call a separate function depending on the results, but this article won't go into that. The finished function call should look like this:
VirtualProtect((LPVOID)dwAddressToPatch, (dwPadSize+5), PAGE_EXECUTE_READWRITE, &dwOldProtect);

Next we need to write an if...else block to handle the detour type boolean. If 'bFlag' is true we will place a JMP, and if it's false we will place a CALL. The byte code for a rel16/rel32 JMP is 0xE9, and the byte code for a rel16/rel32 CALL is 0xE8. We will want to write the proper byte to the address we are patching. Our block should look like this:
if (bFlag) { *(BYTE*)(dwAddressToPatch) = 0xE9; } else { *(BYTE*)(dwAddressToPatch) = 0xE8; }

The next step is giving the CALL/JMP instruction an operand, or parameter. We want to calculate the number of bytes the system will have to travel from the address of the instruction to reach the destination (our detour). This is easily calculated using the formula ((dwDetourAddress - dwAddressToPatch) - 5). Your code should look like this:

*(DWORD*)(dwAddressToPatch+1) = ((dwDetourAddress-dwAddressToPatch)-5);

Now we need to write our NOP instructions. We should use a for... loop to start at the final NOP instruction and work our way backward until we arrive at the final byte of the detour we wrote to memory. The loop will use a counter variable to keep track of how many times the loop has executed, and when it needs to stop. We will initialize the counter as the same value as 'dwPadSize' and it will execute until it is not greater than zero. This apporach has one definite advantage. We can use the counter variable along with the address we are patching with the detour and the size of the detour to determine where the next NOP needs to be written. Each execution of the loop will decrement the control variable. The code for the loop should look like this:

for (DWORD i = dwPadSize; i > 0; i--) { *(BYTE*)(dwAddressToPatch+5+i) = 0x90; }

We are at the final step. All we need to do now is restore the virtual memory protection. This will require another call to VirtualProtect. As a matter of fact, the function call will be exactly the same as the previous call with one caveat - instead of passing the 'PAGE_EXECUTE_READWRITE' constant as we did earlier, we will want to pass 'dwOldProtect' to restore the previous protection settings. You may still pass a pointer to 'dwOldProtect' for the final parameter, as well - it won't adversely affect the functionality of the sub-routine. It should look like this:
VirtualProtect((LPVOID)dwAddressToPatch, (dwPadSize+5), dwOldProtect, &dwOldProtect);
Congratulations! You have written a functional, albeit simple routine for writing detours to a target process' code. Aren't we just the ub3r h4x0r?

1 comment:

Unknown said...

first

how bout them apples, eh?
only a few months tardy, but shtill nomba won.


bwh4eva