简单的学习并编写驱动程序

首先要改属性

注意:配置是所有配置,平台不知道为什么我这里只能x86

C/C++->常规->将警告视为错误->否

Driver Settings->Target OS Version->win7

SDL检查也关了

模板

1
2
3
4
5
6
7
8
9
#include<ntifs.h>
VOID DriverUnload(PDRIVER_OBJECT pDriver) {

}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPeg) {
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

helloworld

基本框架其实最开始就遇到了,在配置环境的时候

简单解释,DriverUnload是自己完善的卸载函数,DiverEntry是驱动的主函数,也就是加载的时候就会被引用

参数一-pDiver是该驱动的”对象”,其实这个也好理解,pDriver->DriverUnload是给驱动挂载卸载函数,最终返回正确,第二个参数是安装路径,驱动安装的路径

printf平替是DbgPrintEx,前两个参数别管,后面跟着就好

1
2
3
4
5
6
7
8
9
10
#include<ntifs.h>
VOID DriverUnload(PDRIVER_OBJECT pDriver) {
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPeg) {
DbgBreakPoint();
DbgPrintEx(77,0,"---------hello world------------------\r\n");
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

链表

练习

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
#include<ntifs.h>

VOID DriverUnload(PDRIVER_OBJECT pDriver) {

}

typedef struct xxx {
struct xxx* next;
struct xxx* last;
INT x;
}XXX, * PXXX;

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPeg) {
DbgBreakPoint();

XXX head;
XXX x1;
XXX x2;
x1.x = 0x10;
x2.x = 0x20;
InitializeListHead(&head);
InitializeListHead(&x1);
InitializeListHead(&x2);

InsertTailList(&head, &x1);
InsertTailList(&head, &x2);

PLIST_ENTRY Tmp = &head;
do {
Tmp = Tmp->Blink;
XXX temp = *(PXXX)Tmp;
if (temp.x == 0x20) {
DbgPrintEx(77, 0, "find 0x20");
}

} while (Tmp != &head);

pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

断链

看懂了,但是感觉写起来很复杂,就是模拟了驱动所在的链表,可以通过链表找到想要断链隐藏的驱动(如果是自身的话要等后续内核对自己的调用结束之后,再断链,可以用休眠的api)

蓝屏分析

练习

首先windbg支持tab自动补全,当蓝屏触发时,可以用!analyze -v指令来查看蓝屏信息,那么我们以该程序测试,这个给0地址赋值包崩溃的
1
2
3
4
5
6
7
8
9
10
11
#include<ntifs.h>
VOID DriverUnload(PDRIVER_OBJECT pDriver) {

}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPeg) {
INT* a = 0;
*a = 100;
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

蓝屏通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Fail to read system\currentcontrolset\services\Lmhosts\Parameters\EnableUserMode, error=2

*** Fatal System Error: 0x0000007e
(0xC0000005,0xA4B9D00E,0x807F98FC,0x807F9360)

Break instruction exception - code 80000003 (first chance)

A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.

A fatal system error has occurred.

For analysis of this file, run !analyze -v
nt!RtlpBreakWithStatusInstruction:
83eb61bc cc int 3

可以看到这里他提示我!analyze了, 那我们输入进入

…跳转io通信里的蓝屏

存放地址

默认蓝屏信息存放地址:`C:\Windows\Minidump`

IO通信

驱动对象->设备对象<----R0某个函数里 打包成irp------r3能根据名字查找到设备,并且返回一个操作句柄

蓝屏分析中... 补药蓝屏口牙┭┮﹏┭┮,但是有意外发生了,他蓝屏了,接上文!an...分析一下
1
2
3
4
5
6
7
8
9
10
11
FAULTING_SOURCE_CODE:  
65: RtlInitUnicodeString(&pdsym, DRIVER_SYM);
66:
67: //?????? ???¡§?¨¨¡À????¨®
68: PDRIVER_OBJECT pDevice = NULL;
> 69: NTSTATUS st = IoCreateDevice(pDriver, 0, &pdname, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, pDevice);
70:
71: if (NT_SUCCESS(st))
72: {
73: return STATUS_UNSUCCESSFUL;
74: }

可以看到问题所在,IoCreateDevice

1
2
nt!memcpy+0x33:
83e76de3 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

再次观察发现是memcpy中赋值出现问题,后面求助ai知道,是pDriver传入的应该是&pDriver好吧。。。,那确实memcpy用到两个指针可能出现问题,最后找到问题了,前面的PUNICODE_STRING 是指针类型,要用UNICODE_STRING,后面就是简单的问题了,学到了可以dd st来查看报错

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
#include <ntddk.h>
#include <ntstatus.h>

#define DEVICE_NAME L"\\device\\myiotest"
// Ring3用CreateFile打开设备时,用"\\\\.\\MyTestDriver"
#define SYMBOLICLINK_NAME L"\\??\\myiotest"

// 0-2047是保留的 2048~4095
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)

// 卸载函数
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UNICODE_STRING SymbolicLinkName = { 0 };

DbgPrint("驱动程序停止运行了 . \r\n");

// 删除符号链接 删除设备
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);
IoDeleteSymbolicLink(&SymbolicLinkName);
IoDeleteDevice(pDriver->DeviceObject);
}

