mirror of
https://github.com/BeneficialCode/WinArk
synced 2026-08-09 12:00:31 +00:00
1281 lines
33 KiB
C++
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;
|
|
} |