avatar

驱动程序与应用程序通讯

驱动程序与应用程序通信

img

鼠标选中项目名字,按下ctrl+shift+A

1.DriverEntry 就是驱动的 main 函数,驱动加载后会从 DriverEntry 开始执行。2.驱动类似 DLL,可以提供接口给应用 程序调用,不过以导出函数的方式,而是用一套专门的通信函数 DeviceIoControl

SCM 加载法:

打开 SCM 管理器(获 得 SCM 句柄)->创建驱动服务(获得服务句柄,如果服务已经存在,此步则变成打开服 务)->启动服务->停止服务->移除服务->关闭服务句柄->关闭 SCM 句柄。如果要与驱动通 信,则用 CreateFile 打开驱动的符号链接(可以理解成获得一个“通信句柄”),然后使 用 DeviceIoControl 与驱动进行信息交互。如果曾经打开过驱动的符号链接,则必须在卸 载驱动前关闭“通信句柄”,否则再次加载相同的驱动时会有一些麻烦。

image-20200227180731356

DeviceIoControl(英文叫做设备控制),5 个重要参数:控制码,输入缓冲区,输 入长度,输出缓冲区,输出长度。

控制码(简称 IOCTL) KrnlHW64.sys 的代码,里面关于 IOCTL 的定义:

1
2
3
#define IOCTL_IO_TEST  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, 
FILE_ANY_ACCESS)
#define IOCTL_SAY_HELLO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)

里面CTL_CODE宏:

1
2
3
4
5
6
7
DWORD CTL_CODE_GEN(DWORD lngFunction) 
{
//const DWORD FILE_DEVICE_UNKNOWN = 0x22;
//const DWORD METHOD_BUFFERED = 0;
//const DWORD FILE_ANY_ACCESS = 0;
return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4)
| METHOD_BUFFERED; }

之所以要这么计算,是因为这个 32 位的 IOCTL 的每一位都有不同的含义(IOCTL 每一 位的具体含义如下图所示) 。如果不遵守这个规则,随意指派控制码,那么在与驱动进行通 信时将会蓝屏

image-20200227181927116

整合为类:

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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include <stdio.h>
#include <windows.h>
#pragma warning(disable : 4996)
class cDrvCtrl
{
public:
cDrvCtrl()//初始化各个变量
{
m_pSysPath = NULL;
m_pServiceName = NULL;
m_pDisplayName = NULL;
m_hSCManager = NULL;
m_hService = NULL;
m_hDriver = INVALID_HANDLE_VALUE;
}
~cDrvCtrl()//清除垃圾
{
CloseServiceHandle(m_hService);
CloseServiceHandle(m_hSCManager);
CloseHandle(m_hDriver);
}
public:
DWORD m_dwLastError; //最后的错误
PCHAR m_pSysPath; //驱动路径
PCHAR m_pServiceName; //服务名
PCHAR m_pDisplayName; //显示名
HANDLE m_hDriver; //驱动句柄
SC_HANDLE m_hSCManager; //SCM句柄
SC_HANDLE m_hService; //服务句柄
BOOL Install(PCHAR pSysPath, PCHAR pServiceName, PCHAR pDisplayName); //安装驱动服务
BOOL Start();//启动驱动服务
BOOL Stop();//停止驱动服务
BOOL Remove();//移除驱动服务
BOOL Open(PCHAR pLinkName); //打开驱动句柄
BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD* RealRetBytes);//IO控制
BOOL GetSvcHandle(PCHAR pServiceName);
DWORD CTL_CODE_GEN(DWORD lngFunction);
};
//打开已经存在的服务
BOOL cDrvCtrl::GetSvcHandle(PCHAR pServiceName)
{
m_pServiceName = pServiceName;
m_hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (NULL == m_hSCManager)
{
m_dwLastError = GetLastError();
return FALSE;
}
m_hService = OpenServiceA(m_hSCManager, m_pServiceName, SERVICE_ALL_ACCESS);
if (NULL == m_hService)
{
CloseServiceHandle(m_hSCManager);
return FALSE;
}
else
{
return TRUE;
}
}

//安装服务服务
BOOL cDrvCtrl::Install(PCHAR pSysPath, PCHAR pServiceName, PCHAR pDisplayName)
{
m_pSysPath = pSysPath;
m_pServiceName = pServiceName;
m_pDisplayName = pDisplayName;
m_hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (NULL == m_hSCManager)
{
m_dwLastError = GetLastError();
return FALSE;
}
m_hService = CreateServiceA(m_hSCManager, m_pServiceName, m_pDisplayName,
SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
m_pSysPath, NULL, NULL, NULL, NULL, NULL);
if (NULL == m_hService)
{
m_dwLastError = GetLastError();
if (ERROR_SERVICE_EXISTS == m_dwLastError)
{
m_hService = OpenServiceA(m_hSCManager, m_pServiceName, SERVICE_ALL_ACCESS);
if (NULL == m_hService)
{
CloseServiceHandle(m_hSCManager);
return FALSE;
}
}
else
{
CloseServiceHandle(m_hSCManager);
return FALSE;
}
}
return TRUE;
}