// IRP_MJ_CREATE处理函数
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("DispatchCreate ... \n");

// 返回状态如果不设置 Ring3返回的是失败
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

// IRP_MJ_CLOSE处理函数
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("DispatchClose ... \n");
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

// IRP_MJ_DEVICE_CONTROL处理函数 用来处理与Ring3交互
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
PIO_STACK_LOCATION pIrpStack;
ULONG uIoControlCode;
PVOID pIoBuffer;
ULONG uInLength;
ULONG uOutLength;
ULONG uRead;
ULONG uWrite;

// 设置临时变量的值
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
// 获取控制码
uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
// 获取缓冲区地址(输入和输出的缓冲区都是一个)
pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
// Ring3 发送数据的长度
uInLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
// Ring0 发送数据的长度
uOutLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

switch (uIoControlCode)
{
case OPER1:
{
DbgPrint("IrpDeviceControlProc -> OPER1 ... \n");

pIrp->IoStatus.Information = 0;
status = STATUS_SUCCESS;
break;
}
case OPER2:
{
DbgPrint("IrpDeviceControlProc -> OPER2 接收字节数:%d \n", uInLength);
DbgPrint("IrpDeviceControlProc -> OPER2 输出字节数:%d \n", uOutLength);
// Read From Buffer
memcpy(&uRead, pIoBuffer, 4);
DbgPrint("IrpDeviceControlProc -> OPER2 ... %x \n", uRead);
// Write To Buffer
memcpy(&pIoBuffer, &uRead, 4);
// Set Status
pIrp->IoStatus.Information = 2;
status = STATUS_SUCCESS;
break;
}
}

// 设置返回状态
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}

// 入口函数 相当于Main函数
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
NTSTATUS status = 0;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL;
UNICODE_STRING Devicename;
UNICODE_STRING SymbolicLinkName;

DbgPrint("驱动程序开始运行了 . \r\n");

// 创建设备名称
RtlInitUnicodeString(&Devicename, DEVICE_NAME);

// 创建设备
status = IoCreateDevice(pDriver, 0, &Devicename,
FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! \r\n");
return status;
}
//设置交互数据的方式
pDeviceObj->Flags |= DO_BUFFERED_IO;

// 创建符号链接名称
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);

// 创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &Devicename);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建符号链接失败! \r\n");
IoDeleteDevice(pDeviceObj);
return status;
}

// 设置分发函数和卸载函数
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpCreateProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpCloseProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceControlProc;
pDriver->DriverUnload = DriverUnload;

return STATUS_SUCCESS;
}

目前卸载还存在问题

