Skip to content

Commit e96bed5

Browse files
committed
Merge branch 'fix-remove-max-limit-patch'
2 parents efa4d7e + 7140645 commit e96bed5

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);
@@ -265,66 +305,128 @@ static PBYTE EmitJump(PBYTE pbCode, PBYTE pbJumpTo) {
265305
return pbCode;
266306
}
267307

308+
268309
//=========================================================================
269310
// Internal function:
270311
//
271-
// Will try to allocate the trampoline structure within 2 gigabytes of
272-
// the target function.
312+
// Round down to the next multiple of rndDown
273313
//=========================================================================
274-
static MHOOKS_TRAMPOLINE* TrampolineAlloc(PBYTE pSystemFunction, S64 nLimitUp, S64 nLimitDown) {
314+
static size_t RoundDown(size_t addr, size_t rndDown)
315+
{
316+
return (addr / rndDown) * rndDown;
317+
}
275318

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

278-
// do we have room to store this guy?
279-
if (g_nHooksInUse < MHOOKS_MAX_SUPPORTED_HOOKS) {
280-
281-
// determine lower and upper bounds for the allocation locations.
282-
// in the basic scenario this is +/- 2GB but IP-relative instructions
283-
// found in the original code may require a smaller window.
284-
PBYTE pLower = pSystemFunction + nLimitUp;
285-
pLower = pLower < (PBYTE)(DWORD_PTR)0x0000000080000000 ?
286-
(PBYTE)(0x1) : (PBYTE)(pLower - (PBYTE)0x7fff0000);
287-
PBYTE pUpper = pSystemFunction + nLimitDown;
288-
pUpper = pUpper < (PBYTE)(DWORD_PTR)0xffffffff80000000 ?
289-
(PBYTE)(pUpper + (DWORD_PTR)0x7ff80000) : (PBYTE)(DWORD_PTR)0xfffffffffff80000;
290-
ODPRINTF((L"mhooks: TrampolineAlloc: Allocating for %p between %p and %p", pSystemFunction, pLower, pUpper));
291-
292-
SYSTEM_INFO sSysInfo = {0};
293-
::GetSystemInfo(&sSysInfo);
294-
295-
// go through the available memory blocks and try to allocate a chunk for us
296-
for (PBYTE pbAlloc = pLower; pbAlloc < pUpper;) {
297-
// determine current state
298-
MEMORY_BASIC_INFORMATION mbi;
299-
ODPRINTF((L"mhooks: TrampolineAlloc: Looking at address %p", pbAlloc));
300-
if (!VirtualQuery(pbAlloc, &mbi, sizeof(mbi)))
358+
// last entry points to the current head of the free list
359+
pRetVal[trampolineCount - 1].pNextTrampoline = g_pFreeList;
301360
break;
302-
// free & large enough?
303-
if (mbi.State == MEM_FREE && mbi.RegionSize >= sizeof(MHOOKS_TRAMPOLINE) && mbi.RegionSize >= sSysInfo.dwAllocationGranularity) {
304-
// yes, align the pointer to the 64K boundary first
305-
pbAlloc = (PBYTE)(ULONG_PTR((ULONG_PTR(pbAlloc) + (sSysInfo.dwAllocationGranularity-1)) / sSysInfo.dwAllocationGranularity) * sSysInfo.dwAllocationGranularity);
306-
// and then try to allocate it
307-
pTrampoline = (MHOOKS_TRAMPOLINE*)VirtualAlloc(pbAlloc, sizeof(MHOOKS_TRAMPOLINE), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READ);
308-
if (pTrampoline) {
309-
ODPRINTF((L"mhooks: TrampolineAlloc: Allocated block at %p as the trampoline", pTrampoline));
310-
break;
311-
}
312361
}
313-
// continue the search
314-
pbAlloc = (PBYTE)mbi.BaseAddress + mbi.RegionSize;
315362
}
363+
364+
// This is a spiral, should be -1, 1, -2, 2, -3, 3, etc. (* cAllocSize)
365+
ptrdiff_t bytesToOffset = (cAllocSize * (loopCount + 1) * ((loopCount % 2 == 0) ? -1 : 1));
366+
pbAlloc = pbAlloc + bytesToOffset;
367+
}
368+
369+
return pRetVal;
370+
}
316371

