Skip to content

Commit 266cb49

Browse files
committed
Add SlimDetoursDetachEx to allow to free the trampoline manually
Usage example: SlimDetoursTransactionBegin(); PVOID pTrampoline = NULL; DETOUR_DETACH_OPTIONS Options; Options.ppTrampolineToFreeManually = &pTrampoline; SlimDetoursDetachEx(pPointer, pDetour, &Options); SlimDetoursTransactionCommit(); Sleep(1000); SlimDetoursFreeTrampoline(pTrampoline);
1 parent 847504d commit 266cb49

3 files changed

Lines changed: 97 additions & 7 deletions

File tree

Source/SlimDetours/SlimDetours.h

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

65+
typedef struct _DETOUR_DETACH_OPTIONS
66+
{
67+
PVOID *ppTrampolineToFreeManually;
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.ppTrampolineToFreeManually = NULL;
87+
return SlimDetoursDetachEx(ppPointer, pDetour, &Options);
88+
}
89+
90+
HRESULT
91+
NTAPI
92+
SlimDetoursFreeTrampoline(
93+
_In_ PVOID pTrampoline);
7094

7195
PVOID
7296
NTAPI

Source/SlimDetours/SlimDetours.inl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ struct _DETOUR_OPERATION
113113
PBYTE pbTarget;
114114
PDETOUR_TRAMPOLINE pTrampoline;
115115
ULONG dwPerm;
116+
PVOID* ppTrampolineToFreeManually;
116117
};
117118

118119
/* Memory management */

Source/SlimDetours/Transaction.c

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,14 @@ SlimDetoursTransactionCommit(VOID)
160160
NtFlushInstructionCache(NtCurrentProcess(), o->pbTarget, o->pTrampoline->cbRestore);
161161
} else
162162
{
163-
// Don't remove in this case, put in bypass mode and leak trampoline.
163+
// Don't remove and leak trampoline in this case.
164164
o->dwOperation = DETOUR_OPERATION_NONE;
165-
o->pTrampoline->pbDetour = o->pTrampoline->rbCode;
166165
DETOUR_TRACE("detours: Leaked hook on pbTarget=%p due to external hooking\n", o->pbTarget);
167166
}
168167

168+
// Put hook in bypass mode.
169+
o->pTrampoline->pbDetour = o->pTrampoline->rbCode;
170+
169171
*o->ppbPointer = o->pbTarget;
170172
} else if (o->dwOperation == DETOUR_OPERATION_ADD)
171173
{
@@ -236,9 +238,16 @@ SlimDetoursTransactionCommit(VOID)
236238
NtProtectVirtualMemory(NtCurrentProcess(), &pMem, &sMem, o->dwPerm, &dwOld);
237239
if (o->dwOperation == DETOUR_OPERATION_REMOVE)
238240
{
239-
detour_free_trampoline(o->pTrampoline);
241+
if (!o->ppTrampolineToFreeManually)
242+
{
243+
detour_free_trampoline(o->pTrampoline);
244+
freed = TRUE;
245+
} else
246+
{
247+
// The caller is responsible for freeing the trampoline.
248+
*o->ppTrampolineToFreeManually = o->pTrampoline;
249+
}
240250
o->pTrampoline = NULL;
241-
freed = TRUE;
242251
}
243252

244253
n = o->pNext;
@@ -471,9 +480,10 @@ SlimDetoursAttach(
471480

472481
HRESULT
473482
NTAPI
474-
SlimDetoursDetach(
483+
SlimDetoursDetachEx(
475484
_Inout_ PVOID* ppPointer,
476-
_In_ PVOID pDetour)
485+
_In_ PVOID pDetour,
486+
_In_ PCDETOUR_DETACH_OPTIONS pOptions)
477487
{
478488
NTSTATUS Status;
479489
PVOID pMem;
@@ -526,12 +536,67 @@ SlimDetoursDetach(
526536
o->pTrampoline = pTrampoline;
527537
o->pbTarget = pbTarget;
528538
o->dwPerm = dwOld;
539+
o->ppTrampolineToFreeManually = pOptions->ppTrampolineToFreeManually;
529540
o->pNext = s_pPendingOperations;
530541
s_pPendingOperations = o;
531542

532543
return HRESULT_FROM_NT(STATUS_SUCCESS);
533544
}
534545

546+
HRESULT
547+
NTAPI
548+
SlimDetoursFreeTrampoline(
549+
_In_ PVOID pTrampoline)
550+
{
551+
if (pTrampoline == NULL)
552+
{
553+
return HRESULT_FROM_NT(STATUS_SUCCESS);
554+
}
555+
556+
// This function can be called as part of a transaction or outside of a transaction.
557+
ULONG nPrevPendingThreadId = _InterlockedCompareExchange(&s_nPendingThreadId, NtCurrentThreadId(), 0);
558+
BOOL bInTransaction = nPrevPendingThreadId != 0;
559+
if (bInTransaction && nPrevPendingThreadId != NtCurrentThreadId())
560+
{
561+
return HRESULT_FROM_NT(STATUS_TRANSACTIONAL_CONFLICT);
562+
}
563+
564+
NTSTATUS Status;
565+
566+
if (!bInTransaction)
567+
{
568+
// Make sure the trampoline pages are writable.
569+
Status = detour_writable_trampoline_regions();
570+
if (!NT_SUCCESS(Status))
571+
{
572+
goto fail;
573+
}
574+
}
575+
576+
detour_free_trampoline((PDETOUR_TRAMPOLINE)pTrampoline);
577+
detour_free_trampoline_region_if_unused((PDETOUR_TRAMPOLINE)pTrampoline);
578+
579+
if (!bInTransaction)
580+
{
581+
detour_runnable_trampoline_regions();
582+
}
583+
584+
Status = STATUS_SUCCESS;
585+
586+
fail:
587+
if (!bInTransaction)
588+
{
589+
#ifdef _MSC_VER
590+
#pragma warning(disable: __WARNING_INTERLOCKED_ACCESS)
591+
#endif
592+
s_nPendingThreadId = 0;
593+
#ifdef _MSC_VER
594+
#pragma warning(default: __WARNING_INTERLOCKED_ACCESS)
595+
#endif
596+
}
597+
return HRESULT_FROM_NT(Status);
598+
}
599+
535600
HRESULT
536601
NTAPI
537602
SlimDetoursUninitialize(VOID)

0 commit comments

Comments
 (0)