简单通信代码模板

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
#include <iostream>
#include<Windows.h>
#define _DRIVER_SYM L"\\??\\myiotest"
#define IOCTL_MYDEVICE_OPERATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
int main()
{
HANDLE hDevice = CreateFile(_DRIVER_SYM, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (hDevice == INVALID_HANDLE_VALUE)
{
printf("打开设备失败 error:%d \r\n", GetLastError());
}
else
{
system("pause");
ULONG x = 0;
ULONG retlen = 0;
BOOL isS = DeviceIoControl(hDevice, IOCTL_MYDEVICE_OPERATION,
&x, sizeof(x),
&x, sizeof(x),
&retlen, NULL);
printf("result %d ,%d\r\n", isS, GetLastError());

system("pause");
}


system("pause");
return 0;
}

Io符号链接

符号链接:

符号链接中用的前缀也就是\??也可以用\\.或者\dosDevice\来代替

api合集

字符串篇

`RtlInitAnsiString` ANSI_STRING初始化

RtlInitUnicodeString(&target,Name_value)UNICODE_STRING初始化

RtlAnsiStringToUnicodeString 宽窄字符转换

RtlFreeUnicodeString UNICODE_STRING释放

链表篇

PLIST_ENTRY定义
1
2
3
4
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;

InitializeListHead(a1);a1初始化:
InsertHeadList(a1,a2);a2插入a1头部中:

InsertTailList(a1,a2);a2插入a1尾部中:

RemoveEntryList(a2);a2删除

RemoveHeadList(a2);//一般不用

Io篇

`IoCreateDevice();`函数参数比较长,对应着定义慢慢填吧

IoCreateSymbolicLink(a1,a2);符号链接,a2生成快捷方式a1

IoDeleteDevice(pDevice);删除对象

IoDeleteSymbolicLink(a1);删除符号链接

IoCompleteRequest(a1,a2);a2是0,a1是Irp,还没有学到,根据名字猜测是完成请求Irp可能是请求

windbg的一些指令

`!analyze -v` 查看蓝屏相关信息

k查看堆栈调用

kv查看堆栈调用详情

?? Driver->Object可以查看设备对象详情

!object Object_Addr可以查看设备地址

dt 链表名 下一个/上一个链表地址即可看结构

dt这个指令可用于看很多种结构,并且dt xxx -r1可以深入看结构下的几层结构

bp手动下断点(好用!!)

u查看目标地址字节码

Irp相关参数

Irp

`dt pIrp`首先查看Irp

-r1展开AssociatedIrp ,通信函数传来的第二个参数在该部分SystemBuffer中

pIrp->AssociatedIrp.SystemBuffer

紧接着-r1展开Tail-r2展开Overlay,获得存储的结构体CurrentStackLocation

CurrentStackLocation

如果用Irp来调用到这里`pIrp->Tail.Overlay.CurrentStackLocation->`基础是这样很冗杂

可以用函数

dt 0x876a8c40 _IO_STACK_LOCATION查看相关参数,MajorFunction是回调函数序号

pIrp->Tail.Overlay.CurrentStackLocation->MajorFunction

-r1展开Parameters找到DeviceIoControl-r2展开

1
2
3
4
5
+0x000 DeviceIoControl  : <unnamed-tag>
+0x000 OutputBufferLength : 4
+0x004 InputBufferLength : 4
+0x008 IoControlCode : 0
+0x00c Type3InputBuffer : (null)

获得相关参数,改变通信代码来查看对应参数

1
2
3
4
5
6
7
ULONG x1 = 10;
ULONG64 x2 = 20;
ULONG retlen = 0;
BOOL isS = DeviceIoControl(hDevice, 1,
&x1, sizeof(x1),
&x2, sizeof(x2),
&retlen, NULL);

改变传参再次查看

1
2
3
4
5
[db]: MajorFumction:14
[db]: InputBufferLength:4
[db]: OutputBufferLength:8
[db]: IoControlCode:1
[db]: SystemBuffer: 87514ec0 10

如此遍明了DeviceIoControl传参如下

1. 设备句柄
2. IoControlCode
3. 传入参数 SystemBuffer
4. InputBufferLength
5. 返回参数
6. OutputBufferLength

改变返回参数

我的大致感觉就是,传入参数就像const,是不会改变了并且复制了一份在SystemBuffer中,后续如果对SystemBuffer操作,再回调函数结束后SystemBuffer的值会再次赋值给返回参数,从而做到返回

注意点:

直接更改SystemBuffer是不会改变的需要有以下操作

1
2
pIrp->IoStatus.Status = STATUS_SUCCESS;	//成功
pIrp->IoStatus.Information = io->Parameters.DeviceIoControl.OutputBufferLength; //规定大小

所以将回调函数改成如下样式

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
#define IOCTL_MYDEVICE_OPERATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
NTSTATUS myDispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
PIO_STACK_LOCATION io = IoGetCurrentIrpStackLocation(pIrp);
ULONG ICC = io->Parameters.DeviceIoControl.IoControlCode;
DbgPrintEx(77, 0, "[db]: In %s\r\n", __FUNCTION__);
//DbgBreakPoint();

DbgPrintEx(77, 0, "[db]: MajorFunction: %x\r\n", io->MajorFunction);
DbgPrintEx(77, 0, "[db]: InputBufferLength: %d\r\n", io->Parameters.DeviceIoControl.InputBufferLength);
DbgPrintEx(77, 0, "[db]: OutputBufferLength: %d\r\n", io->Parameters.DeviceIoControl.OutputBufferLength);
DbgPrintEx(77, 0, "[db]: IoControlCode: %d\r\n", io->Parameters.DeviceIoControl.IoControlCode);
DbgPrintEx(77, 0, "[db]: SystemBuffer: %p %x \r\n", pIrp->AssociatedIrp.SystemBuffer,*((ULONG*)pIrp->AssociatedIrp.SystemBuffer));

switch (ICC) {
case IOCTL_MYDEVICE_OPERATION:
*(ULONG*)pIrp->AssociatedIrp.SystemBuffer = 0x520;
DbgPrintEx(77, 0, "[db]: SystemBuffer: %p %x \r\n", pIrp->AssociatedIrp.SystemBuffer, *((ULONG*)pIrp->AssociatedIrp.SystemBuffer));
}

//正确返回
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = io->Parameters.DeviceIoControl.OutputBufferLength;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

这里的IOCTL_MYDEVICE_OPERATION作用其实就是出了创建和销毁,剩下的回调函数基本都是这个,那么区分回调函数代码作用的自然就是传入的IoControlCode,控制码,这里和传入控制码进行switch筛选

通信代码

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
#include <iostream>
#include<Windows.h>
#define _DRIVER_SYM L"\\??\\myiotest"
#define IOCTL_MYDEVICE_OPERATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
int main()
{
HANDLE hDevice = CreateFile(_DRIVER_SYM, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (hDevice == INVALID_HANDLE_VALUE)
{
printf("打开设备失败 error:%d \r\n", GetLastError());
}
else
{
system("pause");
ULONG xin = 10;
ULONG64 xout = 0;
ULONG retlen = 0;
BOOL isS = DeviceIoControl(hDevice, IOCTL_MYDEVICE_OPERATION,
&xin, sizeof(xin),
&xout, sizeof(xout),
&retlen, NULL);
printf("result %d ,%d, %x\r\n", isS, GetLastError(), xout);
}
system("pause");
return 0;
}

好的我们再捋一遍思路:

xin->SystemBuffer->SystemBuffer=0x520->xout=SystemBuffer->xout=0x520

运行一下试试

最终获取正确结果

ps:retLen的存放的是实际返回参数的大小,可以验证一下

Io通信小记

设备对象下的Flag标志位对应的参数与DeviceIoControl的第二个参数中METHOD_BUFFERED这类的标志是相似的,其规定再传参时,标志采用复制,映射还是直接索引实际地址

所以在实践时会十分冗杂麻烦,火哥建议,先用一层结构体做建议Irp,类似通信传参的封装,在这个中引用真正参数的结构体,后续实践来更好理解(确实会如此,在我自己写计算器的时候,num参数因为sizeof的问题只传了一个参数,但是需要很繁杂的去dt dd来检索问题)

还有就是这样操作会减少蓝屏,毕竟你再怎么操作,都是对R0层结构体中指向的部分,操作。

有世外桃源的感觉了

最终练习代码

计算器

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
#include <iostream>
#include<Windows.h>
#define _DRIVER_SYM L"\\??\\myiotest"
#define CASE0 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CASE1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CASE2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CASE3 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CASE4 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
HANDLE hDevice;
void init(PULONG num,PUCHAR cc) {
ULONG i = 0;
CHAR c[100] = { 0 };
printf("[计算器]: ");
scanf("%s", c);
while (c[i] >= '0' && c[i] <= '9')
num[0] = num[0] * 10 + (c[i++] - '0');

*cc = c[i++];
while (c[i] >= '0' && c[i] <= '9')
num[1] = num[1] * 10 + (c[i++] - '0');
printf("%d %c %d", num[0], *cc, num[1]);
}
ULONG IoC(PULONG num, PUCHAR cc) {
ULONG result = 0;
ULONG retlen = 0;
switch (*cc)
{
case '+':
DeviceIoControl(hDevice, CASE1, num, sizeof(num) * 2, &result, sizeof(result), &retlen, NULL);
break;
case '-':
DeviceIoControl(hDevice, CASE2, num, sizeof(num) * 2, &result, sizeof(result), &retlen, NULL);
break;
case '*':
DeviceIoControl(hDevice, CASE3, num, sizeof(num) * 2, &result, sizeof(result), &retlen, NULL);
break;
case '/':
DeviceIoControl(hDevice, CASE4, num, sizeof(num) * 2, &result, sizeof(result), &retlen, NULL);
break;
default:
break;
}
return result;
}
int main()
{
hDevice = CreateFile(_DRIVER_SYM, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("打开设备失败 error:%d \r\n", GetLastError());
}
else
{
ULONG is = 1;
while (is) {
UCHAR cc;
ULONG num[2] = { 0 };
init(num, &cc);
printf(" = %d\r\n是否继续,继续请按1,反之按0\r\n", IoC(num, &cc));
scanf("%d", &is);
}
}
system("pause");
return 0;
}
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
#include <ntddk.h>
#include <ntstatus.h>

#define DEVICE_NAME L"\\device\\myiotest"
#define SYMBOLICLINK_NAME L"\\??\\myiotest"

#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)

VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UNICODE_STRING SymbolicLinkName = { 0 };

DbgPrint("[db]: Close...\r\n");

RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);
IoDeleteSymbolicLink(&SymbolicLinkName);
IoDeleteDevice(pDriver->DeviceObject);
}