317-
// found and allocated a trampoline?
318-
if (pTrampoline) {
319-
// put it into our list so we know we'll have to free it
320-
for (DWORD i=0; i<MHOOKS_MAX_SUPPORTED_HOOKS; i++) {
321-
if (g_pHooks[i] == NULL) {
322-
g_pHooks[i] = pTrampoline;
323-
g_nHooksInUse++;
324-
break;
325-
}
326-
}
372+
//=========================================================================
373+
// Internal function:
374+
//
375+
// Will try to allocate a big block of memory inside the required range.
376+
//=========================================================================
377+
static MHOOKS_TRAMPOLINE* FindTrampolineInRange(PBYTE pLower, PBYTE pUpper) {
378+
if (!g_pFreeList) {
379+
return NULL;
380+
}
381+
382+
// This is a standard free list, except we're doubly linked to deal with soem return shenanigans.
383+
MHOOKS_TRAMPOLINE* curEntry = g_pFreeList;
384+
while (curEntry) {
385+
if ((MHOOKS_TRAMPOLINE*) pLower < curEntry && curEntry < (MHOOKS_TRAMPOLINE*) pUpper) {
386+
ListRemove(&g_pFreeList, curEntry);
387+
388+
return curEntry;
327389
}
390+
391+
curEntry = curEntry->pNextTrampoline;
392+
}
393+
394+
return NULL;
395+
}
396+
397+
//=========================================================================
398+
// Internal function:
399+
//
400+
// Will try to allocate the trampoline structure within 2 gigabytes of
401+
// the target function.
402+
//=========================================================================
403+
static MHOOKS_TRAMPOLINE* TrampolineAlloc(PBYTE pSystemFunction, S64 nLimitUp, S64 nLimitDown) {
404+
405+
MHOOKS_TRAMPOLINE* pTrampoline = NULL;
406+
407+
// determine lower and upper bounds for the allocation locations.
408+
// in the basic scenario this is +/- 2GB but IP-relative instructions
409+
// found in the original code may require a smaller window.
410+
PBYTE pLower = pSystemFunction + nLimitUp;
411+
pLower = pLower < (PBYTE)(DWORD_PTR)0x0000000080000000 ?
412+
(PBYTE)(0x1) : (PBYTE)(pLower - (PBYTE)0x7fff0000);
413+
PBYTE pUpper = pSystemFunction + nLimitDown;
414+
pUpper = pUpper < (PBYTE)(DWORD_PTR)0xffffffff80000000 ?
415+
(PBYTE)(pUpper + (DWORD_PTR)0x7ff80000) : (PBYTE)(DWORD_PTR)0xfffffffffff80000;
416+
ODPRINTF((L"mhooks: TrampolineAlloc: Allocating for %p between %p and %p", pSystemFunction, pLower, pUpper));
417+
418+
// try to find a trampoline in the specified range
419+
pTrampoline = FindTrampolineInRange(pLower, pUpper);
420+
if (!pTrampoline) {
421+
// if it we can't find it, then we need to allocate a new block and
422+
// try again. Just fail if that doesn't work
423+
g_pFreeList = BlockAlloc(pSystemFunction, pLower, pUpper);
424+
pTrampoline = FindTrampolineInRange(pLower, pUpper);
425+
}
426+
427+
// found and allocated a trampoline?
428+
if (pTrampoline) {
429+
ListPrepend(&g_pHooks, pTrampoline);
328430
}
329431

330432
return pTrampoline;
@@ -336,12 +438,16 @@ static MHOOKS_TRAMPOLINE* TrampolineAlloc(PBYTE pSystemFunction, S64 nLimitUp, S
336438
// Return the internal trampoline structure that belongs to a hooked function.
337439
//=========================================================================
338440
static MHOOKS_TRAMPOLINE* TrampolineGet(PBYTE pHookedFunction) {
339-
for (DWORD i=0; i<MHOOKS_MAX_SUPPORTED_HOOKS; i++) {
340-
if (g_pHooks[i]) {
341-
if (g_pHooks[i]->codeTrampoline == pHookedFunction)
342-
return g_pHooks[i];
441+
MHOOKS_TRAMPOLINE* pCurrent = g_pHooks;
442+
443+
while (pCurrent) {
444+
if (pCurrent->pHookFunction == pHookedFunction) {
445+
return pCurrent;
343446
}
447+
448+
pCurrent = pCurrent->pNextTrampoline;
344449
}
450+
345451
return NULL;
346452
}
347453

@@ -351,20 +457,17 @@ static MHOOKS_TRAMPOLINE* TrampolineGet(PBYTE pHookedFunction) {
351457
// Free a trampoline structure.
352458
//=========================================================================
353459
static VOID TrampolineFree(MHOOKS_TRAMPOLINE* pTrampoline, BOOL bNeverUsed) {
354-
for (DWORD i=0; i<MHOOKS_MAX_SUPPORTED_HOOKS; i++) {
355-
if (g_pHooks[i] == pTrampoline) {
356-
g_pHooks[i] = NULL;
357-
// It might be OK to call VirtualFree, but quite possibly it isn't:
358-
// If a thread has some of our trampoline code on its stack
359-
// and we yank the region from underneath it then it will
360-
// surely crash upon returning. So instead of freeing the
361-
// memory we just let it leak. Ugly, but safe.
362-
if (bNeverUsed)
363-
VirtualFree(pTrampoline, 0, MEM_RELEASE);
364-
g_nHooksInUse--;
365-
break;
366-
}
460+
ListRemove(&g_pHooks, pTrampoline);
461+
462+
// If a thread could feasinbly have some of our trampoline code
463+
// on its stack and we yank the region from underneath it then it will
464+
// surely crash upon returning. So instead of freeing the
465+
// memory we just let it leak. Ugly, but safe.
466+
if (bNeverUsed) {
467+
ListPrepend(&g_pFreeList, pTrampoline);
367468
}
469+
470+
g_nHooksInUse--;
368471
}
369472

370473
//=========================================================================

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)