日本电影一区二区_日本va欧美va精品发布_日本黄h兄妹h动漫一区二区三区_日本欧美黄色

6.4 Windows驅(qū)動(dòng)開發(fā):內(nèi)核枚舉DpcTimer定時(shí)器

在操作系統(tǒng)內(nèi)核中,DPC(Deferred Procedure Call)是一種延遲執(zhí)行的過程調(diào)用機(jī)制,用于在中斷服務(wù)例程(ISR)的上下文之外執(zhí)行一些工作。DPC定時(shí)器是基于DPC機(jī)制的一種定時(shí)執(zhí)行任務(wù)的方式。

DPC定時(shí)器的主要特點(diǎn):

  1. 1. 延遲執(zhí)行: DPC定時(shí)器允許系統(tǒng)在未來的某個(gè)時(shí)間點(diǎn)執(zhí)行一些操作,而不是立即執(zhí)行。這對于一些需要在中斷處理例程之外執(zhí)行的任務(wù)很有用,以避免中斷處理例程的執(zhí)行時(shí)間過長。
  2. 2. 定時(shí)器機(jī)制: DPC定時(shí)器是基于時(shí)間的機(jī)制,允許開發(fā)人員指定一個(gè)將來的時(shí)間點(diǎn),當(dāng)系統(tǒng)時(shí)間達(dá)到該時(shí)間點(diǎn)時(shí),相關(guān)的DPC將被調(diào)度執(zhí)行。這有助于實(shí)現(xiàn)定時(shí)任務(wù),例如定期執(zhí)行某個(gè)函數(shù)或操作。
  3. 3. 中斷上下文之外執(zhí)行: DPC定時(shí)器通常在中斷服務(wù)例程(ISR)之外執(zhí)行,以避免在ISR中執(zhí)行過長時(shí)間的任務(wù),從而提高系統(tǒng)的響應(yīng)性和穩(wěn)定性。
  4. 4. 任務(wù)調(diào)度: DPC定時(shí)器允許內(nèi)核將需要延遲執(zhí)行的任務(wù)排隊(duì),并在指定的時(shí)間點(diǎn)執(zhí)行這些任務(wù)。這種任務(wù)調(diào)度機(jī)制有助于更有效地管理系統(tǒng)資源。

在筆者上一篇文章《內(nèi)核枚舉IOTimer定時(shí)器》中我們通過IoInitializeTimer這個(gè)API函數(shù)為跳板,向下掃描特征碼獲取到了IopTimerQueueHead也就是IO定時(shí)器的隊(duì)列頭,本章學(xué)習(xí)的枚舉DPC定時(shí)器依然使用特征碼掃描,唯一不同的是在新版系統(tǒng)中DPC是被異或加密的,想要找到正確的地址,只是需要在找到DPC表頭時(shí)進(jìn)行解密操作即可。

6.4 Windows驅(qū)動(dòng)開發(fā):內(nèi)核枚舉DpcTimer定時(shí)器

DPC定時(shí)器的作用是什么?

DPC(Deferred Procedure Call)是一種異步執(zhí)行的機(jī)制。它允許內(nèi)核代碼在不中斷當(dāng)前進(jìn)程的情況下,延遲執(zhí)行一些工作。DPC的執(zhí)行是由內(nèi)核定時(shí)器觸發(fā)的。內(nèi)核定時(shí)器是一種特殊的內(nèi)核對象,用于定時(shí)執(zhí)行某個(gè)特定的操作。在DPC的上下文中,內(nèi)核可以安全地訪問任何內(nèi)核數(shù)據(jù)結(jié)構(gòu),而不會(huì)引起死鎖或其他問題。

在內(nèi)核中可以使用DPC定時(shí)器設(shè)置任意定時(shí)任務(wù),當(dāng)?shù)竭_(dá)某個(gè)節(jié)點(diǎn)時(shí)自動(dòng)觸發(fā)定時(shí)回調(diào),定時(shí)器的內(nèi)部使用KTIMER對象,當(dāng)設(shè)置任務(wù)時(shí)會(huì)自動(dòng)插入到DPC隊(duì)列,由操作系統(tǒng)循環(huán)讀取DPC隊(duì)列并執(zhí)行任務(wù),枚舉DPC定時(shí)器可得知系統(tǒng)中存在的DPC任務(wù)。

要想在新版系統(tǒng)中得到DPC定時(shí)器則需要執(zhí)行的步驟有哪些?

  • ? 1.找到KiProcessorBlock地址并解析成_KPRCB結(jié)構(gòu)
  • ? 2.在_KPRCB結(jié)構(gòu)中得到_KTIMER_TABLE偏移
  • ? 3.解析_KTIMER_TABLE_ENTRY得到加密后的雙向鏈表

