1 /*
2 * MinHook - The Minimalistic API Hooking Library for x64/x86
3 * Copyright (C) 2009-2017 Tsuda Kageyu.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
20 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <windows.h>
30 #include <tlhelp32.h>
31 #include <limits.h>
32
33 #include "../include/MinHook.h"
34 #include "buffer.h"
35 #include "trampoline.h"
36
37 #ifndef ARRAYSIZE
38 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
39 #endif
40
41 // Initial capacity of the HOOK_ENTRY buffer.
42 #define INITIAL_HOOK_CAPACITY 32
43
44 // Initial capacity of the thread IDs buffer.
45 #define INITIAL_THREAD_CAPACITY 128
46
47 // Special hook position values.
48 #define INVALID_HOOK_POS UINT_MAX
49 #define ALL_HOOKS_POS UINT_MAX
50
51 // Freeze() action argument defines.
52 #define ACTION_DISABLE 0
53 #define ACTION_ENABLE 1
54 #define ACTION_APPLY_QUEUED 2
55
56 // Thread access rights for suspending/resuming threads.
57 #define THREAD_ACCESS \
58 (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT)
59
60 // Hook information.
61 typedef struct _HOOK_ENTRY
62 {
63 LPVOID pTarget; // Address of the target function.
64 LPVOID pDetour; // Address of the detour or relay function.
65 LPVOID pTrampoline; // Address of the trampoline function.
66 UINT8 backup[8]; // Original prologue of the target function.
67
68 UINT8 patchAbove : 1; // Uses the hot patch area.
69 UINT8 isEnabled : 1; // Enabled.
70 UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled.
71
72 UINT nIP : 4; // Count of the instruction boundaries.
73 UINT8 oldIPs[8]; // Instruction boundaries of the target function.
74 UINT8 newIPs[8]; // Instruction boundaries of the trampoline function.
75 } HOOK_ENTRY, *PHOOK_ENTRY;
76
77 // Suspended threads for Freeze()/Unfreeze().
78 typedef struct _FROZEN_THREADS
79 {
80 LPDWORD pItems; // Data heap
81 UINT capacity; // Size of allocated data heap, items
82 UINT size; // Actual number of data items
83 } FROZEN_THREADS, *PFROZEN_THREADS;
84
85 //-------------------------------------------------------------------------
86 // Global Variables:
87 //-------------------------------------------------------------------------
88
89 // Spin lock flag for EnterSpinLock()/LeaveSpinLock().
90 volatile LONG g_isLocked = FALSE;
91
92 // Private heap handle. If not NULL, this library is initialized.
93 HANDLE g_hHeap = NULL;
94
95 // Hook entries.
96 struct
97 {
98 PHOOK_ENTRY pItems; // Data heap
99 UINT capacity; // Size of allocated data heap, items
100 UINT size; // Actual number of data items
101 } g_hooks;
102
103 //-------------------------------------------------------------------------
104 // Returns INVALID_HOOK_POS if not found.
FindHookEntry(LPVOID pTarget)105 static UINT FindHookEntry(LPVOID pTarget)
106 {
107 UINT i;
108 for (i = 0; i < g_hooks.size; ++i)
109 {
110 if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget)
111 return i;
112 }
113
114 return INVALID_HOOK_POS;
115 }
116
117 //-------------------------------------------------------------------------
AddHookEntry()118 static PHOOK_ENTRY AddHookEntry()
119 {
120 if (g_hooks.pItems == NULL)
121 {
122 g_hooks.capacity = INITIAL_HOOK_CAPACITY;
123 g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc(
124 g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY));
125 if (g_hooks.pItems == NULL)
126 return NULL;
127 }
128 else if (g_hooks.size >= g_hooks.capacity)
129 {
130 PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc(
131 g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY));
132 if (p == NULL)
133 return NULL;
134
135 g_hooks.capacity *= 2;
136 g_hooks.pItems = p;
137 }
138
139 return &g_hooks.pItems[g_hooks.size++];
140 }
141
142 //-------------------------------------------------------------------------
DeleteHookEntry(UINT pos)143 static void DeleteHookEntry(UINT pos)
144 {
145 if (pos < g_hooks.size - 1)
146 g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1];
147
148 g_hooks.size--;
149
150 if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size)
151 {
152 PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc(
153 g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY));
154 if (p == NULL)
155 return;
156
157 g_hooks.capacity /= 2;
158 g_hooks.pItems = p;
159 }
160 }
161
162 //-------------------------------------------------------------------------
FindOldIP(PHOOK_ENTRY pHook,DWORD_PTR ip)163 static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip)
164 {
165 UINT i;
166
167 if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL)))
168 return (DWORD_PTR)pHook->pTarget;
169
170 for (i = 0; i < pHook->nIP; ++i)
171 {
172 if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]))
173 return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i];
174 }
175
176 #if defined(_M_X64) || defined(__x86_64__)
177 // Check relay function.
178 if (ip == (DWORD_PTR)pHook->pDetour)
179 return (DWORD_PTR)pHook->pTarget;
180 #endif
181
182 return 0;
183 }
184
185 //-------------------------------------------------------------------------
FindNewIP(PHOOK_ENTRY pHook,DWORD_PTR ip)186 static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip)
187 {
188 UINT i;
189 for (i = 0; i < pHook->nIP; ++i)
190 {
191 if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]))
192 return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i];
193 }
194
195 return 0;
196 }
197
198 //-------------------------------------------------------------------------
ProcessThreadIPs(HANDLE hThread,UINT pos,UINT action)199 static void ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action)
200 {
201 // If the thread suspended in the overwritten area,
202 // move IP to the proper address.
203
204 CONTEXT c;
205 #if defined(_M_X64) || defined(__x86_64__)
206 DWORD64 *pIP = &c.Rip;
207 #else
208 DWORD *pIP = &c.Eip;
209 #endif
210 UINT count;
211
212 c.ContextFlags = CONTEXT_CONTROL;
213 if (!GetThreadContext(hThread, &c))
214 return;
215
216 if (pos == ALL_HOOKS_POS)
217 {
218 pos = 0;
219 count = g_hooks.size;
220 }
221 else
222 {
223 count = pos + 1;
224 }
225
226 for (; pos < count; ++pos)
227 {
228 PHOOK_ENTRY pHook = &g_hooks.pItems[pos];
229 BOOL enable;
230 DWORD_PTR ip;
231
232 switch (action)
233 {
234 case ACTION_DISABLE:
235 enable = FALSE;
236 break;
237
238 case ACTION_ENABLE:
239 enable = TRUE;
240 break;
241
242 default: // ACTION_APPLY_QUEUED
243 enable = pHook->queueEnable;
244 break;
245 }
246 if (pHook->isEnabled == enable)
247 continue;
248
249 if (enable)
250 ip = FindNewIP(pHook, *pIP);
251 else
252 ip = FindOldIP(pHook, *pIP);
253
254 if (ip != 0)
255 {
256 *pIP = ip;
257 SetThreadContext(hThread, &c);
258 }
259 }
260 }
261
262 //-------------------------------------------------------------------------
EnumerateThreads(PFROZEN_THREADS pThreads)263 static VOID EnumerateThreads(PFROZEN_THREADS pThreads)
264 {
265 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
266 if (hSnapshot != INVALID_HANDLE_VALUE)
267 {
268 THREADENTRY32 te;
269 te.dwSize = sizeof(THREADENTRY32);
270 if (Thread32First(hSnapshot, &te))
271 {
272 do
273 {
274 if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD))
275 && te.th32OwnerProcessID == GetCurrentProcessId()
276 && te.th32ThreadID != GetCurrentThreadId())
277 {
278 if (pThreads->pItems == NULL)
279 {
280 pThreads->capacity = INITIAL_THREAD_CAPACITY;
281 pThreads->pItems
282 = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD));
283 if (pThreads->pItems == NULL)
284 break;
285 }
286 else if (pThreads->size >= pThreads->capacity)
287 {
288 LPDWORD p = (LPDWORD)HeapReAlloc(
289 g_hHeap, 0, pThreads->pItems, (pThreads->capacity * 2) * sizeof(DWORD));
290 if (p == NULL)
291 break;
292
293 pThreads->capacity *= 2;
294 pThreads->pItems = p;
295 }
296 pThreads->pItems[pThreads->size++] = te.th32ThreadID;
297 }
298
299 te.dwSize = sizeof(THREADENTRY32);
300 } while (Thread32Next(hSnapshot, &te));
301 }
302 CloseHandle(hSnapshot);
303 }
304 }
305
306 //-------------------------------------------------------------------------
Freeze(PFROZEN_THREADS pThreads,UINT pos,UINT action)307 static VOID Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action)
308 {
309 pThreads->pItems = NULL;
310 pThreads->capacity = 0;
311 pThreads->size = 0;
312 EnumerateThreads(pThreads);
313
314 if (pThreads->pItems != NULL)
315 {
316 UINT i;
317 for (i = 0; i < pThreads->size; ++i)
318 {
319 HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]);
320 if (hThread != NULL)
321 {
322 SuspendThread(hThread);
323 ProcessThreadIPs(hThread, pos, action);
324 CloseHandle(hThread);
325 }
326 }
327 }
328 }
329
330 //-------------------------------------------------------------------------
Unfreeze(PFROZEN_THREADS pThreads)331 static VOID Unfreeze(PFROZEN_THREADS pThreads)
332 {
333 if (pThreads->pItems != NULL)
334 {
335 UINT i;
336 for (i = 0; i < pThreads->size; ++i)
337 {
338 HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]);
339 if (hThread != NULL)
340 {
341 ResumeThread(hThread);
342 CloseHandle(hThread);
343 }
344 }
345
346 HeapFree(g_hHeap, 0, pThreads->pItems);
347 }
348 }
349
350 //-------------------------------------------------------------------------
EnableHookLL(UINT pos,BOOL enable)351 static MH_STATUS EnableHookLL(UINT pos, BOOL enable)
352 {
353 PHOOK_ENTRY pHook = &g_hooks.pItems[pos];
354 DWORD oldProtect;
355 SIZE_T patchSize = sizeof(JMP_REL);
356 LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget;
357
358 if (pHook->patchAbove)
359 {
360 pPatchTarget -= sizeof(JMP_REL);
361 patchSize += sizeof(JMP_REL_SHORT);
362 }
363
364 if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect))
365 return MH_ERROR_MEMORY_PROTECT;
366
367 if (enable)
368 {
369 PJMP_REL pJmp = (PJMP_REL)pPatchTarget;
370 pJmp->opcode = 0xE9;
371 pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL)));
372
373 if (pHook->patchAbove)
374 {
375 PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget;
376 pShortJmp->opcode = 0xEB;
377 pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL)));
378 }
379 }
380 else
381 {
382 if (pHook->patchAbove)
383 memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
384 else
385 memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL));
386 }
387
388 VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect);
389
390 // Just-in-case measure.
391 FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize);
392
393 pHook->isEnabled = enable;
394 pHook->queueEnable = enable;
395
396 return MH_OK;
397 }
398
399 //-------------------------------------------------------------------------
EnableAllHooksLL(BOOL enable)400 static MH_STATUS EnableAllHooksLL(BOOL enable)
401 {
402 MH_STATUS status = MH_OK;
403 UINT i, first = INVALID_HOOK_POS;
404
405 for (i = 0; i < g_hooks.size; ++i)
406 {
407 if (g_hooks.pItems[i].isEnabled != enable)
408 {
409 first = i;
410 break;
411 }
412 }
413
414 if (first != INVALID_HOOK_POS)
415 {
416 FROZEN_THREADS threads;
417 Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE);
418
419 for (i = first; i < g_hooks.size; ++i)
420 {
421 if (g_hooks.pItems[i].isEnabled != enable)
422 {
423 status = EnableHookLL(i, enable);
424 if (status != MH_OK)
425 break;
426 }
427 }
428
429 Unfreeze(&threads);
430 }
431
432 return status;
433 }
434
435 //-------------------------------------------------------------------------
EnterSpinLock(VOID)436 static VOID EnterSpinLock(VOID)
437 {
438 SIZE_T spinCount = 0;
439
440 // Wait until the flag is FALSE.
441 while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE)
442 {
443 // No need to generate a memory barrier here, since InterlockedCompareExchange()
444 // generates a full memory barrier itself.
445
446 // Prevent the loop from being too busy.
447 if (spinCount < 32)
448 Sleep(0);
449 else
450 Sleep(1);
451
452 spinCount++;
453 }
454 }
455
456 //-------------------------------------------------------------------------
LeaveSpinLock(VOID)457 static VOID LeaveSpinLock(VOID)
458 {
459 // No need to generate a memory barrier here, since InterlockedExchange()
460 // generates a full memory barrier itself.
461
462 InterlockedExchange(&g_isLocked, FALSE);
463 }
464
465 //-------------------------------------------------------------------------
MH_Initialize(VOID)466 MH_STATUS WINAPI MH_Initialize(VOID)
467 {
468 MH_STATUS status = MH_OK;
469
470 EnterSpinLock();
471
472 if (g_hHeap == NULL)
473 {
474 g_hHeap = HeapCreate(0, 0, 0);
475 if (g_hHeap != NULL)
476 {
477 // Initialize the internal function buffer.
478 InitializeBuffer();
479 }
480 else
481 {
482 status = MH_ERROR_MEMORY_ALLOC;
483 }
484 }
485 else
486 {
487 status = MH_ERROR_ALREADY_INITIALIZED;
488 }
489
490 LeaveSpinLock();
491
492 return status;
493 }
494
495 //-------------------------------------------------------------------------
MH_Uninitialize(VOID)496 MH_STATUS WINAPI MH_Uninitialize(VOID)
497 {
498 MH_STATUS status = MH_OK;
499
500 EnterSpinLock();
501
502 if (g_hHeap != NULL)
503 {
504 status = EnableAllHooksLL(FALSE);
505 if (status == MH_OK)
506 {
507 // Free the internal function buffer.
508
509 // HeapFree is actually not required, but some tools detect a false
510 // memory leak without HeapFree.
511
512 UninitializeBuffer();
513
514 HeapFree(g_hHeap, 0, g_hooks.pItems);
515 HeapDestroy(g_hHeap);
516
517 g_hHeap = NULL;
518
519 g_hooks.pItems = NULL;
520 g_hooks.capacity = 0;
521 g_hooks.size = 0;
522 }
523 }
524 else
525 {
526 status = MH_ERROR_NOT_INITIALIZED;
527 }
528
529 LeaveSpinLock();
530
531 return status;
532 }
533
534 //-------------------------------------------------------------------------
MH_CreateHook(LPVOID pTarget,LPVOID pDetour,LPVOID * ppOriginal)535 MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal)
536 {
537 MH_STATUS status = MH_OK;
538
539 EnterSpinLock();
540
541 if (g_hHeap != NULL)
542 {
543 if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour))
544 {
545 UINT pos = FindHookEntry(pTarget);
546 if (pos == INVALID_HOOK_POS)
547 {
548 LPVOID pBuffer = AllocateBuffer(pTarget);
549 if (pBuffer != NULL)
550 {
551 TRAMPOLINE ct;
552
553 ct.pTarget = pTarget;
554 ct.pDetour = pDetour;
555 ct.pTrampoline = pBuffer;
556 if (CreateTrampolineFunction(&ct))
557 {
558 PHOOK_ENTRY pHook = AddHookEntry();
559 if (pHook != NULL)
560 {
561 pHook->pTarget = ct.pTarget;
562 #if defined(_M_X64) || defined(__x86_64__)
563 pHook->pDetour = ct.pRelay;
564 #else
565 pHook->pDetour = ct.pDetour;
566 #endif
567 pHook->pTrampoline = ct.pTrampoline;
568 pHook->patchAbove = ct.patchAbove;
569 pHook->isEnabled = FALSE;
570 pHook->queueEnable = FALSE;
571 pHook->nIP = ct.nIP;
572 memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs));
573 memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs));
574
575 // Back up the target function.
576
577 if (ct.patchAbove)
578 {
579 memcpy(
580 pHook->backup,
581 (LPBYTE)pTarget - sizeof(JMP_REL),
582 sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
583 }
584 else
585 {
586 memcpy(pHook->backup, pTarget, sizeof(JMP_REL));
587 }
588
589 if (ppOriginal != NULL)
590 *ppOriginal = pHook->pTrampoline;
591 }
592 else
593 {
594 status = MH_ERROR_MEMORY_ALLOC;
595 }
596 }
597 else
598 {
599 status = MH_ERROR_UNSUPPORTED_FUNCTION;
600 }
601
602 if (status != MH_OK)
603 {
604 FreeBuffer(pBuffer);
605 }
606 }
607 else
608 {
609 status = MH_ERROR_MEMORY_ALLOC;
610 }
611 }
612 else
613 {
614 status = MH_ERROR_ALREADY_CREATED;
615 }
616 }
617 else
618 {
619 status = MH_ERROR_NOT_EXECUTABLE;
620 }
621 }
622 else
623 {
624 status = MH_ERROR_NOT_INITIALIZED;
625 }
626
627 LeaveSpinLock();
628
629 return status;
630 }
631
632 //-------------------------------------------------------------------------
MH_RemoveHook(LPVOID pTarget)633 MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget)
634 {
635 MH_STATUS status = MH_OK;
636
637 EnterSpinLock();
638
639 if (g_hHeap != NULL)
640 {
641 UINT pos = FindHookEntry(pTarget);
642 if (pos != INVALID_HOOK_POS)
643 {
644 if (g_hooks.pItems[pos].isEnabled)
645 {
646 FROZEN_THREADS threads;
647 Freeze(&threads, pos, ACTION_DISABLE);
648
649 status = EnableHookLL(pos, FALSE);
650
651 Unfreeze(&threads);
652 }
653
654 if (status == MH_OK)
655 {
656 FreeBuffer(g_hooks.pItems[pos].pTrampoline);
657 DeleteHookEntry(pos);
658 }
659 }
660 else
661 {
662 status = MH_ERROR_NOT_CREATED;
663 }
664 }
665 else
666 {
667 status = MH_ERROR_NOT_INITIALIZED;
668 }
669
670 LeaveSpinLock();
671
672 return status;
673 }
674
675 //-------------------------------------------------------------------------
EnableHook(LPVOID pTarget,BOOL enable)676 static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable)
677 {
678 MH_STATUS status = MH_OK;
679
680 EnterSpinLock();
681
682 if (g_hHeap != NULL)
683 {
684 if (pTarget == MH_ALL_HOOKS)
685 {
686 status = EnableAllHooksLL(enable);
687 }
688 else
689 {
690 FROZEN_THREADS threads;
691 UINT pos = FindHookEntry(pTarget);
692 if (pos != INVALID_HOOK_POS)
693 {
694 if (g_hooks.pItems[pos].isEnabled != enable)
695 {
696 Freeze(&threads, pos, ACTION_ENABLE);
697
698 status = EnableHookLL(pos, enable);
699
700 Unfreeze(&threads);
701 }
702 else
703 {
704 status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED;
705 }
706 }
707 else
708 {
709 status = MH_ERROR_NOT_CREATED;
710 }
711 }
712 }
713 else
714 {
715 status = MH_ERROR_NOT_INITIALIZED;
716 }
717
718 LeaveSpinLock();
719
720 return status;
721 }
722
723 //-------------------------------------------------------------------------
MH_EnableHook(LPVOID pTarget)724 MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget)
725 {
726 return EnableHook(pTarget, TRUE);
727 }
728
729 //-------------------------------------------------------------------------
MH_DisableHook(LPVOID pTarget)730 MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget)
731 {
732 return EnableHook(pTarget, FALSE);
733 }
734
735 //-------------------------------------------------------------------------
QueueHook(LPVOID pTarget,BOOL queueEnable)736 static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable)
737 {
738 MH_STATUS status = MH_OK;
739
740 EnterSpinLock();
741
742 if (g_hHeap != NULL)
743 {
744 if (pTarget == MH_ALL_HOOKS)
745 {
746 UINT i;
747 for (i = 0; i < g_hooks.size; ++i)
748 g_hooks.pItems[i].queueEnable = queueEnable;
749 }
750 else
751 {
752 UINT pos = FindHookEntry(pTarget);
753 if (pos != INVALID_HOOK_POS)
754 {
755 g_hooks.pItems[pos].queueEnable = queueEnable;
756 }
757 else
758 {
759 status = MH_ERROR_NOT_CREATED;
760 }
761 }
762 }
763 else
764 {
765 status = MH_ERROR_NOT_INITIALIZED;
766 }
767
768 LeaveSpinLock();
769
770 return status;
771 }
772
773 //-------------------------------------------------------------------------
MH_QueueEnableHook(LPVOID pTarget)774 MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget)
775 {
776 return QueueHook(pTarget, TRUE);
777 }
778
779 //-------------------------------------------------------------------------
MH_QueueDisableHook(LPVOID pTarget)780 MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget)
781 {
782 return QueueHook(pTarget, FALSE);
783 }
784
785 //-------------------------------------------------------------------------
MH_ApplyQueued(VOID)786 MH_STATUS WINAPI MH_ApplyQueued(VOID)
787 {
788 MH_STATUS status = MH_OK;
789 UINT i, first = INVALID_HOOK_POS;
790
791 EnterSpinLock();
792
793 if (g_hHeap != NULL)
794 {
795 for (i = 0; i < g_hooks.size; ++i)
796 {
797 if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable)
798 {
799 first = i;
800 break;
801 }
802 }
803
804 if (first != INVALID_HOOK_POS)
805 {
806 FROZEN_THREADS threads;
807 Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED);
808
809 for (i = first; i < g_hooks.size; ++i)
810 {
811 PHOOK_ENTRY pHook = &g_hooks.pItems[i];
812 if (pHook->isEnabled != pHook->queueEnable)
813 {
814 status = EnableHookLL(i, pHook->queueEnable);
815 if (status != MH_OK)
816 break;
817 }
818 }
819
820 Unfreeze(&threads);
821 }
822 }
823 else
824 {
825 status = MH_ERROR_NOT_INITIALIZED;
826 }
827
828 LeaveSpinLock();
829
830 return status;
831 }
832
833 //-------------------------------------------------------------------------
MH_CreateHookApiEx(LPCWSTR pszModule,LPCSTR pszProcName,LPVOID pDetour,LPVOID * ppOriginal,LPVOID * ppTarget)834 MH_STATUS WINAPI MH_CreateHookApiEx(
835 LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour,
836 LPVOID *ppOriginal, LPVOID *ppTarget)
837 {
838 HMODULE hModule;
839 LPVOID pTarget;
840
841 hModule = GetModuleHandleW(pszModule);
842 if (hModule == NULL)
843 return MH_ERROR_MODULE_NOT_FOUND;
844
845 pTarget = (LPVOID)GetProcAddress(hModule, pszProcName);
846 if (pTarget == NULL)
847 return MH_ERROR_FUNCTION_NOT_FOUND;
848
849 if(ppTarget != NULL)
850 *ppTarget = pTarget;
851
852 return MH_CreateHook(pTarget, pDetour, ppOriginal);
853 }
854
855 //-------------------------------------------------------------------------
MH_CreateHookApi(LPCWSTR pszModule,LPCSTR pszProcName,LPVOID pDetour,LPVOID * ppOriginal)856 MH_STATUS WINAPI MH_CreateHookApi(
857 LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal)
858 {
859 return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL);
860 }
861
862 //-------------------------------------------------------------------------
MH_StatusToString(MH_STATUS status)863 const char * WINAPI MH_StatusToString(MH_STATUS status)
864 {
865 #define MH_ST2STR(x) \
866 case x: \
867 return #x;
868
869 switch (status) {
870 MH_ST2STR(MH_UNKNOWN)
871 MH_ST2STR(MH_OK)
872 MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED)
873 MH_ST2STR(MH_ERROR_NOT_INITIALIZED)
874 MH_ST2STR(MH_ERROR_ALREADY_CREATED)
875 MH_ST2STR(MH_ERROR_NOT_CREATED)
876 MH_ST2STR(MH_ERROR_ENABLED)
877 MH_ST2STR(MH_ERROR_DISABLED)
878 MH_ST2STR(MH_ERROR_NOT_EXECUTABLE)
879 MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION)
880 MH_ST2STR(MH_ERROR_MEMORY_ALLOC)
881 MH_ST2STR(MH_ERROR_MEMORY_PROTECT)
882 MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND)
883 MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND)
884 }
885
886 #undef MH_ST2STR
887
888 return "(unknown)";
889 }
890