Files
BeneficialCode-WinArk/KernelLibrary/khook.cpp
T

1281 lines
33 KiB
C++

#include "pch.h"
#include "khook.h"
#include "Memory.h"
#include "Helpers.h"
#include "PEParser.h"
#include <intrin.h>
#include "Logging.h"
#include "Zydis/Zydis.h"
SystemServiceTable* khook::_ntTable = nullptr;
SystemServiceTable* khook::_win32kTable = nullptr;
PVOID khook::_kernelImageBase = nullptr;
ULONG khook::_kernelImageSize = 0;
PVOID khook::_win32kImageBase = nullptr;
ULONG khook::_win32kImageSize = 0;
PVOID khook::_sectionStart = nullptr;
ULONG khook::_sectionSize = 0;
PVOID khook::_shadowSectionStart = nullptr;
ULONG khook::_shadowSectionSize = 0;
LinkedList<ShadowServiceNameEntry> khook::_shadowServiceNameList;
HANDLE khook::_pid = nullptr;
NTSTATUS khook::RtlSuperCopyMemory(_In_ VOID UNALIGNED* Destination, _In_ VOID UNALIGNED* Source, _In_ ULONG Length) {
KIRQL oldIrql;
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
PMDL mdl = IoAllocateMdl(Destination, Length, FALSE, FALSE, nullptr);
if (mdl == nullptr) {
KeLowerIrql(oldIrql);
return STATUS_NO_MEMORY;
}
MmBuildMdlForNonPagedPool(mdl);
// Hack: prevent bugcheck from Driver Verifier and possible future version of Windows
#pragma prefast(push)
// Disables the warnings specified in a given warning list.
#pragma prefast(disable:__WARNING_MODIFYING_MDL,"Trust me I'm a scientist")
CSHORT flags = mdl->MdlFlags;
mdl->MdlFlags |= MDL_PAGES_LOCKED;
mdl->MdlFlags &= ~MDL_SOURCE_IS_NONPAGED_POOL;
// Map pages and do the copy
PVOID mapped = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmCached, nullptr, FALSE, HighPagePriority);
if (mapped == nullptr) {
mdl->MdlFlags = flags;
IoFreeMdl(mdl);
KeLowerIrql(oldIrql);
return STATUS_NONE_MAPPED;
}
RtlCopyMemory(mapped, Source, Length);
MmUnmapLockedPages(mapped, mdl);
mdl->MdlFlags = flags;
#pragma prefast(pop)
IoFreeMdl(mdl);
KeLowerIrql(oldIrql);
return STATUS_SUCCESS;
}
PVOID khook::GetApiAddress(PCWSTR name) {
UNICODE_STRING uname;
RtlInitUnicodeString(&uname, name);
return MmGetSystemRoutineAddress(&uname);
}
bool khook::Hook(PVOID api, void* newfunc) {
ULONG_PTR addr = (ULONG_PTR)api;
if (!addr)
return false;
// allocate structure
_info = new (NonPagedPool) HookInfo;
if (!_info) {
return false;
}
// set hooking address
_info->Address = addr;
// set hooking opcode
#ifdef _WIN64
_info->Opcodes.mov = 0xB848;
#else
_info->Opcodes.mov = 0xB8;
#endif // _WIN64
_info->Opcodes.addr = (ULONG_PTR)newfunc;
_info->Opcodes.push = 0x50;
_info->Opcodes.ret = 0xc3;
// set original data
RtlCopyMemory(&_info->Original, (const void*)addr, sizeof(OpcodesInfo));
if (!NT_SUCCESS(RtlSuperCopyMemory((void*)addr, &_info->Opcodes, sizeof(OpcodesInfo)))) {
delete _info;
return false;
}
return true;
}
bool khook::Unhook() {
if (!_info || !_info->Address)
return false;
if (!NT_SUCCESS(RtlSuperCopyMemory((void*)_info->Address, _info->Original, sizeof(OpcodesInfo)))) {
return false;
}
delete _info;
return true;
}
bool khook::GetShadowSystemServiceTable() {
if (!_win32kTable) {
#ifndef _WIN64
// x86 code
UNICODE_STRING name = RTL_CONSTANT_STRING(L"KeServiceDescriptorTable");
_ntTable = (SystemServiceTable*)MmGetSystemRoutineAddress(&name);
_win32kTable = (SystemServiceTable*)((ULONG_PTR)_ntTable + 0x50);
#else
// x64 code
PEParser parser(_kernelImageBase);
int count = parser.GetSectionCount();
for (int i = 0; i < count; i++) {
auto pSec = parser.GetSectionHeader(i);
if (pSec->Characteristics & IMAGE_SCN_MEM_NOT_PAGED &&
pSec->Characteristics & IMAGE_SCN_MEM_EXECUTE &&
!(pSec->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) &&
(*(PULONG)pSec->Name != 'TINI') &&
(*(PULONG)pSec->Name != 'EGAP')) {
ULONG_PTR searchAddr = (ULONG_PTR)((PUCHAR)_kernelImageBase + pSec->VirtualAddress);
ULONG_PTR maxAddress = searchAddr + pSec->Misc.VirtualSize;
// Find KiSystemServiceStart
UCHAR pattern[] = { 0x8B, 0xF8, 0xC1, 0xEF, 0x07, 0x83, 0xE7, 0x20, 0x25, 0xFF, 0x0F, 0x00, 0x00 };
ULONG_PTR label = Helpers::SearchSignature(searchAddr, pattern, sizeof(pattern), pSec->Misc.VirtualSize);
if (label == 0)
return false;
searchAddr = label + sizeof(pattern);
UCHAR signature[] = { 0x4c,0x8d,0x1d };
ULONG size = maxAddress - searchAddr;
ULONG_PTR addr = Helpers::SearchSignature(searchAddr, signature, sizeof(signature), size);
if (addr == 0)
return false;
ULONG_PTR offsetAddr = addr + sizeof(signature);
ULONG_PTR offset = 0;
memcpy(&offset, (void*)offsetAddr, 4);
if (offset == 0) {
return false;
}
_win32kTable = (SystemServiceTable*)(addr + offset + 7) + 1;
}
}
#endif //
}
return true;
}
bool khook::GetSystemServiceTable() {
if (!_ntTable) {
#ifndef _WIN64
// x86 code
UNICODE_STRING name = RTL_CONSTANT_STRING(L"KeServiceDescriptorTable");
_ntTable = (SystemServiceTable*)MmGetSystemRoutineAddress(&name);
#else
// x64 code
PEParser parser(_kernelImageBase);
int count = parser.GetSectionCount();
for (int i = 0; i < count; i++) {
auto pSec = parser.GetSectionHeader(i);
if (pSec->Characteristics & IMAGE_SCN_MEM_NOT_PAGED &&
pSec->Characteristics & IMAGE_SCN_MEM_EXECUTE &&
!(pSec->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) &&
(*(PULONG)pSec->Name != 'TINI') &&
(*(PULONG)pSec->Name != 'EGAP')) {
ULONG_PTR searchAddr = (ULONG_PTR)((PUCHAR)_kernelImageBase + pSec->VirtualAddress);
ULONG_PTR maxAddress = searchAddr + pSec->Misc.VirtualSize;
UCHAR pattern[] = { 0x8B, 0xF8, 0xC1, 0xEF, 0x07, 0x83, 0xE7, 0x20, 0x25, 0xFF, 0x0F, 0x00, 0x00 };
ULONG_PTR label = Helpers::SearchSignature(searchAddr,pattern, sizeof(pattern), pSec->Misc.VirtualSize);
if (label == 0)
return false;
searchAddr = label + sizeof(pattern);
UCHAR signature[] = { 0x4c,0x8d,0x15 };
// IA32_LSTAR 0xC0000082H
// 微软补丁,打了个向上的跳转,导致搜索不到,所以__readmsr不好使了。
ULONG size = maxAddress - searchAddr;
ULONG_PTR addr = Helpers::SearchSignature(searchAddr, signature, sizeof(signature), size);
if (addr == 0)
return false;
ULONG_PTR offsetAddr = addr + sizeof(signature);
ULONG_PTR offset = 0;
memcpy(&offset, (void*)offsetAddr, 4);
if (offset == 0) {
return false;
}
// 7为指令长度
_ntTable = (SystemServiceTable*)(addr + offset + 7);
}
}
#endif // !_WIN64
}
return true;
}
bool khook::GetShadowSystemServiceNumber(PUNICODE_STRING symbolName, PULONG index) {
PLIST_ENTRY head = _shadowServiceNameList.GetHead();
ShadowServiceNameEntry* item = _shadowServiceNameList.GetHeadItem();
if (&item->Entry == head) {
return false;
}
PLIST_ENTRY entry = nullptr;
do {
entry = item->Entry.Flink; // 后继
item = CONTAINING_RECORD(entry, ShadowServiceNameEntry, Entry);
if (0 == RtlCompareUnicodeString(symbolName, &item->Name, false)) {
*index = item->Index;
return true;
}
} while (entry->Blink!=entry->Flink); // 头结点
return false;
}
bool khook::GetSystemServiceNumber(const char* exportName,PULONG index) {
PEParser parser(L"\\SystemRoot\\system32\\ntdll.dll");
ULONG offset = parser.GetExportByName(exportName);
if (offset != 0) {
// get system service number
unsigned char* exportData = (unsigned char*)parser.GetBaseAddress() + offset;
for (int i = 0; i < 16; i++) {
if (exportData[i] == 0xC2 || exportData[i] == 0xC3) //RET
break;
if (exportData[i] == 0xB8) //mov eax,X
{
ULONG* address = (ULONG*)(exportData + i + 1);
*index = *address;
address += 1;
auto opcode = *(unsigned short*)address;
unsigned char* p = (unsigned char*)address + 5;
auto code = *(unsigned char*)p;
if (code != 0xFF &&// x86
opcode != 0x04F6 // win10 x64
&& opcode != 0x050F) { // win7 x64
break;
}
return true;
}
}
}
return false;
}
bool khook::GetSectionStart(ULONG_PTR va) {
ULONG rva = va - (ULONG_PTR)_kernelImageBase;
if (!_kernelImageBase) {
return false;
}
IMAGE_DOS_HEADER* dosHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(_kernelImageBase);
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
else {
auto ntHeader = (PIMAGE_NT_HEADERS)((PUCHAR)_kernelImageBase + dosHeader->e_lfanew);
if (ntHeader->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
auto section = PEParser::RvaToSection(ntHeader, _kernelImageBase, rva);
if (!section) {
return false;
}
_sectionStart = (PVOID)((unsigned char*)_kernelImageBase + section->VirtualAddress);
_sectionSize = section->SizeOfRawData;
return true;
}
}
bool khook::GetShadowSectionStart(ULONG_PTR va) {
ULONG rva = va - (ULONG_PTR)_win32kImageBase;
if (!_win32kImageBase) {
return false;
}
IMAGE_DOS_HEADER* dosHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(_win32kImageBase);
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
else {
auto ntHeader = (PIMAGE_NT_HEADERS)((PUCHAR)_win32kImageBase + dosHeader->e_lfanew);
if (ntHeader->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
auto section = PEParser::RvaToSection(ntHeader, _win32kImageBase, rva);
if (!section) {
return false;
}
_shadowSectionStart = (PVOID)((unsigned char*)_win32kImageBase + section->VirtualAddress);
_shadowSectionSize = section->SizeOfRawData;
return true;
}
}
PVOID khook::FindCaveAddress(PVOID start, ULONG size, ULONG caveSize) {
unsigned char* opcode = (unsigned char*)start;
for (unsigned int i = 0, j = 0; i < size; i++) {
if (opcode[i] == 0x90 || opcode[i] == 0xCC) // NOP or INT3
j++;
else
j = 0;
if (j == caveSize)
return (PVOID)((ULONG_PTR)start + i - caveSize + 1);
}
return nullptr;
}
bool khook::HookSSDT(const char* apiName, void* newfunc) {
bool success = GetKernelAndWin32kBase();
if (!success) {
return false;
}
success = GetSystemServiceTable();
if (!success) {
return false;
}
ULONG_PTR base = (ULONG_PTR)_ntTable->ServiceTableBase;
if (!base) {
return false;
}
ULONG index = 0;
success = GetSystemServiceNumber(apiName, &index);
if (success) {
if (index >= _ntTable->NumberOfServices) {
return false;
}
LONG oldValue = _ntTable->ServiceTableBase[index];
LONG newValue = 0;
#ifdef _WIN64
/*
x64 SSDT Hook;
1) find API addr
2) get code page+size
3) find cave address
4) hook cave address
5) change SSDT value
*/
if (!_sectionStart) {
ULONG_PTR lowest = base;
ULONG_PTR highest = lowest + 0x0FFFFFFF;
ULONG_PTR address = (oldValue >> 4) + base;
success = GetSectionStart(address);
if (!success) {
return false;
}
if (!_sectionStart || !_sectionSize) {
return false;
}
if ((ULONG_PTR)_sectionStart < lowest) {
_sectionSize -= (ULONG)(lowest - (ULONG_PTR)_sectionStart);
_sectionStart = (PVOID)lowest;
}
}
PVOID caveAddress = FindCaveAddress(_sectionStart, _sectionSize, sizeof(OpcodesInfo));
if (!caveAddress) {
return false;
}
success = Hook(caveAddress, newfunc);
if (!success)
return false;
newValue = (LONG)((ULONG_PTR)caveAddress - base);
newValue = (newValue << 4) | oldValue & 0xF;
_info->Index = index;
_info->Old = oldValue;
_info->New = newValue;
_info->OriginalAddress = (oldValue >> 4) + base;
#else
newValue = (ULONG)newfunc;
_info = new (NonPagedPool) HookInfo;
_info->Index = index;
_info->Old = oldValue;
_info->New = newValue;
_info->OriginalAddress = oldValue;
#endif // _WIN64
RtlSuperCopyMemory(&_ntTable->ServiceTableBase[index], &newValue, sizeof(newValue));
_success = true;
return true;
}
return false;
}
bool khook::UnhookSSDT() {
RtlSuperCopyMemory(&_ntTable->ServiceTableBase[_info->Index], &_info->Old, sizeof(_info->Old));
#ifdef _WIN64
bool success = Unhook();
if (!success)
return false;
#else
delete _info;
#endif // _WIN64
return true;
}
bool khook::UnhookShadowSSDT() {
// Get the process id of the "winlogon.exe" process
bool success = SearchSessionProcess();
if (!success)
return nullptr;
PEPROCESS Process;
NTSTATUS status = PsLookupProcessByProcessId(_pid, &Process);
if (!NT_SUCCESS(status))
return nullptr;
KAPC_STATE apcState;
KeStackAttachProcess(Process, &apcState);
RtlSuperCopyMemory(&_win32kTable->ServiceTableBase[_info->Index], &_info->Old, sizeof(_info->Old));
do {
#ifdef _WIN64
success = Unhook();
if (!success)
break;
#else
delete _info;
#endif // _WIN64
} while (false);
KeUnstackDetachProcess(&apcState);
ObDereferenceObject(Process);
return success;
}
bool khook::GetKernelAndWin32kBase() {
if (_kernelImageBase && _win32kImageBase) {
return true;
}
void* buffer = nullptr;
ULONG size = 1 << 18;
buffer = ExAllocatePool(NonPagedPool, size);
if (!buffer) {
return false;
}
NTSTATUS status = ZwQuerySystemInformation(SystemModuleInformation, buffer, size, nullptr);
if (!NT_SUCCESS(status)) {
ExFreePool(buffer);
return false;
}
auto info = (RTL_PROCESS_MODULES*)buffer;
_kernelImageBase = info->Modules[0].ImageBase;
_kernelImageSize = info->Modules[0].ImageSize;
UNICODE_STRING win32kPath = RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\win32k.sys");
UNICODE_STRING moduleName;
auto entry = info->Modules;
ANSI_STRING ansiModuleName;
for(;;) {
if (entry->ImageBase == 0) {
break;
}
RtlInitAnsiString(&ansiModuleName, reinterpret_cast<PCSZ>(entry->FullPathName));
RtlAnsiStringToUnicodeString(&moduleName, &ansiModuleName, TRUE);
if (RtlCompareUnicodeString(&win32kPath, &moduleName, TRUE) == 0) {
_win32kImageBase = entry->ImageBase;
_win32kImageSize = entry->ImageSize;
RtlFreeUnicodeString(&moduleName);
break;
}
RtlFreeUnicodeString(&moduleName);
entry += 1;
if (entry == nullptr)
break;
}
ExFreePool(buffer);
return true;
}
PVOID khook::GetOriginalFunction(HookType type,const char* apiName,PUNICODE_STRING symbolName) {
ULONG_PTR address = 0;
switch (type) {
case HookType::SSDT:
{
bool success = GetKernelAndWin32kBase();
if (!success) {
return nullptr;
}
success = GetSystemServiceTable();
if (!success) {
return nullptr;
}
ULONG_PTR base = (ULONG_PTR)_ntTable->ServiceTableBase;
if (!base) {
return nullptr;
}
ULONG index = 0;
success = GetSystemServiceNumber(apiName, &index);
if (success) {
if (index >= _ntTable->NumberOfServices) {
return nullptr;
}
LONG oldValue = _ntTable->ServiceTableBase[index];
#ifdef _WIN64
address = (oldValue >> 4) + base;
#else
address = oldValue;
#endif
}
}
break;
case HookType::ShadowSSDT:
{
bool success = GetKernelAndWin32kBase();
if (!success) {
return nullptr;
}
success = GetShadowSystemServiceTable();
if (!success)
return nullptr;
// Get the process id of the "winlogon.exe" process
success = SearchSessionProcess();
if (!success)
return nullptr;
PEPROCESS Process;
NTSTATUS status = PsLookupProcessByProcessId(_pid, &Process);
if (!NT_SUCCESS(status))
return nullptr;
KAPC_STATE apcState;
KeStackAttachProcess(Process, &apcState);
do {
ULONG_PTR base = (ULONG_PTR)_win32kTable->ServiceTableBase;
if (!base) {
break;
}
ULONG index;
success = GetShadowSystemServiceNumber(symbolName, &index);
if (success) {
if (index > _win32kTable->NumberOfServices) {
break;
}
LONG oldValue = _win32kTable->ServiceTableBase[index];
#ifdef _WIN64
address = (oldValue >> 4) + base;
#else
address = oldValue;
#endif // __WIN64
}
} while (false);
KeUnstackDetachProcess(&apcState);
ObDereferenceObject(Process);
}
break;
default:
break;
}
return (PVOID)address;
}
bool khook::SearchSessionProcess() {
if (!_pid) {
ULONG len = 0;
NTSTATUS status = ZwQuerySystemInformation(SystemProcessInformation, nullptr, 0, &len);
if (status != STATUS_INFO_LENGTH_MISMATCH) {
return false;
}
ULONG size = len * 2;
void* buffer = ExAllocatePool(NonPagedPool, size);
if (!buffer) {
return false;
}
status = ZwQuerySystemInformation(SystemProcessInformation, buffer, size, &len);
if (!NT_SUCCESS(status)) {
ExFreePool(buffer);
return false;
}
auto info = (SYSTEM_PROCESS_INFORMATION*)buffer;
UNICODE_STRING targetProcName = RTL_CONSTANT_STRING(L"winlogon.exe");
for (;;) {
if (info->NextEntryOffset == 0)
break;
//KdPrint(("ImageName: %wZ,SessionId: %d\n", &info->ImageName,info->SessionId));
if (!RtlCompareUnicodeString(&targetProcName, &info->ImageName, TRUE)) {
_pid = info->UniqueProcessId;
break;
}
info = (SYSTEM_PROCESS_INFORMATION*)((PUCHAR)info + info->NextEntryOffset);
if (info == nullptr)
break;
}
ExFreePool(buffer);
return _pid == nullptr ? false : true;
}
return true;
}
/*
ed nt!Kd_SXS_Mask 0
ed nt!Kd_FUSION_Mask 0
*/
bool khook::HookShadowSSDT(PUNICODE_STRING apiName, void* newfunc) {
bool success = GetKernelAndWin32kBase();
if (!success) {
return false;
}
success = GetShadowSystemServiceTable();
if (!success)
return false;
// Get the process id of the "winlogon.exe" process
success = SearchSessionProcess();
if (!success)
return false;
PEPROCESS Process;
NTSTATUS status = PsLookupProcessByProcessId(_pid, &Process);
if (!NT_SUCCESS(status))
return false;
// 此处必须配对
KAPC_STATE apcState;
KeStackAttachProcess(Process, &apcState);
do {
ULONG_PTR base = (ULONG_PTR)_win32kTable->ServiceTableBase;
if (!base) {
success = false;
break;
}
ULONG index = 0;
success = GetShadowSystemServiceNumber(apiName, &index);
if (success) {
if (index > _win32kTable->NumberOfServices) {
success = false;
break;
}
LONG oldValue = _win32kTable->ServiceTableBase[index];
LONG newValue = 0;
#ifdef _WIN64
/*
x64 shadow ssdt hook
1) find API addr
2) get code page+size
3) find cave address
4) hook cave address
5) change Shadow SSDT value
*/
if (!_shadowSectionStart) {
ULONG_PTR lowest = base;
ULONG_PTR highest = lowest + 0x0FFFFFFF;
ULONG_PTR address = (oldValue >> 4) + base;
success = GetShadowSectionStart(address);
if (!success) {
break;
}
if (!_shadowSectionStart || !_shadowSectionSize) {
success = false;
break;
}
if ((ULONG_PTR)_shadowSectionStart < lowest) {
_shadowSectionSize -= (ULONG)(lowest - (ULONG_PTR)_sectionStart);
_shadowSectionStart = (PVOID)lowest;
}
}
PVOID caveAddress = FindCaveAddress(_shadowSectionStart, _shadowSectionSize, sizeof(OpcodesInfo));
if (!caveAddress) {
success = false;
break;
}
KdPrint(("caveAddress: 0x%p", caveAddress));
success = Hook(caveAddress, newfunc);
if (!success)
break;
newValue = (LONG)((ULONG_PTR)caveAddress - base);
newValue = (newValue << 4) | oldValue & 0xF;
_info->Index = index;
_info->Old = oldValue;
_info->New = newValue;
_info->OriginalAddress = (oldValue >> 4) + base;
#else
newValue = (ULONG)newfunc;
_info = new (NonPagedPool) HookInfo;
_info->Index = index;
_info->Old = oldValue;
_info->New = newValue;
_info->OriginalAddress = oldValue;
#endif // _WIN64
RtlSuperCopyMemory(&_win32kTable->ServiceTableBase[index], &newValue, sizeof(newValue));
success = true;
}
} while (false);
KeUnstackDetachProcess(&apcState);
ObDereferenceObject(Process);
_success = success;
return success ? true : false;
}
bool khook::HookKernelApi(PVOID api, void* newfunc,bool first) {
if (first) {
ULONG_PTR addr = (ULONG_PTR)newfunc;
if (!addr)
return false;
// allocate structure
_inlineInfo = new (NonPagedPool)InlineHookInfo;
if (!_inlineInfo) {
return false;
}
// set hooking address
_inlineInfo->Address = (ULONG_PTR)api;
_inlineInfo->Opcodes.lowAddr = addr & 0xFFFFFFFF;
#ifdef _WIN64
_inlineInfo->Opcodes.highAddr = (addr >> 32) & 0xFFFFFFFF;
#endif
// set original data
RtlCopyMemory(&_inlineInfo->Original, api, sizeof(InlineOpcodesInfo));
}
NTSTATUS status = SecureExchange(&_inlineInfo->Opcodes);
if (!NT_SUCCESS(status)) {
delete _inlineInfo;
return false;
}
_success = true;
return true;
}
NTSTATUS khook::SecureExchange(PVOID opcodes) {
PVOID address = reinterpret_cast<PVOID>(_inlineInfo->Address);
ULONG length = sizeof(InlineOpcodesInfo);
// Check for proper alignment, cmpxchg16b works only with 16-byte aligned address.
if ((ULONG_PTR)address != ((ULONG_PTR)address & ~0xf)) {
return STATUS_DATATYPE_MISALIGNMENT;
}
// Create memory descriptor list to map read-only (or RX) memory as read-write.
PMDL mdl = IoAllocateMdl(address, length, FALSE, FALSE, nullptr);
if (mdl == nullptr) {
return STATUS_NO_MEMORY;
}
// Make memory pages resident in RAM and make sure they won't get paged out.
__try {
MmProbeAndLockPages(mdl, KernelMode, IoReadAccess);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
IoFreeMdl(mdl);
return STATUS_INVALID_ADDRESS;
}
// Create new mapping for read-only memory
PVOID mapped = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmNonCached, nullptr, FALSE, NormalPagePriority);
if (mapped == nullptr) {
MmUnlockPages(mdl);
IoFreeMdl(mdl);
return STATUS_NONE_MAPPED;
}
// Set new mapped page protection to read-write in order to modify it.
NTSTATUS status = MmProtectMdlSystemAddress(mdl, PAGE_READWRITE);
if (!NT_SUCCESS(status)) {
MmUnmapLockedPages(mapped, mdl);
MmUnlockPages(mdl);
IoFreeMdl(mdl);
return status;
}
#ifdef _WIN64
PLONG64 dest = reinterpret_cast<PLONG64>(mapped);
LONG64 value[2];
value[0] = dest[0];
value[1] = dest[1];
InterlockedCompareExchange128(dest, ((PLONG64)opcodes)[1], ((PLONG64)opcodes)[0], (LONG64*)value);
#else
PLONG dest = reinterpret_cast<PLONG>(mapped);
InterlockedExchange(dest, *(LONG*)opcodes);
#endif // _WIN64
// Unlock and unmap pages, free MDL
MmUnmapLockedPages(mapped, mdl);
MmUnlockPages(mdl);
IoFreeMdl(mdl);
return STATUS_SUCCESS;
}
bool khook::UnhookKernelApi(bool end) {
if (!_inlineInfo || !_inlineInfo->Address)
return false;
NTSTATUS status = SecureExchange(&_inlineInfo->Original);
if (!NT_SUCCESS(status)) {
return false;
}
if(end)
delete _inlineInfo;
return true;
}
bool khook::GetApiAddress(ULONG index,PVOID* address) {
bool success = GetSystemServiceTable();
if (!success) {
return false;
}
ULONG_PTR base = (ULONG_PTR)_ntTable->ServiceTableBase;
if (!base) {
return false;
}
if (success) {
if (index >= _ntTable->NumberOfServices) {
return false;
}
LONG oldValue = _ntTable->ServiceTableBase[index];
#ifdef _WIN64
*address = (PVOID)((oldValue >> 4) + base);
#else
*address = (PVOID)oldValue;
#endif // _WIN64
return true;
}
return false;
}
bool khook::GetShadowApiAddress(ULONG index, PVOID* address) {
bool success = GetShadowSystemServiceTable();
if (!success)
return false;
// Get the process id of the "winlogon.exe" process
success = SearchSessionProcess();
if (!success)
return false;
PEPROCESS Process;
NTSTATUS status = PsLookupProcessByProcessId(_pid, &Process);
if (!NT_SUCCESS(status))
return false;
// 此处必须配对
KAPC_STATE apcState;
KeStackAttachProcess(Process, &apcState);
do
{
ULONG_PTR base = (ULONG_PTR)_win32kTable->ServiceTableBase;
if (!base) {
success = false;
break;
}
if (success) {
if (index > _win32kTable->NumberOfServices) {
success = false;
break;
}
LONG oldValue = _win32kTable->ServiceTableBase[index];
#ifdef _WIN64
*address = (PVOID)((oldValue >> 4) + base);
#else
*address = (PVOID)oldValue;
#endif // _WIN64
success = true;
}
} while (false);
KeUnstackDetachProcess(&apcState);
ObDereferenceObject(Process);
return success ? true : false;
}
ULONG khook::GetShadowServiceLimit() {
if (!_win32kTable)
return 0;
return _win32kTable->NumberOfServices;
}
void khook::DetectInlineHook(ULONG desiredCount,KernelInlineHookData* pData) {
bool success = GetKernelAndWin32kBase();
if (!success) {
return;
}
#ifndef _WIN64
// x86 code
#else
// x64 code
PEParser parser(_kernelImageBase);
int count = parser.GetSectionCount();
ULONG totalCount = 0;
// Initialize Zydis decoder and formatter
ZydisDecoder decoder;
if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64)))
return;
ZydisFormatter formatter;
if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL)))
return;
SIZE_T readOffset = 0;
ZydisDecodedInstruction instruction;
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
ZyanStatus status;
CHAR printBuffer[128];
for (int i = 0; i < count; i++) {
auto pSec = parser.GetSectionHeader(i);
if (pSec == nullptr) {
continue;
}
if (pSec->Characteristics & IMAGE_SCN_MEM_READ &&
pSec->Characteristics & IMAGE_SCN_CNT_CODE &&
pSec->Characteristics & IMAGE_SCN_MEM_EXECUTE &&
!(pSec->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) &&
(*(PULONG)pSec->Name != 'TINI')) {
ULONG_PTR startAddr = (ULONG_PTR)((PUCHAR)_kernelImageBase + pSec->VirtualAddress);
ULONG_PTR maxAddress = startAddr + pSec->Misc.VirtualSize;
LogInfo("start address: %p max address: %p\n", startAddr, maxAddress);
UCHAR pattern1[] = "\x48\xb8\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xff\xe0";
ULONG patternSize = sizeof(pattern1) - 1;
ULONG_PTR maxSearchAddr = maxAddress - patternSize;
ULONG_PTR searchAddr = startAddr;
while (searchAddr <= maxSearchAddr) {
PVOID pFound = NULL;
NTSTATUS status = Helpers::SearchPattern(pattern1, 0xCC, patternSize, (void*)searchAddr, pSec->Misc.VirtualSize, &pFound);
if (NT_SUCCESS(status)) {
if (totalCount >= desiredCount) {
break;
}
pData[totalCount].Address = (ULONG_PTR)pFound;
pData[totalCount].Type = KernelHookType::x64HookType1;
ULONG_PTR targetAddress = *(PULONG_PTR)((PUCHAR)pFound + 2);
pData[totalCount].TargetAddress = targetAddress;
searchAddr = (ULONG_PTR)pFound + patternSize;
LogInfo("Detect suspicious hook type 1 at %p\n", pFound);
LogInfo("Target Address: 0x%-16llX\n", targetAddress);
totalCount++;
}
else {
break;
}
}
UCHAR pattern2[] = "\x68\xcc\xcc\xcc\xcc\xc7\x44\x24\x04\xcc\xcc\xcc\xcc\xc3";
patternSize = sizeof(pattern2) - 1;
maxSearchAddr = maxAddress - patternSize;
searchAddr = startAddr;
while (searchAddr <= maxSearchAddr) {
PVOID pFound = NULL;
NTSTATUS status = Helpers::SearchPattern(pattern2, 0xCC, patternSize, (void*)searchAddr, pSec->Misc.VirtualSize, &pFound);
if (NT_SUCCESS(status)) {
if (totalCount >= desiredCount) {
break;
}
pData[totalCount].Address = (ULONG_PTR)pFound;
pData[totalCount].Type = KernelHookType::x64HookType2;
ULONG lowAddr = *(PULONG)((PUCHAR)pFound + 1);
ULONG highAddr = *(PULONG)((PUCHAR)pFound + 9);
ULONG_PTR targetAddress = ((ULONG_PTR)highAddr << 32) | lowAddr;
pData[totalCount].TargetAddress = targetAddress;
LogInfo("Detect suspicious hook type 2 at %p\n", pFound);
LogInfo("Target Address: 0x%-16llX\n", targetAddress);
searchAddr = (ULONG_PTR)pFound + patternSize;
totalCount++;
}
else {
break;
}
}
UCHAR pattern3[] = "\xe9\xcc\xcc\xcc\xcc";
patternSize = sizeof(pattern3) - 1;
maxSearchAddr = maxAddress - patternSize;
searchAddr = startAddr;
while (searchAddr <= maxSearchAddr) {
PVOID pFound = NULL;
NTSTATUS status = Helpers::SearchPattern(pattern3, 0xCC, patternSize, (void*)searchAddr, pSec->Misc.VirtualSize, &pFound);
if (NT_SUCCESS(status)) {
SIZE_T length = sizeof(pattern3);
// Start the decode loop
while ((status = ZydisDecoderDecodeFull(&decoder,
pFound,
length - readOffset, &instruction,
operands)) != ZYDIS_STATUS_NO_MORE_DATA)
{
NT_ASSERT(ZYAN_SUCCESS(status));
if (!ZYAN_SUCCESS(status))
{
readOffset++;
continue;
}
// Format and print the instruction
const ZyanU64 instrAddress = (ZyanU64)pFound;
ZydisFormatterFormatInstruction(
&formatter, &instruction, operands, instruction.operand_count_visible, printBuffer,
sizeof(printBuffer), instrAddress, NULL);
readOffset += instruction.length;
if (instruction.machine_mode != ZYDIS_MACHINE_MODE_LONG_64) {
continue;
}
if (instruction.mnemonic != ZYDIS_MNEMONIC_JMP) {
continue;
}
if (instruction.operand_count != 2) {
continue;
}
if (operands[0].type != ZYDIS_OPERAND_TYPE_IMMEDIATE) {
continue;
}
if (operands[1].type != ZYDIS_OPERAND_TYPE_REGISTER) {
continue;
}
if (operands[1].reg.value != ZYDIS_REGISTER_RIP) {
continue;
}
ULONG_PTR targetAddress = instrAddress + instruction.length
+ operands[0].imm.value.u;
if (targetAddress < maxAddress) {
continue;
}
BOOLEAN isValid = MmIsAddressValid((PVOID)targetAddress);
if (!isValid) {
continue;
}
LogInfo("Target Address: 0x%-16llX\n", targetAddress);
if (totalCount >= desiredCount) {
break;
}
pData[totalCount].Address = (ULONG_PTR)pFound;
pData[totalCount].Type = KernelHookType::x64HookType3;
pData[totalCount].TargetAddress = targetAddress;
totalCount++;
LogInfo("Detect suspicious hook type 3 at %p\n", pFound);
LogInfo("0x%-16llX\t\t%hs\n", instrAddress, printBuffer);
}
searchAddr = (ULONG_PTR)pFound + patternSize;
}
else {
break;
}
}
}
}
#endif
}
ULONG khook::GetInlineHookCount() {
bool success = GetKernelAndWin32kBase();
if (!success) {
return 0;
}
ULONG totalCount = 0;
#ifndef _WIN64
// x86 code
#else
// x64 code
PEParser parser(_kernelImageBase);
int count = parser.GetSectionCount();
// Initialize Zydis decoder and formatter
ZydisDecoder decoder;
if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64)))
return 0;
ZydisFormatter formatter;
if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL)))
return 0;
SIZE_T readOffset = 0;
ZydisDecodedInstruction instruction;
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
ZyanStatus status;
CHAR printBuffer[128];
for (int i = 0; i < count; i++) {
auto pSec = parser.GetSectionHeader(i);
if (pSec == nullptr) {
continue;
}
if (pSec->Characteristics & IMAGE_SCN_MEM_READ &&
pSec->Characteristics & IMAGE_SCN_CNT_CODE &&
pSec->Characteristics & IMAGE_SCN_MEM_EXECUTE &&
!(pSec->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) &&
(*(PULONG)pSec->Name != 'TINI')) {
ULONG_PTR startAddr = (ULONG_PTR)((PUCHAR)_kernelImageBase + pSec->VirtualAddress);
ULONG_PTR maxAddress = startAddr + pSec->Misc.VirtualSize;
UCHAR pattern1[] = "\x48\xb8\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xff\xe0";
ULONG patternSize = sizeof(pattern1) - 1;
LogInfo("start address: %p max address: %p\n", startAddr, maxAddress);
ULONG_PTR maxSearchAddr = maxAddress - patternSize;
ULONG_PTR searchAddr = startAddr;
while (searchAddr <= maxSearchAddr) {
PVOID pFound = NULL;
NTSTATUS status = Helpers::SearchPattern(pattern1, 0xCC, patternSize, (void*)searchAddr, pSec->Misc.VirtualSize, &pFound);
if (NT_SUCCESS(status)) {
searchAddr = (ULONG_PTR)pFound + patternSize;
LogInfo("Detect suspicious hook type 1 at %p\n", pFound);
totalCount++;
}
else {
break;
}
}
UCHAR pattern2[] = "\x68\xcc\xcc\xcc\xcc\xc7\x44\x24\x04\xcc\xcc\xcc\xcc\xc3";
patternSize = sizeof(pattern2) - 1;
maxSearchAddr = maxAddress - patternSize;
searchAddr = startAddr;
while (searchAddr <= maxSearchAddr) {
PVOID pFound = NULL;
NTSTATUS status = Helpers::SearchPattern(pattern2, 0xCC, patternSize, (void*)searchAddr, pSec->Misc.VirtualSize, &pFound);
if (NT_SUCCESS(status)) {
LogInfo("Detect suspicious hook type 2 at %p\n", pFound);
searchAddr = (ULONG_PTR)pFound + patternSize;
totalCount++;
}
else {
break;
}
}
UCHAR pattern3[] = "\xe9\xcc\xcc\xcc\xcc";
patternSize = sizeof(pattern3) - 1;
maxSearchAddr = maxAddress - patternSize;
searchAddr = startAddr;
while (searchAddr <= maxSearchAddr) {
PVOID pFound = NULL;
NTSTATUS status = Helpers::SearchPattern(pattern3, 0xCC, patternSize, (void*)searchAddr, pSec->Misc.VirtualSize, &pFound);
if (NT_SUCCESS(status)) {
SIZE_T length = sizeof(pattern3);
// Start the decode loop
while ((status = ZydisDecoderDecodeFull(&decoder,
pFound,
length - readOffset, &instruction,
operands)) != ZYDIS_STATUS_NO_MORE_DATA)
{
NT_ASSERT(ZYAN_SUCCESS(status));
if (!ZYAN_SUCCESS(status))
{
readOffset++;
continue;
}
// Format and print the instruction
const ZyanU64 instrAddress = (ZyanU64)pFound;
ZydisFormatterFormatInstruction(
&formatter, &instruction, operands, instruction.operand_count_visible, printBuffer,
sizeof(printBuffer), instrAddress, NULL);
readOffset += instruction.length;
if (instruction.machine_mode != ZYDIS_MACHINE_MODE_LONG_64) {
continue;
}
if (instruction.mnemonic != ZYDIS_MNEMONIC_JMP) {
continue;
}
if (instruction.operand_count != 2) {
continue;
}
if (operands[0].type != ZYDIS_OPERAND_TYPE_IMMEDIATE) {
continue;
}
if (operands[1].type != ZYDIS_OPERAND_TYPE_REGISTER) {
continue;
}
if (operands[1].reg.value != ZYDIS_REGISTER_RIP) {
continue;
}
ULONG_PTR targetAddress = instrAddress + instruction.length
+ operands[0].imm.value.u;
if (targetAddress < maxAddress) {
continue;
}
BOOLEAN isValid = MmIsAddressValid((PVOID)targetAddress);
if (!isValid) {
continue;
}
LogInfo("Target Address: 0x%-16llX\n", targetAddress);
totalCount++;
LogInfo("Detect suspicious hook type 3 at %p\n", pFound);
LogInfo("0x%-16llX\t\t%hs\n", instrAddress, printBuffer);
}
searchAddr = (ULONG_PTR)pFound + patternSize;
}
else {
break;
}
}
}
}
#endif
LogInfo("Total inline count: %d\n", totalCount);
return totalCount;
}