驅動開發:內核枚舉DpcTimer定時器( 三 )

輸出尋找CALL地址效果圖如下:

驅動開發:內核枚舉DpcTimer定時器

文章插圖
第三步: 也是最重要的一步 , 在KiSetTimerEx里面,搜索特征,拿到里面的KiWaitNever(),KiWaitAlways()這兩個函數地址 。
  • 488b05850c5100 KiWaitNever
  • 488b356b0e5100 KiWaitAlways
這個過程需要重復搜索,所以要把第一步和第二部過程歸納起來,具體代碼如下所示 。
/*0: kd> uf KiSetTimerExnt!KiSetTimerEx:fffff807`70520a50 48895c2408movqword ptr [rsp+8],rbxfffff807`70520a55 48896c2410movqword ptr [rsp+10h],rbpfffff807`70520a5a 4889742418movqword ptr [rsp+18h],rsifffff807`70520a5f 57pushrdifffff807`70520a60 4154pushr12fffff807`70520a62 4155pushr13fffff807`70520a64 4156pushr14fffff807`70520a66 4157pushr15fffff807`70520a68 4883ec50subrsp,50hfffff807`70520a6c 488b05850c5100movrax,qword ptr [nt!KiWaitNever (fffff807`70a316f8)]fffff807`70520a73 488bf9movrdi,rcxfffff807`70520a76 488b356b0e5100movrsi,qword ptr [nt!KiWaitAlways (fffff807`70a318e8)]fffff807`70520a7d 410fb6e9movzxebp,r9b*/#include <ntddk.h>#include <ntstrsafe.h>// 得到KiProcessorBlock地址ULONG64 GetKeSetTimerEx(){// 獲取 KeSetTimer 地址ULONG64 ul_KeSetTimer = 0;UNICODE_STRINGuc_KeSetTimer = { 0 };RtlInitUnicodeString(&uc_KeSetTimer, L"KeSetTimer");ul_KeSetTimer = (ULONG64)MmGetSystemRoutineAddress(&uc_KeSetTimer);if (ul_KeSetTimer == 0){return 0;}// 前 30 字節找 call 指令BOOLEAN b_e8 = FALSE;ULONG64 ul_e8Addr = 0;for (INT i = 0; i < 30; i++){// 驗證地址是否可讀寫if (!MmIsAddressValid((PVOID64)ul_KeSetTimer)){continue;}// e8 0c 00 00 00 call nt!KiSetTimerEx (fffff807`70520a50)if (*(PUCHAR)(ul_KeSetTimer + i) == 0xe8){b_e8 = TRUE;ul_e8Addr = ul_KeSetTimer + i;break;}}// 找到 call 則解析目的地址if (b_e8 == TRUE){if (!MmIsAddressValid((PVOID64)ul_e8Addr)){return 0;}INT ul_callCode = *(INT*)(ul_e8Addr + 1);ULONG64 ul_KiSetTimerEx = ul_e8Addr + ul_callCode + 5;return ul_KiSetTimerEx;}return 0;}// 得到KiWaitNever地址ULONG64 GetKiWaitNever(ULONG64 address){// 驗證地址是否可讀寫if (!MmIsAddressValid((PVOID64)address)){return 0;}// 前 100 字節找 找 KiWaitNeverfor (INT i = 0; i < 100; i++){// 48 8b 05 85 0c 51 00 | mov rax, qword ptr[nt!KiWaitNever(fffff807`70a316f8)]if (*(PUCHAR)(address + i) == 0x48 && *(PUCHAR)(address + i + 1) == 0x8b && *(PUCHAR)(address + i + 2) == 0x05){ULONG64 ul_movCode = *(UINT32*)(address + i + 3);ULONG64 ul_movAddr = address + i + ul_movCode + 7;// DbgPrint("找到KiWaitNever地址: %p \n", ul_movAddr);return ul_movAddr;}}return 0;}// 得到KiWaitAlways地址ULONG64 GetKiWaitAlways(ULONG64 address){// 驗證地址是否可讀寫if (!MmIsAddressValid((PVOID64)address)){return 0;}// 前 100 字節找 找 KiWaitNeverfor (INT i = 0; i < 100; i++){// 48 8b 35 6b 0e 51 00 | mov rsi,qword ptr [nt!KiWaitAlways (fffff807`70a318e8)]if (*(PUCHAR)(address + i) == 0x48 && *(PUCHAR)(address + i + 1) == 0x8b && *(PUCHAR)(address + i + 2) == 0x35){ULONG64 ul_movCode = *(UINT32*)(address + i + 3);ULONG64 ul_movAddr = address + i + ul_movCode + 7;return ul_movAddr;}}return 0;}VOID UnDriver(PDRIVER_OBJECT driver){DbgPrint("卸載完成... \n");}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){DbgPrint("hello lyshark.com \n");ULONG64 address = GetKeSetTimerEx();if (address != 0){ULONG64 KiWaitNeverAddress = GetKiWaitNever(address);DbgPrint("KiWaitNeverAddress = %p \n", KiWaitNeverAddress);ULONG64 KiWaitAlwaysAddress = GetKiWaitAlways(address);DbgPrint("KiWaitAlwaysAddress = %p \n", KiWaitAlwaysAddress);}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;}運行這個程序,我們看下尋找到的地址是否與WinDBG中找到的地址一致 。
驅動開發:內核枚舉DpcTimer定時器