首先_KPRCB這個(gè)結(jié)構(gòu)體與CPU內(nèi)核對應(yīng),獲取方式可通過一個(gè)未導(dǎo)出的變量nt!KiProcessorBlock來得到,如下雙核電腦,結(jié)構(gòu)體存在兩個(gè)與之對應(yīng)的結(jié)構(gòu)地址。

kd> dq nt!KiProcessorBlockfffff807`70a32cc0 fffff807`6f77c180 ffffbe81`3cee0180fffff807`70a32cd0 00000000`00000000 00000000`00000000fffff807`70a32ce0 00000000`00000000 00000000`00000000

KiProcessorBlock是一個(gè)數(shù)組,其第一個(gè)結(jié)構(gòu)體TimerTable則是結(jié)構(gòu)體的偏移。

kd> dt _KPRCB fffff807`6f77c180ntdll!_KPRCB 0x000 MxCsr : 0x1f80 0x3680 TimerTable : _KTIMER_TABLE (此處) 0x5880 DpcGate : _KGATE

接下來是把所有的KTIMER都枚舉出來,KTimeR在TimerTable中的存儲(chǔ)方式是數(shù)組 雙向鏈表。

kd> dt _KTIMER_TABLEntdll!_KTIMER_TABLE 0x000 TimerExpiry : [64] Ptr64 _KTIMER 0x200 TimerEntries : [256] _KTIMER_TABLE_ENTRY (此處)

到了_KTIMER_TABLE_ENTRY這里,Entry開始的雙向鏈表,每一個(gè)元素都對應(yīng)一個(gè)Timer也就是說我們已經(jīng)可以遍歷所有未解密的Time變量了。

