Skip to content

Commit 21fb49a

Browse files
committed
Add SlimDetoursDetachEx to allow to free the trampoline manually
Usage example: SlimDetoursTransactionBegin(); PVOID pTrampoline = pPointer; DETOUR_DETACH_OPTIONS Options; Options.fFreeTrampoline = FALSE; SlimDetoursDetachEx(pPointer, pDetour, &Options); SlimDetoursTransactionCommit(); Sleep(1000); SlimDetoursFreeTrampoline(pTrampoline);
1 parent 06bcb22 commit 21fb49a

File tree

4 files changed

+75
-9
lines changed

4 files changed

+75
-9
lines changed

Source/SlimDetours/SlimDetours.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,34 @@ SlimDetoursAttach(
6262
_Inout_ PVOID* ppPointer,
6363
_In_ PVOID pDetour);
6464

65+
typedef struct _DETOUR_DETACH_OPTIONS
66+
{
67+
BOOL fFreeTrampoline;
68+
} DETOUR_DETACH_OPTIONS, *PDETOUR_DETACH_OPTIONS;
69+
70+
typedef const DETOUR_DETACH_OPTIONS* PCDETOUR_DETACH_OPTIONS;
71+
6572
HRESULT
6673
NTAPI
74+
SlimDetoursDetachEx(
75+
_Inout_ PVOID* ppPointer,
76+
_In_ PVOID pDetour,
77+
_In_ PCDETOUR_DETACH_OPTIONS pOptions);
78+
79+
FORCEINLINE
80+
HRESULT
6781
SlimDetoursDetach(
6882
_Inout_ PVOID* ppPointer,
69-
_In_ PVOID pDetour);
83+
_In_ PVOID pDetour)
84+
{
85+
DETOUR_DETACH_OPTIONS Options;
86+
Options.fFreeTrampoline = TRUE;
87+
return SlimDetoursDetachEx(ppPointer, pDetour, &Options);
88+
}
89+
HRESULT
90+
NTAPI
91+
SlimDetoursFreeTrampoline(
92+
_In_ PVOID pTrampoline);
7093

7194
PVOID
7295
NTAPI

Source/SlimDetours/SlimDetours.inl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ enum
105105
DETOUR_OPERATION_NONE = 0,
106106
DETOUR_OPERATION_ADD,
107107
DETOUR_OPERATION_REMOVE,
108+
DETOUR_OPERATION_REMOVE_AND_FREE,
108109
};
109110

110111
typedef struct _DETOUR_OPERATION DETOUR_OPERATION, *PDETOUR_OPERATION;

