mirror of
https://github.com/8damon/Blackbird-Platform
synced 2026-06-21 13:41:12 +00:00
653 lines
22 KiB
C++
653 lines
22 KiB
C++
#include "module.h"
|
|
|
|
#include "ws.h"
|
|
|
|
#include <winternl.h>
|
|
#include <intrin.h>
|
|
|
|
#include <cstring>
|
|
|
|
#pragma intrinsic(_ReturnAddress)
|
|
|
|
#ifndef STATUS_UNSUCCESSFUL
|
|
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
static ModuleHookInitFault g_LastModuleHookInitFault{};
|
|
|
|
struct ModuleRange
|
|
{
|
|
std::uintptr_t Base = 0;
|
|
std::uintptr_t End = 0;
|
|
};
|
|
|
|
static void ResetModuleHookInitFault() noexcept
|
|
{
|
|
std::memset(&g_LastModuleHookInitFault, 0, sizeof(g_LastModuleHookInitFault));
|
|
g_LastModuleHookInitFault.Code = ModuleHookInitFaultCode::None;
|
|
}
|
|
|
|
static void CaptureFaultSample(const void *address, std::uint8_t sample[16]) noexcept
|
|
{
|
|
std::memset(sample, 0, 16);
|
|
if (address == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
__try
|
|
{
|
|
std::memcpy(sample, address, 16);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
std::memset(sample, 0, 16);
|
|
}
|
|
}
|
|
|
|
static void SetModuleHookInitFault(ModuleHookInitFaultCode code, const wchar_t *moduleName, const char *exportName,
|
|
void *address, void *redirectTarget = nullptr) noexcept
|
|
{
|
|
ResetModuleHookInitFault();
|
|
g_LastModuleHookInitFault.Code = code;
|
|
g_LastModuleHookInitFault.ModuleName = moduleName;
|
|
g_LastModuleHookInitFault.ExportName = exportName;
|
|
g_LastModuleHookInitFault.Address = address;
|
|
g_LastModuleHookInitFault.RedirectTarget = redirectTarget;
|
|
CaptureFaultSample(address, g_LastModuleHookInitFault.Sample);
|
|
}
|
|
|
|
static bool TryResolveModuleImageRange(HMODULE module, ModuleRange &range) noexcept
|
|
{
|
|
auto *dos = reinterpret_cast<const IMAGE_DOS_HEADER *>(module);
|
|
if (module == nullptr || dos->e_magic != IMAGE_DOS_SIGNATURE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto *nt =
|
|
reinterpret_cast<const IMAGE_NT_HEADERS *>(reinterpret_cast<const std::uint8_t *>(module) + dos->e_lfanew);
|
|
if (nt->Signature != IMAGE_NT_SIGNATURE || nt->OptionalHeader.SizeOfImage == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
range.Base = reinterpret_cast<std::uintptr_t>(module);
|
|
range.End = range.Base + nt->OptionalHeader.SizeOfImage;
|
|
return true;
|
|
}
|
|
|
|
static bool AddressWithinRange(void *address, const ModuleRange &range) noexcept
|
|
{
|
|
std::uintptr_t value = reinterpret_cast<std::uintptr_t>(address);
|
|
return value >= range.Base && value < range.End;
|
|
}
|
|
|
|
static bool TryDecodeAbsoluteTarget(void *entry, void *&target) noexcept
|
|
{
|
|
target = nullptr;
|
|
if (entry == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto *bytes = static_cast<std::uint8_t *>(entry);
|
|
__try
|
|
{
|
|
if (bytes[0] == 0xE9)
|
|
{
|
|
std::int32_t rel = *reinterpret_cast<std::int32_t *>(&bytes[1]);
|
|
target = bytes + 5 + rel;
|
|
return true;
|
|
}
|
|
|
|
if (bytes[0] == 0xFF && bytes[1] == 0x25)
|
|
{
|
|
std::int32_t disp = *reinterpret_cast<std::int32_t *>(&bytes[2]);
|
|
auto **slot = reinterpret_cast<void **>(bytes + 6 + disp);
|
|
target = *slot;
|
|
return true;
|
|
}
|
|
|
|
if (bytes[0] == 0x48 && bytes[1] == 0xB8 && bytes[10] == 0xFF && bytes[11] == 0xE0)
|
|
{
|
|
target = *reinterpret_cast<void **>(&bytes[2]);
|
|
return true;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
target = nullptr;
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
using LoadLibraryAFn = HMODULE(WINAPI *)(LPCSTR);
|
|
using LoadLibraryWFn = HMODULE(WINAPI *)(LPCWSTR);
|
|
using LoadLibraryExAFn = HMODULE(WINAPI *)(LPCSTR, HANDLE, DWORD);
|
|
using LoadLibraryExWFn = HMODULE(WINAPI *)(LPCWSTR, HANDLE, DWORD);
|
|
using LdrLoadDllFn = NTSTATUS(NTAPI *)(PWSTR, PULONG, PUNICODE_STRING, PHANDLE);
|
|
using RtlAddFunctionTableFn = BOOLEAN(WINAPI *)(PRUNTIME_FUNCTION, DWORD, DWORD64);
|
|
using RtlInstallFunctionTableCallbackFn = BOOLEAN(WINAPI *)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK,
|
|
PVOID, PCWSTR);
|
|
using RtlDeleteFunctionTableFn = BOOLEAN(WINAPI *)(PRUNTIME_FUNCTION);
|
|
|
|
struct InlineHook
|
|
{
|
|
const wchar_t *ModuleName;
|
|
const char *ExportName;
|
|
const char *SourceModule;
|
|
void *HookEntry;
|
|
void **OriginalFunction;
|
|
void *TargetAddress;
|
|
void *Trampoline;
|
|
std::uint8_t OriginalBytes[16];
|
|
bool Installed;
|
|
};
|
|
|
|
static ModuleHookCallback g_ActiveCallback = nullptr;
|
|
static __declspec(thread) bool g_InHook = false;
|
|
|
|
static LoadLibraryAFn g_OriginalLoadLibraryA = nullptr;
|
|
static LoadLibraryWFn g_OriginalLoadLibraryW = nullptr;
|
|
static LoadLibraryExAFn g_OriginalLoadLibraryExA = nullptr;
|
|
static LoadLibraryExWFn g_OriginalLoadLibraryExW = nullptr;
|
|
static LdrLoadDllFn g_OriginalLdrLoadDll = nullptr;
|
|
static RtlAddFunctionTableFn g_OriginalRtlAddFunctionTable = nullptr;
|
|
static RtlInstallFunctionTableCallbackFn g_OriginalRtlInstallFunctionTableCallback = nullptr;
|
|
static RtlDeleteFunctionTableFn g_OriginalRtlDeleteFunctionTable = nullptr;
|
|
|
|
HMODULE WINAPI LoadLibraryAHook(LPCSTR lpLibFileName);
|
|
HMODULE WINAPI LoadLibraryWHook(LPCWSTR lpLibFileName);
|
|
HMODULE WINAPI LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
|
|
HMODULE WINAPI LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
|
|
NTSTATUS NTAPI LdrLoadDllHook(PWSTR searchPath, PULONG loadFlags, PUNICODE_STRING moduleFileName,
|
|
PHANDLE moduleHandle);
|
|
BOOLEAN WINAPI RtlAddFunctionTableHook(PRUNTIME_FUNCTION functionTable, DWORD entryCount, DWORD64 baseAddress);
|
|
BOOLEAN WINAPI RtlInstallFunctionTableCallbackHook(DWORD64 tableIdentifier, DWORD64 baseAddress, DWORD length,
|
|
PGET_RUNTIME_FUNCTION_CALLBACK callback, PVOID context,
|
|
PCWSTR outOfProcessCallbackDll);
|
|
BOOLEAN WINAPI RtlDeleteFunctionTableHook(PRUNTIME_FUNCTION functionTable);
|
|
|
|
static InlineHook g_Hooks[] = {
|
|
{L"KernelBase.dll",
|
|
"LoadLibraryA",
|
|
"KERNELBASE",
|
|
reinterpret_cast<void *>(&LoadLibraryAHook),
|
|
reinterpret_cast<void **>(&g_OriginalLoadLibraryA),
|
|
nullptr,
|
|
nullptr,
|
|
{},
|
|
false},
|
|
{L"KernelBase.dll",
|
|
"LoadLibraryW",
|
|
"KERNELBASE",
|
|
reinterpret_cast<void *>(&LoadLibraryWHook),
|
|
reinterpret_cast<void **>(&g_OriginalLoadLibraryW),
|
|
nullptr,
|
|
nullptr,
|
|
{},
|
|
false},
|
|
{L"KernelBase.dll",
|
|
"LoadLibraryExA",
|
|
"KERNELBASE",
|
|
reinterpret_cast<void *>(&LoadLibraryExAHook),
|
|
reinterpret_cast<void **>(&g_OriginalLoadLibraryExA),
|
|
nullptr,
|
|
nullptr,
|
|
{},
|
|
false},
|
|
{L"KernelBase.dll",
|
|
"LoadLibraryExW",
|
|
"KERNELBASE",
|
|
reinterpret_cast<void *>(&LoadLibraryExWHook),
|
|
reinterpret_cast<void **>(&g_OriginalLoadLibraryExW),
|
|
nullptr,
|
|
nullptr,
|
|
{},
|
|
false},
|
|
{L"ntdll.dll",
|
|
"LdrLoadDll",
|
|
"ntdll",
|
|
reinterpret_cast<void *>(&LdrLoadDllHook),
|
|
reinterpret_cast<void **>(&g_OriginalLdrLoadDll),
|
|
nullptr,
|
|
nullptr,
|
|
{},
|
|
false},
|
|
{L"ntdll.dll",
|
|
"RtlAddFunctionTable",
|
|
"ntdll",
|
|
reinterpret_cast<void *>(&RtlAddFunctionTableHook),
|
|
reinterpret_cast<void **>(&g_OriginalRtlAddFunctionTable),
|
|
nullptr,
|
|
nullptr,
|
|
{},
|
|
false},
|
|
{L"ntdll.dll",
|
|
"RtlInstallFunctionTableCallback",
|
|
"ntdll",
|
|
reinterpret_cast<void *>(&RtlInstallFunctionTableCallbackHook),
|
|
reinterpret_cast<void **>(&g_OriginalRtlInstallFunctionTableCallback),
|
|
nullptr,
|
|
nullptr,
|
|
{},
|
|
false},
|
|
{L"ntdll.dll",
|
|
"RtlDeleteFunctionTable",
|
|
"ntdll",
|
|
reinterpret_cast<void *>(&RtlDeleteFunctionTableHook),
|
|
reinterpret_cast<void **>(&g_OriginalRtlDeleteFunctionTable),
|
|
nullptr,
|
|
nullptr,
|
|
{},
|
|
false},
|
|
};
|
|
|
|
static bool InstallInlineHook(void *target, void *hook, std::uint8_t original[16], void **trampolineOut) noexcept
|
|
{
|
|
constexpr std::size_t kPatchSize = 16;
|
|
constexpr std::size_t kTrampolineSize = 32;
|
|
|
|
if (target == nullptr || hook == nullptr || original == nullptr || trampolineOut == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void *trampoline = VirtualAlloc(nullptr, kTrampolineSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
|
if (trampoline == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto *dst = static_cast<std::uint8_t *>(target);
|
|
auto *gate = static_cast<std::uint8_t *>(trampoline);
|
|
|
|
DWORD oldProtect = 0;
|
|
if (!VirtualProtect(dst, kPatchSize, PAGE_EXECUTE_READWRITE, &oldProtect))
|
|
{
|
|
VirtualFree(trampoline, 0, MEM_RELEASE);
|
|
return false;
|
|
}
|
|
|
|
std::memcpy(original, dst, kPatchSize);
|
|
std::memcpy(gate, dst, kPatchSize);
|
|
gate[16] = 0x48;
|
|
gate[17] = 0xB8;
|
|
*reinterpret_cast<void **>(&gate[18]) = dst + kPatchSize;
|
|
gate[26] = 0xFF;
|
|
gate[27] = 0xE0;
|
|
for (std::size_t i = 28; i < kTrampolineSize; ++i)
|
|
{
|
|
gate[i] = 0xCC;
|
|
}
|
|
|
|
dst[0] = 0x48;
|
|
dst[1] = 0xB8;
|
|
*reinterpret_cast<void **>(&dst[2]) = hook;
|
|
dst[10] = 0xFF;
|
|
dst[11] = 0xE0;
|
|
dst[12] = 0xCC;
|
|
dst[13] = 0xCC;
|
|
dst[14] = 0xCC;
|
|
dst[15] = 0xCC;
|
|
|
|
DWORD temp = 0;
|
|
VirtualProtect(dst, kPatchSize, oldProtect, &temp);
|
|
FlushInstructionCache(GetCurrentProcess(), dst, kPatchSize);
|
|
FlushInstructionCache(GetCurrentProcess(), trampoline, kTrampolineSize);
|
|
|
|
*trampolineOut = trampoline;
|
|
return true;
|
|
}
|
|
|
|
static void RemoveInlineHook(void *target, const std::uint8_t original[16], void *trampoline) noexcept
|
|
{
|
|
if (target == nullptr || original == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DWORD oldProtect = 0;
|
|
if (VirtualProtect(target, 16, PAGE_EXECUTE_READWRITE, &oldProtect))
|
|
{
|
|
std::memcpy(target, original, 16);
|
|
DWORD temp = 0;
|
|
VirtualProtect(target, 16, oldProtect, &temp);
|
|
FlushInstructionCache(GetCurrentProcess(), target, 16);
|
|
}
|
|
|
|
if (trampoline != nullptr)
|
|
{
|
|
VirtualFree(trampoline, 0, MEM_RELEASE);
|
|
}
|
|
}
|
|
|
|
static void PublishModuleEvent(ModuleHookOperation operation, const char *functionName, const char *sourceModule,
|
|
HMODULE moduleHandle, const void *nameBuffer, std::size_t nameLength,
|
|
std::uint64_t arg0 = 0, std::uint64_t arg1 = 0, std::uint64_t arg2 = 0,
|
|
std::uint64_t arg3 = 0) noexcept
|
|
{
|
|
if (g_InHook || g_ActiveCallback == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_InHook = true;
|
|
if (moduleHandle != nullptr)
|
|
{
|
|
(void)KeRefreshWinsockHooks(moduleHandle);
|
|
}
|
|
|
|
ModuleHookContext context{};
|
|
context.Operation = operation;
|
|
context.FunctionName = functionName;
|
|
context.SourceModule = sourceModule;
|
|
context.Caller = _ReturnAddress();
|
|
context.ModuleHandle = moduleHandle;
|
|
context.NameBuffer = nameBuffer;
|
|
context.NameLength = nameLength;
|
|
context.Args[0] = arg0;
|
|
context.Args[1] = arg1;
|
|
context.Args[2] = arg2;
|
|
context.Args[3] = arg3;
|
|
g_ActiveCallback(context);
|
|
g_InHook = false;
|
|
}
|
|
|
|
static std::size_t CopyAnsiLength(LPCSTR value) noexcept
|
|
{
|
|
return (value != nullptr) ? strnlen_s(value, 31) : 0;
|
|
}
|
|
|
|
static std::size_t CopyWideLength(LPCWSTR value) noexcept
|
|
{
|
|
std::size_t chars = 0;
|
|
if (value != nullptr)
|
|
{
|
|
while (value[chars] != L'\0' && chars < 31)
|
|
{
|
|
++chars;
|
|
}
|
|
}
|
|
return chars * sizeof(wchar_t);
|
|
}
|
|
|
|
HMODULE WINAPI LoadLibraryAHook(LPCSTR lpLibFileName)
|
|
{
|
|
if (g_OriginalLoadLibraryA == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
HMODULE moduleHandle = g_OriginalLoadLibraryA(lpLibFileName);
|
|
PublishModuleEvent(ModuleHookOperation::LoadLibraryA, "LoadLibraryA", "KERNELBASE", moduleHandle, lpLibFileName,
|
|
CopyAnsiLength(lpLibFileName));
|
|
return moduleHandle;
|
|
}
|
|
|
|
HMODULE WINAPI LoadLibraryWHook(LPCWSTR lpLibFileName)
|
|
{
|
|
if (g_OriginalLoadLibraryW == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
HMODULE moduleHandle = g_OriginalLoadLibraryW(lpLibFileName);
|
|
PublishModuleEvent(ModuleHookOperation::LoadLibraryW, "LoadLibraryW", "KERNELBASE", moduleHandle, lpLibFileName,
|
|
CopyWideLength(lpLibFileName));
|
|
return moduleHandle;
|
|
}
|
|
|
|
HMODULE WINAPI LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
|
|
{
|
|
if (g_OriginalLoadLibraryExA == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
HMODULE moduleHandle = g_OriginalLoadLibraryExA(lpLibFileName, hFile, dwFlags);
|
|
PublishModuleEvent(ModuleHookOperation::LoadLibraryExA, "LoadLibraryExA", "KERNELBASE", moduleHandle,
|
|
lpLibFileName, CopyAnsiLength(lpLibFileName), static_cast<std::uint64_t>(dwFlags),
|
|
static_cast<std::uint64_t>(reinterpret_cast<ULONG_PTR>(hFile)));
|
|
return moduleHandle;
|
|
}
|
|
|
|
HMODULE WINAPI LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
|
|
{
|
|
if (g_OriginalLoadLibraryExW == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
HMODULE moduleHandle = g_OriginalLoadLibraryExW(lpLibFileName, hFile, dwFlags);
|
|
PublishModuleEvent(ModuleHookOperation::LoadLibraryExW, "LoadLibraryExW", "KERNELBASE", moduleHandle,
|
|
lpLibFileName, CopyWideLength(lpLibFileName), static_cast<std::uint64_t>(dwFlags),
|
|
static_cast<std::uint64_t>(reinterpret_cast<ULONG_PTR>(hFile)));
|
|
return moduleHandle;
|
|
}
|
|
|
|
NTSTATUS NTAPI LdrLoadDllHook(PWSTR searchPath, PULONG loadFlags, PUNICODE_STRING moduleFileName,
|
|
PHANDLE moduleHandle)
|
|
{
|
|
if (g_OriginalLdrLoadDll == nullptr)
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS status = g_OriginalLdrLoadDll(searchPath, loadFlags, moduleFileName, moduleHandle);
|
|
HMODULE resolved = nullptr;
|
|
if (NT_SUCCESS(status) && moduleHandle != nullptr)
|
|
{
|
|
resolved = reinterpret_cast<HMODULE>(*moduleHandle);
|
|
}
|
|
|
|
PublishModuleEvent(ModuleHookOperation::LdrLoadDll, "LdrLoadDll", "ntdll", resolved,
|
|
(moduleFileName != nullptr) ? moduleFileName->Buffer : nullptr,
|
|
(moduleFileName != nullptr && moduleFileName->Length > 0) ? moduleFileName->Length : 0,
|
|
static_cast<std::uint64_t>((loadFlags != nullptr) ? *loadFlags : 0),
|
|
static_cast<std::uint64_t>(static_cast<ULONG>(status)),
|
|
static_cast<std::uint64_t>(reinterpret_cast<ULONG_PTR>(searchPath)),
|
|
static_cast<std::uint64_t>((moduleFileName != nullptr) ? moduleFileName->Length : 0));
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN WINAPI RtlAddFunctionTableHook(PRUNTIME_FUNCTION functionTable, DWORD entryCount, DWORD64 baseAddress)
|
|
{
|
|
if (g_OriginalRtlAddFunctionTable == nullptr)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN ok = g_OriginalRtlAddFunctionTable(functionTable, entryCount, baseAddress);
|
|
if (ok)
|
|
{
|
|
PublishModuleEvent(ModuleHookOperation::RtlAddFunctionTable, "RtlAddFunctionTable", "ntdll", nullptr,
|
|
nullptr, 0, static_cast<std::uint64_t>(reinterpret_cast<ULONG_PTR>(functionTable)),
|
|
static_cast<std::uint64_t>(entryCount), static_cast<std::uint64_t>(baseAddress), 0);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
BOOLEAN WINAPI RtlInstallFunctionTableCallbackHook(DWORD64 tableIdentifier, DWORD64 baseAddress, DWORD length,
|
|
PGET_RUNTIME_FUNCTION_CALLBACK callback, PVOID context,
|
|
PCWSTR outOfProcessCallbackDll)
|
|
{
|
|
if (g_OriginalRtlInstallFunctionTableCallback == nullptr)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN ok = g_OriginalRtlInstallFunctionTableCallback(tableIdentifier, baseAddress, length, callback, context,
|
|
outOfProcessCallbackDll);
|
|
if (ok)
|
|
{
|
|
PublishModuleEvent(ModuleHookOperation::RtlInstallFunctionTableCallback,
|
|
"RtlInstallFunctionTableCallback", "ntdll", nullptr, outOfProcessCallbackDll,
|
|
CopyWideLength(outOfProcessCallbackDll), static_cast<std::uint64_t>(tableIdentifier),
|
|
static_cast<std::uint64_t>(baseAddress), static_cast<std::uint64_t>(length),
|
|
static_cast<std::uint64_t>(reinterpret_cast<ULONG_PTR>(callback)));
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
BOOLEAN WINAPI RtlDeleteFunctionTableHook(PRUNTIME_FUNCTION functionTable)
|
|
{
|
|
if (g_OriginalRtlDeleteFunctionTable == nullptr)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN ok = g_OriginalRtlDeleteFunctionTable(functionTable);
|
|
if (ok)
|
|
{
|
|
PublishModuleEvent(ModuleHookOperation::RtlDeleteFunctionTable, "RtlDeleteFunctionTable", "ntdll", nullptr,
|
|
nullptr, 0, static_cast<std::uint64_t>(reinterpret_cast<ULONG_PTR>(functionTable)), 0, 0,
|
|
0);
|
|
}
|
|
return ok;
|
|
}
|
|
} // namespace
|
|
|
|
bool KeSetModuleHook(ModuleHookCallback callback) noexcept
|
|
{
|
|
if (callback == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
g_ActiveCallback = callback;
|
|
ResetModuleHookInitFault();
|
|
|
|
bool anyInstalled = false;
|
|
for (auto &hook : g_Hooks)
|
|
{
|
|
if (hook.Installed)
|
|
{
|
|
anyInstalled = true;
|
|
continue;
|
|
}
|
|
|
|
HMODULE moduleHandle = GetModuleHandleW(hook.ModuleName);
|
|
if (moduleHandle == nullptr)
|
|
{
|
|
SetModuleHookInitFault(ModuleHookInitFaultCode::ModuleMissing, hook.ModuleName, hook.ExportName, nullptr);
|
|
continue;
|
|
}
|
|
|
|
ModuleRange moduleRange{};
|
|
if (!TryResolveModuleImageRange(moduleHandle, moduleRange))
|
|
{
|
|
SetModuleHookInitFault(ModuleHookInitFaultCode::ExportOutsideImage, hook.ModuleName, hook.ExportName,
|
|
moduleHandle);
|
|
continue;
|
|
}
|
|
|
|
FARPROC exportAddress = GetProcAddress(moduleHandle, hook.ExportName);
|
|
if (exportAddress == nullptr)
|
|
{
|
|
SetModuleHookInitFault(ModuleHookInitFaultCode::ExportMissing, hook.ModuleName, hook.ExportName, nullptr);
|
|
continue;
|
|
}
|
|
|
|
hook.TargetAddress = reinterpret_cast<void *>(exportAddress);
|
|
if (!AddressWithinRange(hook.TargetAddress, moduleRange))
|
|
{
|
|
SetModuleHookInitFault(ModuleHookInitFaultCode::ExportOutsideImage, hook.ModuleName, hook.ExportName,
|
|
hook.TargetAddress);
|
|
continue;
|
|
}
|
|
|
|
void *redirectTarget = nullptr;
|
|
if (TryDecodeAbsoluteTarget(hook.TargetAddress, redirectTarget) && redirectTarget != nullptr &&
|
|
!AddressWithinRange(redirectTarget, moduleRange))
|
|
{
|
|
SetModuleHookInitFault(ModuleHookInitFaultCode::ExportRedirectedOutsideImage, hook.ModuleName,
|
|
hook.ExportName, hook.TargetAddress, redirectTarget);
|
|
continue;
|
|
}
|
|
|
|
if (!InstallInlineHook(hook.TargetAddress, hook.HookEntry, hook.OriginalBytes, &hook.Trampoline))
|
|
{
|
|
SetModuleHookInitFault(ModuleHookInitFaultCode::PatchInstallFailed, hook.ModuleName, hook.ExportName,
|
|
hook.TargetAddress);
|
|
continue;
|
|
}
|
|
|
|
*hook.OriginalFunction = hook.Trampoline;
|
|
hook.Installed = true;
|
|
anyInstalled = true;
|
|
}
|
|
|
|
if (!anyInstalled)
|
|
{
|
|
g_ActiveCallback = nullptr;
|
|
}
|
|
|
|
return anyInstalled;
|
|
}
|
|
|
|
void KeRemoveModuleHook() noexcept
|
|
{
|
|
for (auto &hook : g_Hooks)
|
|
{
|
|
if (!hook.Installed || hook.TargetAddress == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RemoveInlineHook(hook.TargetAddress, hook.OriginalBytes, hook.Trampoline);
|
|
hook.TargetAddress = nullptr;
|
|
hook.Trampoline = nullptr;
|
|
*hook.OriginalFunction = nullptr;
|
|
hook.Installed = false;
|
|
}
|
|
|
|
g_ActiveCallback = nullptr;
|
|
}
|
|
|
|
bool KeCheckModuleHookIntegrity(std::uint32_t *mismatchCount) noexcept
|
|
{
|
|
std::uint32_t mismatches = 0;
|
|
|
|
for (const auto &hook : g_Hooks)
|
|
{
|
|
if (!hook.Installed || hook.TargetAddress == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const auto *bytes = static_cast<const std::uint8_t *>(hook.TargetAddress);
|
|
void *patchedTarget = nullptr;
|
|
std::memcpy(&patchedTarget, &bytes[2], sizeof(patchedTarget));
|
|
bool intact = bytes[0] == 0x48 && bytes[1] == 0xB8 && patchedTarget == hook.HookEntry && bytes[10] == 0xFF &&
|
|
bytes[11] == 0xE0;
|
|
if (!intact)
|
|
{
|
|
++mismatches;
|
|
}
|
|
}
|
|
|
|
if (mismatchCount != nullptr)
|
|
{
|
|
*mismatchCount = mismatches;
|
|
}
|
|
|
|
return mismatches == 0;
|
|
}
|
|
|
|
bool KeGetLastModuleHookInitFault(ModuleHookInitFault *faultOut) noexcept
|
|
{
|
|
if (faultOut == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
*faultOut = g_LastModuleHookInitFault;
|
|
return faultOut->Code != ModuleHookInitFaultCode::None;
|
|
}
|