Skip to content

Commit 7140645

Browse files
committed
Fix 6e8d0b3 applied to wrong files:
Patch by John McDonald of Nvidia (2012-12-05) to remove hard limit on maximum hookable functions.
1 parent 6e8d0b3 commit 7140645

File tree

4 files changed

+175
-1010
lines changed

4 files changed

+175
-1010
lines changed

mhook-lib/mhook.cpp

Lines changed: 175 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,10 @@ struct MHOOKS_TRAMPOLINE {
110110
// in the original location
111111
BYTE codeUntouched[MHOOKS_MAX_CODE_BYTES]; // placeholder for unmodified original code
112112
// (we patch IP-relative addressing)
113+
MHOOKS_TRAMPOLINE* pPrevTrampoline; // When in the free list, thess are pointers to the prev and next entry.
114+
MHOOKS_TRAMPOLINE* pNextTrampoline; // When not in the free list, this is a pointer to the prev and next trampoline in use.
113115
};
114116

115-
116117
//=========================================================================
117118
// The patch data structures - store info about rip-relative instructions
118119
// during hook placement
@@ -134,26 +135,28 @@ struct MHOOKS_PATCHDATA
134135
// Global vars
135136
static BOOL g_bVarsInitialized = FALSE;
136137
static CRITICAL_SECTION g_cs;
137-
static MHOOKS_TRAMPOLINE* g_pHooks[MHOOKS_MAX_SUPPORTED_HOOKS];
138+
static MHOOKS_TRAMPOLINE* g_pHooks = NULL;
139+
static MHOOKS_TRAMPOLINE* g_pFreeList = NULL;
138140
static DWORD g_nHooksInUse = 0;
139141
static HANDLE* g_hThreadHandles = NULL;
140142
static DWORD g_nThreadHandles = 0;
141143
#define MHOOK_JMPSIZE 5
144+
#define MHOOK_MINALLOCSIZE 4096
142145

143146
//=========================================================================
144147
// Toolhelp defintions so the functions can be dynamically bound to
145148
typedef HANDLE (WINAPI * _CreateToolhelp32Snapshot)(
146-
DWORD dwFlags,
149+
DWORD dwFlags,
147150
DWORD th32ProcessID
148151
);
149152

150153
typedef BOOL (WINAPI * _Thread32First)(
151-
HANDLE hSnapshot,
154+
HANDLE hSnapshot,
152155
LPTHREADENTRY32 lpte
153156
);
154157

155158
typedef BOOL (WINAPI * _Thread32Next)(
156-
HANDLE hSnapshot,
159+
HANDLE hSnapshot,
157160
LPTHREADENTRY32 lpte
158161
);
159162

