Introduction | |||||
============ | |||||
This project aims to give a simple overview on how good various x64 hooking | |||||
engines (on windows) are. I'll try to write various functions, that are hard to | |||||
patch and then see how each hooking engine does. | |||||
I'll test: | |||||
* [EasyHook]() | |||||
* [PolyHook]() | |||||
(I'd like to test detours, but I'm not willing to pay for it. So that isn't | |||||
tested :( ) | |||||
There are multiple things that make hooking difficult. Maybe you want to patch | |||||
while the application is running -- in that case you might get race conditions, | |||||
as the application is executing your half finished hook. Maybe the software has | |||||
some self protection features (or other software on the system provides that, | |||||
e.g. Trustee Rapport) | |||||
Evaluating how the hooking engines stack up against that is not the goal here. | |||||
This is just about the challenges the function to be hooked itself poses. | |||||
Namely: | |||||
* Are jumps relocated? | |||||
* What about RIP adressing? | |||||
* If it's a tail recurisve function, does the hooking engine handle it? | |||||
* How good is the dissassembler, how many instructions does it know? | |||||
Test cases | |||||
========== |
</Link> | </Link> | ||||
</ItemDefinitionGroup> | </ItemDefinitionGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<ClInclude Include="catch.hpp" /> | |||||
<ClInclude Include="simple_tests.h" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ClCompile Include="main.cpp" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Include="README.md" /> | |||||
<None Include="simple_tests.asm" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Object Include="simple_tests.obj" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||||
<ImportGroup Label="ExtensionTargets"> | <ImportGroup Label="ExtensionTargets"> |
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> | <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> | ||||
</Filter> | </Filter> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<ClInclude Include="catch.hpp"> | |||||
<Filter>Header Files</Filter> | |||||
</ClInclude> | |||||
<ClInclude Include="simple_tests.h"> | |||||
<Filter>Header Files</Filter> | |||||
</ClInclude> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ClCompile Include="main.cpp"> | |||||
<Filter>Source Files</Filter> | |||||
</ClCompile> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Include="simple_tests.asm"> | |||||
<Filter>Source Files</Filter> | |||||
</None> | |||||
<None Include="README.md" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Object Include="simple_tests.obj" /> | |||||
</ItemGroup> | |||||
</Project> | </Project> |
#include <stdint.h> | |||||
#include <iostream> | |||||
#define CATCH_CONFIG_MAIN | |||||
#include "catch.hpp" | |||||
#include "simple_tests.h" | |||||
TEST_CASE("Functions work as expected, unhooked") { | |||||
REQUIRE(_small() == 0); | |||||
REQUIRE(_branch(1) == 0); | |||||
REQUIRE(_branch(0) == 0); | |||||
for (int i = 0; i < 1000; i++) { | |||||
REQUIRE(_rip_relative() == rand()); | |||||
} | |||||
} |
format ms64 coff | |||||
section '.text' code readable writeable executable | |||||
use64 | |||||
public _small | |||||
_small: | |||||
xor eax, eax | |||||
ret | |||||
public _rip_relative | |||||
_rip_relative: | |||||
mov eax, [seed] | |||||
mov ecx, 214013 | |||||
mul ecx | |||||
add eax, 2531011 | |||||
mov [seed], eax | |||||
shr eax, 16 | |||||
and eax, 0x7FFF | |||||
ret | |||||
seed dd 1 | |||||
public _branch | |||||
_branch: | |||||
and rax, 1 | |||||
jz @branch_ret | |||||
xor rax, rax | |||||
nop ; Just some padding, so the function can't be copied entirely into the | |||||
nop ; trampoline | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
@branch_ret: | |||||
ret | |||||
#pragma once | |||||
extern "C" { | |||||
/** | |||||
* A small function, that always returns 0 | |||||
*/ | |||||
uint64_t _small(void); | |||||
/** | |||||
* This function checks if the parameter is even or odd, and then | |||||
* always returns 0. | |||||
* | |||||
* The check is done with a branch, so the hooking engine has to take that | |||||
* into account. | |||||
* | |||||
* @param Number to be checked | |||||
*/ | |||||
uint64_t _branch(uint64_t); | |||||
/** | |||||
* Replicates the MSVCRT rand(). | |||||
* | |||||
* This function is used to check whether the hooking engine correctly fixes | |||||
* rip relative addressing. | |||||
* | |||||
* @internal: | |||||
* static seed = 1; | |||||
* return( ((seed = seed * 214013L | |||||
* + 2531011L) >> 16) & 0x7fff ); | |||||
*/ | |||||
uint64_t _rip_relative(void); | |||||
}; |