NTSTATUS Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrintEx(77, 0, "[db]: Being work...\r\n");
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
#define CASE0 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CASE1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CASE2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CASE3 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CASE4 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
NTSTATUS myDispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
PIO_STACK_LOCATION io = IoGetCurrentIrpStackLocation(pIrp);
ULONG ICC = io->Parameters.DeviceIoControl.IoControlCode;
DbgPrintEx(77, 0, "[db]: In %s\r\n", __FUNCTION__);
//DbgBreakPoint();

DbgPrintEx(77, 0, "[db]: MajorFunction: %x\r\n", io->MajorFunction);
DbgPrintEx(77, 0, "[db]: InputBufferLength: %d\r\n", io->Parameters.DeviceIoControl.InputBufferLength);
DbgPrintEx(77, 0, "[db]: OutputBufferLength: %d\r\n", io->Parameters.DeviceIoControl.OutputBufferLength);
DbgPrintEx(77, 0, "[db]: IoControlCode: %d\r\n", io->Parameters.DeviceIoControl.IoControlCode);
DbgPrintEx(77, 0, "[db]: SystemBuffer: %p %x \r\n", pIrp->AssociatedIrp.SystemBuffer,*((ULONG*)pIrp->AssociatedIrp.SystemBuffer));

ULONG* num;
ULONG result;
DbgPrintEx(77, 0, "[db]: SystemBuffer: %p %x %x\r\n", pIrp->AssociatedIrp.SystemBuffer, *(ULONG*)pIrp->AssociatedIrp.SystemBuffer, *((ULONG*)pIrp->AssociatedIrp.SystemBuffer + 1));
//DbgBreakPoint();
switch (ICC) {
case CASE0:
*(ULONG*)pIrp->AssociatedIrp.SystemBuffer = 0x520;
break;
case CASE1:
num = (ULONG*)pIrp->AssociatedIrp.SystemBuffer;
result = num[0] + num[1];
*(ULONG*)pIrp->AssociatedIrp.SystemBuffer = result;
break;
case CASE2:
num = (ULONG*)pIrp->AssociatedIrp.SystemBuffer;
result = num[0] - num[1];
*(ULONG*)pIrp->AssociatedIrp.SystemBuffer = result;
break;
case CASE3:
num = (ULONG*)pIrp->AssociatedIrp.SystemBuffer;
result = num[0] * num[1];
*(ULONG*)pIrp->AssociatedIrp.SystemBuffer = result;
break;
case CASE4:
num = (ULONG*)pIrp->AssociatedIrp.SystemBuffer;
result = num[0] / num[1];
*(ULONG*)pIrp->AssociatedIrp.SystemBuffer = result;
break;
}