//启动服务
BOOL cDrvCtrl::Start()
{
if (!StartServiceA(m_hService, NULL, NULL))
{
m_dwLastError = GetLastError();
return FALSE;
}
return TRUE;
}

//停止服务
BOOL cDrvCtrl::Stop()
{
SERVICE_STATUS ss;
if (!ControlService(m_hService, SERVICE_CONTROL_STOP, &ss))
{
m_dwLastError = GetLastError();
return FALSE;
}
return TRUE;

}

//移除服务
BOOL cDrvCtrl::Remove()
{
if (!DeleteService(m_hService))
{
m_dwLastError = GetLastError();
return FALSE;
}
return TRUE;
}

//打开驱动的符号链接
BOOL cDrvCtrl::Open(PCHAR pLinkName)//example: \\\\.\\xxoo
{
if (m_hDriver != INVALID_HANDLE_VALUE)
return TRUE;
m_hDriver = CreateFileA(pLinkName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if (m_hDriver != INVALID_HANDLE_VALUE)
return TRUE;
else
return FALSE;
}


DWORD cDrvCtrl::CTL_CODE_GEN(DWORD lngFunction)
{
return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) |
METHOD_BUFFERED;
}

//和驱动实现通信的核心函数
BOOL cDrvCtrl::IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD* RealRetBytes)
{
DWORD dw;
BOOL b = DeviceIoControl(m_hDriver, CTL_CODE_GEN(dwIoCode), InBuff, InBuffLen, OutBuff, OutBuffLen, &dw, NULL);
if (RealRetBytes)
*RealRetBytes = dw;
return b;
}
//测试
void GetAppPath(char* szCurFile) //最后带斜杠
{
GetModuleFileNameA(0, szCurFile, MAX_PATH);
for (SIZE_T i = strlen(szCurFile) - 1;i >= 0;i--)
{
if (szCurFile[i] == '\\')
{
szCurFile[i + 1] = '\0';
break;
}
}
}
int main()
{
BOOL b;
cDrvCtrl dc;
//设置驱动名称
char szSysFile[MAX_PATH] = { 0 };
char szSvcLnkName[] = "KrnlHW64";;
GetAppPath(szSysFile);
strcat(szSysFile, "KrnlHW64.sys");//这里吗,这里是一个原因
//安装并启动驱动
b = dc.Install(szSysFile, szSvcLnkName, szSvcLnkName);
b = dc.Start();
printf("%d\n", b);
printf("LoadDriver=%d\n", b);
//“打开”驱动的符号链接
CHAR sym[] = "\\\\.\\KrnlHW64";
dc.Open(sym);
//使用控制码控制驱动(0x800:传入一个数字并返回一个数字)
DWORD x = 100, y = 0, z = 0;
dc.IoControl(0x800, &x, sizeof(x), &y, sizeof(y), &z);
printf("INPUT=%ld\nOUTPUT=%ld\nReturnBytesLength=%ld\n", x, y, z);
//使用控制码控制驱动(0x801:在DBGVIEW里显示HELLOWORLD)
dc.IoControl(0x801, 0, 0, 0, 0, 0);
//关闭符号链接句柄
CloseHandle(dc.m_hDriver);
//停止并卸载驱动
b = dc.Stop();
b = dc.Remove();
printf("UnloadDriver=%d\n", b);
getchar();
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<windef.h>
#include<stdlib.h>
//定义符号链接,一般来说修改为驱动的名字即可
#define DEVICE_NAME L"\\Device\\KrnlHW64"//大写字母L代表long,这将告诉编译器该字符串按宽字符保存,即每个字符占用2个字节。
#define LINK_NAME L"\\DosDevices\\KrnlHW64"
#define LINK_GLOBAL_NAME L"\\DosDevices\\Global\\KrnlHW64"
//定义驱动功能号和名字,提供接口给应用程序调用
#define IOCTL_IO_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SAY_HELLO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
//驱动卸载的处理例程
VOID DriverUnload(PDRIVER_OBJECT pDriverObj) {
UNICODE_STRING strLink;//过使用各种本地安全认证(LSA)的功能来指定一个 Unicode 字符串
DbgPrint("[KrnlHW64]DriverUnload\n");//打印
RtlInitUnicodeString(&strLink, LINK_NAME);//指向LINK_NAME的内存指针
IoDeleteSymbolicLink(&strLink);//从系统中删除一个符号链接
IoDeleteDevice(pDriverObj->DeviceObject);//从系统中删除一个设备对象,例如,当底层设备从系统中除去,指向要删除的设备对象的指针
}
//IRP_MJ_CREATE对应的处理例程,一般不用管它
//NTSTATUS 是被定义为32位的无符号长整型,用来检测状态是否正确
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) {
DbgPrint("[KrnlHW64]DispatchCreate\n");//打印
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);//函数表示调用方已完成所有I/O请求处理操作,并将给定的 IRP 返回给 I/O 管理器
return STATUS_SUCCESS;
}
//IRP_MJ_CLOSE对应的处理例程,一般不用管它
//NTSTATUS 是被定义为32位的无符号长整型,用来检测状态是否正确
NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp) {
DbgPrint("[KrnlHW64]DispatchClose\n");//打印
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);//如上,表示函数表示调用方已经完成所有的I/O请求
return STATUS_SUCCESS;
}
//IRP_MJ_DEVICE_CONTROL对应的处理例程,驱动最重要的函数之一,一般走正常途径调 用驱动功能的程序,都会经过这个函数
////NTSTATUS 是被定义为32位的无符号长整型,用来检测状态是否正确
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp) {
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
PIO_STACK_LOCATION pIrpStack;//加个P是指针,指针用-> 结构用.
/*IRP可以看成是Win32窗口程序中的消息(Message),DEVICE_OBJECT可以看成是Win32窗口程序中的窗口(Window)
任何内核模式程序在创建一个IRP时,同时还创建了一个与之关联的IO_STACK_LOCATION结构数组:数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序
IRP的头部有一个当前IO_STACK_LOCATION的数组索引,同时也有一个指向该IO_STACK_LOCATION的指针。索引是从1开始,没有0。当驱动程序准备向次低层驱动程序传递IRP时可以调用IoCallDriver例程,它其中的一个工作是递减当前IO_STACK_LOCATION的索引,使之与下一层的驱动程序匹配。但该索引不会设置成0,如果设置成0,系统将会崩溃。就是说,最底层的驱动程序不会调用IoCallDriver例程
*/
ULONG uIoControlCode;//无符号 64 位整数
PVOID pIoBuffer;//一个普通指针类型等价于(viod *)
ULONG uInSize;
ULONG uOutSize;
DbgPrint("[KrnlHW64]DispatchIoctl\n");//打印
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);//返回调用者在指定IRP中的 I/O stack location
//控制码
uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//输入输出缓冲区
pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
//输入区域大小
uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//输出区域大小
uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (uIoControlCode) {
//在这里加入接口
case IOCTL_IO_TEST: {
//__asm int 3
DWORD dw = 0;
//获得输入的内容
memcpy(&dw, pIoBuffer, sizeof(DWORD));
//使用输入的内容
dw++;
//输出处理的结果
memcpy(pIoBuffer, &dw, sizeof(DWORD));
//处理成功,返回非STATUS_SUCCESS会让DeviveIoControl返回失败
status = STATUS_SUCCESS;
break;
}
case IOCTL_SAY_HELLO: {
DbgPrint("[KrnlHW64]IOCTL_SAY_HELLO\n");//打印
status = STATUS_SUCCESS;
break;
}
//你之前写这里面了
}
// 代码 位置好像不对
if (status == STATUS_SUCCESS)//与上面的STATUS_SUCCESS的进行判断
pIrp->IoStatus.Information = uOutSize;
else
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);//函数表示调用方已完成所有I/O请求处理操作,并将给定的 IRP 返回给 I/O 管理器
return status;
}
//驱动加载的处理例程,里面进行了驱动的初始化工作
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING ustrLinkName;
UNICODE_STRING ustrDevName;
PDEVICE_OBJECT pDevObj;
//初始化驱动例程
pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
pDriverObj->DriverUnload = DriverUnload;
//创建驱动设备
RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
if (!NT_SUCCESS(status)) return status;
if (IoIsWdmVersionAvailable(1, 0x10))
RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME);
else
RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
//创建符号链接
status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
if (!NT_SUCCESS(status)) {
IoDeleteDevice(pDevObj);
return status;
}
//走到这里驱动实际上已经初始化完成,下面添加的是功能初始化的代码
DbgPrint("[KrnlHW64]DriverEntry\n");
return STATUS_SUCCESS;
}

双机调试环境调试:

image-20200227211614623

Author: L0x1c
Link: https://l0x1c.github.io/2020/04/26/2020-2-27/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