X64 Trampoline Hook

0 views
Skip to first unread message

Laura N Gerard

unread,
Aug 5, 2024, 10:19:51 AM8/5/24
to zardrawomrue
Nowthat I know enough to be dangerous, it seemed like fun to rectify this lack of sample code by building some hooking code from the ground up and walking through how to use that code to hook a running program. My past two blog posts were about making Notepad do weird stuff, so for the sake of variety, this post is going to pick on MSPaint instead.

If we look at the functions imported by mspaint in a debugger (like x64dbg), we can see that it is in fact importing OpenProcess(), so our first step is to throw a breakpoint there and then see what happens when we press the paint3d button.


I wish I had a better procedure to share, but my next step after realizing the above was to retry the same procedure except draw something in paint before I clicked the Paint3D button. The callstack I got then had a number of calls inside uiribbon.dll, and the highest mspaint.exe function in that stack ended up being the button handler. Its RVA was 0x6C6F0.


The first item on this list is really easy to get working for contrived cases, but really difficult to get right for real world use. Consider the following assembly (shown with the addresses of the instructions on the left):


Functions that are harder to hook with a trampoline might have multiple instructions contained in their first 5 bytes, or use instructions with relative operands, like jumps or rip-relative addresses. The snippet below shows an example of a function that has some of these issues.


To make sure we steal whole instructions, we need to use a disassembly library. The rest of this article is going to use the Capstone library for all disassembly tasks. Any disassembler will do, but Capstone has some features that are going to make our life easier later on.


One case where our naiive trampoline building function will fail is if any of the stolen instructions contain rip-relative addressing. In x64, there are a lot of instructions that do this, but the easiest example is a function that calls printf.


Next We need to figure out the address that the original instruction wanted to go to, and add an absolute jump (or call) to that address to our Absolute Instruction Table. The Capstone library handles calculating the target address of relative instructions for us automatically, which is handy.


Adding instructions to the Absolute Instruction Table is great and all, but in order for any of that work to matter, we also need to rewrite our stolen relative instructions to actually go to the AIT. Similar to the last step, this needs to be handled differently for jumps vs calls.


The snippet below shows how these new functions should be added to BuildTrampoline(). Notice that we need to wait until after calling these new rewrite functions before we can increment the absTableMem pointer.


Recall that the ARGB type is a uint32 with each byte representing a color channel. This means that all our payload need to do to make things orange is set some bits and pass the new ARGB value to the trampoline:


What is itInline hooking is a method of intercepting calls to target functions,which is mainly used by antiviruses, sandboxes, and malware. The general idea is to redirect a function to our own, so that we can perform processing before and/or after the function does its; this could include: checking parameters, shimming, logging, spoofing returned data, and filtering calls. Rootkits tend to use hooks to modify data returned from system calls in order to hide their presence, whilst security software uses them to prevent/monitor potentially malicious operations. The hooks are placed by directly modifying code within the target function (inline modification), usually by overwriting the first few bytes with a jump; this allows execution to be redirected before the function does any processing. Most hooking engines use a 32-bit relative jump (opcode 0xE9), which takes up 5 bytes of space.


How it worksWe will be using a trampoline based hook, which allows us to intercept functions, whilst still being able to call the originals (without unhooking them first). this hook is made up of 3 parts:


We could simply unhook MessageBoxA from within the proxy function, call it, then re hooking it; but if multiple threads are calling MessageBoxA at the same time, this would cause a race condition and possibly crash the program.


The site is secure.

The ensures that you are connecting to the official website and that any information you provide is encrypted and transmitted securely.


Purpose: To evaluate the accuracy of the trampoline and hook tests, used in the arthroscopic assessment of triangular fibrocartilage complex (TFCC) tears compared with arthroscopic direct visualization of the radiocarpal joint (RCJ) and of the distal radial ulnar joint (DRUJ).


