aboutsummaryrefslogtreecommitdiff
path: root/MidfunctionHook.cpp
blob: 9f2069124ec0fc38cdae7d87018fbcf379baeb09 (plain)
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;
}