@@ -156,6 +156,8 @@ inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
156
156
157
157
inline PBYTE detour_skip_jmp (PBYTE pbCode, PVOID *ppGlobals)
158
158
{
159
+ PBYTE pbCodeOriginal;
160
+
159
161
if (pbCode == NULL ) {
160
162
return NULL ;
161
163
}
@@ -179,6 +181,7 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
179
181
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1 ];
180
182
DETOUR_TRACE ((" %p->%p: skipped over short jump.\n " , pbCode, pbNew));
181
183
pbCode = pbNew;
184
+ pbCodeOriginal = pbCode;
182
185
183
186
// First, skip over the import vector if there is one.
184
187
if (pbCode[0 ] == 0xff && pbCode[1 ] == 0x25 ) { // jmp [imm32]
@@ -195,6 +198,23 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
195
198
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1 ];
196
199
DETOUR_TRACE ((" %p->%p: skipped over long jump.\n " , pbCode, pbNew));
197
200
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
+
198
218
}
199
219
}
200
220
return pbCode;
@@ -369,6 +389,8 @@ inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
369
389
370
390
inline PBYTE detour_skip_jmp (PBYTE pbCode, PVOID *ppGlobals)
371
391
{
392
+ PBYTE pbCodeOriginal;
393
+
372
394
if (pbCode == NULL ) {
373
395
return NULL ;
374
396
}
@@ -392,6 +414,7 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
392
414
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1 ];
393
415
DETOUR_TRACE ((" %p->%p: skipped over short jump.\n " , pbCode, pbNew));
394
416
pbCode = pbNew;
417
+ pbCodeOriginal = pbCode;
395
418
396
419
// First, skip over the import vector if there is one.
397
420
if (pbCode[0 ] == 0xff && pbCode[1 ] == 0x25 ) { // jmp [+imm32]
@@ -408,6 +431,21 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
408
431
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1 ];
409
432
DETOUR_TRACE ((" %p->%p: skipped over long jump.\n " , pbCode, pbNew));
410
433
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
+ }
411
449
}
412
450
}
413
451
return pbCode;
@@ -1151,9 +1189,45 @@ inline void detour_find_jmp_bounds(PBYTE pbCode,
1151
1189
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
1152
1190
}
1153
1191
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
+
1154
1223
inline BOOL detour_does_code_end_function (PBYTE pbCode)
1155
1224
{
1156
1225
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
+ }
1157
1231
if ((Opcode & 0xfffffc1f ) == 0xd65f0000 || // br <reg>
1158
1232
(Opcode & 0xfc000000 ) == 0x14000000 ) { // b <imm26>
1159
1233
return TRUE ;
0 commit comments