CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/external/source/byakugan/detours/detours.cpp
Views: 11777
1
//////////////////////////////////////////////////////////////////////////////
2
//
3
// Core Detours Functionality (detours.cpp of detours.lib)
4
//
5
// Microsoft Research Detours Package, Version 2.1.
6
//
7
// Copyright (c) Microsoft Corporation. All rights reserved.
8
//
9
#include <windows.h>
10
11
#if (_MSC_VER < 1299)
12
#pragma warning(disable: 4710)
13
#endif
14
15
//#define DETOUR_DEBUG 1
16
#define DETOURS_INTERNAL
17
18
#include "detours.h"
19
#include "detoured.h"
20
21
#if !defined(DETOURS_X86) && !defined(DETOURS_X64) && !defined(DETOURS_IA64)
22
#error Must define one of DETOURS_X86, DETOURS_X64, or DETOURS_IA64
23
#endif
24
25
//////////////////////////////////////////////////////////////////////////////
26
//
27
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
28
{
29
MEMORY_BASIC_INFORMATION mbi;
30
VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
31
__try {
32
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
33
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
34
return false;
35
}
36
37
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
38
pDosHeader->e_lfanew);
39
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
40
return false;
41
}
42
43
if (pbAddress >= ((PBYTE)pDosHeader +
44
pNtHeader->OptionalHeader
45
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
46
pbAddress < ((PBYTE)pDosHeader +
47
pNtHeader->OptionalHeader
48
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
49
pNtHeader->OptionalHeader
50
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
51
return true;
52
}
53
return false;
54
}
55
__except(EXCEPTION_EXECUTE_HANDLER) {
56
return false;
57
}
58
}
59
60
///////////////////////////////////////////////////////////////////////// X86.
61
//
62
#ifdef DETOURS_X86
63
64
struct _DETOUR_TRAMPOLINE
65
{
66
BYTE rbCode[23]; // target code + jmp to pbRemain
67
BYTE cbTarget; // size of target code moved.
68
PBYTE pbRemain; // first instruction after moved code. [free list]
69
PBYTE pbDetour; // first instruction of detour function.
70
};
71
72
enum {
73
SIZE_OF_JMP = 5
74
};
75
76
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
77
{
78
PBYTE pbJmpSrc = pbCode + 5;
79
*pbCode++ = 0xE9; // jmp +imm32
80
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
81
return pbCode;
82
}
83
84
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
85
{
86
PBYTE pbJmpSrc = pbCode + 6;
87
*pbCode++ = 0xff; // jmp [+imm32]
88
*pbCode++ = 0x25;
89
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);
90
return pbCode;
91
}
92
93
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
94
{
95
while (pbCode < pbLimit) {
96
*pbCode++ = 0xcc; // brk;
97
}
98
return pbCode;
99
}
100
101
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
102
{
103
if (pbCode == NULL) {
104
return NULL;
105
}
106
if (ppGlobals != NULL) {
107
*ppGlobals = NULL;
108
}
109
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
110
// Looks like an import alias jump, then get the code it points to.
111
PBYTE pbTarget = *(PBYTE *)&pbCode[2];
112
if (detour_is_imported(pbCode, pbTarget)) {
113
PBYTE pbNew = *(PBYTE *)pbTarget;
114
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
115
return pbNew;
116
}
117
}
118
else if (pbCode[0] == 0xeb) { // jmp +imm8
119
// These just started appearing with CL13.
120
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
121
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
122
if (pbNew[0] == 0xe9) { // jmp +imm32
123
pbCode = pbNew;
124
pbNew = pbCode + *(INT32 *)&pbCode[1];
125
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
126
}
127
return pbNew;
128
}
129
return pbCode;
130
}
131
132
inline BOOL detour_does_code_end_function(PBYTE pbCode)
133
{
134
if (pbCode[0] == 0xe9 || // jmp +imm32
135
pbCode[0] == 0xe0 || // jmp eax
136
pbCode[0] == 0xc2 || // ret +imm8
137
pbCode[0] == 0xc3 || // ret
138
pbCode[0] == 0xcc) { // brk
139
return TRUE;
140
}
141
else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
142
return TRUE;
143
}
144
else if ((pbCode[0] == 0x26 || // jmp es:
145
pbCode[0] == 0x2e || // jmp cs:
146
pbCode[0] == 0x36 || // jmp ss:
147
pbCode[0] == 0xe3 || // jmp ds:
148
pbCode[0] == 0x64 || // jmp fs:
149
pbCode[0] == 0x65) && // jmp gs:
150
pbCode[1] == 0xff && // jmp [+imm32]
151
pbCode[2] == 0x25) {
152
return TRUE;
153
}
154
return FALSE;
155
}
156
#endif // DETOURS_X86
157
158
///////////////////////////////////////////////////////////////////////// X64.
159
//
160
#ifdef DETOURS_X64
161
#error Feature not supported in this release.
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#endif // DETOURS_X64
259
260
//////////////////////////////////////////////////////////////////////// IA64.
261
//
262
#ifdef DETOURS_IA64
263
#error Feature not supported in this release.
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
#endif
406
407
//////////////////////////////////////////////// Trampoline Memory Management.
408
//
409
struct DETOUR_REGION
410
{
411
ULONG dwSignature;
412
DETOUR_REGION * pNext; // Next region in list of regions.
413
DETOUR_TRAMPOLINE * pFree; // List of free trampolines in this region.
414
};
415
typedef DETOUR_REGION * PDETOUR_REGION;
416
417
const ULONG DETOUR_REGION_SIGNATURE = 'Rrtd';
418
const ULONG DETOUR_REGION_SIZE = 0x10000;
419
const ULONG DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE
420
/ sizeof(DETOUR_TRAMPOLINE)) - 1;
421
static PDETOUR_REGION s_pRegions = NULL; // List of all regions.
422
static PDETOUR_REGION s_pRegion = NULL; // Default region.
423
424
static void detour_writable_trampoline_regions()
425
{
426
// Mark all of the regions as writable.
427
for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
428
DWORD dwOld;
429
VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READWRITE, &dwOld);
430
}
431
}
432
433
static void detour_runnable_trampoline_regions()
434
{
435
// Mark all of the regions as executable.
436
for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
437
DWORD dwOld;
438
VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READ, &dwOld);
439
}
440
}
441
442
static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
443
{
444
// We have to place trampolines within +/- 2GB of target.
445
// The allocation code assumes that
446
447
PDETOUR_TRAMPOLINE pLo = (PDETOUR_TRAMPOLINE)
448
((pbTarget > (PBYTE)0x7ff80000)
449
? pbTarget - 0x7ff80000 : (PBYTE)(ULONG_PTR)DETOUR_REGION_SIZE);
450
PDETOUR_TRAMPOLINE pHi = (PDETOUR_TRAMPOLINE)
451
((pbTarget < (PBYTE)0xffffffff80000000)
452
? pbTarget + 0x7ff80000 : (PBYTE)0xfffffffffff80000);
453
DETOUR_TRACE(("[%p..%p..%p]\n", pLo, pbTarget, pHi));
454
455
PDETOUR_TRAMPOLINE pTrampoline = NULL;
456
457
// Insure that there is a default region.
458
if (s_pRegion == NULL && s_pRegions != NULL) {
459
s_pRegion = s_pRegions;
460
}
461
462
// First check the default region for an valid free block.
463
if (s_pRegion != NULL && s_pRegion->pFree != NULL &&
464
s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {
465
466
found_region:
467
pTrampoline = s_pRegion->pFree;
468
// do a last sanity check on region.
469
if (pTrampoline < pLo || pTrampoline > pHi) {
470
return NULL;
471
}
472
s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pTrampoline->pbRemain;
473
memset(pTrampoline, 0xcc, sizeof(*pTrampoline));
474
return pTrampoline;
475
}
476
477
// Then check the existing regions for a valid free block.
478
for (s_pRegion = s_pRegions; s_pRegion != NULL; s_pRegion = s_pRegion->pNext) {
479
if (s_pRegion != NULL && s_pRegion->pFree != NULL &&
480
s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {
481
goto found_region;
482
}
483
}
484
485
// We need to allocate a new region.
486
487
// Round pbTarget down to 64K block.
488
pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);
489
490
// First we search down (within the valid region)
491
492
DETOUR_TRACE((" Looking for free region below %p:\n", pbTarget));
493
494
PBYTE pbTry;
495
for (pbTry = pbTarget; pbTry > (PBYTE)pLo;) {
496
MEMORY_BASIC_INFORMATION mbi;
497
498
DETOUR_TRACE((" Try %p\n", pbTry));
499
if (pbTry >= (PBYTE)(ULONG_PTR)0x70000000 &&
500
pbTry <= (PBYTE)(ULONG_PTR)0x80000000) {
501
// Skip region reserved for system DLLs.
502
pbTry = (PBYTE)(ULONG_PTR)(0x70000000 - DETOUR_REGION_SIZE);
503
}
504
if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) {
505
break;
506
}
507
508
DETOUR_TRACE((" Try %p => %p..%p %6x\n",
509
pbTry,
510
mbi.BaseAddress,
511
(PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
512
mbi.State));
513
514
if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) {
515
s_pRegion = (DETOUR_REGION*)VirtualAlloc(pbTry,
516
DETOUR_REGION_SIZE,
517
MEM_COMMIT|MEM_RESERVE,
518
PAGE_EXECUTE_READWRITE);
519
if (s_pRegion != NULL) {
520
alloced_region:
521
s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE;
522
s_pRegion->pFree = NULL;
523
s_pRegion->pNext = s_pRegions;
524
s_pRegions = s_pRegion;
525
DETOUR_TRACE((" Allocated region %p..%p\n\n",
526
s_pRegion, ((PBYTE)s_pRegion) + DETOUR_REGION_SIZE - 1));
527
528
// Put everything but the first trampoline on the free list.
529
PBYTE pFree = NULL;
530
pTrampoline = ((PDETOUR_TRAMPOLINE)s_pRegion) + 1;
531
for (int i = DETOUR_TRAMPOLINES_PER_REGION - 1; i > 1; i--) {
532
pTrampoline[i].pbRemain = pFree;
533
pFree = (PBYTE)&pTrampoline[i];
534
}
535
s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pFree;
536
goto found_region;
537
}
538
else {
539
DETOUR_TRACE(("Error: %p %d\n", pbTry, GetLastError()));
540
break;
541
}
542
}
543
pbTry = (PBYTE)mbi.AllocationBase - DETOUR_REGION_SIZE;
544
}
545
546
DETOUR_TRACE((" Looking for free region above %p:\n", pbTarget));
547
548
for (pbTry = pbTarget; pbTry < (PBYTE)pHi;) {
549
MEMORY_BASIC_INFORMATION mbi;
550
551
if (pbTry >= (PBYTE)(ULONG_PTR)0x70000000 &&
552
pbTry <= (PBYTE)(ULONG_PTR)0x80000000) {
553
// Skip region reserved for system DLLs.
554
pbTry = (PBYTE)(ULONG_PTR)(0x80000000 + DETOUR_REGION_SIZE);
555
}
556
if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) {
557
break;
558
}
559
560
DETOUR_TRACE((" Try %p => %p..%p %6x\n",
561
pbTry,
562
mbi.BaseAddress,
563
(PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
564
mbi.State));
565
566
if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) {
567
ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);
568
if (extra != 0) {
569
// WinXP64 returns free areas that aren't REGION aligned to
570
// 32-bit applications.
571
ULONG_PTR adjust = DETOUR_REGION_SIZE - extra;
572
mbi.RegionSize -= adjust;
573
((PBYTE&)mbi.BaseAddress) += adjust;
574
DETOUR_TRACE(("--Try %p => %p..%p %6x\n",
575
pbTry,
576
mbi.BaseAddress,
577
(PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
578
mbi.State));
579
pbTry = (PBYTE)mbi.BaseAddress;
580
}
581
s_pRegion = (DETOUR_REGION*)VirtualAlloc(pbTry,
582
DETOUR_REGION_SIZE,
583
MEM_COMMIT|MEM_RESERVE,
584
PAGE_EXECUTE_READWRITE);
585
if (s_pRegion != NULL) {
586
goto alloced_region;
587
}
588
else {
589
DETOUR_TRACE(("Error: %p %d\n", pbTry, GetLastError()));
590
}
591
}
592
593
pbTry = (PBYTE)mbi.BaseAddress + mbi.RegionSize;
594
}
595
596
DETOUR_TRACE(("Couldn't find available memory region!\n"));
597
return NULL;
598
}
599
600
static VOID detour_free_trampoline(PDETOUR_TRAMPOLINE pTrampoline)
601
{
602
PDETOUR_REGION pRegion = (PDETOUR_REGION)
603
((ULONG_PTR)pTrampoline & ~(ULONG_PTR)0xffff);
604
605
memset(pTrampoline, 0, sizeof(*pTrampoline));
606
pTrampoline->pbRemain = (PBYTE)pRegion->pFree;
607
pRegion->pFree = pTrampoline;
608
}
609
610
//////////////////////////////////////////////////////////////////////////////
611
//
612
static PIMAGE_DOS_HEADER detour_find_header(PBYTE pbTarget)
613
{
614
MEMORY_BASIC_INFORMATION mbi;
615
616
if (!VirtualQuery(pbTarget, &mbi, sizeof(mbi))) {
617
return NULL;
618
}
619
620
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
621
__try {
622
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
623
return NULL;
624
}
625
626
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
627
pDosHeader->e_lfanew);
628
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
629
return NULL;
630
}
631
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
632
return NULL;
633
}
634
return pDosHeader;
635
} __except(EXCEPTION_EXECUTE_HANDLER) {
636
return NULL;
637
}
638
}
639
640
///////////////////////////////////////////////////////// Transaction Structs.
641
//
642
struct DetourThread
643
{
644
DetourThread * pNext;
645
HANDLE hThread;
646
};
647
648
struct DetourOperation
649
{
650
DetourOperation * pNext;
651
BOOL fIsRemove;
652
PBYTE * ppbPointer;
653
PBYTE pbTarget;
654
PDETOUR_TRAMPOLINE pTrampoline;
655
ULONG dwPerm;
656
};
657
658
static BOOL s_fIgnoreTooSmall = FALSE;
659
660
static LONG s_nPendingThreadId = 0; // Thread owning pending transaction.
661
static LONG s_nPendingError = NO_ERROR;
662
static PVOID * s_ppPendingError = NULL;
663
static DetourThread * s_pPendingThreads = NULL;
664
static DetourOperation * s_pPendingOperations = NULL;
665
666
//////////////////////////////////////////////////////////////////////////////
667
//
668
PVOID WINAPI DetourCodeFromPointer(PVOID pPointer, PVOID *ppGlobals)
669
{
670
return detour_skip_jmp((PBYTE)pPointer, ppGlobals);
671
}
672
673
//////////////////////////////////////////////////////////// Transaction APIs.
674
//
675
VOID WINAPI DetourSetIgnoreTooSmall(BOOL fIgnore)
676
{
677
s_fIgnoreTooSmall = fIgnore;
678
}
679
680
LONG WINAPI DetourTransactionBegin()
681
{
682
// Only one transaction is allowed at a time.
683
if (s_nPendingThreadId != 0) {
684
return ERROR_INVALID_OPERATION;
685
}
686
// Make sure only one thread can start a transaction.
687
if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0) {
688
return ERROR_INVALID_OPERATION;
689
}
690
691
s_fIgnoreTooSmall = FALSE;
692
s_pPendingOperations = NULL;
693
s_pPendingThreads = NULL;
694
s_nPendingError = NO_ERROR;
695
s_ppPendingError = NULL;
696
697
// Make sure the trampoline pages are writable.
698
detour_writable_trampoline_regions();
699
700
return NO_ERROR;
701
}
702
703
LONG WINAPI DetourTransactionAbort()
704
{
705
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
706
return ERROR_INVALID_OPERATION;
707
}
708
709
// Restore all of the page permissions.
710
for (DetourOperation *o = s_pPendingOperations; o != NULL;) {
711
// We don't care if this fails, because the code is still accessible.
712
DWORD dwOld;
713
VirtualProtect(o->pbTarget, o->pTrampoline->cbTarget,
714
o->dwPerm, &dwOld);
715
716
if (!o->fIsRemove) {
717
if (o->pTrampoline) {
718
detour_free_trampoline(o->pTrampoline);
719
o->pTrampoline = NULL;
720
}
721
}
722
723
DetourOperation *n = o->pNext;
724
delete o;
725
o = n;
726
}
727
s_pPendingOperations = NULL;
728
729
// Make sure the trampoline pages are no longer writable.
730
detour_runnable_trampoline_regions();
731
732
// Resume any suspended threads.
733
for (DetourThread *t = s_pPendingThreads; t != NULL;) {
734
// There is nothing we can do if this fails.
735
ResumeThread(t->hThread);
736
737
DetourThread *n = t->pNext;
738
delete t;
739
t = n;
740
}
741
s_pPendingThreads = NULL;
742
s_nPendingThreadId = 0;
743
744
return NO_ERROR;
745
}
746
747
LONG WINAPI DetourTransactionCommit()
748
{
749
return DetourTransactionCommitEx(NULL);
750
}
751
752
LONG WINAPI DetourTransactionCommitEx(PVOID **pppFailedPointer)
753
{
754
if (pppFailedPointer != NULL) {
755
// Used to get the last error.
756
*pppFailedPointer = s_ppPendingError;
757
}
758
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
759
return ERROR_INVALID_OPERATION;
760
}
761
762
// If any of the pending operations failed, then we abort the whole transaction.
763
if (s_nPendingError != NO_ERROR) {
764
DETOUR_BREAK();
765
DetourTransactionAbort();
766
return s_nPendingError;
767
}
768
769
// Common variables.
770
DetourOperation *o;
771
DetourThread *t;
772
773
// Insert or remove each of the detours.
774
for (o = s_pPendingOperations; o != NULL; o = o->pNext) {
775
if (o->fIsRemove) {
776
PBYTE pbSrc = o->pTrampoline->rbCode;
777
LONG cbCopy = 0;
778
for (; cbCopy < o->pTrampoline->cbTarget;) {
779
LONG lExtra = 0;
780
pbSrc = (PBYTE)DetourCopyInstructionEx(o->pbTarget + cbCopy,
781
pbSrc, NULL, &lExtra);
782
if (lExtra != 0) {
783
break; // Abort if offset doesn't fit.
784
}
785
cbCopy = (LONG)(pbSrc - o->pTrampoline->rbCode);
786
}
787
if (cbCopy != o->pTrampoline->cbTarget) { // Count came out different!
788
// This is a drastic error as the backward copy should never fail.
789
s_nPendingError = ERROR_INVALID_DATA;
790
s_ppPendingError = (PVOID*)o->ppbPointer;
791
DETOUR_BREAK();
792
}
793
#ifdef DETOURS_IA64
794
#error Feature not supported in this release.
795
#else // DETOURS_IA64
796
*o->ppbPointer = o->pbTarget;
797
#endif
798
}
799
else {
800
DETOUR_TRACE(("detours: pbTramp =%p, pbRemain=%p, pbDetour=%p, cbTarget=%d\n",
801
o->pTrampoline,
802
o->pTrampoline->pbRemain,
803
o->pTrampoline->pbDetour,
804
o->pTrampoline->cbTarget));
805
806
DETOUR_TRACE(("detours: pbTarget=%p: "
807
"%02x %02x %02x %02x "
808
"%02x %02x %02x %02x "
809
"%02x %02x %02x %02x [before]\n",
810
o->pbTarget,
811
o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],
812
o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],
813
o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));
814
815
#ifdef DETOURS_IA64
816
#error Feature not supported in this release.
817
818
819
#endif // DETOURS_IA64
820
821
#ifdef DETOURS_X64
822
#error Feature not supported in this release.
823
824
825
826
#endif // DETOURS_X64
827
828
#ifdef DETOURS_X86
829
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->pbDetour);
830
pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);
831
*o->ppbPointer = o->pTrampoline->rbCode;
832
#endif // DETOURS_X86
833
834
DETOUR_TRACE(("detours: pbTarget=%p: "
835
"%02x %02x %02x %02x "
836
"%02x %02x %02x %02x "
837
"%02x %02x %02x %02x [after]\n",
838
o->pbTarget,
839
o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],
840
o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],
841
o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));
842
843
DETOUR_TRACE(("detours: pbTramp =%p: "
844
"%02x %02x %02x %02x "
845
"%02x %02x %02x %02x "
846
"%02x %02x %02x %02x\n",
847
o->pTrampoline,
848
o->pTrampoline->rbCode[0], o->pTrampoline->rbCode[1],
849
o->pTrampoline->rbCode[2], o->pTrampoline->rbCode[3],
850
o->pTrampoline->rbCode[4], o->pTrampoline->rbCode[5],
851
o->pTrampoline->rbCode[6], o->pTrampoline->rbCode[7],
852
o->pTrampoline->rbCode[8], o->pTrampoline->rbCode[9],
853
o->pTrampoline->rbCode[10], o->pTrampoline->rbCode[11]));
854
855
#ifdef DETOURS_IA64
856
#error Feature not supported in this release.
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
#endif // DETOURS_IA64
886
}
887
}
888
889
// Update any suspended threads.
890
for (t = s_pPendingThreads; t != NULL; t = t->pNext) {
891
CONTEXT cxt;
892
cxt.ContextFlags = CONTEXT_CONTROL;
893
894
#undef DETOURS_EIP
895
#undef DETOURS_EIP_TYPE
896
897
#ifdef DETOURS_X86
898
#define DETOURS_EIP Eip
899
#define DETOURS_EIP_TYPE DWORD
900
#endif // DETOURS_X86
901
902
#ifdef DETOURS_X64
903
#error Feature not supported in this release.
904
905
#endif // DETOURS_X64
906
907
#ifdef DETOURS_IA64
908
#error Feature not supported in this release.
909
910
#endif // DETOURS_IA64
911
912
if (GetThreadContext(t->hThread, &cxt)) {
913
for (DetourOperation *o = s_pPendingOperations; o != NULL; o = o->pNext) {
914
if (o->fIsRemove) {
915
if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline &&
916
cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline + sizeof(o->pTrampoline)) {
917
918
cxt.DETOURS_EIP -= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline;
919
cxt.DETOURS_EIP += (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget;
920
921
SetThreadContext(t->hThread, &cxt);
922
}
923
}
924
else {
925
if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget &&
926
cxt.DETOURS_EIP < ((DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget +
927
o->pTrampoline->cbTarget)) {
928
929
cxt.DETOURS_EIP -= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget;
930
cxt.DETOURS_EIP += (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline;
931
932
SetThreadContext(t->hThread, &cxt);
933
}
934
}
935
}
936
}
937
#undef DETOURS_EIP
938
}
939
940
// Restore all of the page permissions and flush the icache.
941
HANDLE hProcess = GetCurrentProcess();
942
for (o = s_pPendingOperations; o != NULL;) {
943
// We don't care if this fails, because the code is still accessible.
944
DWORD dwOld;
945
VirtualProtect(o->pbTarget, o->pTrampoline->cbTarget, o->dwPerm, &dwOld);
946
FlushInstructionCache(hProcess, o->pbTarget, o->pTrampoline->cbTarget);
947
948
if (o->fIsRemove && o->pTrampoline) {
949
detour_free_trampoline(o->pTrampoline);
950
o->pTrampoline = NULL;
951
}
952
953
DetourOperation *n = o->pNext;
954
delete o;
955
o = n;
956
}
957
s_pPendingOperations = NULL;
958
959
// Make sure the trampoline pages are no longer writable.
960
detour_runnable_trampoline_regions();
961
962
// Resume any suspended threads.
963
for (t = s_pPendingThreads; t != NULL;) {
964
// There is nothing we can do if this fails.
965
ResumeThread(t->hThread);
966
967
DetourThread *n = t->pNext;
968
delete t;
969
t = n;
970
}
971
s_pPendingThreads = NULL;
972
s_nPendingThreadId = 0;
973
974
if (pppFailedPointer != NULL) {
975
*pppFailedPointer = s_ppPendingError;
976
}
977
978
return s_nPendingError;
979
}
980
981
LONG WINAPI DetourUpdateThread(HANDLE hThread)
982
{
983
LONG error;
984
985
// If any of the pending operations failed, then we don't need to do this.
986
if (s_nPendingError != NO_ERROR) {
987
return s_nPendingError;
988
}
989
990
// Silently (and safely) drop any attempt to suspend our own thread.
991
if (hThread == GetCurrentThread()) {
992
return NO_ERROR;
993
}
994
995
DetourThread *t = new DetourThread;
996
if (t == NULL) {
997
error = ERROR_NOT_ENOUGH_MEMORY;
998
fail:
999
if (t != NULL) {
1000
delete t;
1001
t = NULL;
1002
}
1003
s_nPendingError = error;
1004
s_ppPendingError = NULL;
1005
DETOUR_BREAK();
1006
return error;
1007
}
1008
1009
if (SuspendThread(hThread) == (DWORD)-1) {
1010
error = GetLastError();
1011
DETOUR_BREAK();
1012
goto fail;
1013
}
1014
1015
t->hThread = hThread;
1016
t->pNext = s_pPendingThreads;
1017
s_pPendingThreads = t;
1018
1019
return NO_ERROR;
1020
}
1021
1022
///////////////////////////////////////////////////////////// Transacted APIs.
1023
//
1024
LONG WINAPI DetourAttach(PVOID *ppPointer,
1025
PVOID pDetour)
1026
{
1027
return DetourAttachEx(ppPointer, pDetour, NULL, NULL, NULL);
1028
}
1029
1030
LONG WINAPI DetourAttachEx(PVOID *ppPointer,
1031
PVOID pDetour,
1032
PDETOUR_TRAMPOLINE *ppRealTrampoline,
1033
PVOID *ppRealTarget,
1034
PVOID *ppRealDetour)
1035
{
1036
LONG error = NO_ERROR;
1037
1038
if (ppRealTrampoline != NULL) {
1039
*ppRealTrampoline = NULL;
1040
}
1041
if (ppRealTarget != NULL) {
1042
*ppRealTarget = NULL;
1043
}
1044
if (ppRealDetour != NULL) {
1045
*ppRealDetour = NULL;
1046
}
1047
1048
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
1049
DETOUR_TRACE(("transaction conflict with thread id=%d\n", s_nPendingThreadId));
1050
return ERROR_INVALID_OPERATION;
1051
}
1052
1053
// If any of the pending operations failed, then we don't need to do this.
1054
if (s_nPendingError != NO_ERROR) {
1055
DETOUR_TRACE(("pending transaction error=%d\n", s_nPendingError));
1056
return s_nPendingError;
1057
}
1058
1059
if (ppPointer == NULL) {
1060
DETOUR_TRACE(("ppPointer is null\n"));
1061
return ERROR_INVALID_HANDLE;
1062
}
1063
if (*ppPointer == NULL) {
1064
error = ERROR_INVALID_HANDLE;
1065
s_nPendingError = error;
1066
s_ppPendingError = ppPointer;
1067
DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", ppPointer));
1068
DETOUR_BREAK();
1069
return error;
1070
}
1071
1072
PBYTE pbTarget = (PBYTE)*ppPointer;
1073
PDETOUR_TRAMPOLINE pTrampoline = NULL;
1074
DetourOperation *o = NULL;
1075
1076
#ifdef DETOURS_IA64
1077
#error Feature not supported in this release.
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
#else
1089
pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL);
1090
pDetour = DetourCodeFromPointer(pDetour, NULL);
1091
#endif
1092
1093
// Don't follow a jump if its destination is the target function.
1094
// This happens when the detour does nothing other than call the target.
1095
if (pDetour == (PVOID)pbTarget) {
1096
if (s_fIgnoreTooSmall) {
1097
goto stop;
1098
}
1099
else {
1100
DETOUR_BREAK();
1101
goto fail;
1102
}
1103
}
1104
1105
if (ppRealTarget != NULL) {
1106
*ppRealTarget = pbTarget;
1107
}
1108
if (ppRealDetour != NULL) {
1109
*ppRealDetour = pDetour;
1110
}
1111
1112
o = new DetourOperation;
1113
if (o == NULL) {
1114
error = ERROR_NOT_ENOUGH_MEMORY;
1115
fail:
1116
s_nPendingError = error;
1117
DETOUR_BREAK();
1118
stop:
1119
if (pTrampoline != NULL) {
1120
detour_free_trampoline(pTrampoline);
1121
pTrampoline = NULL;
1122
}
1123
if (o != NULL) {
1124
delete o;
1125
o = NULL;
1126
}
1127
s_ppPendingError = ppPointer;
1128
return error;
1129
}
1130
1131
// Mark process as having detoured code.
1132
#ifdef DETOURS_INTERNAL_USAGE
1133
#error Feature not supported in this release.
1134
1135
#else
1136
Detoured();
1137
#endif
1138
1139
pTrampoline = detour_alloc_trampoline(pbTarget);
1140
if (pTrampoline == NULL) {
1141
error = ERROR_NOT_ENOUGH_MEMORY;
1142
DETOUR_BREAK();
1143
goto fail;
1144
}
1145
1146
if (ppRealTrampoline != NULL) {
1147
*ppRealTrampoline = pTrampoline;
1148
}
1149
1150
DETOUR_TRACE(("detours: pbTramp=%p, pDetour=%p\n", pTrampoline, pDetour));
1151
1152
// Determine the number of movable target instructions.
1153
PBYTE pbSrc = pbTarget;
1154
LONG cbTarget = 0;
1155
while (cbTarget < SIZE_OF_JMP) {
1156
PBYTE pbOp = pbSrc;
1157
LONG lExtra = 0;
1158
1159
DETOUR_TRACE((" DetourCopyInstructionEx(%p,%p)\n",
1160
pTrampoline->rbCode + cbTarget, pbSrc));
1161
pbSrc = (PBYTE)DetourCopyInstructionEx(pTrampoline->rbCode + cbTarget,
1162
pbSrc, NULL, &lExtra);
1163
DETOUR_TRACE((" DetourCopyInstructionEx() = %p (%d bytes)\n",
1164
pbSrc, (int)(pbSrc - pbOp)));
1165
1166
if (lExtra != 0) {
1167
break; // Abort if offset doesn't fit.
1168
}
1169
cbTarget = (LONG)(pbSrc - pbTarget);
1170
1171
if (detour_does_code_end_function(pbOp)) {
1172
break;
1173
}
1174
}
1175
if (cbTarget < SIZE_OF_JMP) {
1176
// Too few instructions.
1177
1178
error = ERROR_INVALID_BLOCK;
1179
if (s_fIgnoreTooSmall) {
1180
goto stop;
1181
}
1182
else {
1183
DETOUR_BREAK();
1184
goto fail;
1185
}
1186
}
1187
1188
#if !defined(DETOURS_IA64)
1189
if (cbTarget > sizeof(pTrampoline->rbCode) - SIZE_OF_JMP) {
1190
// Too many instructions.
1191
error = ERROR_INVALID_HANDLE;
1192
DETOUR_BREAK();
1193
goto fail;
1194
}
1195
#endif
1196
1197
pTrampoline->pbRemain = pbTarget + cbTarget;
1198
pTrampoline->pbDetour = (PBYTE)pDetour;
1199
pTrampoline->cbTarget = (BYTE)cbTarget;
1200
1201
#ifdef DETOURS_IA64
1202
#error Feature not supported in this release.
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
#endif // DETOURS_IA64
1240
1241
#ifdef DETOURS_X64
1242
#error Feature not supported in this release.
1243
1244
1245
#endif // DETOURS_X64
1246
1247
#ifdef DETOURS_X86
1248
pbSrc = detour_gen_jmp_immediate(pTrampoline->rbCode + cbTarget, pTrampoline->pbRemain);
1249
pbSrc = detour_gen_brk(pbSrc,
1250
pTrampoline->rbCode + sizeof(pTrampoline->rbCode));
1251
#endif // DETOURS_X86
1252
1253
DWORD dwOld = 0;
1254
if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld)) {
1255
error = GetLastError();
1256
DETOUR_BREAK();
1257
goto fail;
1258
}
1259
1260
DETOUR_TRACE(("detours: pbTarget=%p: "
1261
"%02x %02x %02x %02x "
1262
"%02x %02x %02x %02x "
1263
"%02x %02x %02x %02x\n",
1264
pbTarget,
1265
pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3],
1266
pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7],
1267
pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11]));
1268
DETOUR_TRACE(("detours: pbTramp =%p: "
1269
"%02x %02x %02x %02x "
1270
"%02x %02x %02x %02x "
1271
"%02x %02x %02x %02x\n",
1272
pTrampoline,
1273
pTrampoline->rbCode[0], pTrampoline->rbCode[1],
1274
pTrampoline->rbCode[2], pTrampoline->rbCode[3],
1275
pTrampoline->rbCode[4], pTrampoline->rbCode[5],
1276
pTrampoline->rbCode[6], pTrampoline->rbCode[7],
1277
pTrampoline->rbCode[8], pTrampoline->rbCode[9],
1278
pTrampoline->rbCode[10], pTrampoline->rbCode[11]));
1279
1280
/////////////////////////////////////////// Mark binary as being detoured.
1281
//
1282
PIMAGE_DOS_HEADER pDosHeader = detour_find_header(pbTarget);
1283
if (pDosHeader != NULL && pDosHeader->e_res[0] != 'eD') {
1284
DWORD dwDos = 0;
1285
if (!VirtualProtect(pDosHeader, sizeof(*pDosHeader), PAGE_EXECUTE_READWRITE, &dwDos)) {
1286
error = GetLastError();
1287
DETOUR_BREAK();
1288
goto fail;
1289
}
1290
pDosHeader->e_res[0] = 'eD';
1291
pDosHeader->e_res[1] = 'ot';
1292
pDosHeader->e_res[2] = 'ru';
1293
pDosHeader->e_res[3] = '!s';
1294
}
1295
1296
o->fIsRemove = FALSE;
1297
o->ppbPointer = (PBYTE*)ppPointer;
1298
o->pTrampoline = pTrampoline;
1299
o->pbTarget = pbTarget;
1300
o->dwPerm = dwOld;
1301
o->pNext = s_pPendingOperations;
1302
s_pPendingOperations = o;
1303
1304
return NO_ERROR;
1305
}
1306
1307
LONG WINAPI DetourDetach(PVOID *ppPointer,
1308
PVOID pDetour)
1309
{
1310
LONG error = NO_ERROR;
1311
1312
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
1313
return ERROR_INVALID_OPERATION;
1314
}
1315
1316
// If any of the pending operations failed, then we don't need to do this.
1317
if (s_nPendingError != NO_ERROR) {
1318
return s_nPendingError;
1319
}
1320
1321
if (ppPointer == NULL) {
1322
return ERROR_INVALID_HANDLE;
1323
}
1324
if (*ppPointer == NULL) {
1325
error = ERROR_INVALID_HANDLE;
1326
s_nPendingError = error;
1327
s_ppPendingError = ppPointer;
1328
DETOUR_BREAK();
1329
return error;
1330
}
1331
1332
DetourOperation *o = new DetourOperation;
1333
if (o == NULL) {
1334
error = ERROR_NOT_ENOUGH_MEMORY;
1335
fail:
1336
s_nPendingError = error;
1337
DETOUR_BREAK();
1338
stop:
1339
if (o != NULL) {
1340
delete o;
1341
o = NULL;
1342
}
1343
s_ppPendingError = ppPointer;
1344
return error;
1345
}
1346
1347
PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE)*ppPointer;
1348
pDetour = DetourCodeFromPointer(pDetour, NULL);
1349
1350
////////////////////////////////////// Verify that Trampoline is in place.
1351
//
1352
PBYTE pbTarget = pTrampoline->pbRemain - pTrampoline->cbTarget;
1353
LONG cbTarget = pTrampoline->cbTarget;
1354
if (cbTarget == 0 || cbTarget > sizeof(pTrampoline->rbCode)) {
1355
error = ERROR_INVALID_BLOCK;
1356
if (s_fIgnoreTooSmall) {
1357
goto stop;
1358
}
1359
else {
1360
DETOUR_BREAK();
1361
goto fail;
1362
}
1363
}
1364
1365
if (pTrampoline->pbDetour != pDetour) {
1366
error = ERROR_INVALID_BLOCK;
1367
if (s_fIgnoreTooSmall) {
1368
goto stop;
1369
}
1370
else {
1371
DETOUR_BREAK();
1372
goto fail;
1373
}
1374
}
1375
1376
DWORD dwOld = 0;
1377
if (!VirtualProtect(pbTarget, cbTarget,
1378
PAGE_EXECUTE_READWRITE, &dwOld)) {
1379
error = GetLastError();
1380
DETOUR_BREAK();
1381
goto fail;
1382
}
1383
1384
o->fIsRemove = TRUE;
1385
o->ppbPointer = (PBYTE*)ppPointer;
1386
o->pTrampoline = pTrampoline;
1387
o->pbTarget = pbTarget;
1388
o->dwPerm = dwOld;
1389
o->pNext = s_pPendingOperations;
1390
s_pPendingOperations = o;
1391
1392
return NO_ERROR;
1393
}
1394
1395
HMODULE WINAPI DetourGetDetouredMarker()
1396
{
1397
#ifdef DETOURS_INTERNAL_USAGE
1398
#error Feature not supported in this release.
1399
1400
1401
#else
1402
return Detoured();
1403
#endif
1404
}
1405
1406
// End of File
1407
1408