1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
#include "MidfunctionHook.h"
#include <cassert>
//
//Overwrites the VALUE of a register. To be used inside a proxy function.
//This function is required because changes like: registers->rdi = 0xdeadbeef will get restored by the trampoline (pop rdi)
//To change the value pointed to by a register just dereference it instead: *(int64*)registers->rdi = 0x4711 (usage of this function not required)
void MidfunctionHook::OverwriteRegister(int64_t rsp, Register reg, int64_t value)
{
*((int64_t*)rsp + (int)reg) = value;
}
//
//Hooks a function anywhere by placing jumping to a trampoline. The trampoline saves all registers and then calls the proxy function
//0xff, 0x25, 0x0, 0x0, 0x0, 0x0 JMP[rip + 0]
//0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 absolute address of jump
//The proxy functions signature is expected to be: void __fastcall proxyFunction(registers* registers)
//Important: the overwritten bytes are NOT relocated meaning only position independet instructions can be overwritten
//
//TODO:
//When hooking within prologe or epicloge of a function the stack may not be aligned
//use BTR RSP, 3 to align stack and save RSP to then later restore it
void MidfunctionHook::Hook(BYTE* sourceAddress, BYTE* targetAddress, const int hookLength)
{
const int stubLength = 380;
const int stubJumpBackLength = 14;
const int proxyFunctionAddressIndex = 186;
//14 bytes are required to place JMP[rip+0x] 0x1122334455667788
assert(hookLength >= stubJumpBackLength);
//1. save xmm registers
//2. save general purpose registers
//3. call proxy function
//4. restore all registers
//5. jump back to orignal function
BYTE stub[stubLength] = {
0x9C, //pushfq
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x44, 0x0F, 0x7F, 0x3C, 0x24, //movdqu XMMWORD PTR [rsp],xmm15
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x44, 0x0F, 0x7F, 0x34, 0x24, //movdqu XMMWORD PTR [rsp],xmm14
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x44, 0x0F, 0x7F, 0x2C, 0x24, //movdqu XMMWORD PTR [rsp],xmm13
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x44, 0x0F, 0x7F, 0x24, 0x24, //movdqu XMMWORD PTR [rsp],xmm12
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x44, 0x0F, 0x7F, 0x1C, 0x24, //movdqu XMMWORD PTR [rsp],xmm11
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x44, 0x0F, 0x7F, 0x14, 0x24, //movdqu XMMWORD PTR [rsp],xmm10
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x44, 0x0F, 0x7F, 0x0C, 0x24, //movdqu XMMWORD PTR [rsp],xmm9
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x44, 0x0F, 0x7F, 0x04, 0x24, //movdqu XMMWORD PTR [rsp],xmm8
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x0F, 0x7F, 0x3C, 0x24, //movdqu XMMWORD PTR [rsp],xmm7
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x0F, 0x7F, 0x34, 0x24, //movdqu XMMWORD PTR [rsp],xmm6
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x0F, 0x7F, 0x2C, 0x24, //movdqu XMMWORD PTR [rsp],xmm5
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x0F, 0x7F, 0x24, 0x24, //movdqu XMMWORD PTR [rsp],xmm4
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x0F, 0x7F, 0x1C, 0x24, //movdqu XMMWORD PTR [rsp],xmm3
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x0F, 0x7F, 0x14, 0x24, //movdqu XMMWORD PTR [rsp],xmm2
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x0F, 0x7F, 0x0C, 0x24, //movdqu XMMWORD PTR [rsp],xmm1
0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10
0xF3, 0x0F, 0x7F, 0x04, 0x24, //movdqu XMMWORD PTR [rsp],xmm0
0x41, 0x57, //push r15
0x41, 0x56, //push r14
0x41, 0x55, //push r13
0x41, 0x54, //push r12
0x41, 0x53, //push r11
0x41, 0x52, //push r10
0x41, 0x51, //push r9
0x41, 0x50, //push r8
0x57, //push rdi
0x56, //push rsi
0x55, //push rbp
0x53, //push rbx
0x52, //push rdx
0x51, //push rcx
0x50, //push rax
0x54, //push rsp
0x48, 0x89, 0xE1, //mov rcx,rsp
0x48, 0x83, 0xEC, 0x28, //sub rsp,0x20 (allocate shadow space)
0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, //movabs rax,0x1122334455667788 (use register to have an absolute 8 byte call)
0xFF, 0xD0, //call rax (call proxy function)
0x48, 0x83, 0xC4, 0x28, //add rsp,0x20 (deallocate shadow space)
0x48, 0x83, 0xC4, 0x08, //add rsp, 0x8
0x58, //pop rax
0x59, //pop rcx
0x5A, //pop rdx
0x5B, //pop rbx
0x5D, //pop rbp
0x5E, //pop rsi
0x5F, //pop rdi
0x41, 0x58, //pop r8
0x41, 0x59, //pop r9
0x41, 0x5A, //pop r10
0x41, 0x5B, //pop r11
0x41, 0x5C, //pop r12
0x41, 0x5D, //pop r13
0x41, 0x5E, //pop r14
0x41, 0x5F, //pop r15
0xF3, 0x0F, 0x6F, 0x04, 0x24, //movdqu xmm0,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x0F, 0x6F, 0x0C, 0x24, //movdqu xmm1,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x0F, 0x6F, 0x14, 0x24, //movdqu xmm2,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x0F, 0x6F, 0x1C, 0x24, //movdqu xmm3,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x0F, 0x6F, 0x24, 0x24, //movdqu xmm4,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x0F, 0x6F, 0x2C, 0x24, //movdqu xmm5,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x0F, 0x6F, 0x34, 0x24, //movdqu xmm6,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x0F, 0x6F, 0x3C, 0x24, //movdqu xmm7,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x44, 0x0F, 0x6F, 0x04, 0x24, //movdqu xmm8,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x44, 0x0F, 0x6F, 0x0C, 0x24, //movdqu xmm9,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x44, 0x0F, 0x6F, 0x14, 0x24, //movdqu xmm10,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x44, 0x0F, 0x6F, 0x1C, 0x24, //movdqu xmm11,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x44, 0x0F, 0x6F, 0x24, 0x24, //movdqu xmm12,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x44, 0x0F, 0x6F, 0x2C, 0x24, //movdqu xmm13,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x44, 0x0F, 0x6F, 0x34, 0x24, //movdqu xmm14,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0xF3, 0x44, 0x0F, 0x6F, 0x3C, 0x24, //movdqu xmm15,XMMWORD PTR[rsp]
0x48, 0x83, 0xC4, 0x10, //add rsp,0x10
0x9D //popfq
};
BYTE stubJumpBack[stubJumpBackLength] = {
0xff, 0x25, 0x0, 0x0, 0x0,0x0, //JMP[rip + 0]
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 //absolute address of jump
};
//remember for unhooking
this->hookLength = hookLength;
this->sourceAddress = sourceAddress;
//save original bytes
originalBytes = new BYTE[hookLength];
memcpy(originalBytes, sourceAddress, hookLength);
//allocate space for stub + space for overwritten bytes + jumpback
trampoline = (BYTE*)VirtualAlloc(NULL, stubLength + hookLength + stubJumpBackLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//copy stub to trampoline
memcpy(trampoline, stub, stubLength);
//copy original bytes to trampoline
memcpy(&trampoline[stubLength], originalBytes, hookLength);
//copy jump back to original code
memcpy(&trampoline[stubLength + hookLength], stubJumpBack, hookLength);
//insert address of proxy function to call instruction
*(int64_t*)&trampoline[proxyFunctionAddressIndex] = (int64_t)targetAddress;
//write jump from trampoline to original code
*(int64_t*)&trampoline[stubLength + hookLength + 6] = (int64_t)&sourceAddress[hookLength];
//make trampoline executable
//DWORD pageProtection;
//VirtualProtect(trampoline, stubLength + hookLength + stubJumpBackLength, PAGE_EXECUTE_READWRITE, &pageProtection);
//make page of original code writeable
DWORD pageProtection;
VirtualProtect(sourceAddress, hookLength, PAGE_READWRITE, &pageProtection);
//write JMP from original code to trampoline
sourceAddress[0] = 0xFF; //opcodes = JMP [rip+0]
sourceAddress[1] = 0x25; //opcodes = JMP [rip+0]
*(uint32_t*)(&sourceAddress[2]) = 0; //relative distance from RIP (+0)
*(uint64_t*)(&sourceAddress[2 + 4]) = (uint64_t)(trampoline); //destination to jump to
//NOP left over bytes
for (int i = stubJumpBackLength; i < hookLength; i++)
{
sourceAddress[i] = 0x90;
}
//restore page protection of original code
VirtualProtect(sourceAddress, hookLength, pageProtection, &pageProtection);
}
//
//Unhooks a previously hooked function by copying back the original bytes
void MidfunctionHook::Unhook()
{
//make page writeable
DWORD dwback;
VirtualProtect(sourceAddress, hookLength, PAGE_READWRITE, &dwback);
//copy back original bytes
memcpy(sourceAddress, originalBytes, hookLength);
//restore page protection
VirtualProtect(sourceAddress, hookLength, dwback, &dwback);
//clean up allocated memory
delete[] originalBytes;
//memory leak but enables unhooking inside hooked function and makes it threadsafe?
//delete[] trampoline;
}
|