So we can just focus on the hook itself, and not worry about any obscurities, I have decided to write up a small - and rather stupid - base to focus our work on. You can either scroll down to the end of the post to download it, or you can simply plop the following code in any C++ compiler and have fun:
Code:#define WIN32_LEAN_AND_MEAN #include <windows.h> int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmdLine, int nShowCmd){ do{ if(MessageBox(NULL, "Do you touch yourself?", "Hi", MB_YESNO) == IDYES) MessageBox(NULL, "Thrilling", "Hi", MB_OK); else MessageBox(NULL, "BAD!", "Hi", MB_OK); }while(MessageBox(NULL, "Exit", "Hi", MB_YESNO) == IDNO); return 0; }
- Please note that if you decide to compile this yourself, your addresses will probably be different.
- The define statement ensures that while compiling our program, Visual Studios doesn't throw in any un-wanted headers and libraries.
There is nothing to overly complicated about this code - it simply loops through a series of commands that display a series of yes-no message boxes until exit is selected to be yes.
I find - through my gradual course of talking to people - is that the main issue with hooking, and reversing in general, is the interpretation of assembly. Obviously, thinking in C, as demonstrated above, is rather straight-forward as you have a nice block on English words to coat each line. Assembly, however, takes a much different approach to code-layout: in place of words, assembly uses a series of numonetics to represent op-codes (add, sub, (compare)cmp, etc.), and in place of the rapid amount of words per line uses only one command.
Now, this is of course distinctly different to how most English-speakers construct their sentences, as often we have formulated what to say before we speak the first word; rarely we wait until one word has completed to follow with the other. Slang is a perfect definition of this, as blurring the links between words is something that perfectly relates the difference between C and assembly. Despite all this, I find that assembly is actually far easier than C, an opinion which many do not hold. The reason why is quite simple; even though C falls closer to the English language, the 'block' (if, else, while, etc.) structure widely violates human thinking.
I am obviously far from understanding the intricacies of the human brain, but personally, when I evaluate a condition is false, I jump to the next method of thought, and idle shortly in the dealings of the current system. Far more, my mind jumps around a lot; rarely does the human brain run in a linear pattern, as is so defined by C - and here we stumble on one of the most decisive abilities of assembly: its ability to switch its location in code with ease.
However, jumping through code is useless unless we can actively and easily control the conditions on which our code will jump. To accomplish this, assembly uses a series of three components: the cmp/test command, the zero flag, and the conditional jump. To explain this, let me give a brief example; first some C code]
if(val == 1)
foo();
blah();[/syntax]
Nothing you haven't seen before, we simply compare a value 'val' to one, and if it is true, we call foo() before proceeding onto blah(). Now, let's convert this to assembly, using what we have covered:
Few things I haven't covered yet appear in the code above - so let's step through each line of this code. We start off with the mov, which by its name, should have a pretty obvious purpose. The only thing we need to focus on is the dword ptr ds:[] statement; in simplest terms, this retrieves the actual value held at the address passed in the square brackets. Next, we compare eax to one, and jump if not zero. To explain why the label holds the term not zero, we need to cover how the cmp instruction works. When executed, the cmp takes two values, passed in the form 'a,b.' The first thing cmp does with these two instructions is minus b from a; if the result is zero, the zero flag is set. From there, it should be pretty self-explanatory, no?Code:mov eax,dword ptr ds:[val] cmp eax,1 jnz @continueon call foo @continueon: call blah
Now that we have covered all that, we can almost guess what our written program is going to look like in it's disassembled form.
Time to start debugging this; open up Olly, and when it starts, open up our program (File>Open). It should land you in a clump of assembly, but worry not, we will soon make sense of this all.
I see many posts on gamehacking and other forums relating to one simply question: "How do I debug an application?" For many people, who already know the basics of C and assembly, this can be a daunting subject - all of a sudden, you lose control of your environment, and are forced to play by the rules of another programmer, who, in most cases, has deliberately screwed around with his native code to make your job a pain. What a douche-bag.
But the answer so many people search for is rather simply, and the one I think best (although practice and experience, as I often see as the replies are also good steps): the first job you need to accomplish is to find a place of reference. Debugging an application is much like solving a puzzle, albeit, a lot more fun, but the reference still works. When your pour your puzzle out of the box, it is simply a series of little pieces that you cannot make heads or tails of. But, if you use the picture on the box, you will discover that the puzzle has something notable in it - an odd colour, a face - something of value that you can put to use. From there, you can construct out, until finally, you have the completed puzzle. Except if you're me, because I, although lucky in pretty much every segment of my life, am I not lucky in solving puzzles, and always fail. Woe is me.
By now, the metaphor should be clear; just like solving a puzzle, to debug our application, our first step is to find a good place of reference. But how are we to do this?
For any of you with previous experience with Olly (and for any of you about to conclude this sentence I guess), Olly has the ability to locate all the text strings within an application. Simply right click, go to "Search For," and then in the menu, "All Referenced Text Strings," and a lovely box should pop up with several familiar strings. Click on any of them, it really doesn't matter, as they all land you in the same (relatively) spot. If you look to left of which ever reference you choose, you should notice a line to the right of the address that 'blocks' a section of code together - seems we have found our main function, so scroll up to the first address 'blocked' (401000h), and examine the whole function:
Debugging seems much less scary when we have a reference, I do dare say. Since we already have the C code, there is no point in stepping through each line of this in this tutorial, but it should be easy enough to follow.Code:00401000 /$ 56 PUSH ESI 00401001 |. 8B35 94404000 MOV ESI,DWORD PTR DS:[<&USER32.MessageBo>; USER32.MessageBoxA 00401007 |> 6A 04 /PUSH 4 00401009 |. 68 64504000 |PUSH HookPrac.00405064 ; ASCII "Hi" 0040100E |. 68 4C504000 |PUSH HookPrac.0040504C ; ASCII "Do you touch yourself?" 00401013 |. 6A 00 |PUSH 0 00401015 |. FFD6 |CALL ESI 00401017 |. 83F8 06 |CMP EAX,6 0040101A |. 6A 00 |PUSH 0 0040101C |. 68 64504000 |PUSH HookPrac.00405064 ; ASCII "Hi" 00401021 | 75 07 |JNZ SHORT HookPrac.0040102A 00401023 |. 68 40504000 |PUSH HookPrac.00405040 ; ASCII "Thrilling" 00401028 |. EB 05 |JMP SHORT HookPrac.0040102F 0040102A |> 68 38504000 |PUSH HookPrac.00405038 ; ASCII "BAD!" 0040102F |> 6A 00 |PUSH 0 00401031 |. FFD6 |CALL ESI 00401033 |. 6A 04 |PUSH 4 00401035 |. 68 64504000 |PUSH HookPrac.00405064 ; ASCII "Hi" 0040103A |. 68 30504000 |PUSH HookPrac.00405030 ; ASCII "Exit" 0040103F |. 6A 00 |PUSH 0 00401041 |. FFD6 |CALL ESI 00401043 |. 83F8 07 |CMP EAX,7 00401046 |.^74 BF \JE SHORT HookPrac.00401007 00401048 |. 33C0 XOR EAX,EAX 0040104A |. 5E POP ESI 0040104B \. C2 1000 RETN 10
Now, in my opinion, there are two basic main types of hook you can possibly implement - the traditional hook that calls your function from an overwritten section of code, and the conditional jump hook, which is less a hook, and more a simple byte-modification that acts like a hook. Since the conditional jump method is easier, let's start with that.
You may be wondering why there is any point of doing a simple byte-modification, as surly anything you can accomplish whatever you are doing with a more traditional hook - and this is certainly true. However, that being said, I would like to ask you a simple question: are you lazy? Because I sure as hell am, and whenever the possibility to avoid work presents itself, I will be the first to jump on it. (Ha, ha, "jump on it" when talking about conditional jumps. See what I did there? Ha... alright, I'll stop.)
Let's say for example you are incredibly Christian, or even more shocking, a female on the internet; you would disagree that touching yourself is "Thrilling" and not touching yourself is "BAD!". So how do we fix that? Well, we could hook the whole function, and re-write the entire section of code dedicated to determining that - or, as I would much rather do, we could simply change the jump if not zero at 401021h to a jump so it would always display "BAD!". Now, doesn't that seem so much easier?
Open up your C++ compiler of choice - I'm using VS6.0, because I'm oldskool - and create a new .DLL project(File>New>Win32 Dynamic Link Library) and name it something like "HookPractice_Met1," selecting an empty project. Next, add a new .cpp file(File>New>C++ Source File) and call it main.
With it created, start off with the typical C++ base for a .DLL:
*I realise the typical "approiate" title would be APIENTRY, and not __stdcall, but I like complete control over my functions.*Code:#define WIN32_LEAN_AND_MEAN #include <windows.h> bool __stdcall DllMain(HANDLE hProcess, DWORD reason, LPVOID reserved){ }
None of this code should look new to anyone who has touched C++ before, but a quick run-down: we start off with a #include statement, like all C++ programs do, and bring in the windows header so we can use VirtualProtect - then, we move onto a define statement, which simply tells our compiler to eliminate any un-needed headers and libraries - and finally, we conclude with our DllMain, which is called whenever our target process refers to our .DLL and is passed the reason for being called.
*Yes, I realise I butchered that last sentence.*
Now since our we went our .DLL to modify memory when it is attached to the process, makes sense to filter for that event; in the DllMain:
Obviously, if we attach our .DLL, then we want our patch to be executed - and, to clean up code, above our .DLL main we are going to declare a void function called patch:Code:if(reason == DLL_PROCESS_ATTACH){ return 1; } else return 0;
void patch(){}
As with any memory modification, the golden rule applies here too; before you modify, you must de-protect! And, as we also know, we need a loving variable to hold our old protection type. So, within "patch":
unsigned long hold = NULL;
Now onto our VirtualProtect call(s), which will give us access to that section of the application's memory, and allow us to actually modify it:
*If you refer back to the original disassembled code, you will see why we are de-protecting two bytes.*Code:VirtualProtect((void*)0x401021, 2, PAGE_EXECUTE_READWRITE, &hold); //our modification here VirtualProtect((void*)0x401021, 2, hold, NULL);]
With our memory at 401021h now un-protected, we can continue on!
*An interesting tidbit here: we are not actually un-protecting our address. What we are doing lies more along the lines of re-protecting it - we give our .DLL access to allow it to read, write, and execute memory at the location 401021h to 401023h. And then, after we have done our modifications, we simply restore the old protection type to ensure that the application, or something else hooking it (as piling on hacks is a pretty common user trend these days) does not modify the memory in a way that will cause the application to crash.*
But how do we modify our data? Here's where c+p'ing from online sources is often a bad idea - when dealing with memory modifications, a lot of people like to continue with what they know best, which are, of course, conventions brought over from .exe-type programs. As such, many sources I see (sadly) use WriteProcessMemory to modify code from within the .DLL.
Now you may be going, hey, attila, WPM works, so why is it bad? This, like all questions, can be answered with a simple analogy: when writing an essay, is it more efficient to just write on the paper yourself, or to call someone over to write for you? The same applies true for WPM - calling it from your .DLL forces the application you reside in to call another .DLL's code, and then come back to yours. And there is really no need for that, as we just gave ourselves privilege to modify away.
So, knowing this, and knowing the address, the only other thing we need to grab is our new op-code; go back into Olly, and notice that 401021h currently holds the op-code 75 07h. Modify it to a jmp (space-bar) and you notice it change to EB 07h. However, this is not enough, because we have to factor in another newancse - the x86's little endianess architecture, which basically states that when writing or reading memory, the process interprets the op-codes "backwards" or "right-to-left." With the small switch our new op-code to write becomes 07 EBh. Without further ado, replace the previous comment with:
*(WORD*)(0x401021) = 0x07EB;
This simple line can be daunting to interpret at first, but taking it by segments will make it easy to understand. Starting off on the left, we know that we are not modifying the actually modifying 401021h; no, we are modifying the memory it points to, so time to call out our pointer (*). Next, we need to convert the left-side so it can convert to accept the address on the right, which, we know, is a pointer to a dword.
*Nicely for us, if you can't remember the exact type-casting, VS will point it out.*
After that, we just have the memory we know, and the reversed op-code we interpreted above. Just remember "0x" designates hex in C++, and you are good to go.
And that's it for our "patch" function - all that is left to do is call it. In the if conditional of your .DLL main:
patch();
Compile, and inject, and you should notice your small change working perfectly! The final code of this part:
While that is fun, sometimes the full power of a typical hook is nessacary - say, oh I don't know, if we would like to display a message box from us at the end of every loop.Code:#define WIN32_LEAN_AND_MEAN #include <windows.h> void patch(){ unsigned long hold = NULL; VirtualProtect((void*)0x401021, 2, PAGE_EXECUTE_READWRITE, &hold); *(WORD*)(0x401021) = 0x07EB; VirtualProtect((void*)0x401021, 2, hold, NULL); } bool __stdcall DllMain(HANDLE hProcess, DWORD reason, LPVOID reserved){ if(reason == DLL_PROCESS_ATTACH){ patch(); return 1; } else return 0; }
Now, the theory behind this kind of patch is easy, and, at the same time, very intricate; all we need to do is re-direct a section of the application's code to ours, execute our code, and then return - however, this is easier said than done. When dealing with this type of hook, the primary concern you need to keep in your mind is management: the application will not be aware of our endeavors at all, and as a result, will assume that from before our hook until after, the same data stays in place. This means we have to be certain we restore every register, every flag, and the stack, or else our application will blow up. Literally, shards of code will come flying out of your screen. Not a pretty sight.
Before we can start diving into the actual code of the hook though, we need to focus on finding a place, and coding our patching function. The same guidelines described above work here perfectly as well - we, ideally, want to touch as little code as possible, as every line we will have to replace. We also want to search for five secluded bytes, as a call (or jump, like we will be doing here) requires five bytes to place.
Since this is such an isolated loop, we really have limited choices - but luckily for us, our compiler generated a beautiful place:
Five secluded bytes! Perfect for what we need to do; and, with this, we can begin coding.Code:00401043 |. 83F8 07 |CMP EAX,7 00401046 |.^74 BF \JE SHORT HookPrac.00401007
A large portion of the code for this we can copy out of the previous section, as we still need to modify a memory address; so, our code (through some c+p'ing):
Nothing here we haven't seen before - in fact, taking away the adjusted address and length in VirtualProtect, it is a carbon-copy of our previous section.Code:#define WIN32_LEAN_AND_MEAN #include <windows.h> void patch(){ unsigned long hold = NULL; VirtualProtect((void*)0x401043, 5, PAGE_EXECUTE_READWRITE, &hold); VirtualProtect((void*)0x401043, 5, hold, NULL); } bool __stdcall DllMain(HANDLE process, DWORD reason, LPVOID lpReserved){ if(reason == DLL_PROCESS_ATTACH){ patch(); return 1; } else return 0; }
*Since we will be displaying a message-box, it makes sense to declare some strings globally in our program; so above patch, but below the #include:
char *title = "Title", *body = "body text";
Before we we do anything relating to patching, we should start by writing our hook. Now our hook is nothing more than a void function, but to avoid screwing up registers, we are going to give it a special label - declspec(naked). All this type does, in most basic terms, is ensures that when our function is called, it accesses no registers or flags, which is, as we know, perfect for what we are doing:
__declspec(naked) void hook(){}
Now, since we are injecting this section of code, we need to make sure that it is interpreted exactly as we want it - so time to switch to inline assembly! Inside the hook block:
__asm{}
Our first order of business is to save all our registers, so we can restore them at a later point; to do this, we can use the instruction pushad:
pushad
With that saved, we can display our message box. The code for this you can simply pull out of the disassembly (c+p ftw):
*If this code is confusing, remember that during optimisation, VS moved a pointer to MessageBoxA into esi.*Code:push 0 push title push body push 0 call esi
And with that, we need to restore our registers:
popad
Onto the last step of any hook - replacing the code! Now, please understand, I did not expect this program to compile like this, so I thought I would have an easier example to show you, but alas, I do not. Our difficulty comes from the fact that, to my knowledge, conditionals are impossible to evaluate in a .DLL that jump to the main portion of the program. So looks like we will have to switch around the code we are replacing to make it work.
*Before jumping into this, it is important to remember two things; the original instructions, which were:
And that the location we want to jump back to if we are done with the loop is 401048h. With that, we may continue.*Code:00401043 |. 83F8 07 |CMP EAX,7 00401046 |.^74 BF \JE SHORT HookPrac.00401007
First let us salvage what little we can, as we can still keep the first line:
cmp eax,7
Since we know that when it is equal it jumps back to the top of the loop, it makes sense that when it is not equal it finishes, no? With this simple twisted logic, the code is not so hard for us to write; all that is left is the label trick needed for inline assembly. Since VS will throw a fit if you try a direct jump, you need to move the address into a register, and then jump to the register, and since eax is pointless in the scheme of things (for this program at least, as it gets overwritten), we should maybe choose that:
And yes, I know, I have awesomely descriptive label names.Code:jnz candy mov eax,401007h jmp eax candy: mov eax,401048h jmp eax
With that, our hook function is done, and we can now work on our patch function. However, unlike with our last example, we no longer have a static address to place in for our patch. To understand this, we need to cover how both the call and jump op-codes work; and no, it is not just a jumble of hex. The five byte op-code is made up of two parts: the E8h(call) or E9(jump) identification byte, and then a four byte (DWORD) collection which is generated by subtracting the caller+5 from the callee's address. Now since in our case the hook function will be dynamically placed, we need to generate this code. But first, we can focus on the leading identification byte; which, since we are jumping back in both cases of our hook function, makes sense to make it a jump - so, in-between the VirtualProtects:
*(BYTE*)(0x401043) = 0xE9;
Besides the BYTE designation, this code is almost identical to our last patch, so no need to explain it again, hopefully. Now for the fun operation:
*(DWORD*)(0x401044) = (unsigned long)&hook - 0x401048;
This can look confusing at first, but take the strategy of breaking it in parts - already, we have covered the left side, so we can just focus on the math. We know that the & symbol retrieves the address of a function, so we ensure it is in DWORD form with a forced conversion, and then subtract it from the place we put the patch plus five. See, easy?
Compile, and inject, and you should see some sexy results. The final code:
Code:#define WIN32_LEAN_AND_MEAN #include <windows.h> char *title = "DoxCoding", *body = "HaX4LawLz"; __declspec(naked) void hook(){ __asm{ pushad push 0 push title push body push 0 call esi popad cmp eax,7 jnz candy mov eax,401007h jmp eax candy: mov eax,401048h jmp eax } } void patch(){ unsigned long hold = NULL; VirtualProtect((void*)0x401043, 5, PAGE_EXECUTE_READWRITE, &hold); *(BYTE*)(0x401043) = 0xE9; *(DWORD*)(0x401044) = (unsigned long)&hook - 0x401048; VirtualProtect((void*)0x401043, 5, hold, NULL); } bool __stdcall DllMain(HANDLE process, DWORD reason, LPVOID lpReserved){ if(reason == DLL_PROCESS_ATTACH){ patch(); return 1; } else return 0; }Author: <3 attilathedud
Shoutouts:
-atom0s = for being smexy at C, and always providing me with help
-Couch = for the lawls, the fun code, and pointing out my un-clearness in regards to the use of eax
-learn_more = for correcting some issues with my code
-Muted = for pointing out un-clear descriptions
Please register or login to download attachments.