Understanding/Detecting Inline Hooks/ WinAPI Hooks (Ring3)

Ever wonder how malware is able to harvest credentials from within web browsers? The most popular method is a Man-in-The-Browser (MiTB) attack known as inline hooking (sometimes referred to as detours). Inline hooks are incredibly versatile and very common in malware. With inline hooks, malware can become puppetmaster of any process, manipulating it into doing whatever the malware author pleases. Let’s see how they do it.


How do you get your code into the target process?

The first step in inline hooking is to get your code to run inside the target process. This process is known as injection.

There are several common injection methods that we see in the wild. For our example, we use VirtualAllocEx to create a memory region inside of Firefox at 0x0D000000CreateRemoteThread would then be used to begin execution inside of Firefox in the 0x0D000000 region in order to set the hooks and to take care of initialization. (The actual injection process is an intricate topic on its own and is outside the scope of this post.) Here is what our injected code block looks like. The highlighted areas will become clearer as we go on.

There are four parts to the inline hook: the hook, the malicious code (which is filled with NOPs here), the execution of trampled bytes, and the return.

What is a Windows API?

It’s a set of functions and data structures that a Windows program can use to ask Windows to do something, like opening a file, displaying a message, etc.

Pretty much everything that a Windows program does involves calling various API functions.

Collectively, all the API functions that Windows makes available are called “The Windows API”.

The hook

The hook, sometimes referred to as the trampoline, is analogous to a traffic officer redirecting traffic to another location. For our example, we will be hooking the send() function from the ws2_32 library. Below is a picture of the first few instructions in the ws2_32!send function as it appears normally. In order to place the hook, we need to overwrite some existing instructions in the function. The green box highlights the bytes that we will overwrite.

Notice that five bytes are going to be overwritten. This is the exact number of bytes needed for our hook: a jmp instruction that jumps into our code at 0x0D000000. (There are other ways to place the hook such as with a push addr, ret instruction combination.) Here is the same send() function after the hook is placed:

The malicious code

Now that execution flow has been redirected to our injected code, registers must be saved to ensure that we do not crash when we return execution to the send()function. In 32-bit processes, we can save all of the registers with the pusha instruction. When the malicious code is finished executing, the popad instruction will restore all the registers to the state they were in when the send() call was initially executed. Now that we are in control of the send() function, we must come up with something interesting to do. One thing we could do is to look and see if a POST request is being sent and send that data back to our command and control in hopes it will contain login credentials.


The execution of trampled bytes and the return

After we have done something useful with our malicious code, we have to make sure our process returns to the exact same state it was in before our hook took over. First, we use the popad instruction to restore all of our registers. Remember those five bytes we trampled over when we set the hook? Those original instructions still need to be run, so they were copied to the end of the malicious code during the hooking process so that they seamlessly run after the registers are restored. Lastly, we jump home safely into the send function + 0x05 (the length of our hook).

Putting it all together

Let’s take one more look at everything we did to get the full picture. Follow the arrows to see the flow of execution for a normal, unhooked send() function in Firefox:

After injecting our detour, the flow of execution now looks like this:

Reasons for hooking API calls

You can probably think of a reason to hook every API call imaginable. Here are a few:

  • urlmon!URLDownloadToFile – Used to intercept downloaded files. Shifu hooks this function in every process in order to prevent new malware from downloading.
  • ws2_32!send – Used to capture POST credentials from unencrypted traffic.
  • GetHostByName – Shifu hooks this function in order to ignore traffic to its command and control sites.
  • ws2_32!recv – Used to capture incoming packet data from unencrypted traffic.
  • Advapi32!CryptEncrypt – Used for capturing data before it gets encrypted because it won’t be plain text when it goes out the send() function.
  • Advapi32!CryptDecrypt – Used to decrypt encrypted data coming in from the recv() function.
  • User32!GetMessage – Shifu uses a hook here to intercept mouse click messages in order to take pictures of virtual keyboard clicks.
  • Kernel32!ExitProcess– Useful to prevent process from closing itself (bypassing game anticheats)


Here is a pseudocode of a simple solution, but not very effective if the attacker is a harduser. This following code will check if the prologue of api ExitProcess starts with 0xE9 (opcode JMP)

FARPROC Address = GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitProcess");
if (*(BYTE*)Address == 0xE9)
 printf("Api hooked\n");//Do your thing


4 thoughts on “Understanding/Detecting Inline Hooks/ WinAPI Hooks (Ring3)”

  1. Great article. One question, at what point in the injection payload do you actually overwrite the first 5 bytes of send()?

    Is that handled where you say “in order to set the hooks and to take care of initialization.”, but not shown in the layout of memory at 0x0D000000? Meaning you start execution somewhere before 0x0D000000, and the first code that runs overwrites send, hooking subsequent calls to 0x0D000000?


  2. What he/she is saying is that after they exploit ( gain control of a program via Instruction Pointer, which is your processor’s way of telling a program what to execute next.) , they clear out some empty space using legitimate Windows Programming Functions ( with that Pointer described before ). In this case he is using VirtualAllocEx to say “I want X number of bytes/space.” You can write/modify anything into that space at that point, which is where the code is sitting idle.

    Now that the code is sitting idle, they can using many methods to execute it. In this example they are taking another legitimate windows function, overwriting the beginning of it, it goes to the “custom code”, and then it returns to where it left off in the legit windows function. As displayed above, he is overwriting an exact number of bytes because code usually works by offsets and not exact addresses when moving about executing. you dont go to an exact address in main(), but you go to main() plus a number of bytes where you left off.

    This is kind of an oversimplification because I am addressing a generic audience. Its like if you were robbing a bank and you had a lookout. The lookout could go do whatever he wanted while you are waiting on him, but as long as he is there when you return, you dont know the difference, but something happened.

    A key note to reference is that few lines of code are able to have read, write, and executable access in memory. that’s why they are “segments” that regulate the roles of executable code that never changes(executable), and varaibles that change(readable and writable). I would be suspicious of a any line of code that had all three permission, aside from software such as virus protection which is usually permitted based on its role.

  3. I’m not 100% sure, but I thought the function of the mov edi, edi instruction in the prologue was as a 2-byte nop (effectively) that was supposed to be overwritten for a 2-byte relative jump to the 0x90’s just before, which gives you a larger (5-byte) trampoline. That way you don’t victimize any important instructions like the push ebp, etc.

Leave a Reply

Your email address will not be published. Required fields are marked *