@@ -163,11 +166,48 @@ _CreateToolhelp32Snapshot fnCreateToolhelp32Snapshot = (_CreateToolhelp32Snapsho
163166
_Thread32First fnThread32First = (_Thread32First) GetProcAddress(GetModuleHandle(L"kernel32"), "Thread32First");
164167
_Thread32Next fnThread32Next = (_Thread32Next) GetProcAddress(GetModuleHandle(L"kernel32"), "Thread32Next");
165168

169+
//=========================================================================
170+
// Internal function:
171+
//
172+
// Remove the trampoline from the specified list, updating the head pointer
173+
// if necessary.
174+
//=========================================================================
175+
static VOID ListRemove(MHOOKS_TRAMPOLINE** pListHead, MHOOKS_TRAMPOLINE* pNode) {
176+
if (pNode->pPrevTrampoline) {
177+
pNode->pPrevTrampoline->pNextTrampoline = pNode->pNextTrampoline;
178+
}
179+
180+
if (pNode->pNextTrampoline) {
181+
pNode->pNextTrampoline->pPrevTrampoline = pNode->pPrevTrampoline;
182+
}
183+
184+
if ((*pListHead) == pNode) {
185+
(*pListHead) = pNode->pNextTrampoline;
186+
assert((*pListHead)->pPrevTrampoline == NULL);
187+
}
188+
189+
pNode->pPrevTrampoline = NULL;
190+
pNode->pNextTrampoline = NULL;
191+
}
192+
193+
//=========================================================================
194+
// Internal function:
195+
//
196+
// Prepend the trampoline from the specified list and update the head pointer.
197+
//=========================================================================
198+
static VOID ListPrepend(MHOOKS_TRAMPOLINE** pListHead, MHOOKS_TRAMPOLINE* pNode) {
199+
pNode->pPrevTrampoline = NULL;
200+
pNode->pNextTrampoline = (*pListHead);
201+
if ((*pListHead)) {
202+
(*pListHead)->pPrevTrampoline = pNode;
203+
}
204+
(*pListHead) = pNode;
205+
}
206+
166207
//=========================================================================
167208
static VOID EnterCritSec() {
168209
if (!g_bVarsInitialized) {
169210
InitializeCriticalSection(&g_cs);
170-
ZeroMemory(g_pHooks, sizeof(g_pHooks));
171211
g_bVarsInitialized = TRUE;
172212
}
173213
EnterCriticalSection(&g_cs);
@@ -250,66 +290,128 @@ static PBYTE EmitJump(PBYTE pbCode, PBYTE pbJumpTo) {
250290
return pbCode;
251291
}
252292

293+
253294
//=========================================================================
254295
// Internal function:
255296
//
256-
// Will try to allocate the trampoline structure within 2 gigabytes of
257-
// the target function.
297+
// Round down to the next multiple of rndDown
258298
//=========================================================================
259-
static MHOOKS_TRAMPOLINE* TrampolineAlloc(PBYTE pSystemFunction, S64 nLimitUp, S64 nLimitDown) {
299+
static size_t RoundDown(size_t addr, size_t rndDown)
300+
{
301+
return (addr / rndDown) * rndDown;
302+
}
260303

261-
MHOOKS_TRAMPOLINE* pTrampoline = NULL;
304+
//=========================================================================
305+
// Internal function:
306+
//
307+
// Will attempt allocate a block of memory within the specified range, as
308+
// near as possible to the specified function.
309+
//=========================================================================
310+
static MHOOKS_TRAMPOLINE* BlockAlloc(PBYTE pSystemFunction, PBYTE pbLower, PBYTE pbUpper) {
311+
SYSTEM_INFO sSysInfo = {0};
312+
::GetSystemInfo(&sSysInfo);
313+
314+
// Always allocate in bulk, in case the system actually has a smaller allocation granularity than MINALLOCSIZE.
315+
const ptrdiff_t cAllocSize = max(sSysInfo.dwAllocationGranularity, MHOOK_MINALLOCSIZE);
316+
317+
MHOOKS_TRAMPOLINE* pRetVal = NULL;
318+
PBYTE pModuleGuess = (PBYTE) RoundDown((size_t)pSystemFunction, cAllocSize);
319+
int loopCount = 0;
320+
for (PBYTE pbAlloc = pModuleGuess; pbLower < pbAlloc && pbAlloc < pbUpper; ++loopCount) {
321+
// determine current state
322+
MEMORY_BASIC_INFORMATION mbi;
323+
ODPRINTF((L"mhooks: BlockAlloc: Looking at address %p", pbAlloc));
324+
if (!VirtualQuery(pbAlloc, &mbi, sizeof(mbi)))
325+
break;
326+
// free & large enough?
327+
if (mbi.State == MEM_FREE && mbi.RegionSize >= (unsigned)cAllocSize) {
328+
// and then try to allocate it
329+
pRetVal = (MHOOKS_TRAMPOLINE*) VirtualAlloc(pbAlloc, cAllocSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
330+
if (pRetVal) {
331+
size_t trampolineCount = cAllocSize / sizeof(MHOOKS_TRAMPOLINE);
332+
ODPRINTF((L"mhooks: BlockAlloc: Allocated block at %p as %d trampolines", pRetVal, trampolineCount));
333+
334+
pRetVal[0].pPrevTrampoline = NULL;
335+
pRetVal[0].pNextTrampoline = &pRetVal[1];
336+
337+
// prepare them by having them point down the line at the next entry.
338+
for (size_t s = 1; s < trampolineCount; ++s) {
339+
pRetVal[s].pPrevTrampoline = &pRetVal[s - 1];
340+
pRetVal[s].pNextTrampoline = &pRetVal[s + 1];
341+
}
262342

263-
// do we have room to store this guy?
264-
if (g_nHooksInUse < MHOOKS_MAX_SUPPORTED_HOOKS) {
265-
266-
// determine lower and upper bounds for the allocation locations.
267-
// in the basic scenario this is +/- 2GB but IP-relative instructions
268-
// found in the original code may require a smaller window.
269-
PBYTE pLower = pSystemFunction + nLimitUp;
270-
pLower = pLower < (PBYTE)(DWORD_PTR)0x0000000080000000 ?
271-
(PBYTE)(0x1) : (PBYTE)(pLower - (PBYTE)0x7fff0000);
272-
PBYTE pUpper = pSystemFunction + nLimitDown;
273-
pUpper = pUpper < (PBYTE)(DWORD_PTR)0xffffffff80000000 ?
274-
(PBYTE)(pUpper + (DWORD_PTR)0x7ff80000) : (PBYTE)(DWORD_PTR)0xfffffffffff80000;
275-
ODPRINTF((L"mhooks: TrampolineAlloc: Allocating for %p between %p and %p", pSystemFunction, pLower, pUpper));
276-
277-
SYSTEM_INFO sSysInfo = {0};
278-
::GetSystemInfo(&sSysInfo);
279-
280-
// go through the available memory blocks and try to allocate a chunk for us
281-
for (PBYTE pbAlloc = pLower; pbAlloc < pUpper;) {
282-
// determine current state
283-
MEMORY_BASIC_INFORMATION mbi;
284-
ODPRINTF((L"mhooks: TrampolineAlloc: Looking at address %p", pbAlloc));
285-
if (!VirtualQuery(pbAlloc, &mbi, sizeof(mbi)))
343+
// last entry points to the current head of the free list
344+
pRetVal[trampolineCount - 1].pNextTrampoline = g_pFreeList;
286345
break;
287-
// free & large enough?
288-
if (mbi.State == MEM_FREE && mbi.RegionSize >= sizeof(MHOOKS_TRAMPOLINE) && mbi.RegionSize >= sSysInfo.dwAllocationGranularity) {
289-
// yes, align the pointer to the 64K boundary first
290-
pbAlloc = (PBYTE)(ULONG_PTR((ULONG_PTR(pbAlloc) + (sSysInfo.dwAllocationGranularity-1)) / sSysInfo.dwAllocationGranularity) * sSysInfo.dwAllocationGranularity);
291-
// and then try to allocate it
292-
pTrampoline = (MHOOKS_TRAMPOLINE*)VirtualAlloc(pbAlloc, sizeof(MHOOKS_TRAMPOLINE), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READ);
293-
if (pTrampoline) {
294-
ODPRINTF((L"mhooks: TrampolineAlloc: Allocated block at %p as the trampoline", pTrampoline));
295-
break;
296-
}
297346
}
298-
// continue the search
299-
pbAlloc = (PBYTE)mbi.BaseAddress + mbi.RegionSize;
300347
}
348+
349+
// This is a spiral, should be -1, 1, -2, 2, -3, 3, etc. (* cAllocSize)
350+
ptrdiff_t bytesToOffset = (cAllocSize * (loopCount + 1) * ((loopCount % 2 == 0) ? -1 : 1));
351+
pbAlloc = pbAlloc + bytesToOffset;
352+
}
353+
354+
return pRetVal;
355+
}
301356

302-
// found and allocated a trampoline?
303-
if (pTrampoline) {
304-
// put it into our list so we know we'll have to free it
305-
for (DWORD i=0; i<MHOOKS_MAX_SUPPORTED_HOOKS; i++) {
306-
if (g_pHooks[i] == NULL) {
307-
g_pHooks[i] = pTrampoline;
308-
g_nHooksInUse++;
309-
break;
310-
}
311-
}
357+
//=========================================================================
358+
// Internal function:
359+
//
360+
// Will try to allocate a big block of memory inside the required range.
361+
//=========================================================================
362+
static MHOOKS_TRAMPOLINE* FindTrampolineInRange(PBYTE pLower, PBYTE pUpper) {
363+
if (!g_pFreeList) {
364+
return NULL;
365+
}
366+
367+
// This is a standard free list, except we're doubly linked to deal with soem return shenanigans.
368+
MHOOKS_TRAMPOLINE* curEntry = g_pFreeList;
369+
while (curEntry) {
370+
if ((MHOOKS_TRAMPOLINE*) pLower < curEntry && curEntry < (MHOOKS_TRAMPOLINE*) pUpper) {
371+
ListRemove(&g_pFreeList, curEntry);
372+
373+
return curEntry;
312374
}
375+
376+
curEntry = curEntry->pNextTrampoline;
377+
}
378+
379+
return NULL;
380+
}
381+
382+
//=========================================================================
383+
// Internal function:
384+
//
385+
// Will try to allocate the trampoline structure within 2 gigabytes of
386+
// the target function.
387+
//=========================================================================
388+
static MHOOKS_TRAMPOLINE* TrampolineAlloc(PBYTE pSystemFunction, S64 nLimitUp, S64 nLimitDown) {
389+
390+
MHOOKS_TRAMPOLINE* pTrampoline = NULL;
391+
392+
// determine lower and upper bounds for the allocation locations.
393+
// in the basic scenario this is +/- 2GB but IP-relative instructions
394+
// found in the original code may require a smaller window.
395+
PBYTE pLower = pSystemFunction + nLimitUp;
396+
pLower = pLower < (PBYTE)(DWORD_PTR)0x0000000080000000 ?
397+
(PBYTE)(0x1) : (PBYTE)(pLower - (PBYTE)0x7fff0000);
398+
PBYTE pUpper = pSystemFunction + nLimitDown;
399+
pUpper = pUpper < (PBYTE)(DWORD_PTR)0xffffffff80000000 ?
400+
(PBYTE)(pUpper + (DWORD_PTR)0x7ff80000) : (PBYTE)(DWORD_PTR)0xfffffffffff80000;
401+
ODPRINTF((L"mhooks: TrampolineAlloc: Allocating for %p between %p and %p", pSystemFunction, pLower, pUpper));
402+
403+
// try to find a trampoline in the specified range
404+
pTrampoline = FindTrampolineInRange(pLower, pUpper);
405+
if (!pTrampoline) {
406+
// if it we can't find it, then we need to allocate a new block and
407+
// try again. Just fail if that doesn't work
408+
g_pFreeList = BlockAlloc(pSystemFunction, pLower, pUpper);
409+
pTrampoline = FindTrampolineInRange(pLower, pUpper);
410+
}
411+
412+
// found and allocated a trampoline?
413+
if (pTrampoline) {
414+
ListPrepend(&g_pHooks, pTrampoline);
313415
}
314416

315417
return pTrampoline;
@@ -321,12 +423,16 @@ static MHOOKS_TRAMPOLINE* TrampolineAlloc(PBYTE pSystemFunction, S64 nLimitUp, S
321423
// Return the internal trampoline structure that belongs to a hooked function.
322424
//=========================================================================
323425
static MHOOKS_TRAMPOLINE* TrampolineGet(PBYTE pHookedFunction) {
324-
for (DWORD i=0; i<MHOOKS_MAX_SUPPORTED_HOOKS; i++) {
325-
if (g_pHooks[i]) {
326-
if (g_pHooks[i]->codeTrampoline == pHookedFunction)
327-
return g_pHooks[i];
426+
MHOOKS_TRAMPOLINE* pCurrent = g_pHooks;
427+
428+
while (pCurrent) {
429+
if (pCurrent->pHookFunction == pHookedFunction) {
430+
return pCurrent;
328431
}
432+
433+
pCurrent = pCurrent->pNextTrampoline;
329434
}
435+
330436
return NULL;
331437
}
332438

@@ -336,20 +442,17 @@ static MHOOKS_TRAMPOLINE* TrampolineGet(PBYTE pHookedFunction) {
336442
// Free a trampoline structure.
337443
//=========================================================================
338444
static VOID TrampolineFree(MHOOKS_TRAMPOLINE* pTrampoline, BOOL bNeverUsed) {
339-
for (DWORD i=0; i<MHOOKS_MAX_SUPPORTED_HOOKS; i++) {
340-
if (g_pHooks[i] == pTrampoline) {
341-
g_pHooks[i] = NULL;
342-
// It might be OK to call VirtualFree, but quite possibly it isn't:
343-
// If a thread has some of our trampoline code on its stack
344-
// and we yank the region from underneath it then it will
345-
// surely crash upon returning. So instead of freeing the
346-
// memory we just let it leak. Ugly, but safe.
347-
if (bNeverUsed)
348-
VirtualFree(pTrampoline, 0, MEM_RELEASE);
349-
g_nHooksInUse--;
350-
break;
351-
}
445+
ListRemove(&g_pHooks, pTrampoline);
446+
447+
// If a thread could feasinbly have some of our trampoline code
448+
// on its stack and we yank the region from underneath it then it will
449+
// surely crash upon returning. So instead of freeing the
450+
// memory we just let it leak. Ugly, but safe.
451+
if (bNeverUsed) {
452+
ListPrepend(&g_pFreeList, pTrampoline);
352453
}
454+
455+
g_nHooksInUse--;
353456
}
354457

355458
//=========================================================================

mhook-lib/mhook.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,3 @@
2626

2727
BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction);
2828
BOOL Mhook_Unhook(PVOID *ppHookedFunction);
29-
30-
#define MHOOKS_MAX_SUPPORTED_HOOKS 64
31-

0 commit comments

Comments
 (0)