在R0层中Hook函数

R0层内存属性修改

同样的R0层的内存也有读写保护,如果写了不可写的内存或者读了不可读的内存,此时会直接触发蓝屏。这里也是搜集到了一些修改 R0 层内存属性的方法,可以使用以下代码来修改内存属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
KIRQL WPOFFx64()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
}

void WPONx64(KIRQL irql)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}

WPOFFx64函数可以关闭内存写保护,使我们可以写任何一块区域的内存,完成之后使用 WPONx64 重新开启保护。

但是切忌不要使用真机去调试,否则会直接蓝屏。根据砍老板的说法,WP 的全称是写保护(Write Protect), 复位状态下,会无视写保护强写内存,但是这玩意和 cet 的shadow stack起冲突,cr4.cet开启的时候,复位cr0.wp就会异常,虚拟机可能不开这个东西,但是现在的主机一般都有。

内核inline hook的弊端

传统的inline hook就是劫持函数头,然后跳转到自己的函数上,执行一些操作之后,需要把钩子解除,调用等待返回之后,再重新把钩子挂上,因为要反复改这个 CR0 寄存器,不太好,容易 Crash。

代码如下:

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#define SYMBOL L"\\??\\xia0ji2333"
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)



NTSTATUS CreateDevice(PDEVICE_OBJECT driver) {
NTSTATUS status;
UNICODE_STRING MyDriver;
PDEVICE_OBJECT device = NULL;
RtlInitUnicodeString(&MyDriver, L"\\DEVICE\\xia0ji233");
status = IoCreateDevice(
driver,
sizeof(driver->DeviceExtension),
&MyDriver,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&device
);
if (status == STATUS_SUCCESS) {
UNICODE_STRING Sym;
RtlInitUnicodeString(&Sym, SYMBOL);
status = IoCreateSymbolicLink(&Sym, &MyDriver);
if (status == STATUS_SUCCESS) {
kprintf(("Line %d:xia0ji233: symbol linked success\n"), __LINE__);
}
else {
kprintf(("Line %d:xia0ji233: symbol linked failed status=%x\n"), __LINE__, status);
}
}
else {
kprintf(("Line %d:xia0ji233: create device fail status=%x\n"), __LINE__, status);
}
}

void DeleteDevice(PDRIVER_OBJECT pDriver) {
kprintf(("Line %d:xia0ji233: start delete device\n"), __LINE__);
if (pDriver->DeviceObject) {
UNICODE_STRING Sym;
RtlInitUnicodeString(&Sym, SYMBOL);//CreateFile
kprintf(("Line %d:xia0ji233: Delete Symbol\n"), __LINE__);
IoDeleteSymbolicLink(&Sym);
kprintf(("Line %d:xia0ji233: Delete Device\n"), __LINE__);
IoDeleteDevice(pDriver->DeviceObject);
}
kprintf(("Line %d:xia0ji233: end delete device\n"), __LINE__);
}



char newcode[] = {
0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//mov rax,xxx
0xFF,0xE0//jmp rax
};
char oldcode[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,
};

char *target;

KIRQL WPOFFx64()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
}

void WPONx64(KIRQL irql)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}

NTSTATUS Unhook() {
KIRQL irql = WPOFFx64();
for (int i = 0; i < sizeof(newcode); i++) {
target[i] = oldcode[i];
}
WPONx64(irql);
return STATUS_SUCCESS;
}

NTSTATUS Hook() {
KIRQL irql = WPOFFx64();
for (int i = 0; i < sizeof(newcode); i++) {
target[i] = newcode[i];
}
WPONx64(irql);
return STATUS_SUCCESS;
}
typedef NTSTATUS(*Copy)(PVOID, MM_COPY_ADDRESS, SIZE_T, ULONG, SIZE_T *);




NTSTATUS
myMmCopyMemory(
_In_ PVOID TargetAddress,
_In_ MM_COPY_ADDRESS SourceAddress,
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Flags,
_Out_ PSIZE_T NumberOfBytesTransferred
) {

kprintf(("xia0ji233: calls MmCopyMemory(%p,%p,%d,%p,%p)\n"), TargetAddress, SourceAddress, NumberOfBytes, Flags, NumberOfBytesTransferred);
Unhook();
Copy func = (Copy)target;
NTSTATUS s = func(TargetAddress, SourceAddress, NumberOfBytes, Flags, NumberOfBytesTransferred);
Hook();

return s;
}

void DriverUnload(PDRIVER_OBJECT pDriver) {
kprintf(("Line %d:xia0ji233: start unload\n"), __LINE__);
Unhook();
DeleteDevice(pDriver);
}


NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath
) {
DriverObject->DriverUnload = DriverUnload;
CreateDevice(DriverObject);
kprintf(("Line %d:xia0ji233: RegistryPath = %S\n"), __LINE__, RegistryPath->Buffer);
target = MmCopyMemory;
kprintf(("Line %d:xia0ji233: MmCopyMemory=%p\n"), __LINE__, target);

if (target) {
for (int i = 0; i < sizeof(oldcode); i++) {
oldcode[i] = target[i];
}
*(UINT64*)(newcode + 2) = myMmCopyMemory;
Hook();
}
else {
kprintf(("xia0ji233:hahaha"));
}
return 0;
}

结果很不幸的 crash 了。

所以我们可以选择 hotfix hook,就是用短跳跳到函数头前面,然后再上面 jmp 到我们 hook 函数的地址,要去调用原函数的话我们只需要再在函数头前面把破坏的指令重新执行一遍,然后再断跳回去即可,并且此时我们不使用 WPOFF 的方式去关闭内存写保护,使用 MDL 的方式去写,下面是代码:

上面这段代码中,我们勾住 MmCopyMemory 函数,这个函数的第一条指令是 mov qword ptr[rsp+20h], rbx,把它替换成短跳指令,转移到上方12个字节,用于我们执行 mov rax,xxxxxx;jmp rax 的指令转移到 HOOK 的函数中。同时上方还有两条指令,一个是 push rbp,一个是短跳跳到 KeStackAttachProcess+5 的位置。

钩住之后,我们内存段是这样的:

调用 MmCopyMemory 的时候,就会像我们箭头画的那样去走。

这里给一个模板函数,通过使用 MDL 的方式去写内存,也是无视读写属性的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MDLWriteMemory(PVOID pBaseAddress, PVOID pWriteData, SIZE_T writeDataSize)
{
PMDL pMdl = NULL;
PVOID pNewAddress = NULL;
pMdl = MmCreateMdl(NULL, pBaseAddress, writeDataSize);
if (NULL == pMdl)
{
return FALSE;
}
MmBuildMdlForNonPagedPool(pMdl);
pNewAddress = MmMapLockedPages(pMdl, KernelMode);
if (NULL == pNewAddress)
{
IoFreeMdl(pMdl);
}
RtlCopyMemory(pNewAddress, pWriteData, writeDataSize);
MmUnmapLockedPages(pNewAddress, pMdl);
IoFreeMdl(pMdl);
return TRUE;
}

这几天虽然 all in在上面没有复习考研的内容,但是我觉得还是可以的,学到挺多东西。