襄阳信息网站建设,二级域名做很多网站,免费php企业网站,广州安全教育平台账号是多少内核I/O定时器#xff08;Kernel I/O Timer#xff09;是Windows内核中的一个对象#xff0c;它允许内核或驱动程序设置一个定时器#xff0c;以便在指定的时间间隔内调用一个回调函数。通常#xff0c;内核I/O定时器用于周期性地执行某个任务#xff0c;例如检查驱动程序…内核I/O定时器Kernel I/O Timer是Windows内核中的一个对象它允许内核或驱动程序设置一个定时器以便在指定的时间间隔内调用一个回调函数。通常内核I/O定时器用于周期性地执行某个任务例如检查驱动程序的状态、收集性能数据等。
今天继续分享内核枚举系列知识这次我们来学习如何通过代码的方式枚举内核IoTimer定时器内核定时器其实就是在内核中实现的时钟该定时器的枚举非常简单因为在IoInitializeTimer初始化部分就可以找到IopTimerQueueHead地址该变量内存储的就是定时器的链表头部。 内核I/O定时器通常由内核或驱动程序创建使用KeInitializeTimerEx函数进行初始化。然后使用KeSetTimerEx函数启动定时器以指定间隔和回调函数。每次定时器超时时回调函数都会被调用然后定时器重新启动以等待下一个超时。
内核I/O定时器是内核中常见的机制之一它允许内核和驱动程序实现各种功能如性能监视、定时执行任务等。但是使用内核I/O定时器必须小心谨慎因为它们可能会影响系统的性能和稳定性特别是当存在大量定时器时。
枚举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属性。
kd dt _DEVICE_OBJECT
ntdll!_DEVICE_OBJECT0x000 Type : Int2B0x002 Size : Uint2B0x004 ReferenceCount : Int4B0x008 DriverObject : Ptr64 _DRIVER_OBJECT0x010 NextDevice : Ptr64 _DEVICE_OBJECT0x018 AttachedDevice : Ptr64 _DEVICE_OBJECT0x020 CurrentIrp : Ptr64 _IRP0x028 Timer : Ptr64 _IO_TIMER0x030 Flags : Uint4B0x034 Characteristics : Uint4B0x038 Vpb : Ptr64 _VPB0x040 DeviceExtension : Ptr64 Void0x048 DeviceType : Uint4B0x04c StackSize : Char0x050 Queue : anonymous-tag0x098 AlignmentRequirement : Uint4B0x0a0 DeviceQueue : _KDEVICE_QUEUE0x0c8 Dpc : _KDPC0x108 ActiveThreadCount : Uint4B0x110 SecurityDescriptor : Ptr64 Void0x118 DeviceLock : _KEVENT0x130 SectorSize : Uint2B0x132 Spare1 : Uint2B0x138 DeviceObjectExtension : Ptr64 _DEVOBJ_EXTENSION0x140 Reserved : Ptr64 Void这里的这个0x028 Timer定时器是一个结构体_IO_TIMER其就是IO定时器的所需结构体。
kd dt _IO_TIMER
ntdll!_IO_TIMER0x000 Type : Int2B0x002 TimerFlag : Int2B0x008 TimerList : _LIST_ENTRY0x018 TimerRoutine : Ptr64 void 0x020 Context : Ptr64 Void0x028 DeviceObject : Ptr64 _DEVICE_OBJECT如上方的基础知识有了也就够了接着就是实际开发部分首先我们需要编写一个GetIoInitializeTimerAddress()函数让该函数可以定位到IoInitializeTimer所在内核中的基地址上面具体实现调用代码如下所示。
#include ntifs.h// 得到IoInitializeTimer基址
PVOID GetIoInitializeTimerAddress()
{PVOID VariableAddress 0;UNICODE_STRING uioiTime { 0 };RtlInitUnicodeString(uioiTime, LIoInitializeTimer);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 \n));// 得到基址PUCHAR IoInitializeTimer GetIoInitializeTimerAddress();DbgPrint(IoInitializeTimer Address %p \n, IoInitializeTimer);Driver-DriverUnload UnDriver;return STATUS_SUCCESS;
}运行这个驱动程序然后对比下是否一致: 接着我们在反汇编代码中寻找IoTimerQueueHead此处在LyShark系统内这个偏移位置是nt!IoInitializeTimer0x5d 具体输出位置如下。
kd uf IoInitializeTimernt!IoInitializeTimer0x5d:
fffff80574b85bed 488d5008 lea rdx,[rax8]
fffff80574b85bf1 48897018 mov qword ptr [rax18h],rsi
fffff80574b85bf5 4c8d054475e0ff lea r8,[nt!IopTimerLock (fffff8057498d140)]
fffff80574b85bfc 48897820 mov qword ptr [rax20h],rdi
fffff80574b85c00 488d0dd9ddcdff lea rcx,[nt!IopTimerQueueHead (fffff805748639e0)]
fffff80574b85c07 e8141e98ff call nt!ExInterlockedInsertTailList (fffff80574507a20)
fffff80574b85c0c 33c0 xor eax,eax在WinDBG中标注出颜色lea rcx,[nt!IopTimerQueueHead (fffff805748639e0)]更容易看到。 接着就是通过代码实现对此处的定位定位我们就采用特征码搜索的方式如下代码是特征搜索部分。
StartSearchAddress 代表开始位置EndSearchAddress 代表结束位置粗略计算0xff就可以定位到了。
#include ntifs.h// 得到IoInitializeTimer基址
PVOID GetIoInitializeTimerAddress()
{PVOID VariableAddress 0;UNICODE_STRING uioiTime { 0 };RtlInitUnicodeString(uioiTime, LIoInitializeTimer);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 \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) 得到结构体循环输出即可。
#include ntddk.h
#include ntstrsafe.htypedef 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, LIoInitializeTimer);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 \n));// 得到基址PUCHAR IoInitializeTimer GetIoInitializeTimerAddress();DbgPrint(IoInitializeTimer Address %p \n, IoInitializeTimer);// 搜索IoTimerQueueHead地址/*nt!IoInitializeTimer0x5d:fffff806349963cd 488d5008 lea rdx,[rax8]fffff806349963d1 48897018 mov qword ptr [rax18h],rsifffff806349963d5 4c8d05648de0ff lea r8,[nt!IopTimerLock (fffff8063479f140)]fffff806349963dc 48897820 mov qword ptr [rax20h],rdifffff806349963e0 488d0d99f6cdff lea rcx,[nt!IopTimerQueueHead (fffff80634675a80)]fffff806349963e7 e8c43598ff call nt!ExInterlockedInsertTailList (fffff806343199b0)fffff806349963ec 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);// fffff806349963e0 48 8d 0d 99 f6 cd ff lea rcx,[nt!IopTimerQueueHead (fffff80634675a80)]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定时器所以输出结果是空的 至此IO定时器的枚举就介绍完了在教程中你已经学会了使用特征码定位这门技术相信你完全可以输出内核中想要得到的任何结构体。