文章插圖
功能實現部分: 最后將這些功能整合在一起 , 循環輸出鏈表元素,并解密元素即可實現枚舉當前系統DPC定時器 。
代碼核心API分析:
  • KeNumberProcessors 得到CPU數量(內核常量)
  • KeSetSystemAffinityThread 線程綁定到特定CPU上
  • GetKiProcessorBlock 獲得KPRCB的地址
  • KeRevertToUserAffinityThread 取消綁定CPU
解密部分提取出KiWaitNeverKiWaitAlways用于解密計算,轉換PKDPC對象結構,并輸出即可 。
#include <Fltkernel.h>#include <ntddk.h>#include <intrin.h>typedef struct _KTIMER_TABLE_ENTRY{ ULONG_PTR Lock; LIST_ENTRY Entry; ULONG_PTR Time;}KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY;typedef struct _KTIMER_TABLE{ ULONG_PTR TimerExpiry[64]; KTIMER_TABLE_ENTRY TimerEntries[256];}KTIMER_TABLE, *PKTIMER_TABLE;BOOLEAN get_KiWait(PULONG64 never, PULONG64 always){ // 獲取 KeSetTimer 地址 ULONG64 ul_KeSetTimer = 0; UNICODE_STRING uc_KeSetTimer = { 0 }; RtlInitUnicodeString(&uc_KeSetTimer, L"KeSetTimer"); ul_KeSetTimer = (ULONG64)MmGetSystemRoutineAddress(&uc_KeSetTimer); if (ul_KeSetTimer == NULL) {return FALSE; } // 前 30 字節找 KeSetTimer BOOLEAN b_e8 = FALSE; ULONG64 ul_e8Addr = 0; for (INT i = 0; i < 30; i++) {if (!MmIsAddressValid((PVOID64)ul_KeSetTimer)){continue;}/*0: kd> u nt!KeSetTimernt!KeSetTimer:fffff803`0fc63a40 4883ec38subrsp,38hfffff803`0fc63a44 4c89442420movqword ptr [rsp+20h],r8fffff803`0fc63a49 4533c9xorr9d,r9dfffff803`0fc63a4c 4533c0xorr8d,r8dfffff803`0fc63a4f e80c000000callnt!KiSetTimerEx (fffff803`0fc63a60)fffff803`0fc63a54 4883c438addrsp,38hfffff803`0fc63a58 c3retfffff803`0fc63a59 ccint3*/// fffff803`0fc63a4f e8 0c 00 00 00callnt!KiSetTimerEx (fffff803`0fc63a60)if (*(PUCHAR)(ul_KeSetTimer + i) == 0xe8){b_e8 = TRUE;ul_e8Addr = ul_KeSetTimer + i;break;} } // 找到 call 則解析目的地址 /*0: kd> u nt!KiSetTimerEx l20nt!KiSetTimerEx:fffff803`0fc63a60 48895c2408movqword ptr [rsp+8],rbxfffff803`0fc63a65 48896c2410movqword ptr [rsp+10h],rbpfffff803`0fc63a6a 4889742418movqword ptr [rsp+18h],rsifffff803`0fc63a6f 57pushrdifffff803`0fc63a70 4154pushr12fffff803`0fc63a72 4155pushr13fffff803`0fc63a74 4156pushr14fffff803`0fc63a76 4157pushr15fffff803`0fc63a78 4883ec50subrsp,50hfffff803`0fc63a7c 488b057d0c5100movrax,qword ptr [nt!KiWaitNever (fffff803`10174700)]fffff803`0fc63a83 488bf9movrdi,rcx */ ULONG64 ul_KiSetTimerEx = 0; if (b_e8 == TRUE) {if (!MmIsAddressValid((PVOID64)ul_e8Addr)){return FALSE;}INT ul_callCode = *(INT*)(ul_e8Addr + 1);ULONG64 ul_callAddr = ul_e8Addr + ul_callCode + 5;ul_KiSetTimerEx = ul_callAddr; } // 沒有 call 則直接在當前函數找 else {ul_KiSetTimerEx = ul_KeSetTimer; } // 前 50 字節找 找 KiWaitNever 和 KiWaitAlways /* 0: kd> u nt!KiSetTimerEx l20 nt!KiSetTimerEx: fffff803`0fc63a60 48895c2408movqword ptr [rsp+8],rbx fffff803`0fc63a65 48896c2410movqword ptr [rsp+10h],rbp fffff803`0fc63a6a 4889742418movqword ptr [rsp+18h],rsi fffff803`0fc63a6f 57pushrdi fffff803`0fc63a70 4154pushr12 fffff803`0fc63a72 4155pushr13 fffff803`0fc63a74 4156pushr14 fffff803`0fc63a76 4157pushr15 fffff803`0fc63a78 4883ec50subrsp,50h fffff803`0fc63a7c 488b057d0c5100movrax,qword ptr [nt!KiWaitNever (fffff803`10174700)] fffff803`0fc63a83 488bf9movrdi,rcx fffff803`0fc63a86 488b35630e5100movrsi,qword ptr [nt!KiWaitAlways (fffff803`101748f0)] */ if (!MmIsAddressValid((PVOID64)ul_KiSetTimerEx)) {return FALSE; } // 存放 KiWaitNever 和 KiWaitAlways 的地址 ULONG64 ul_arr_ret[2]; // 對應 ul_arr_ret 的下標 INT i_sub = 0; for (INT i = 0; i < 50; i++) {// fffff803`0fc63a7c 488b057d0c5100movrax,qword ptr [nt!KiWaitNever (fffff803`10174700)]if (*(PUCHAR)(ul_KiSetTimerEx + i) == 0x48 && *(PUCHAR)(ul_KiSetTimerEx + i + 1) == 0x8b && *(PUCHAR)(ul_KiSetTimerEx + i + 6) == 0x00){ULONG64 ul_movCode = *(UINT32*)(ul_KiSetTimerEx + i + 3);ULONG64 ul_movAddr = ul_KiSetTimerEx + i + ul_movCode + 7;// 只拿符合條件的前兩個值if (i_sub < 2){ul_arr_ret[i_sub++] = ul_movAddr;}} } *never = ul_arr_ret[0]; *always = ul_arr_ret[1]; return TRUE;}NTSTATUS DriverUnload(IN PDRIVER_OBJECT DriverObject){ return STATUS_SUCCESS;}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath){ DbgPrint("hello lyshark.com \n"); // 獲取 CPU 核心數 INT i_cpuNum = KeNumberProcessors; DbgPrint("CPU核心數: %d \n", i_cpuNum); for (KAFFINITY i = 0; i < i_cpuNum; i++) {// 線程綁定特定 CPUKeSetSystemAffinityThread(i + 1);// 獲得 KPRCB 的地址ULONG64 p_PRCB = (ULONG64)__readmsr(0xC0000101) + 0x20;if (!MmIsAddressValid((PVOID64)p_PRCB)){return FALSE;}// 取消綁定 CPUKeRevertToUserAffinityThread();// 判斷操作系統版本RTL_OSVERSIONINFOEXW OSVersion = { 0 };OSVersion.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);RtlGetVersion((PRTL_OSVERSIONINFOW)&OSVersion);// 計算 TimerTable 在 _KPRCB 結構中的偏移PKTIMER_TABLE p_TimeTable = NULL;if (OSVersion.dwMajorVersion == 10 && OSVersion.dwMinorVersion == 0){p_TimeTable = (PKTIMER_TABLE)(*(PULONG64)p_PRCB + 0x3680);}else if (OSVersion.dwMajorVersion == 6 && OSVersion.dwMinorVersion == 1){p_TimeTable = (PKTIMER_TABLE)(*(PULONG64)p_PRCB + 0x2200);}else{return FALSE;}// 遍歷 TimerEntries[] 數組(大小 256)for (INT j = 0; j < 256; j++){// 獲取 Entry 雙向鏈表地址if (!MmIsAddressValid((PVOID64)p_TimeTable)){continue;}PLIST_ENTRY p_ListEntryHead = &(p_TimeTable->TimerEntries[j].Entry);// 遍歷 Entry 雙向鏈表for (PLIST_ENTRY p_ListEntry = p_ListEntryHead->Flink; p_ListEntry != p_ListEntryHead; p_ListEntry = p_ListEntry->Flink){// 根據 Entry 取 _KTIMER 對象地址if (!MmIsAddressValid((PVOID64)p_ListEntry)){continue;}PKTIMER p_Timer = CONTAINING_RECORD(p_ListEntry, KTIMER, TimerListEntry);// 硬編碼取 KiWaitNever 和 KiWaitAlwaysULONG64 never = 0, always = 0;if (get_KiWait(&never, &always) == FALSE){return FALSE;}// 獲取解密前的 Dpc 對象if (!MmIsAddressValid((PVOID64)p_Timer)){continue;}ULONG64 ul_Dpc = (ULONG64)p_Timer->Dpc;INT i_Shift = (*((PULONG64)never) & 0xFF);// 解密 Dpc 對象ul_Dpc ^= *((ULONG_PTR*)never);// 異或ul_Dpc = _rotl64(ul_Dpc, i_Shift);// 循環左移ul_Dpc ^= (ULONG_PTR)p_Timer;// 異或ul_Dpc = _byteswap_uint64(ul_Dpc);// 顛倒順序ul_Dpc ^= *((ULONG_PTR*)always);// 異或// 對象類型轉換PKDPC p_Dpc = (PKDPC)ul_Dpc;// 打印驗證if (!MmIsAddressValid((PVOID64)p_Dpc)){continue;}DbgPrint("[LyShark] 定時器對象:0x%p | 函數入口:0x%p | 觸發周期: %d \n ", p_Timer, p_Dpc->DeferredRoutine);}} } return STATUS_SUCCESS;}

推薦閱讀