Skip to content

Commit d425fa4

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 06bcb22 commit d425fa4

File tree

3 files changed

+97
-7
lines changed

3 files changed

+97
-7
lines changed

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
@@ -117,6 +117,7 @@ struct _DETOUR_OPERATION
117117
PBYTE pbTarget;
118118
PDETOUR_TRAMPOLINE pTrampoline;
119119
ULONG dwPerm;
120+
PVOID* ppTrampolineToFreeManually;
120121
};
121122

122123
/* Memory management */

Source/SlimDetours/Transaction.c

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,14 @@ SlimDetoursTransactionCommit(VOID)
194194
NtFlushInstructionCache(NtCurrentProcess(), o->pbTarget, o->pTrampoline->cbRestore);
195195
} else
196196
{
197-
// Don't remove in this case, put in bypass mode and leak trampoline.
197+
// Don't remove and leak trampoline in this case.
198198
o->dwOperation = DETOUR_OPERATION_NONE;
199-
o->pTrampoline->pbDetour = o->pTrampoline->rbCode;
200199
DETOUR_TRACE("detours: Leaked hook on pbTarget=%p due to external hooking\n", o->pbTarget);
201200
}
202201

202+
// Put hook in bypass mode.
203+
o->pTrampoline->pbDetour = o->pTrampoline->rbCode;
204+
203205
*o->ppbPointer = o->pbTarget;
204206
} else if (o->dwOperation == DETOUR_OPERATION_ADD)
205207
{
@@ -270,9 +272,16 @@ SlimDetoursTransactionCommit(VOID)
270272
NtProtectVirtualMemory(NtCurrentProcess(), &pMem, &sMem, o->dwPerm, &dwOld);
271273
if (o->dwOperation == DETOUR_OPERATION_REMOVE)
272274
{
273-
detour_free_trampoline(o->pTrampoline);
275+
if (!o->ppTrampolineToFreeManually)
276+
{
277+
detour_free_trampoline(o->pTrampoline);
278+
freed = TRUE;
279+
} else
280+
{
281+
// The caller is responsible for freeing the trampoline.
282+
*o->ppTrampolineToFreeManually = o->pTrampoline;
283+
}
274284
o->pTrampoline = NULL;
275-
freed = TRUE;
276285
}
277286

278287
n = o->pNext;
@@ -505,9 +514,10 @@ SlimDetoursAttach(
505514

506515
HRESULT
507516
NTAPI
508-
SlimDetoursDetach(
517+
SlimDetoursDetachEx(
509518
_Inout_ PVOID* ppPointer,
510-
_In_ PVOID pDetour)
519+
_In_ PVOID pDetour,
520+
_In_ PCDETOUR_DETACH_OPTIONS pOptions)
511521
{
512522
NTSTATUS Status;
513523
PVOID pMem;
@@ -560,12 +570,67 @@ SlimDetoursDetach(
560570
o->pTrampoline = pTrampoline;
561571
o->pbTarget = pbTarget;
562572
o->dwPerm = dwOld;
573+
o->ppTrampolineToFreeManually = pOptions->ppTrampolineToFreeManually;
563574
o->pNext = s_pPendingOperations;
564575
s_pPendingOperations = o;
565576

566577
return HRESULT_FROM_NT(STATUS_SUCCESS);
567578
}
568579

580+
HRESULT
581+
NTAPI
582+
SlimDetoursFreeTrampoline(
583+
_In_ PVOID pTrampoline)
584+
{
585+
if (pTrampoline == NULL)
586+
{
587+
return HRESULT_FROM_NT(STATUS_SUCCESS);
588+
}
589+
590+
// This function can be called as part of a transaction or outside of a transaction.
591+
ULONG nPrevPendingThreadId = _InterlockedCompareExchange(&s_nPendingThreadId, NtCurrentThreadId(), 0);
592+
BOOL bInTransaction = nPrevPendingThreadId != 0;
593+
if (bInTransaction && nPrevPendingThreadId != NtCurrentThreadId())
594+
{
595+
return HRESULT_FROM_NT(STATUS_TRANSACTIONAL_CONFLICT);
596+
}
597+
598+
NTSTATUS Status;
599+
600+
if (!bInTransaction)
601+
{
602+
// Make sure the trampoline pages are writable.
603+
Status = detour_writable_trampoline_regions();
604+
if (!NT_SUCCESS(Status))
605+
{
606+
goto fail;
607+
}
608+
}
609+
610+
detour_free_trampoline((PDETOUR_TRAMPOLINE)pTrampoline);
611+
detour_free_trampoline_region_if_unused((PDETOUR_TRAMPOLINE)pTrampoline);
612+
613+
if (!bInTransaction)
614+
{
615+
detour_runnable_trampoline_regions();
616+
}
617+
618+
Status = STATUS_SUCCESS;
619+
620+
fail:
621+
if (!bInTransaction)
622+
{
623+
#ifdef _MSC_VER
624+
#pragma warning(disable: __WARNING_INTERLOCKED_ACCESS)
625+
#endif
626+
s_nPendingThreadId = 0;
627+
#ifdef _MSC_VER
628+
#pragma warning(default: __WARNING_INTERLOCKED_ACCESS)
629+
#endif
630+
}
631+
return HRESULT_FROM_NT(Status);
632+
}
633+
569634
HRESULT
570635
NTAPI
571636
SlimDetoursUninitialize(VOID)

0 commit comments

Comments
 (0)