您好,登錄后才能下訂單哦!
本篇內容主要講解“C語言驅動開發內核枚舉IoTimer定時器怎么實現”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“C語言驅動開發內核枚舉IoTimer定時器怎么實現”吧!
IoTimer
內核定時器其實就是在內核中實現的時鐘,該定時器的枚舉非常簡單,因為在IoInitializeTimer
初始化部分就可以找到IopTimerQueueHead
地址,該變量內存儲的就是定時器的鏈表頭部。枚舉IO定時器的案例并不多見,即便有也是無法使用過時的,此教程學到肯定就是賺到了。
1.找到IoInitializeTimer
函數,該函數可以通過MmGetSystemRoutineAddress
得到。
2.找到地址以后,我們向下增加0xFF
偏移量,并搜索特征定位到IopTimerQueueHead
鏈表頭。
3.將鏈表頭轉換為IO_TIMER
結構體,并循環鏈表頭輸出。
這里解釋一下為什么要找IoInitializeTimer
這個函數他是一個初始化函數,既然是初始化里面一定會涉及到鏈表的存儲問題,找到他就能找到定時器鏈表基址,該函數的定義如下。
NTSTATUS IoInitializeTimer( IN PDEVICE_OBJECT DeviceObject, // 設備對象指針 IN PIO_TIMER_ROUTINE TimerRoutine, // 定時器例程 IN PVOID Context // 傳給定時器例程的函數 );
接著我們需要得到IO定時器的結構定義,在DEVICE_OBJECT
設備對象指針中存在一個Timer
屬性。
lyshark.com: kd> dt _DEVICE_OBJECT ntdll!_DEVICE_OBJECT +0x000 Type : Int2B +0x002 Size : Uint2B +0x004 ReferenceCount : Int4B +0x008 DriverObject : Ptr64 _DRIVER_OBJECT +0x010 NextDevice : Ptr64 _DEVICE_OBJECT +0x018 AttachedDevice : Ptr64 _DEVICE_OBJECT +0x020 CurrentIrp : Ptr64 _IRP +0x028 Timer : Ptr64 _IO_TIMER +0x030 Flags : Uint4B +0x034 Characteristics : Uint4B +0x038 Vpb : Ptr64 _VPB +0x040 DeviceExtension : Ptr64 Void +0x048 DeviceType : Uint4B +0x04c StackSize : Char +0x050 Queue : <anonymous-tag> +0x098 AlignmentRequirement : Uint4B +0x0a0 DeviceQueue : _KDEVICE_QUEUE +0x0c8 Dpc : _KDPC +0x108 ActiveThreadCount : Uint4B +0x110 SecurityDescriptor : Ptr64 Void +0x118 DeviceLock : _KEVENT +0x130 SectorSize : Uint2B +0x132 Spare1 : Uint2B +0x138 DeviceObjectExtension : Ptr64 _DEVOBJ_EXTENSION +0x140 Reserved : Ptr64 Void
這里的這個+0x028 Timer
定時器是一個結構體_IO_TIMER
其就是IO定時器的所需結構體。
lyshark.com: kd> dt _IO_TIMER ntdll!_IO_TIMER +0x000 Type : Int2B +0x002 TimerFlag : Int2B +0x008 TimerList : _LIST_ENTRY +0x018 TimerRoutine : Ptr64 void +0x020 Context : Ptr64 Void +0x028 DeviceObject : Ptr64 _DEVICE_OBJECT
如上方的基礎知識有了也就夠了,接著就是實際開發部分,首先我們需要編寫一個GetIoInitializeTimerAddress()
函數,讓該函數可以定位到IoInitializeTimer
所在內核中的基地址上面,具體實現調用代碼如下所示。
#include <ntifs.h> // 得到IoInitializeTimer基址 // By: LyShark 內核開發系列教程 PVOID GetIoInitializeTimerAddress() { PVOID VariableAddress = 0; UNICODE_STRING uioiTime = { 0 }; RtlInitUnicodeString(&uioiTime, L"IoInitializeTimer"); VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime); if (VariableAddress != 0) { return VariableAddress; } return 0; } VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint(("Uninstall Driver Is OK \n")); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint(("hello lyshark.com \n")); // 得到基址 PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress(); DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
運行這個驅動程序,然后對比下是否一致:
接著我們在反匯編代碼中尋找IoTimerQueueHead
,此處在LyShark系統內這個偏移位置是nt!IoInitializeTimer+0x5d
具體輸出位置如下。
lyshark.com: kd> uf IoInitializeTimer nt!IoInitializeTimer+0x5d: fffff805`74b85bed 488d5008 lea rdx,[rax+8] fffff805`74b85bf1 48897018 mov qword ptr [rax+18h],rsi fffff805`74b85bf5 4c8d054475e0ff lea r8,[nt!IopTimerLock (fffff805`7498d140)] fffff805`74b85bfc 48897820 mov qword ptr [rax+20h],rdi fffff805`74b85c00 488d0dd9ddcdff lea rcx,[nt!IopTimerQueueHead (fffff805`748639e0)] fffff805`74b85c07 e8141e98ff call nt!ExInterlockedInsertTailList (fffff805`74507a20) fffff805`74b85c0c 33c0 xor eax,eax
在WinDBG中標注出顏色lea rcx,[nt!IopTimerQueueHead (fffff805748639e0)]
更容易看到。
接著就是通過代碼實現對此處的定位,定位我們就采用特征碼搜索的方式,如下代碼是特征搜索部分。
StartSearchAddress 代表開始位置
EndSearchAddress 代表結束位置,粗略計算0xff就可以定位到了。
#include <ntifs.h> // 得到IoInitializeTimer基址 // By: LyShark 內核開發系列教程 PVOID GetIoInitializeTimerAddress() { PVOID VariableAddress = 0; UNICODE_STRING uioiTime = { 0 }; RtlInitUnicodeString(&uioiTime, L"IoInitializeTimer"); VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime); if (VariableAddress != 0) { return VariableAddress; } return 0; } VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint(("Uninstall Driver Is OK \n")); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint(("hello lyshark.com \n")); // 得到基址 PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress(); DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer); INT32 iOffset = 0; PLIST_ENTRY IoTimerQueueHead = NULL; PUCHAR StartSearchAddress = IoInitializeTimer; PUCHAR EndSearchAddress = IoInitializeTimer + 0xFF; UCHAR v1 = 0, v2 = 0, v3 = 0; for (PUCHAR i = StartSearchAddress; i < EndSearchAddress; i++) { if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2)) { v1 = *i; v2 = *(i + 1); v3 = *(i + 2); // 三個特征碼 if (v1 == 0x48 && v2 == 0x8d && v3 == 0x0d) { memcpy(&iOffset, i + 3, 4); IoTimerQueueHead = (PLIST_ENTRY)(iOffset + (ULONG64)i + 7); DbgPrint("IoTimerQueueHead = %p \n", IoTimerQueueHead); break; } } } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
搜索三個特征碼v1 == 0x48 && v2 == 0x8d && v3 == 0x0d
從而得到內存位置,運行驅動對比下。
運行代碼會取出lea
指令后面的操作數,而不是取出lea指令的內存地址。
最后一步就是枚舉部分,我們需要前面提到的IO_TIMER
結構體定義。
PIO_TIMER Timer = CONTAINING_RECORD(NextEntry, IO_TIMER, TimerList) 得到結構體,循環輸出即可。
// By: LyShark 內核開發系列教程 // https://www.cnblogs.com/LyShark/articles/16784393.html #include <ntddk.h> #include <ntstrsafe.h> typedef struct _IO_TIMER { INT16 Type; INT16 TimerFlag; LONG32 Unknown; LIST_ENTRY TimerList; PVOID TimerRoutine; PVOID Context; PVOID DeviceObject; }IO_TIMER, *PIO_TIMER; // 得到IoInitializeTimer基址 PVOID GetIoInitializeTimerAddress() { PVOID VariableAddress = 0; UNICODE_STRING uioiTime = { 0 }; RtlInitUnicodeString(&uioiTime, L"IoInitializeTimer"); VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime); if (VariableAddress != 0) { return VariableAddress; } return 0; } VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint("卸載完成... \n"); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint(("hello lyshark.com \n")); // 得到基址 PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress(); DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer); // 搜索IoTimerQueueHead地址 /* nt!IoInitializeTimer+0x5d: fffff806`349963cd 488d5008 lea rdx,[rax+8] fffff806`349963d1 48897018 mov qword ptr [rax+18h],rsi fffff806`349963d5 4c8d05648de0ff lea r8,[nt!IopTimerLock (fffff806`3479f140)] fffff806`349963dc 48897820 mov qword ptr [rax+20h],rdi fffff806`349963e0 488d0d99f6cdff lea rcx,[nt!IopTimerQueueHead (fffff806`34675a80)] fffff806`349963e7 e8c43598ff call nt!ExInterlockedInsertTailList (fffff806`343199b0) fffff806`349963ec 33c0 xor eax,eax */ INT32 iOffset = 0; PLIST_ENTRY IoTimerQueueHead = NULL; PUCHAR StartSearchAddress = IoInitializeTimer; PUCHAR EndSearchAddress = IoInitializeTimer + 0xFF; UCHAR v1 = 0, v2 = 0, v3 = 0; for (PUCHAR i = StartSearchAddress; i < EndSearchAddress; i++) { if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2)) { v1 = *i; v2 = *(i + 1); v3 = *(i + 2); // fffff806`349963e0 48 8d 0d 99 f6 cd ff lea rcx,[nt!IopTimerQueueHead (fffff806`34675a80)] if (v1 == 0x48 && v2 == 0x8d && v3 == 0x0d) { memcpy(&iOffset, i + 3, 4); IoTimerQueueHead = (PLIST_ENTRY)(iOffset + (ULONG64)i + 7); DbgPrint("IoTimerQueueHead = %p \n", IoTimerQueueHead); break; } } } // 枚舉列表 KIRQL OldIrql; // 獲得特權級 OldIrql = KeRaiseIrqlToDpcLevel(); if (IoTimerQueueHead && MmIsAddressValid((PVOID)IoTimerQueueHead)) { PLIST_ENTRY NextEntry = IoTimerQueueHead->Flink; while (MmIsAddressValid(NextEntry) && NextEntry != (PLIST_ENTRY)IoTimerQueueHead) { PIO_TIMER Timer = CONTAINING_RECORD(NextEntry, IO_TIMER, TimerList); if (Timer && MmIsAddressValid(Timer)) { DbgPrint("IO對象地址: %p \n", Timer); } NextEntry = NextEntry->Flink; } } // 恢復特權級 KeLowerIrql(OldIrql); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
運行這段源代碼,并可得到以下輸出,由于沒有IO定時器所以輸出結果是空的:
到此,相信大家對“C語言驅動開發內核枚舉IoTimer定時器怎么實現”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。