//正确返回
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = io->Parameters.DeviceIoControl.OutputBufferLength;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
NTSTATUS status = 0;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL;
UNICODE_STRING Devicename;
UNICODE_STRING SymbolicLinkName;

RtlInitUnicodeString(&Devicename, DEVICE_NAME);

status = IoCreateDevice(pDriver, 0, &Devicename,
FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! \r\n");
return status;
}
pDeviceObj->Flags |= DO_BUFFERED_IO;

RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);

status = IoCreateSymbolicLink(&SymbolicLinkName, &Devicename);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建符号链接失败! \r\n");
IoDeleteDevice(pDeviceObj);
return status;
}

pDriver->MajorFunction[IRP_MJ_CREATE] = Dispatch;
pDriver->MajorFunction[IRP_MJ_CLOSE] = Dispatch;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = myDispatch;
pDriver->DriverUnload = DriverUnload;
DbgPrint("[db]: Finish...\r\n");

return STATUS_SUCCESS;
}

结构体传参练习

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
#include <iostream>
#include<Windows.h>
#define _DRIVER_SYM L"\\??\\myiotest"
#define CASE0 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
HANDLE hDevice;

typedef struct __MyInputBuffer {
CHAR initstring[100]; //输入
CHAR outstring[100]; //输出
ULONG len; //长度
}MIB, * PMIB;

