In this blogpost we are going to look into hooks, how to find them, and how to restore the original functions.
I’ve developed the methods discussed here by myself and they have been proven to be useful for me. I was assigned to evaluate the security and the inner working of a specific application control solution. I needed a practical and easy solution, without too much coding preferably using windbg. For that I wanted to be able to:
- Detect the DLL which performs hooking
- Detect all the hooks that it sets up
- Restore all the previous instructions (before the hook)
What are hooks?
As hooks is the thing we are looking for let’s briefly talk about what hooks actually are and how they look like.
Specifically we will cover MS Detours.
Basically hooking allows you to execute your own code when the target function is called. It was originally developed to extend the functionality of the functions of closed software. When your code is called by the hooked function it’s up to you what to you want to do next. You can for example inspect the arguments and based on that resume the execution of the original target function if you wish.
To better illustrate how a hook looks like, I’m going to use the picture from the “Detours: Binary Interception of Win32 Functions” document.
The picture above shows trampoline and target functions, before and after insertion of the detour (left and right).
Of course in order for this to be useful the trampoline function would normally end up calling your custom code, before resuming the target function. For us one important thing to notice is the jump instruction at the beginning of the target function. If it’s there this is a good indicator that a function is hooked.
As we can see, a jump instruction is used to hook a target function and replace the first 4 instructions of the original target function. This results in the target function jumping to a trampoline function and the trampoline function executing the original 4 instructions that were replaced. Then, a jump instruction is used again in the trampoline function to resume the execution of the target function after the jump instruction (TargetFunction+5).
To better demonstrate the concept, I’ve created a few simple programs.
- begin.exe – Calls CreateProcess API to start myapp.exe.
- myapp.exe – Simple program that shows a message box.
- AChook.dll – Application Control hooking DLL. Simple DLL that forbids any execution of CreateProcessA and CreateProcessW APIs.
Firs let’s show these programs in action. Let’s run begin.exe:
It shows a message box asking to inject a DLL. This halts execution until the “OK” button is clicked and allows us to take our time injecting a DLL if we want to.
Then it launches myapp.exe, which just shows another message box asking if you want to launch a virus. Of course myapp.exe is not a virus and just exits after showing the message box (no matter if the user clicks on “Yes” or “No”).
Now let’s run begin.exe again but this time let’s inject the AChook.dll into it while the message box is shown.
We use “Process Hacker” to inject AChook.dll.
AChook.dll also prints some additional messages to the console output:
When we click now on the OK button, myapp.exe does not run anymore and thus the virus message box is no longer shown. Instead additional messages are printed to the console by AChook.dll.
IDENTIFYING THE HOOKING DLL
First we need to identify which DLL is the one that sets the hooks.
To list the loaded DLLs of a running process we use “Process Explorer”.
We select the process begin.exe and press [Ctrl]+[D]:
Now we can look for any DLL that looks suspicious. In this case it’s easy because the DLL has the phrase “hook” in its name, which is something that certainly stands out!
A different way to identify the hooking DLL is to compare the list of loaded DLLs with and without the security solution active. To simulate this we run begin.exe twice – once with and once without the AChook.dll. To list the DLLs as a text we can use “listdlls”:
First we need to identify which DLL was injected into a process. We start by running listdlls against the just started begin.exe process and saving the output:
listdlls begin.exe > before
Then we inject AChook.dll using Process Hacker and save listdlls’s output again:
listdlls begin.exe > after
Next, we compare those two output files using “gvim” (of course any other text editor can be used).
As we can see below, a new DLL AChook.dll was added:
Alright. So far we determined that a DLL was injected to the process. At this point we could search the DLL on disk to see to if it belongs to your target security solution. In our case we created it ourselves though, so we’re not going to do that.
The DLL is suspicious because its name contains the phrase “hook”. However we want to gain more confidence that it really hooks anything.
When you are examining a security solution it’s always a good idea to read its documentation. The product that I was analysing had specifically mentioned that it uses MS Detours hooks to function. However, it did not mention anything regarding the application control implemented in kernel space and also did not mention which DLL it used for hooking.
Unfortunately there is no single (special) Windows API that would do the hooking. Instead it uses multiple APIs to do its job. I wanted to find a rare API or a sequence of APIs that I could use as some sort of signature. I found one API that is quite special and rarely used (unless you want to do hooking): “FlushInstructionCache”.
As the documentation says:
“Applications should call FlushInstructionCache if they generate or modify code in memory. The CPU cannot detect the change, and may execute the old code it cached.”
So if the MS Detours code wants its new jump instruction to be executed it needs to call FlushInstructionCache API. In summary what MS Detours needs to do when installing the hook is to:
- Allocate memory for the trampoline function;
- Change the access of the code of the target function to make it writable;
- Copy the instructions from the beginning of the target function (the ones that it’s going to replace) to previously allocated space; and make changes there so that the trampoline function ends up executing your code;
- Replace the beginning of the target function with a jump instruction to trampoline function;
- Change the access of the code of the target function back to the original access;
- Flush the instruction cache.
You can find the FlushInstructionCache function in the imports of AChook.dll as can be seen in IDA:
Or you can use “dumpbin” to do the same:
At this point we have a very suspicious DLL and we want to determine which APIs it hooks and also restore them.
IDENTIFYING HOOKS AND RESTORING THEM
Since I was experimenting with dynamic binary instrumentation tools before, I knew that it is also possible to detect hooks by using Intel’s Pintools. It requires you to write a small program however. I won’t go into detail here, maybe this is a topic for another blogpost.
But in short Pintools enables you to split the code into blocks, something very similar to what IDA does. It also enables you to determine to which binary or DLL this code block belongs to.
Remember that MS detours installed a jmp instruction at the beginning of the target API which jumped to a newly allocated memory region. So if you see at the beginning of the API that a code block is executed that does not belong to the API’s DLL then this API is hooked. The drawback of this solution is that the hooked API needs to run in order to be detected. It also does not allow you to retrieve the original bytes of the hooked API for restoration.
More information about Pintools can be found here.
Let’s discuss something much simpler and more effective instead. Remember that MS Detours first changes the memory to be writable and then changes it back, let’s use that to our advantage.
We will use windbg for this. What we need to do is to:
- Start begin.exe
- Attach windbg to the begin.exe process.
- Set a breakpoint on loading of AChook.dll (sxe ld AChook.dll)
- Continue execution of begin.exe process (g)
- Inject AChook.dll into begin.exe process (Process Hacker)
- The breakpoint will hit.
- Set new breakpoint on VirtualProtect with a custom command to print first 5 instructions and continue execution. (bp KERNELBASE!VirtualProtect “u rcx L5;g” )
- Set output log file and continue execution (.logopen C:\BLOGPOST\OUTPUT.log ; g)
- The debugger will start hitting and continuing the breakpoints. After the output stops moving click the pause button on the debugger.
- Don’t click on the ok button of the message box. Close the log file. Collect and inspect the data in the log file. Remove a few – if any – false positives (.logclose).
The whole process might look like this:
The output above shows that when the breakpoint of the CreateProcessWStub and CreateProcessAStub are hit for the first time, they are not hooked yet: they don’t contain the jmp instruction at the beginning yet. However, the second time they are hit we can see a jmp instruction at the beginning, thus we can cunclude that they are hooked.
From this output we know that CreateProcessW and CreateProcessA were hooked. It also gives us the original bytes so we could restore the original functions if we wanted to.
RESTORING ORIGINAL FUNCTIONS:
Using the above output of windbg, we can restore the original functions with the following windbg commands:
eb KERNEL32!CreateProcessWStub 4c 8b dc 48 83 ec 58 eb KERNEL32!CreateProcessAStub 4c 8b dc 48 83 ec 58
The steps are easier this time:
- Run begin.exe
- Inject AChook.dll into it (using Process Hacker)
- Attach windbg to the begin.exe process
- Run the commands mentioned above and continue execution (eb … ; g)
- Click on the “OK” button of the message box to launch myapp.exe
And – voilà! – here is the result:
In this blogpost we have discussed what hooks are, how to identify a DLL that does the hooking, how to identify the hooks that it sets and also how to restore the original functions once the hooking DLL was loaded. Of course a proper security solution uses controls in kernel space to do application control, so it’s not possible for the application to just restore the original functions. Although there could be implementation mistakes in that as well, but that is a story for another time.
I hope you enjoyed.
About the author
Oliver, is a cyber security expert at NVISO. He has almost a decade and a half of IT experience of which half of it is in cyber security. Throughout his career he has obtained many useful skills and also certificates. He’s constantly exploring and looking for more knowledge. You can find Oliver on LinkedIn.