Skip to content

Commit 2cddeba

Browse files
committed
[DOCS] Update TechWiki to match current code
1 parent acf9a22 commit 2cddeba

2 files changed

Lines changed: 18 additions & 368 deletions

File tree

Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.md

Lines changed: 9 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@ LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread);
1919
```
2020
In other words, the caller needs to traverse all threads in the process except itself and pass them to this function, which is complicated and inconvenient to use.
2121
22-
But [Detours](https://github.com/microsoft/Detours) updates threads very precisely, it accurately adjusts the PC (Program Counter) in the thread context to the correct position by using [`GetThreadContext`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadcontext) and [`SetThreadContext`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadcontext), see [Detours/src/detours.cpp at 4b8c659f · microsoft/Detours](https://github.com/microsoft/Detours/blob/4b8c659f549b0ab21cf649377c7a84eb708f5e68/src/detours.cpp#L1840) for implementation.
22+
[Detours](https://github.com/microsoft/Detours) updates threads very precisely, it accurately adjusts the PC (Program Counter) in the thread context to the correct position by using [`GetThreadContext`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadcontext) and [`SetThreadContext`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadcontext), see [Detours/src/detours.cpp at 4b8c659f · microsoft/Detours](https://github.com/microsoft/Detours/blob/4b8c659f549b0ab21cf649377c7a84eb708f5e68/src/detours.cpp#L1840-L1906) for implementation.
23+
24+
But [Detours](https://github.com/microsoft/Detours) still has inadequacy in thread updating under x64, see also [PR #344: Improve thread program counter adjustment](https://github.com/microsoft/Detours/pull/344) submitted by me for this.
2325
2426
> [!TIP]
2527
> While its official example "[Using Detours](https://github.com/microsoft/Detours/wiki/Using-Detours)" has code like `DetourUpdateThread(GetCurrentThread())`, such usage is pointless and invalid, and should be used to update all threads in the process except the current thread, see also: [`DetourUpdateThread`](https://github.com/microsoft/Detours/wiki/DetourUpdateThread). But even updating threads in the right way, it also brings a new risk, see [🔗 TechWiki: Avoid Deadlocking on The Heap When Updating Threads](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Avoid%20Deadlocking%20on%20The%20Heap%20When%20Updating%20Threads/README.md).
2628
2729
### MinHook
2830
29-
[MinHook](https://github.com/TsudaKageyu/minhook) does a better job, it updates threads automatically when set (or unset) hooks, and adjusts PC (Program Counter) in the thread context as accurately as [Detours](https://github.com/microsoft/Detours).
31+
[MinHook](https://github.com/TsudaKageyu/minhook) does a better job, it calls [CreateToolhelp32Snapshot](https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot) to obtain other threads and updates them automatically when set (or unset) hooks, then adjusts PC (Program Counter) in the thread context as accurately as [Detours](https://github.com/microsoft/Detours).
3032
3133
### mhook
3234
33-
[mhook](https://github.com/martona/mhook) updates threads automatically when set (or unset) hooks, see [mhook/mhook-lib/mhook.cpp at e58a58ca · martona/mhook](https://github.com/martona/mhook/blob/e58a58ca31dbe14f202b9b26315bff9f7a32598c/mhook-lib/mhook.cpp#L557) for implementation.
34-
35-
But the way it updates threads is a bit hacky compared to the others mentioned above, wait 100ms if the thread is exactly in the area where the instruction is about to be modified, try up to 3 times:
35+
[mhook](https://github.com/martona/mhook) calls [NtQuerySystemInformation](https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation) to obtain other threads and updates them automatically when set (or unset) hooks. But the way to update threads is more hacky, wait 100ms if the thread is exactly in the area where the instruction is about to be modified, try up to 3 times, see [mhook/mhook-lib/mhook.cpp at e58a58ca · martona/mhook](https://github.com/martona/mhook/blob/e58a58ca31dbe14f202b9b26315bff9f7a32598c/mhook-lib/mhook.cpp#L557-L631) for implementation:
3636
```C
3737
while (GetThreadContext(hThread, &ctx))
3838
{
@@ -50,189 +50,14 @@ while (GetThreadContext(hThread, &ctx))
5050
}
5151
```
5252

53-
## SlimDetours implementation
54-
55-
[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) takes all of the above advantages into account, traverse all threads of the process at hook (or unhook) time, and then update the thread context in the same way as [Detours](https://github.com/microsoft/Detours).
56-
57-
Suspend all threads in the current process except the current thread and return their handles:
58-
```C
59-
NTSTATUS
60-
detour_thread_suspend(
61-
_Outptr_result_maybenull_ PHANDLE* SuspendedHandles,
62-
_Out_ PULONG SuspendedHandleCount)
63-
{
64-
NTSTATUS Status;
65-
ULONG i, ThreadCount, SuspendedCount;
66-
PSYSTEM_PROCESS_INFORMATION pSPI, pCurrentSPI;
67-
PSYSTEM_THREAD_INFORMATION pSTI;
68-
PHANDLE Buffer;
69-
HANDLE ThreadHandle, CurrentPID, CurrentTID;
70-
OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, 0);
71-
72-
/* Get system process and thread information */
73-
i = _1MB;
74-
_Try_alloc:
75-
pSPI = (PSYSTEM_PROCESS_INFORMATION)detour_memory_alloc(i);
76-
if (pSPI == NULL)
77-
{
78-
return STATUS_NO_MEMORY;
79-
}
80-
Status = NtQuerySystemInformation(SystemProcessInformation, pSPI, i, &i);
81-
if (!NT_SUCCESS(Status))
82-
{
83-
detour_memory_free(pSPI);
84-
if (Status == STATUS_INFO_LENGTH_MISMATCH)
85-
{
86-
goto _Try_alloc;
87-
}
88-
return Status;
89-
}
90-
91-
/* Find current process and threads */
92-
CurrentPID = NtGetCurrentProcessId();
93-
pCurrentSPI = pSPI;
94-
while (pCurrentSPI->UniqueProcessId != CurrentPID)
95-
{
96-
if (pCurrentSPI->NextEntryOffset == 0)
97-
{
98-
Status = STATUS_NOT_FOUND;
99-
goto _Exit;
100-
}
101-
pCurrentSPI = (PSYSTEM_PROCESS_INFORMATION)Add2Ptr(pCurrentSPI, pCurrentSPI->NextEntryOffset);
102-
}
103-
pSTI = (PSYSTEM_THREAD_INFORMATION)Add2Ptr(pCurrentSPI, sizeof(*pCurrentSPI));
104-
105-
/* Skip if no other threads */
106-
ThreadCount = pCurrentSPI->NumberOfThreads - 1;
107-
if (ThreadCount == 0)
108-
{
109-
*SuspendedHandles = NULL;
110-
*SuspendedHandleCount = 0;
111-
Status = STATUS_SUCCESS;
112-
goto _Exit;
113-
}
114-
115-
/* Create handle array */
116-
Buffer = (PHANDLE)detour_memory_alloc(ThreadCount * sizeof(HANDLE));
117-
if (Buffer == NULL)
118-
{
119-
Status = STATUS_NO_MEMORY;
120-
goto _Exit;
121-
}
122-
123-
/* Suspend threads */
124-
SuspendedCount = 0;
125-
CurrentTID = NtGetCurrentThreadId();
126-
for (i = 0; i < pCurrentSPI->NumberOfThreads; i++)
127-
{
128-
if (pSTI[i].ClientId.UniqueThread == CurrentTID ||
129-
!NT_SUCCESS(NtOpenThread(&ThreadHandle,
130-
THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT,
131-
&ObjectAttributes,
132-
&pSTI[i].ClientId)))
133-
{
134-
continue;
135-
}
136-
if (NT_SUCCESS(NtSuspendThread(ThreadHandle, NULL)))
137-
{
138-
_Analysis_assume_(SuspendedCount < ThreadCount);
139-
Buffer[SuspendedCount++] = ThreadHandle;
140-
} else
141-
{
142-
NtClose(ThreadHandle);
143-
}
144-
}
145-
146-
/* Return suspended thread handles */
147-
if (SuspendedCount == 0)
148-
{
149-
detour_memory_free(Buffer);
150-
*SuspendedHandles = NULL;
151-
} else
152-
{
153-
*SuspendedHandles = Buffer;
154-
}
155-
*SuspendedHandleCount = SuspendedCount;
156-
Status = STATUS_SUCCESS;
157-
158-
_Exit:
159-
detour_memory_free(pSPI);
160-
return Status;
161-
}
162-
```
163-
164-
Update threads' context PC (Program Counter) precisely:
165-
```C
166-
NTSTATUS
167-
detour_thread_update(
168-
_In_ HANDLE ThreadHandle,
169-
_In_ PDETOUR_OPERATION PendingOperations)
170-
{
171-
NTSTATUS Status;
172-
PDETOUR_OPERATION o;
173-
CONTEXT cxt;
174-
BOOL bUpdateContext;
175-
176-
cxt.ContextFlags = CONTEXT_CONTROL;
177-
Status = NtGetContextThread(ThreadHandle, &cxt);
178-
if (!NT_SUCCESS(Status))
179-
{
180-
return Status;
181-
}
53+
## SlimDetours' implementation
18254

183-
for (o = PendingOperations; o != NULL; o = o->pNext)
184-
{
185-
bUpdateContext = FALSE;
186-
if (o->fIsRemove)
187-
{
188-
if (cxt.CONTEXT_PC >= (ULONG_PTR)o->pTrampoline &&
189-
cxt.CONTEXT_PC < ((ULONG_PTR)o->pTrampoline + sizeof(o->pTrampoline)))
190-
{
191-
cxt.CONTEXT_PC = (ULONG_PTR)o->pbTarget +
192-
detour_align_from_trampoline(o->pTrampoline, (BYTE)(cxt.CONTEXT_PC - (ULONG_PTR)o->pTrampoline));
193-
bUpdateContext = TRUE;
194-
}
195-
} else
196-
{
197-
if (cxt.CONTEXT_PC >= (ULONG_PTR)o->pbTarget &&
198-
cxt.CONTEXT_PC < ((ULONG_PTR)o->pbTarget + o->pTrampoline->cbRestore))
199-
{
200-
cxt.CONTEXT_PC = (ULONG_PTR)o->pTrampoline +
201-
detour_align_from_target(o->pTrampoline, (BYTE)(cxt.CONTEXT_PC - (ULONG_PTR)o->pbTarget));
202-
bUpdateContext = TRUE;
203-
}
204-
}
205-
if (bUpdateContext)
206-
{
207-
Status = NtSetContextThread(ThreadHandle, &cxt);
208-
break;
209-
}
210-
}
211-
212-
return Status;
213-
}
214-
```
55+
[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) has two methods to obtain other threads, calls [NtQuerySystemInformation](https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation) when targeting NT5, but use `NtGetNextThread` when targeting NT6+ (default) to significantly improve performance and correctness guarantees.
21556

216-
Resume suspended threads and release handles:
217-
```C
218-
VOID
219-
detour_thread_resume(
220-
_In_reads_(SuspendedHandleCount) _Frees_ptr_ PHANDLE SuspendedHandles,
221-
_In_ ULONG SuspendedHandleCount)
222-
{
223-
ULONG i;
224-
225-
for (i = 0; i < SuspendedHandleCount; i++)
226-
{
227-
NtResumeThread(SuspendedHandles[i], NULL);
228-
NtClose(SuspendedHandles[i]);
229-
}
230-
detour_memory_free(SuspendedHandles);
231-
}
232-
```
57+
Threads updating follows the [Detours](https://github.com/microsoft/Detours) with some fixes and improvements.
23358

23459
Key points:
235-
1. Call `NtQuerySystemInformation` to acquire all threads of the current process
60+
1. Call `NtGetNextThread` to enumerate all threads of the current process
23661
2. Call `NtSuspendThread` to suspend all threads except the current thread
23762
3. Modify the instruction to implement the inline hook
23863
4. Update the threads that were successfully suspended

0 commit comments

Comments
 (0)