kd> dt _KTIMER_TABLE_ENTRY 0xfffff807`6f77c180 0x3680ntdll!_KTIMER_TABLE_ENTRY 0x000 Lock : 0 0x008 Entry : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ] 0x018 Time : _ULARGE_INTEGER 0x0kd> dt _KTIMER_TABLE_ENTRY 0xfffff807`6f77c180 0x3680 0x200ntdll!_KTIMER_TABLE_ENTRY 0x000 Lock : 0 0x008 Entry : _LIST_ENTRY [ 0xffffa707`a0d3e1a0 - 0xffffa707`a0d3e1a0 ] 0x018 Time : _ULARGE_INTEGER 0x00000001`a8030353

至于如何解密,我們需要得到加密位置,如下通過KeSetTimer找到KeSetTimerEx從中得到DCP加密流程。

kd> u nt!KeSetTimernt!KeSetTimer:fffff803`0fc63a40 4883ec38 sub rsp,38hfffff803`0fc63a44 4c89442420 mov qword ptr [rsp 20h],r8fffff803`0fc63a49 4533c9 xor r9d,r9dfffff803`0fc63a4c 4533c0 xor r8d,r8dfffff803`0fc63a4f e80c000000 call nt!KiSetTimerEx (fffff803`0fc63a60)fffff803`0fc63a54 4883c438 add rsp,38hfffff803`0fc63a58 c3 retfffff803`0fc63a59 cc int 3kd> u nt!KiSetTimerEx l50nt!KiSetTimerEx:fffff803`0fc63a60 48895c2408 mov qword ptr [rsp 8],rbxfffff803`0fc63a65 48896c2410 mov qword ptr [rsp 10h],rbpfffff803`0fc63a6a 4889742418 mov qword ptr [rsp 18h],rsifffff803`0fc63a6f 57 push rdifffff803`0fc63a70 4154 push r12fffff803`0fc63a72 4155 push r13fffff803`0fc63a74 4156 push r14fffff803`0fc63a76 4157 push r15fffff803`0fc63a78 4883ec50 sub rsp,50hfffff803`0fc63a7c 488b057d0c5100 mov rax,qword ptr [nt!KiWaitNever (fffff803`10174700)]fffff803`0fc63a83 488bf9 mov rdi,rcxfffff803`0fc63a86 488b35630e5100 mov rsi,qword ptr [nt!KiWaitAlways (fffff803`101748f0)]fffff803`0fc63a8d 410fb6e9 movzx ebp,r9bfffff803`0fc63a91 4c8bac24a0000000 mov r13,qword ptr [rsp 0A0h]fffff803`0fc63a99 458bf8 mov r15d,r8dfffff803`0fc63a9c 4933f5 xor rsi,r13fffff803`0fc63a9f 488bda mov rbx,rdxfffff803`0fc63aa2 480fce bswap rsifffff803`0fc63aa5 4833f1 xor rsi,rcxfffff803`0fc63aa8 8bc8 mov ecx,eaxfffff803`0fc63aaa 48d3ce ror rsi,clfffff803`0fc63aad 4833f0 xor rsi,raxfffff803`0fc63ab0 440f20c1 mov rcx,cr8fffff803`0fc63ab4 48898c24a0000000 mov qword ptr [rsp 0A0h],rcxfffff803`0fc63abc b802000000 mov eax,2fffff803`0fc63ac1 440f22c0 mov cr8,raxfffff803`0fc63ac5 8b05dd0a5100 mov eax,DWORD ptr [nt!KiIrqlFlags (fffff803`101745a8)]fffff803`0fc63acb 85c0 test eax,eaxfffff803`0fc63acd 0f85b72d1a00 jne nt!KiSetTimerEx 0x1a2e2a (fffff803`0fe0688a)fffff803`0fc63ad3 654c8b342520000000 mov r14,qword ptr gs:[20h]fffff803`0fc63adc 33d2 xor edx,edxfffff803`0fc63ade 488bcf mov rcx,rdifffff803`0fc63ae1 e86aa2fdff call nt!KiCancelTimer (fffff803`0fc3dd50)fffff803`0fc63ae6 440fb6e0 movzx r12d,alfffff803`0fc63aea 48897730 mov qword ptr [rdi 30h],rsifffff803`0fc63aee 33c0 xor eax,eaxfffff803`0fc63af0 44897f3c mov dword ptr [rdi 3Ch],r15dfffff803`0fc63af4 8b0f mov ecx,dword ptr [rdi]fffff803`0fc63af6 4889442430 mov qword ptr [rsp 30h],raxfffff803`0fc63afb 894c2430 mov dword ptr [rsp 30h],ecxfffff803`0fc63aff 488bcb mov rcx,rbxfffff803`0fc63b02 48c1e920 shr rcx,20hfffff803`0fc63b06 4889442438 mov qword ptr [rsp 38h],raxfffff803`0fc63b0b 4889442440 mov qword ptr [rsp 40h],raxfffff803`0fc63b10 40886c2431 mov byte ptr [rsp 31h],bplfffff803`0fc63b15 85c9 test ecx,ecxfffff803`0fc63b17 0f89c0000000 jns nt!KiSetTimerEx 0x17d (fffff803`0fc63bdd)fffff803`0fc63b1d 33c9 xor ecx,ecxfffff803`0fc63b1f 8bd1 mov edx,ecxfffff803`0fc63b21 40f6c5fc test bpl,0FChfffff803`0fc63b25 0f85a3000000 jne nt!KiSetTimerEx 0x16e (fffff803`0fc63bce)fffff803`0fc63b2b 48894c2420 mov qword ptr [rsp 20h],rcxfffff803`0fc63b30 48b80800000080f7ffff mov rax,0FFFFF78000000008hfffff803`0fc63b3a 4d8bc5 mov r8,r13fffff803`0fc63b3d 488b00 mov rax,qword ptr [rax]fffff803`0fc63b40 804c243340 or byte ptr [rsp 33h],40hfffff803`0fc63b45 482bc3 sub rax,rbxfffff803`0fc63b48 48894718 mov qword ptr [rdi 18h],raxfffff803`0fc63b4c 4803c2 add rax,rdxfffff803`0fc63b4f 48c1e812 shr rax,12hfffff803`0fc63b53 488bd7 mov rdx,rdifffff803`0fc63b56 440fb6c8 movzx r9d,alfffff803`0fc63b5a 44884c2432 mov byte ptr [rsp 32h],r9bfffff803`0fc63b5f 8b442430 mov eax,dword ptr [rsp 30h]fffff803`0fc63b63 8907 mov dword ptr [rdi],eaxfffff803`0fc63b65 894f04 mov dword ptr [rdi 4],ecxfffff803`0fc63b68 498bce mov rcx,r14fffff803`0fc63b6b e8209ffdff call nt!KiInsertTimerTable (fffff803`0fc3da90)fffff803`0fc63b70 84c0 test al,alfffff803`0fc63b72 0f8495000000 je nt!KiSetTimerEx 0x1ad (fffff803`0fc63c0d)fffff803`0fc63b78 f7058608510000000200 test dword ptr [nt!PerfGlobalGroupMask 0x8 (fffff803`10174408)],20000hfffff803`0fc63b82 0f852f2d1a00 jne nt!KiSetTimerEx 0x1a2e57 (fffff803`0fe068b7)fffff803`0fc63b88 f081277fffffff lock and dword ptr [rdi],0FFFFFF7Fhfffff803`0fc63b8f 488b8424a0000000 mov rax,qword ptr [rsp 0A0h]fffff803`0fc63b97 4533c9 xor r9d,r9dfffff803`0fc63b9a 33d2 xor edx,edxfffff803`0fc63b9c 88442420 mov byte ptr [rsp 20h],alfffff803`0fc63ba0 498bce mov rcx,r14fffff803`0fc63ba3 458d4101 lea r8d,[r9 1]fffff803`0fc63ba7 e8044efeff call nt!KiExitDispatcher (fffff803`0fc489b0)

如上匯編代碼KiSetTimerEx中就是DPC加密細(xì)節(jié),如果需要解密只需要逆操作即可,此處我就具體分析下加密細(xì)節(jié),分析這個(gè)東西我建議你使用記事本帶著色的。

分析思路是這樣的,首先這里要傳入待加密的DPC數(shù)據(jù),然后經(jīng)過KiWaitNeverKiWaitAlways對數(shù)據(jù)進(jìn)行xor,ror,bswap等操作。

6.4 Windows驅(qū)動(dòng)開發(fā):內(nèi)核枚舉DpcTimer定時(shí)器

將如上所示的匯編解密流程通過C語言的方式實(shí)現(xiàn),解密函數(shù)DPC_Print過程可以被總結(jié)為如下幾個(gè)流程:

  • ? 首先獲取定時(shí)器結(jié)構(gòu)體中Dpc成員的地址,將其轉(zhuǎn)換為ULONG_PTR類型的指針ptrDpc。
  • ? 然后將ptrDpc異或上一個(gè)常量p2dq(ptrKiWaitNever),這個(gè)常量是KiWaitNever的指針地址強(qiáng)制轉(zhuǎn)換為ULONG_PTR類型后的結(jié)果,相當(dāng)于異或上一個(gè)隨機(jī)值來進(jìn)行簡單的加密。
  • ? 然后將ptrDpc循環(huán)左移nShift位,其中nShift的值為KiWaitNever指針值的低8位,即取最后一個(gè)字節(jié)。
  • ? 接著將ptrDpc異或上定時(shí)器結(jié)構(gòu)體的地址,相當(dāng)于對加密結(jié)果進(jìn)行一個(gè)簡單的混淆。然后對ptrDpc進(jìn)行字節(jié)交換,相當(dāng)于將ptrDpc的字節(jié)序進(jìn)行翻轉(zhuǎn),以便在后面的代碼中能夠正確地解密DPC結(jié)構(gòu)體。最后將ptrDpc異或上一個(gè)常量p2dq(ptrKiWaitAlways),這個(gè)常量是KiWaitAlways的指針地址強(qiáng)制轉(zhuǎn)換為ULONG_PTR類型后的結(jié)果,相當(dāng)于再進(jìn)行一次簡單的加密。

最后,如果解密得到的DPC結(jié)構(gòu)體指針DecDpc是一個(gè)有效的內(nèi)核地址,就輸出該DPC的地址和它的延遲函數(shù)地址。其中DeferredRoutine是KDPC結(jié)構(gòu)體的一個(gè)成員,用于保存DPC的回調(diào)函數(shù)地址,將上述流程通過代碼方式實(shí)現(xiàn)則如下所示;

#include <ntddk.h>#include <ntstrsafe.h>// 解密DPCvoid DPC_Print(PKTIMER ptrTimer){ ULONG_PTR ptrDpc = (ULONG_PTR)ptrTimer->Dpc; KDPC* DecDpc = NULL; DWORD nShift = (p2dq(ptrKiWaitNever) & 0xFF); // _RSI->Dpc = (_KDPC *)v19; // _RSI = Timer; ptrDpc ^= p2dq(ptrKiWaitNever); // v19 = KiWaitNever ^ v18; ptrDpc = _rotl64(ptrDpc, nShift); // v18 = __ROR8__((unsigned __int64)Timer ^ _RBX, KiWaitNever); ptrDpc ^= (ULONG_PTR)ptrTimer; ptrDpc = _byteswap_uint64(ptrDpc); // __asm { bswap rbx } ptrDpc ^= p2dq(ptrKiWaitAlways); // _RBX = (unsigned __int64)DPC ^ KiWaitAlways; // real DPC if (MmIsAddressValid((PVOID)ptrDpc)) { DecDpc = (KDPC*)ptrDpc; DbgPrint("DPC = %p | routine = %p n", DecDpc, DecDpc->DeferredRoutine); }}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint("卸載完成... n");}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint("hello lyshark"); PKTIMER ptrTimer = NULL; DPC_Print(ptrTimer); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}

接著將這些功能通過代碼實(shí)現(xiàn),首先得到我們需要的函數(shù)地址,這些地址包括。

ULONG_PTR ptrKiProcessorBlock = 0xfffff80770a32cc0;ULONG_PTR ptrOffsetKTimerTable = 0x3680;ULONG_PTR ptrKiWaitNever = 0xfffff80770a316f8;ULONG_PTR ptrKiWaitAlways = 0xfffff80770a318e8;

此處我把它分為三步走,第一步找到KiProcessorBlock函數(shù)地址,第二步找到KeSetTimer并從里面尋找KeSetTimerEx,第三步根據(jù)KiSetTimerEx地址,搜索到KiWaitNever(),KiWaitAlways()這兩個(gè)函數(shù)內(nèi)存地址,最終循環(huán)鏈表并解密DPC隊(duì)列。

尋找KiProcessorBlock地址

找到KiProcessorBlock函數(shù)地址,該地址可通過__readmsr()寄存器相加偏移得到。

在WinDBG中可以輸入rdmsr c0000082得到MSR地址。

6.4 Windows驅(qū)動(dòng)開發(fā):內(nèi)核枚舉DpcTimer定時(shí)器

MSR寄存器使用代碼獲取也是很容易,只要找到MSR地址在加上0x20即可得到KiProcessorBlock的地址了。

/* lyshark 0: kd> dp !KiProcessorBlock fffff807`70a32cc0 fffff807`6f77c180 ffffbe81`3cee0180 fffff807`70a32cd0 00000000`00000000 00000000`00000000 fffff807`70a32ce0 00000000`00000000 00000000`00000000 fffff807`70a32cf0 00000000`00000000 00000000`00000000 fffff807`70a32d00 00000000`00000000 00000000`00000000 fffff807`70a32d10 00000000`00000000 00000000`00000000 fffff807`70a32d20 00000000`00000000 00000000`00000000 fffff807`70a32d30 00000000`00000000 00000000`00000000*/#include <ntddk.h>#include <ntstrsafe.h>// 得到KiProcessorBlock地址ULONG64 GetKiProcessorBlock(){ ULONG64 PrcbAddress = 0; PrcbAddress = (ULONG64)__readmsr(0xC0000101) 0x20; if (PrcbAddress != 0) { // PrcbAddress 是一個(gè)地址 這個(gè)地址存放了某個(gè) CPU 的 _KPRCB 的地址 return *(ULONG_PTR*)PrcbAddress; } return 0;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint("卸載完成... n");}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint("hello lyshark n"); ULONG64 address = GetKiProcessorBlock(); if (address != 0) { DbgPrint("KiProcessorBlock = %p n", address); } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}

運(yùn)行后即可得到輸出效果如下:

6.4 Windows驅(qū)動(dòng)開發(fā):內(nèi)核枚舉DpcTimer定時(shí)器

尋找KeSetTimer地址

找到KeSetTimer從里面搜索特征得到call KeSetTimerEx函數(shù)地址,還記得《內(nèi)核枚舉IoTimer定時(shí)器》中我們采用的特征碼定位方式嗎,沒錯(cuò)本次還要使用這個(gè)方法,我們此處需要搜索到e80c000000這段特征。

/* lyshark 0: kd> uf KeSetTimer nt!KeSetTimer: fffff807`70520a30 4883ec38 sub rsp,38h fffff807`70520a34 4c89442420 mov qword ptr [rsp 20h],r8 fffff807`70520a39 4533c9 xor r9d,r9d fffff807`70520a3c 4533c0 xor r8d,r8d fffff807`70520a3f e80c000000 call nt!KiSetTimerEx (fffff807`70520a50) fffff807`70520a44 4883c438 add rsp,38h fffff807`70520a48 c3 ret*/#include <ntddk.h>#include <ntstrsafe.h>// 得到KiProcessorBlock地址ULONG64 GetKeSetTimerEx(){ // 獲取 KeSetTimer 地址 ULONG64 ul_KeSetTimer = 0; UNICODE_STRING uc_KeSetTimer = { 0 }; RtlInitUnicodeString(&uc_KeSetTimer, L"KeSetTimer"); ul_KeSetTimer = (ULONG64)MmGetSystemRoutineAddress(&uc_KeSetTimer); if (ul_KeSetTimer == 0) { return 0; } // 前 30 字節(jié)找 call 指令 BOOLEAN b_e8 = FALSE; ULONG64 ul_e8Addr = 0; for (INT i = 0; i < 30; i ) { // 驗(yàn)證地址是否可讀寫 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;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint("卸載完成... n");}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint("hello lyshark n"); ULONG64 address = GetKeSetTimerEx(); if (address != 0) { DbgPrint("KeSetTimerEx = %p n", address); } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}

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

6.4 Windows驅(qū)動(dòng)開發(fā):內(nèi)核枚舉DpcTimer定時(shí)器

尋找KiWaitNever和KiWaitAlways地址

也是最重要的一步,在KiSetTimerEx里面,搜索特征,拿到里面的KiWaitNever(),KiWaitAlways()這兩個(gè)函數(shù)地址。

  • ? 488b05850c5100 KiWaitNever
  • ? 488b356b0e5100 KiWaitAlways

這個(gè)過程需要重復(fù)搜索,所以要把第一步和第二部過程歸納起來,具體代碼如下所示。

/* 0: kd> uf KiSetTimerEx nt!KiSetTimerEx: fffff807`70520a50 48895c2408 mov qword ptr [rsp 8],rbx fffff807`70520a55 48896c2410 mov qword ptr [rsp 10h],rbp fffff807`70520a5a 4889742418 mov qword ptr [rsp 18h],rsi fffff807`70520a5f 57 push rdi fffff807`70520a60 4154 push r12 fffff807`70520a62 4155 push r13 fffff807`70520a64 4156 push r14 fffff807`70520a66 4157 push r15 fffff807`70520a68 4883ec50 sub rsp,50h fffff807`70520a6c 488b05850c5100 mov rax,qword ptr [nt!KiWaitNever (fffff807`70a316f8)] fffff807`70520a73 488bf9 mov rdi,rcx fffff807`70520a76 488b356b0e5100 mov rsi,qword ptr [nt!KiWaitAlways (fffff807`70a318e8)] fffff807`70520a7d 410fb6e9 movzx ebp,r9b*/#include <ntddk.h>#include <ntstrsafe.h>// 得到KiProcessorBlock地址ULONG64 GetKeSetTimerEx(){ // 獲取 KeSetTimer 地址 ULONG64 ul_KeSetTimer = 0; UNICODE_STRING uc_KeSetTimer = { 0 }; RtlInitUnicodeString(&uc_KeSetTimer, L"KeSetTimer"); ul_KeSetTimer = (ULONG64)MmGetSystemRoutineAddress(&uc_KeSetTimer); if (ul_KeSetTimer == 0) { return 0; } // 前 30 字節(jié)找 call 指令 BOOLEAN b_e8 = FALSE; ULONG64 ul_e8Addr = 0; for (INT i = 0; i < 30; i ) { // 驗(yàn)證地址是否可讀寫 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){ // 驗(yàn)證地址是否可讀寫 if (!MmIsAddressValid((PVOID64)address)) { return 0; } // 前 100 字節(jié)找 找 KiWaitNever for (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){ // 驗(yàn)證地址是否可讀寫 if (!MmIsAddressValid((PVOID64)address)) { return 0; } // 前 100 字節(jié)找 找 KiWaitNever for (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 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;}

運(yùn)行這個(gè)程序,我們看下尋找到的地址是否與WinDBG中找到的地址一致。

6.4 Windows驅(qū)動(dòng)開發(fā):內(nèi)核枚舉DpcTimer定時(shí)器

實(shí)現(xiàn)枚舉DPCTimer

最后將這些功能整合在一起,循環(huán)輸出鏈表元素,并解密元素即可實(shí)現(xiàn)枚舉當(dāng)前系統(tǒng)DPC定時(shí)器。

代碼核心API分析:

  • ? KeNumberProcessors 得到CPU數(shù)量(內(nèi)核常量)
  • ? KeSetSystemAffinityThread 線程綁定到特定CPU上
  • ? GetKiProcessorBlock 獲得KPRCB的地址
  • ? KeRevertToUserAffinityThread 取消綁定CPU

解密部分提取出KiWaitNeverKiWaitAlways用于解密計(jì)算,轉(zhuǎn)換PKDPC對象結(jié)構(gòu),并輸出即可。

#include <Fltkernel.h>#include <ntddk.h>#include <intrin.h>#define IRP_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)UNICODE_STRING name_device; // 設(shè)備名UNICODE_STRING name_symbol; // 符號鏈接PDEVICE_OBJECT deviceObj; // 設(shè)備對象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 字節(jié)找 call 指令 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!KeSetTimer nt!KeSetTimer: fffff803`0fc63a40 4883ec38 sub rsp,38h fffff803`0fc63a44 4c89442420 mov qword ptr [rsp 20h],r8 fffff803`0fc63a49 4533c9 xor r9d,r9d fffff803`0fc63a4c 4533c0 xor r8d,r8d fffff803`0fc63a4f e80c000000 call nt!KiSetTimerEx (fffff803`0fc63a60) fffff803`0fc63a54 4883c438 add rsp,38h fffff803`0fc63a58 c3 ret fffff803`0fc63a59 cc int 3 */ // fffff803`0fc63a4f e8 0c 00 00 00 call nt!KiSetTimerEx (fffff803`0fc63a60) if (*(PUCHAR)(ul_KeSetTimer i) == 0xe8) { b_e8 = TRUE; ul_e8Addr = ul_KeSetTimer i; break; } } // 找到 call 則解析目的地址 /* 0: kd> u nt!KiSetTimerEx l20 nt!KiSetTimerEx: fffff803`0fc63a60 48895c2408 mov qword ptr [rsp 8],rbx fffff803`0fc63a65 48896c2410 mov qword ptr [rsp 10h],rbp fffff803`0fc63a6a 4889742418 mov qword ptr [rsp 18h],rsi fffff803`0fc63a6f 57 push rdi fffff803`0fc63a70 4154 push r12 fffff803`0fc63a72 4155 push r13 fffff803`0fc63a74 4156 push r14 fffff803`0fc63a76 4157 push r15 fffff803`0fc63a78 4883ec50 sub rsp,50h fffff803`0fc63a7c 488b057d0c5100 mov rax,qword ptr [nt!KiWaitNever (fffff803`10174700)] fffff803`0fc63a83 488bf9 mov rdi,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 則直接在當(dāng)前函數(shù)找 else { ul_KiSetTimerEx = ul_KeSetTimer; } // 前 50 字節(jié)找 找 KiWaitNever 和 KiWaitAlways /* 0: kd> u nt!KiSetTimerEx l20 nt!KiSetTimerEx: fffff803`0fc63a60 48895c2408 mov qword ptr [rsp 8],rbx fffff803`0fc63a65 48896c2410 mov qword ptr [rsp 10h],rbp fffff803`0fc63a6a 4889742418 mov qword ptr [rsp 18h],rsi fffff803`0fc63a6f 57 push rdi fffff803`0fc63a70 4154 push r12 fffff803`0fc63a72 4155 push r13 fffff803`0fc63a74 4156 push r14 fffff803`0fc63a76 4157 push r15 fffff803`0fc63a78 4883ec50 sub rsp,50h fffff803`0fc63a7c 488b057d0c5100 mov rax,qword ptr [nt!KiWaitNever (fffff803`10174700)] fffff803`0fc63a83 488bf9 mov rdi,rcx fffff803`0fc63a86 488b35630e5100 mov rsi,qword ptr [nt!KiWaitAlways (fffff803`101748f0)] */ if (!MmIsAddressValid((PVOID64)ul_KiSetTimerEx)) { return FALSE; } ULONG64 ul_arr_ret[2]; // 存放 KiWaitNever 和 KiWaitAlways 的地址 INT i_sub = 0; // 對應(yīng) ul_arr_ret 的下標(biāo) for (INT i = 0; i < 50; i ) { // // fffff803`0fc63a7c 488b057d0c5100 mov rax,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; // 只拿符合條件的前兩個(gè)值 if (i_sub < 2) { ul_arr_ret[i_sub ] = ul_movAddr; } } } *never = ul_arr_ret[0]; *always = ul_arr_ret[1]; return TRUE;}BOOLEAN EnumDpc(){ DbgPrint("hello lyshark n"); // 獲取 CPU 核心數(shù) INT i_cpuNum = KeNumberProcessors; DbgPrint("CPU核心數(shù): %d n", i_cpuNum); for (KAFFINITY i = 0; i < i_cpuNum; i ) { // 線程綁定特定 CPU KeSetSystemAffinityThread(i 1); // 獲得 KPRCB 的地址 ULONG64 p_PRCB = (ULONG64)__readmsr(0xC0000101) 0x20; if (!MmIsAddressValid((PVOID64)p_PRCB)) { return FALSE; } // 取消綁定 CPU KeRevertToUserAffinityThread(); // 計(jì)算 TimerTable 在 _KPRCB 結(jié)構(gòu)中的偏移 PKTIMER_TABLE p_TimeTable = NULL; // Windows 10 得到_KPRCB 0x3680 p_TimeTable = (PKTIMER_TABLE)(*(PULONG64)p_PRCB 0x3680); // 遍歷 TimerEntries[] 數(shù)組(大小 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) { // 根據(jù) Entry 取 _KTIMER 對象地址 if (!MmIsAddressValid((PVOID64)p_ListEntry)) { continue; } PKTIMER p_Timer = CONTAINING_RECORD(p_ListEntry, KTIMER, TimerListEntry); // 硬編碼取 KiWaitNever 和 KiWaitAlways ULONG64 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); // 循環(huán)左移 ul_Dpc ^= (ULONG_PTR)p_Timer; // 異或 ul_Dpc = _byteswap_uint64(ul_Dpc); // 顛倒順序 ul_Dpc ^= *((ULONG_PTR*)always); // 異或 // 對象類型轉(zhuǎn)換 PKDPC p_Dpc = (PKDPC)ul_Dpc; // 打印驗(yàn)證 if (!MmIsAddressValid((PVOID64)p_Dpc)) { continue; } DbgPrint("定時(shí)器對象:0x%p | 函數(shù)入口:0x%p | 觸發(fā)周期: %d n ", p_Timer, p_Dpc->DeferredRoutine, p_Timer->Period); } } } return TRUE;}// 對應(yīng) IRP_MJ_DEVICE_CONTROLNTSTATUS myIrpControl(IN PDEVICE_OBJECT pDevObj, IN PIRP pIRP){ // 獲取 IRP 對應(yīng)的 I/O 堆棧指針 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIRP); // 得到輸入緩沖區(qū)大小 ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength; // 得到輸出緩沖區(qū)大小 ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength; // 得到 IOCTL 碼 ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; // 捕獲 I/O 操作類型(MajorFunction) switch (code) { case IRP_TEST: { break; } default: break; } // 完成 IO 請求 IoCompleteRequest(pIRP, IO_NO_INCREMENT); return STATUS_SUCCESS;}// 對應(yīng) IRP_MJ_CREATE 、 IRP_MJ_CLOSENTSTATUS dpc_CAC(IN PDEVICE_OBJECT pDevObj, IN PIRP pIRP){ // 將 IRP 返回給 I/O 管理器 IoCompleteRequest( pIRP, // IRP 指針 IO_NO_INCREMENT // 線程優(yōu)先級,IO_NO_INCREMENT :不增加優(yōu)先級 ); // 設(shè)置 I/O 請求狀態(tài) pIRP->IoStatus.Status = STATUS_SUCCESS; // 設(shè)置 I/O 請求傳輸?shù)淖止?jié)數(shù) pIRP->IoStatus.Information = 0; return STATUS_SUCCESS;}NTSTATUS CreateDevice(IN PDRIVER_OBJECT DriverObject){ // 定義返回值 NTSTATUS status; // 初始化設(shè)備名 RtlInitUnicodeString(&name_device, L"DeviceLySharkDriver"); // 創(chuàng)建設(shè)備 status = IoCreateDevice( DriverObject, // 指向驅(qū)動(dòng)對象的指針 0, // 設(shè)備擴(kuò)展分配的字節(jié)數(shù) &name_device, // 設(shè)備名 FILE_DEVICE_UNKNOWN, // 設(shè)備類型 0, // 驅(qū)動(dòng)設(shè)備附加信息 TRUE, // 設(shè)備對象是否獨(dú)占設(shè)備 &deviceObj // 設(shè)備對象指針 ); if (!NT_SUCCESS(status)) { return status; } // 初始化符號鏈接名 RtlInitUnicodeString(&name_symbol, L"??LySharkDriver"); // 創(chuàng)建符號鏈接 status = IoCreateSymbolicLink(&name_symbol, &name_device); if (!NT_SUCCESS(status)) { return status; } return STATUS_SUCCESS;}NTSTATUS DriverUnload(IN PDRIVER_OBJECT DriverObject){ // 定義返回值 NTSTATUS status; // 刪除符號鏈接 status = IoDeleteSymbolicLink(&name_symbol); if (!NT_SUCCESS(status)) { return status; } // 刪除設(shè)備 IoDeleteDevice(deviceObj); return STATUS_SUCCESS;}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath){ // 定義返回值 NTSTATUS status; // 指定驅(qū)動(dòng)卸載函數(shù) DriverObject->DriverUnload = (PDRIVER_UNLOAD)DriverUnload; // 指定派遣函數(shù) DriverObject->MajorFunction[IRP_MJ_CREATE] = dpc_CAC; DriverObject->MajorFunction[IRP_MJ_CLOSE] = dpc_CAC; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = myIrpControl; // 創(chuàng)建設(shè)備 status = CreateDevice(DriverObject); if (!NT_SUCCESS(status)) { return status; } // 執(zhí)行枚舉 EnumDpc(); return STATUS_SUCCESS;}

最終運(yùn)行枚舉程序,你將會(huì)看到系統(tǒng)中所有的定時(shí)器,與ARK工具對比是一致的。

6.4 Windows驅(qū)動(dòng)開發(fā):內(nèi)核枚舉DpcTimer定時(shí)器

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號
公眾號
在線咨詢
分享本頁
返回頂部
高唐县| 高青县| 东兴市| 台湾省| 珲春市| 磐安县| 高碑店市| 黄山市| 仁寿县| 河津市| 定日县| 平泉县| 嘉善县| 揭东县| 姚安县| 策勒县| 晋州市| 汉沽区| 锦州市| 松原市| 泰州市| 桑日县| 南安市| 高碑店市| 封开县| 汾西县| 定边县| 松原市| 巴彦淖尔市| 西林县| 湖口县| 滁州市| 东光县| 犍为县| 江陵县| 青州市| 湖口县| 乌鲁木齐县| 淮南市| 乳源| 富源县|