Source/SlimDetours/Thread.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ detour_thread_update(
188188
bUpdateContext = FALSE;
189189
for (o = PendingOperations; o != NULL && !bUpdateContext; o = o->pNext)
190190
{
191-
if (o->dwOperation == DETOUR_OPERATION_REMOVE)
191+
if (o->dwOperation == DETOUR_OPERATION_REMOVE ||
192+
o->dwOperation == DETOUR_OPERATION_REMOVE_AND_FREE)
192193
{
193194
if (cxt.CONTEXT_PC >= (ULONG_PTR)o->pTrampoline->rbCode &&
194195
cxt.CONTEXT_PC < ((ULONG_PTR)o->pTrampoline->rbCode + RTL_FIELD_SIZE(DETOUR_TRAMPOLINE, rbCode)))

Source/SlimDetours/Transaction.c

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ SlimDetoursTransactionCommit(VOID)
177177
o = s_pPendingOperations;
178178
do
179179
{
180-
if (o->dwOperation == DETOUR_OPERATION_REMOVE)
180+
if (o->dwOperation == DETOUR_OPERATION_REMOVE ||
181+
o->dwOperation == DETOUR_OPERATION_REMOVE_AND_FREE)
181182
{
182183
// Check if the jmps still points where we expect, otherwise someone might have hooked us.
183184
BOOL hookIsStillThere =
@@ -194,12 +195,14 @@ SlimDetoursTransactionCommit(VOID)
194195
NtFlushInstructionCache(NtCurrentProcess(), o->pbTarget, o->pTrampoline->cbRestore);
195196
} else
196197
{
197-
// Don't remove in this case, put in bypass mode and leak trampoline.
198+
// Don't remove and leak trampoline in this case.
198199
o->dwOperation = DETOUR_OPERATION_NONE;
199-
o->pTrampoline->pbDetour = o->pTrampoline->rbCode;
200200
DETOUR_TRACE("detours: Leaked hook on pbTarget=%p due to external hooking\n", o->pbTarget);
201201
}
202202

203+
// Put hook in bypass mode.
204+
o->pTrampoline->pbDetour = o->pTrampoline->rbCode;
205+
203206
*o->ppbPointer = o->pbTarget;
204207
} else if (o->dwOperation == DETOUR_OPERATION_ADD)
205208
{
@@ -268,7 +271,7 @@ SlimDetoursTransactionCommit(VOID)
268271
pMem = o->pbTarget;
269272
sMem = o->pTrampoline->cbRestore;
270273
NtProtectVirtualMemory(NtCurrentProcess(), &pMem, &sMem, o->dwPerm, &dwOld);
271-
if (o->dwOperation == DETOUR_OPERATION_REMOVE)
274+
if (o->dwOperation == DETOUR_OPERATION_REMOVE_AND_FREE)
272275
{
273276
detour_free_trampoline(o->pTrampoline);
274277
o->pTrampoline = NULL;
@@ -505,9 +508,10 @@ SlimDetoursAttach(
505508

506509
HRESULT
507510
NTAPI
508-
SlimDetoursDetach(
511+
SlimDetoursDetachEx(
509512
_Inout_ PVOID* ppPointer,
510-
_In_ PVOID pDetour)
513+
_In_ PVOID pDetour,
514+
_In_ PCDETOUR_DETACH_OPTIONS pOptions)
511515
{
512516
NTSTATUS Status;
513517
PVOID pMem;
@@ -555,7 +559,9 @@ SlimDetoursDetach(
555559
goto fail;
556560
}
557561

558-
o->dwOperation = DETOUR_OPERATION_REMOVE;
562+
o->dwOperation = pOptions->fFreeTrampoline
563+
? DETOUR_OPERATION_REMOVE_AND_FREE
564+
: DETOUR_OPERATION_REMOVE;
559565
o->ppbPointer = (PBYTE*)ppPointer;
560566
o->pTrampoline = pTrampoline;
561567
o->pbTarget = pbTarget;
@@ -566,6 +572,41 @@ SlimDetoursDetach(
566572
return HRESULT_FROM_NT(STATUS_SUCCESS);
567573
}
568574

575+
HRESULT
576+
NTAPI
577+
SlimDetoursFreeTrampoline(
578+
_In_ PVOID pTrampoline)
579+
{
580+
NTSTATUS Status;
581+
582+
// Make sure only one thread can start a transaction.
583+
if (_InterlockedCompareExchange(&s_nPendingThreadId, NtCurrentThreadId(), 0) != 0)
584+
{
585+
return HRESULT_FROM_NT(STATUS_TRANSACTIONAL_CONFLICT);
586+
}
587+
588+
// Make sure the trampoline pages are writable.
589+
Status = detour_writable_trampoline_regions();
590+
if (!NT_SUCCESS(Status))
591+
{
592+
goto fail;
593+
}
594+
595+
detour_free_trampoline((PDETOUR_TRAMPOLINE)pTrampoline);
596+
detour_free_trampoline_region_if_unused((PDETOUR_TRAMPOLINE)pTrampoline);
597+
598+
detour_runnable_trampoline_regions();
599+
600+
fail:
601+
#ifdef _MSC_VER
602+
#pragma warning(disable: __WARNING_INTERLOCKED_ACCESS)
603+
#endif
604+
s_nPendingThreadId = 0;
605+
#ifdef _MSC_VER
606+
#pragma warning(default: __WARNING_INTERLOCKED_ACCESS)
607+
#endif
608+
}
609+
569610
HRESULT
570611
NTAPI
571612
SlimDetoursUninitialize(VOID)

0 commit comments

Comments
 (0)