今天继续分享内核枚举系列知识,这次我们来学习如何通过代码的方式枚举内核IoTimer
定时器,内核定时器其实就是在内核中实现的时钟,该定时器的枚举非常简单,因为在IoInitializeTimer
初始化部分就可以找到IopTimerQueueHead
地址,该变量内存储的就是定时器的链表头部。枚举IO定时器的案例并不多见,即便有也是无法使用过时的,此教程学到肯定就是赚到了。
这里解释一下为什么要找IoInitializeTimer
这个函数他是一个初始化函数,既然是初始化里面一定会涉及到链表的存储问题,找到他就能找到定时器链表基址,该函数的定义如下。
如上方的基础知识有了也就够了,接着就是实际开发部分,首先我们需要编写一个GetIoInitializeTimerAddress()
函数,让该函数可以定位到IoInitializeTimer
所在内核中的基地址上面,具体实现调用代码如下所示。
接着我们在反汇编代码中寻找IoTimerQueueHead
,此处在LyShark系统内这个偏移位置是nt!IoInitializeTimer+0x5d
具体输出位置如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/
/
By: LyShark 内核开发系列教程
/
/
https:
/
/
www.cnblogs.com
/
LyShark
/
articles
/
16784393.html
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;
}