Skip to content

Commit 309926a

Browse files
authored
Fix detours ability to identify OS applied patches. (microsoft#318)
* Updated detours ability to identify patches applied by OS * add back checksum zeroing * cleanup spacing and stay consistent with logical AND usage
1 parent 4b8c659 commit 309926a

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

src/detours.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
156156

157157
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
158158
{
159+
PBYTE pbCodeOriginal;
160+
159161
if (pbCode == NULL) {
160162
return NULL;
161163
}
@@ -179,6 +181,7 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
179181
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
180182
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
181183
pbCode = pbNew;
184+
pbCodeOriginal = pbCode;
182185

183186
// First, skip over the import vector if there is one.
184187
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32]
@@ -195,6 +198,23 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
195198
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
196199
DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew));
197200
pbCode = pbNew;
201+
202+
// Patches applied by the OS will jump through an HPAT page to get
203+
// the target function in the patch image. The jump is always performed
204+
// to the target function found at the current instruction pointer +
205+
// PAGE_SIZE - 6 (size of jump).
206+
// If this is an OS patch, we want to detour at the point of the target function
207+
// padding in the base image. Ideally, we would detour at the target function, but
208+
// since it's patched it begins with a short jump (to padding) which isn't long
209+
// enough to hold the detour code bytes.
210+
if (pbCode[0] == 0xff &&
211+
pbCode[1] == 0x25 &&
212+
*(UNALIGNED INT32 *)&pbCode[2] == (UNALIGNED INT32)(pbCode + 0x1000)) { // jmp [rip+PAGE_SIZE-6]
213+
214+
DETOUR_TRACE(("%p->%p: OS patch encountered, reset back to long jump 5 bytes prior to target function. \n", pbCode, pbCodeOriginal));
215+
pbCode = pbCodeOriginal;
216+
}
217+
198218
}
199219
}
200220
return pbCode;
@@ -369,6 +389,8 @@ inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
369389

370390
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
371391
{
392+
PBYTE pbCodeOriginal;
393+
372394
if (pbCode == NULL) {
373395
return NULL;
374396
}
@@ -392,6 +414,7 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
392414
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
393415
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
394416
pbCode = pbNew;
417+
pbCodeOriginal = pbCode;
395418

396419
// First, skip over the import vector if there is one.
397420
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
@@ -408,6 +431,21 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
408431
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
409432
DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew));
410433
pbCode = pbNew;
434+
435+
// Patches applied by the OS will jump through an HPAT page to get
436+
// the target function in the patch image. The jump is always performed
437+
// to the target function found at the current instruction pointer +
438+
// PAGE_SIZE - 6 (size of jump).
439+
// If this is an OS patch, we want to detour at the point of the target function
440+
// in the base image. Since we need 5 bytes to perform the jump, detour at the
441+
// point of the long jump instead of the short jump at the start of the target.
442+
if (pbCode[0] == 0xff &&
443+
pbCode[1] == 0x25 &&
444+
*(UNALIGNED INT32 *)&pbCode[2] == 0xFFA) { // jmp [rip+PAGE_SIZE-6]
445+
446+
DETOUR_TRACE(("%p->%p: OS patch encountered, reset back to long jump 5 bytes prior to target function. \n", pbCode, pbCodeOriginal));
447+
pbCode = pbCodeOriginal;
448+
}
411449
}
412450
}
413451
return pbCode;
@@ -1151,9 +1189,45 @@ inline void detour_find_jmp_bounds(PBYTE pbCode,
11511189
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
11521190
}
11531191

1192+
inline BOOL detour_is_code_os_patched(PBYTE pbCode)
1193+
{
1194+
// Identify whether the provided code pointer is a OS patch jump.
1195+
// We can do this by checking if a branch (b <imm26>) is present, and if so,
1196+
// it must be jumping to an HPAT page containing ldr <reg> [PC+PAGE_SIZE-4], br <reg>.
1197+
ULONG Opcode = fetch_opcode(pbCode);
1198+
1199+
if ((Opcode & 0xfc000000) != 0x14000000) {
1200+
return FALSE;
1201+
}
1202+
// The branch must be jumping forward if it's going into the HPAT.
1203+
// Check that the sign bit is cleared.
1204+
if ((Opcode & 0x2000000) != 0) {
1205+
return FALSE;
1206+
}
1207+
ULONG Delta = (ULONG)((Opcode & 0x1FFFFFF) * 4);
1208+
PBYTE BranchTarget = pbCode + Delta;
1209+
1210+
// Now inspect the opcodes of the code we jumped to in order to determine if it's HPAT.
1211+
ULONG HpatOpcode1 = fetch_opcode(BranchTarget);
1212+
ULONG HpatOpcode2 = fetch_opcode(BranchTarget + 4);
1213+
1214+
if (HpatOpcode1 != 0x58008010) { // ldr <reg> [PC+PAGE_SIZE]
1215+
return FALSE;
1216+
}
1217+
if (HpatOpcode2 != 0xd61f0200) { // br <reg>
1218+
return FALSE;
1219+
}
1220+
return TRUE;
1221+
}
1222+
11541223
inline BOOL detour_does_code_end_function(PBYTE pbCode)
11551224
{
11561225
ULONG Opcode = fetch_opcode(pbCode);
1226+
// When the OS has patched a function entry point, it will incorrectly
1227+
// appear as though the function is just a single branch instruction.
1228+
if (detour_is_code_os_patched(pbCode)) {
1229+
return FALSE;
1230+
}
11571231
if ((Opcode & 0xfffffc1f) == 0xd65f0000 || // br <reg>
11581232
(Opcode & 0xfc000000) == 0x14000000) { // b <imm26>
11591233
return TRUE;

0 commit comments

Comments
 (0)