目录
不调用内核API定位到NT内核基址
目录
通用思路
- 明确一点,所有的内核模块基址,一定是4KB对齐的
- 所以我们可以想办法获取到处于NT内核中的一个函数地址,将其页面对齐后,以
PAGE_SIZE为单位向前搜索MZ - 搜索到
MZ后,继续搜索NT头特征、可选头特征,都满足的话就可以认为是NT内核了
实现思路
- 在x64系统调用中,我们知道内核的落脚地为
KiSystemCall64,这个函数地址是保存在MSR寄存器中的,即__readmsr(0xC0000082) - 所以我们的思路就很清楚了,获取
KiSystemCall64地址、页面对齐、向前搜索PE特征,代码如下:
PVOID
GetKernelBaseWithoutAPI(
VOID
)
{
static ULONG64 KernelBase = 0;
if (KernelBase != 0)
{
return (PVOID)KernelBase;
}
ULONG64 AddrAligned = __readmsr(0xC0000082) & ~(0xFFFull);
while (AddrAligned > 0)
{
PIMAGE_DOS_HEADER Dos = (PIMAGE_DOS_HEADER)AddrAligned;
if (Dos->e_magic == IMAGE_DOS_SIGNATURE)
{
LONG e_lfanew = Dos->e_lfanew;
if (e_lfanew > 0 && e_lfanew <= 0x1000)
{
PIMAGE_NT_HEADERS64 Nt = (PIMAGE_NT_HEADERS64)(AddrAligned + (ULONG64)e_lfanew);
if (Nt->Signature == IMAGE_NT_SIGNATURE &&
Nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC &&
Nt->OptionalHeader.ImageBase == AddrAligned &&
Nt->OptionalHeader.SizeOfImage != 0)
{
KernelBase = AddrAligned;
return (PVOID)KernelBase;
}
}
}
AddrAligned -= PAGE_SIZE;
}
return NULL;
}