typedef struct __MyEasyIo {
ULONG type; //处理类型
ULONG64 in; //处理参数
ULONG64 is; //是否继续
}MEI, * PMEI;

void init(PMEI m, PMIB in) {
ULONG len = 0;
printf("请选择您的功能(倒置 1,单双 2): ");
scanf("%d", &m->type);
getchar();
fgets(in->initstring, 100, stdin);
while (in->initstring[len] != '\n')
len++;
in->initstring[len] = 0;
in->len = len;
m->in = (ULONG64)in;
}
void IoC(PMEI m) {
ULONG retlen = 0;
DeviceIoControl(hDevice, CASE0, m, sizeof(MEI), m, sizeof(MEI), &retlen, NULL);
}
int main()
{
hDevice = CreateFile(_DRIVER_SYM, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("打开设备失败 error:%d \r\n", GetLastError());
}
else
{
MEI m;
MIB in;
m.is = 1;
while (m.is) {
init(&m, &in);
IoC(&m);
printf("result=%s\r\n是否继续?\r\n", in.outstring);
scanf("%d", &m.is);
}
}
system("pause");
return 0;
}
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
#include <ntddk.h>
#include <ntstatus.h>

#define DEVICE_NAME L"\\device\\myiotest"
#define SYMBOLICLINK_NAME L"\\??\\myiotest"

#define CASE0 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

typedef struct __MyInputBuffer {
CHAR initstring[100]; //输入
CHAR outstring[100]; //输出
ULONG len; //长度
}MIB, * PMIB;

typedef struct __MyEasyIo {
ULONG type; //处理类型
ULONG64 in; //处理参数
ULONG64 is; //是否继续
}MEI, * PMEI;

VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UNICODE_STRING SymbolicLinkName = { 0 };

DbgPrint("[db]: Close...\r\n");

RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);
IoDeleteSymbolicLink(&SymbolicLinkName);
IoDeleteDevice(pDriver->DeviceObject);
}

NTSTATUS Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrintEx(77, 0, "[db]: Being work...\r\n");
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS myDispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
PIO_STACK_LOCATION io = IoGetCurrentIrpStackLocation(pIrp);
ULONG ICC = io->Parameters.DeviceIoControl.IoControlCode;
DbgPrintEx(77, 0, "[db]: In %s\r\n", __FUNCTION__);
//DbgBreakPoint();

DbgPrintEx(77, 0, "[db]: MajorFunction: %x\r\n", io->MajorFunction);
DbgPrintEx(77, 0, "[db]: InputBufferLength: %d\r\n", io->Parameters.DeviceIoControl.InputBufferLength);
DbgPrintEx(77, 0, "[db]: OutputBufferLength: %d\r\n", io->Parameters.DeviceIoControl.OutputBufferLength);
DbgPrintEx(77, 0, "[db]: IoControlCode: %d\r\n", io->Parameters.DeviceIoControl.IoControlCode);

