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