MH_STATUS CreateHook(void* pTarget, void* const pDetour, void** ppOriginal) { CriticalSection::ScopedLock lock(*gCS); if (!gIsInitialized) { return MH_ERROR_NOT_INITIALIZED; } HOOK_ENTRY *pHook = FindHook(pTarget); if (pHook != NULL) { return MH_ERROR_ALREADY_CREATED; } if (!IsExecutableAddress(pTarget) || !IsExecutableAddress(pDetour)) { return MH_ERROR_NOT_EXECUTABLE; } { bool committed = false; RollbackIfNotCommitted scopedRollback(&committed); // トランポリン関数を作成する CREATE_TREMPOLINE_T ct = { 0 }; ct.pTarget = pTarget; if (!CreateTrampolineFunction(ct)) { return MH_ERROR_UNSUPPORTED_FUNCTION; } void* pJmpPtr = pTarget; if (ct.patchAbove) { pJmpPtr = reinterpret_cast<char*>(pJmpPtr) - sizeof(JMP_REL); } void* pTrampoline = AllocateCodeBuffer(pJmpPtr, ct.trampoline.size()); if (pTrampoline == NULL) { return MH_ERROR_MEMORY_ALLOC; } #if defined _M_X64 void* pTable = AllocateDataBuffer(pTrampoline, (ct.table.size() + 1) * sizeof(uintptr_t)); if (pTable == NULL) { return MH_ERROR_MEMORY_ALLOC; } #endif ct.pTrampoline = pTrampoline; #if defined _M_X64 ct.pTable = pTable; #endif if (!ResolveTemporaryAddresses(ct)) { return MH_ERROR_UNSUPPORTED_FUNCTION; } memcpy(pTrampoline, &ct.trampoline[ 0 ], ct.trampoline.size()); #if defined _M_X64 if (ct.table.size() != 0) { memcpy(pTable, &ct.table[ 0 ], ct.table.size() * sizeof(uintptr_t)); } #endif // ターゲット関数のバックアップをとる size_t backupSize = sizeof(JMP_REL); if (ct.patchAbove) { backupSize += sizeof(JMP_REL_SHORT); } void* pBackup = AllocateDataBuffer(NULL, backupSize); if (pBackup == NULL) { return MH_ERROR_MEMORY_ALLOC; } memcpy(pBackup, pJmpPtr, backupSize); // 中継関数を作成する #if defined _M_X64 void* pRelay = AllocateCodeBuffer(pJmpPtr, sizeof(JMP_ABS)); if (pRelay == NULL) { return MH_ERROR_MEMORY_ALLOC; } WriteAbsoluteJump(pRelay, pDetour, reinterpret_cast<uintptr_t*>(pTable) + ct.table.size()); #endif CommitBuffer(); committed = true; // フック情報の登録 HOOK_ENTRY hook = { 0 }; hook.pTarget = pTarget; hook.pDetour = pDetour; #if defined _M_X64 hook.pTable = pTable; hook.pRelay = pRelay; #endif hook.pTrampoline = pTrampoline; hook.pBackup = pBackup; hook.patchAbove = ct.patchAbove; hook.isEnabled = false; hook.queueEnable = false; hook.oldIPs = ct.oldIPs; hook.newIPs = ct.newIPs; std::vector<HOOK_ENTRY>::iterator i = std::lower_bound(gHooks.begin(), gHooks.end(), hook); i = gHooks.insert(i, hook); pHook = &(*i); } // OUT引数の処理 *ppOriginal = pHook->pTrampoline; return MH_OK; }
//------------------------------------------------------------------------- MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) { MH_STATUS status = MH_OK; EnterSpinLock(); if (g_hHeap != NULL) { if ((g_Flags & MH_FLAGS_SKIP_EXEC_CHECK) || (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour)) ) { UINT pos = FindHookEntry(pTarget); if (pos == INVALID_HOOK_POS) { LPVOID pBuffer = AllocateBuffer(pTarget); if (pBuffer != NULL) { TRAMPOLINE ct; ct.pTarget = pTarget; ct.pDetour = pDetour; ct.pTrampoline = pBuffer; if (CreateTrampolineFunction(&ct)) { PHOOK_ENTRY pHook = AddHookEntry(); if (pHook != NULL) { pHook->pTarget = ct.pTarget; #ifdef _M_X64 pHook->pDetour = ct.pRelay; #else pHook->pDetour = ct.pDetour; #endif pHook->pTrampoline = ct.pTrampoline; pHook->patchAbove = ct.patchAbove; pHook->isEnabled = FALSE; pHook->queueEnable = FALSE; pHook->nIP = ct.nIP; memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); // Back up the target function. if (ct.patchAbove) { memcpy( pHook->backup, (LPBYTE)pTarget - sizeof(JMP_REL), sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); } else { memcpy(pHook->backup, pTarget, sizeof(JMP_REL)); } if (ppOriginal != NULL) *ppOriginal = pHook->pTrampoline; } else { status = MH_ERROR_MEMORY_ALLOC; } } else { status = MH_ERROR_UNSUPPORTED_FUNCTION; } if (status != MH_OK) { FreeBuffer(pBuffer); } } else { status = MH_ERROR_MEMORY_ALLOC; } } else { status = MH_ERROR_ALREADY_CREATED; } } else { status = MH_ERROR_NOT_EXECUTABLE; } } else { status = MH_ERROR_NOT_INITIALIZED; } LeaveSpinLock(); return status; }
PHOOK_ENTRY MinHookEnable(LPVOID pTarget, LPVOID pDetour, HANDLE *hHeap) { LPVOID pBuffer; TRAMPOLINE ct; PHOOK_ENTRY pHook; DWORD oldProtect; SIZE_T patchSize = sizeof(JMP_REL); LPBYTE pPatchTarget; PJMP_REL pJmp; pBuffer = AllocateBuffer(pTarget); if (pBuffer == NULL) return NULL; ct.pTarget = pTarget; ct.pDetour = pDetour; ct.pTrampoline = pBuffer; if (!CreateTrampolineFunction(&ct)) { FreeBuffer(pBuffer); return NULL; } *hHeap = HeapCreate(0, 0, 0); if (*hHeap == NULL) return NULL; pHook = (PHOOK_ENTRY)HeapAlloc(*hHeap, 0, sizeof(HOOK_ENTRY)); if (pHook == NULL) { FreeBuffer(pBuffer); return NULL; } pHook->pTarget = ct.pTarget; #ifdef _M_X64 pHook->pDetour = ct.pRelay; #else pHook->pDetour = ct.pDetour; #endif pHook->pTrampoline = ct.pTrampoline; pHook->patchAbove = ct.patchAbove; pHook->isEnabled = FALSE; pHook->queueEnable = FALSE; pHook->nIP = ct.nIP; memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); // Back up the target function. if (ct.patchAbove) { memcpy( pHook->backup, (LPBYTE)pTarget - sizeof(JMP_REL), sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); } else { memcpy(pHook->backup, (LPVOID)pTarget, sizeof(JMP_REL)); } // Enable Hook pPatchTarget = (LPBYTE)pHook->pTarget; if (pHook->patchAbove) { pPatchTarget -= sizeof(JMP_REL); patchSize += sizeof(JMP_REL_SHORT); } if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) return NULL; pJmp = (PJMP_REL)pPatchTarget; pJmp->opcode = 0xE9; pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL))); if (pHook->patchAbove) { PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget; pShortJmp->opcode = 0xEB; pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL))); } VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect); // Just-in-case measure. FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize); return pHook; }