ULONG* num;
ULONG result;
DbgPrintEx(77, 0, "[db]: SystemBuffer: %p\r\n", pIrp->AssociatedIrp.SystemBuffer);
//DbgBreakPoint();
PMEI myezio = (PMEI)pIrp->AssociatedIrp.SystemBuffer;
switch (myezio->type)
{
case 1:
{
PMIB input = (PMIB)myezio->in;
for (int i = 0; i < input->len; i++)
{
input->outstring[input->len - 1 - i] = input->initstring[i];
}
input->outstring[input->len] = 0;
}
break;
case 2:
{
PMIB input = (PMIB)myezio->in;
ULONG i, j = 0;
for (i = 0; i < input->len; i+=2)
{
input->outstring[j++] = input->initstring[i];
}
for (i = 1; i < input->len; i += 2)
{
input->outstring[j++] = input->initstring[i];
}
input->outstring[j] = 0;
}
break;
default:
break;
}
//正确返回
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = io->Parameters.DeviceIoControl.OutputBufferLength;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
NTSTATUS status = 0;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL;
UNICODE_STRING Devicename;
UNICODE_STRING SymbolicLinkName;

RtlInitUnicodeString(&Devicename, DEVICE_NAME);

status = IoCreateDevice(pDriver, 0, &Devicename,
FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! \r\n");
return status;
}
pDeviceObj->Flags |= DO_BUFFERED_IO;

RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);

status = IoCreateSymbolicLink(&SymbolicLinkName, &Devicename);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建符号链接失败! \r\n");
IoDeleteDevice(pDeviceObj);
return status;
}

pDriver->MajorFunction[IRP_MJ_CREATE] = Dispatch;
pDriver->MajorFunction[IRP_MJ_CLOSE] = Dispatch;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = myDispatch;
pDriver->DriverUnload = DriverUnload;
DbgPrint("[db]: Finish...\r\n");

return STATUS_SUCCESS;
}

驱动加载

首先我的代码爆蓝屏了,总是蓝屏重启十分麻烦,所以第一个要学的就是异常捕捉

异常

1
2
3
4
5
6
7
8
__try
{
//...
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
//...
}

像这样捕捉异常来避免蓝屏

分析

我的蓝屏发生在最后EntryPoint的地方,于是可以
1
2
DbgPrintEx(77, 0, "EntryPointFunc: %x\n", EntryPointFunc);
DbgBreakPoint();

打印进入点,然后下断点,方便后续在windbg中操作

1
2
3
4
5
EntryPointFunc: 85854000
Break instruction exception - code 80000003 (first chance)
DLTools!LoadDriver+0xb6:
a175f1fa cc int 3
kd>

停住之后我们可以u 85854000来观察字节码

1
2
3
4
5
6
7
kd> u 85854000 
85854000 8bff mov edi,edi
85854002 55 push ebp
85854003 8bec mov ebp,esp
85854005 e806000000 call 85854010
8585400a 5d pop ebp
8585400b e9f0cfffff jmp 85851000

可以看到入口点是没有问题的,那么手动下断点运行过去

1
2
kd> bp 85854000
kd> g

最后发现是在85854010函数崩掉的,用ida查看源码

1
2
3
4
5
6
7
INIT:00404000 8B FF                         mov     edi, edi
INIT:00404002 55 push ebp
INIT:00404003 8B EC mov ebp, esp
INIT:00404005 E8 06 00 00 00 call ___security_init_cookie
INIT:00404005
INIT:0040400A 5D pop ebp
INIT:0040400B E9 F0 CF FF FF jmp _DriverEntry@8 ; DriverEntry(x,x)

可以看到应该是在___security_init_cookie函数中崩了,进去发现

1
mov     dword ptr ds:[00403004h],eax

最后的赋值重定位表没有正确定位,找到问题原因了,是重定位表写的有问题

排查发现计算获得重定位表基址的时候+的是PIMAGE…而不是IMAGE

PIMAGE_RELOC RelocationBlock = (PIMAGE_RELOC)((PUINT8)pImageReloc + sizeof(PIMAGE_BASE_RELOCATION));