Methods: In total, 135 patients (97 male, 38 female, mean age 43.5 years) were divided into 2 groups: (1) 80 patients with chronic ulnar-sided wrist pain and positive fovea sign and (2) 55 patients with other complaints. TFCC was assessed by RCJ and DRUJ arthroscopy and by the trampoline and hook tests to detect rupture of distal and proximal components of the TFCC. Accuracy, specificity, sensitivity, and likelihood ratio of the 2 diagnostic methods were measured and compared, using RCJ and DRUJ arthroscopy as reference.


Conclusions: The trampoline and hook tests can assure accurate diagnosis of peripheral TFCC tear. The hook test shows greater specificity and sensitivity to recognize foveal TFCC tears. Values of positive likelihood ratio suggest a greater probability to detect foveal laceration of peripheral TFCC for the hook test than for the trampoline test. These findings suggest that DRUJ arthroscopy is not necessary to confirm foveal incompetence of the TFCC, if the hook test is positive.


To implement this will require allocating an executable block of memory, copying in the original instructions, and then adding the unconditional jump at the end. You can write a CreateTrampoline function to do this:


Here you allocate a block of executable memory by calling the VirtualAlloc function with the PAGE_EXECUTE_READWRITE flag. After successfully allocating the memory, it is simply a matter of copying in the instructions with memcpy and appending the unconditional jump at the end. The memory block will have instructions similar to the ones in the table below, though with different runtime and destination addresses.


Now all that is left is to define the trampoline signature and function pointer, modify the HookDisplayMessageOnInterval function to call the trampoline, and add functionality in the InstallInlineHook function to set the trampoline pointer. This is achieved with the code shown below:


You may notice that there is a magic number (19) that is being passed to the CreateTrampoline function. This is the size, in bytes, of the instructions that will be relocated to the trampoline. Since the mov and jmp overwrite size takes up 12 bytes, and entire instructions need to be relocated (just copying 12 bytes will result in a partial copy of an instruction), the size needs to be rounded up to capture the full instruction. This is better illustrated in the table below; count the instruction bytes for the five instructions that are relocated to the trampoline.


The fix for this is not quite so simple; it boils down to needing a disassembler to identify whether the instruction performs any sort operation on a relative address. Fortunately, there is an inline hooking library that takes care of this and provides an easy-to-use API, which will be covered in the second half of this post.


The InlineHookWithTrampoline project provides the full implementation that was presented in this section. As before, you can begin by setting breakpoints on the InstallInlineHook and the line immediately following, as is shown below:


In the Disassembly window, you can set breakpoints on individual instructions. As in the code editor, click to the left of the address to enable a breakpoint on it. Once your breakpoint is enabled, you will see a red circle.


After setting this breakpoint, continue execution of the program. The breakpoint on the while loop should be hit, since it is the line of code that immediately follows the InstallInlineHook function. Continue execution once more. You should now be broken on the mov instruction in the trampoline stub. The call stack shows that the trampoline stub is being called from the HookDisplayMessageOnInterval function, as expected.


In the Disassembly window, with press the F11 key to let the Visual Studio debugger perform a Step Into operation. Continue doing this until you have executed past the final jmp rax instruction of the trampoline stub. Notice that you are back in the original HookDisplayMessage function, but at the address past the overwritten bytes.


This has shown the entire flow of events, starting with the generation of the trampoline stub, then its invocation by the hook function, and finally how the trampoline stub passes control back to the original function.


Detours is a library released by Microsoft that, among other useful features, provides the ability to install inline hooks on arbitrary functions. The library itself is open source, available for multiple architectures, and well documented. Detours has some nice abstractions; the library hides the need to change memory page protections, performs the trampoline function allocation and copy, and is able to handle fixing up relative instructions that were relocated. Internally, Detours is able to disassemble instructions at the target address, so as a user you do not need to calculate how many instructions to relocate and overwrite.


The InlineHookDetours project provides the full implementation that was presented in this post. When opening this project, a Windows x64 package of Detours will be installed via the provided vcpkg.json file. Once installed, the project can be built and the demo application can be run. Detours will work in a very similar manner to the previous section where inline hooks with trampolines were covered. The best way to see this is to add breakpoints on the InstallInlineHook function and the subsequent line, as shown below.

3a8082e126
Reply all
Reply to author
Forward
0 new messages