15、Windows驱动开发技术详解笔记(11) 基本概念

简介: 9、Windows驱动程序的入口函数规定为_DriverEntry@8,所以用C++编写时要用extern。 驱动程序中,不能使用编译器运行时函数,甚至C语言中的malloc,C++的new函数都不能用,因为他们在VC中的实现都是调用了Win32 API了。

9Windows驱动程序的入口函数规定为_DriverEntry@8,所以用C++编写时要用extern

驱动程序中,不能使用编译器运行时函数,甚至C语言中的malloc,C++new函数都不能用,因为他们在VC中的实现都是调用了Win32 API了。要用的话,必须自己重载new等运算符。大部分运行时函数是通过Win32 API实现的。在内核模式下无法调用用户模式的程序,而用户模式下通过参数审核可以调用内核态程序。

内核态的运行时函数来替换用户态的运行时函数,一般形如RtlXXXX

设备名称用UNICODE字符串指定,且必须是“\Device\[设备名]”形式。用户模式下,通过两种方法找到设备:

1:符号连接

2:通过设备接口。

一般虚拟设备用FILE_DEVICE_UNKNOWN类型。

******************

"采用C++编程,所以需要用extern "C",因为我们导入的是C的函数的符号表",系统内核是有C语言编写的,连接时要调用相应的函数,所以应当把驱动头文件编译成C语言形式。

10、用build编译

Build首先设置环境变量,然后调用nmakenmake解析makefile,调用cl,link来编译程序。

Makefile中依赖关系如下声明:

ABC

Action

说明:A依赖于BC。如果A的最后修改时间早于BC任一个文件的最后修改时间,则执行actionAction前面是tab

VC编译,我们在另一篇中论述。

11、查看调试信息

法一:打印信息

尽量用KdPrint,free版本中不显示。同prrinf使用,双括号,宽字符用%ws%S

法二:用DriverStudio中的DriverMonitor。不过可以用免费的DbgView,下载地址是:

http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx

12、加载驱动

NT式驱动程序的加载,是基本服务的方式加载的。设备驱动程序的动态加载主要由服务控制管理程序(SCM)系统组件完成的。Windows服务应用程序遵循控制管理器。Driver ServiceWindows服务的一个特例,它遵守Windows服务的协议。

wps_clip_image-28690

图 加载 P72

wps_clip_image-15815

图 卸载驱动流程 P72

代码 加载和卸载驱动代码

代码
 
   
1 #include < windows.h >
2 #include < winsvc.h >
3 #include < conio.h >
4 #include < stdio.h >
5
6   #define DRIVER_NAME "HelloDDK"
7   #define DRIVER_PATH "..\\MyDriver\\MyDriver_Check\\HelloDDK.sys"
8
9 // 装载NT驱动程序
10 BOOL LoadNTDriver( char * lpszDriverName, char * lpszDriverPath)
11 {
12 char szDriverImagePath[ 256 ];
13 // 得到完整的驱动路径
14 GetFullPathName(lpszDriverPath, 256 , szDriverImagePath, NULL);
15
16 BOOL bRet = FALSE;
17
18 SC_HANDLE hServiceMgr = NULL; // SCM管理器的句柄
19 SC_HANDLE hServiceDDK = NULL; // NT驱动程序的服务句柄
20
21 // 打开服务控制管理器
22 hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
23
24 if ( hServiceMgr == NULL )
25 {
26 // OpenSCManager失败
27 printf( " OpenSCManager() Faild %d ! \n " , GetLastError() );
28 bRet = FALSE;
29 goto BeforeLeave;
30 }
31 else
32 {
33 /// /OpenSCManager成功
34 printf( " OpenSCManager() ok ! \n " );
35 }
36
37 // 创建驱动所对应的服务
38 hServiceDDK = CreateService( hServiceMgr,
39 lpszDriverName, // 驱动程序的在注册表中的名字
40 lpszDriverName, // 注册表驱动程序的 DisplayName 值
41 SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
42 SERVICE_KERNEL_DRIVER, // 表示加载的服务是驱动程序
43 SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值
44 SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值
45 szDriverImagePath, // 注册表驱动程序的 ImagePath 值
46 NULL,
47 NULL,
48 NULL,
49 NULL,
50 NULL);
51
52 DWORD dwRtn;
53 // 判断服务是否失败
54 if ( hServiceDDK == NULL )
55 {
56 dwRtn = GetLastError();
57 if ( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS )
58 {
59 // 由于其他原因创建服务失败
60 printf( " CrateService() Faild %d ! \n " , dwRtn );
61 bRet = FALSE;
62 goto BeforeLeave;
63 }
64 else
65 {
66 // 服务创建失败,是由于服务已经创立过
67 printf( " CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n " );
68 }
69
70 // 驱动程序已经加载,只需要打开
71 hServiceDDK = OpenService( hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS );
72 if ( hServiceDDK == NULL )
73 {
74 // 如果打开服务也失败,则意味错误
75 dwRtn = GetLastError();
76 printf( " OpenService() Faild %d ! \n " , dwRtn );
77 bRet = FALSE;
78 goto BeforeLeave;
79 }
80 else
81 {
82 printf( " OpenService() ok ! \n " );
83 }
84 }
85 else
86 {
87 printf( " CrateService() ok ! \n " );
88 }
89
90 // 开启此项服务
91 bRet = StartService( hServiceDDK, NULL, NULL );
92 if ( ! bRet )
93 {
94 DWORD dwRtn = GetLastError();
95 if ( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING )
96 {
97 printf( " StartService() Faild %d ! \n " , dwRtn );
98 bRet = FALSE;
99 goto BeforeLeave;
100 }
101 else
102 {
103 if ( dwRtn == ERROR_IO_PENDING )
104 {
105 // 设备被挂住
106 printf( " StartService() Faild ERROR_IO_PENDING ! \n " );
107 bRet = FALSE;
108 goto BeforeLeave;
109 }
110 else
111 {
112 // 服务已经开启
113 printf( " StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! \n " );
114 bRet = TRUE;
115 goto BeforeLeave;
116 }
117 }
118 }
119 bRet = TRUE;
120 // 离开前关闭句柄
121 BeforeLeave:
122 if (hServiceDDK)
123 {
124 CloseServiceHandle(hServiceDDK);
125 }
126 if (hServiceMgr)
127 {
128 CloseServiceHandle(hServiceMgr);
129 }
130 return bRet;
131 }
132
133 // 卸载驱动程序
134 BOOL UnloadNTDriver( char * szSvrName )
135 {
136 BOOL bRet = FALSE;
137 SC_HANDLE hServiceMgr = NULL; // SCM管理器的句柄
138 SC_HANDLE hServiceDDK = NULL; // NT驱动程序的服务句柄
139 SERVICE_STATUS SvrSta;
140 // 打开SCM管理器
141 hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
142 if ( hServiceMgr == NULL )
143 {
144 // 带开SCM管理器失败
145 printf( " OpenSCManager() Faild %d ! \n " , GetLastError() );
146 bRet = FALSE;
147 goto BeforeLeave;
148 }
149 else
150 {
151 // 带开SCM管理器失败成功
152 printf( " OpenSCManager() ok ! \n " );
153 }
154 // 打开驱动所对应的服务
155 hServiceDDK = OpenService( hServiceMgr, szSvrName, SERVICE_ALL_ACCESS );
156
157 if ( hServiceDDK == NULL )
158 {
159 // 打开驱动所对应的服务失败
160 printf( " OpenService() Faild %d ! \n " , GetLastError() );
161 bRet = FALSE;
162 goto BeforeLeave;
163 }
164 else
165 {
166 printf( " OpenService() ok ! \n " );
167 }
168 // 停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。
169 if ( ! ControlService( hServiceDDK, SERVICE_CONTROL_STOP , & SvrSta ) )
170 {
171 printf( " ControlService() Faild %d !\n " , GetLastError() );
172 }
173 else
174 {
175 // 打开驱动所对应的失败
176 printf( " ControlService() ok !\n " );
177 }
178 // 动态卸载驱动程序。
179 if ( ! DeleteService( hServiceDDK ) )
180 {
181 // 卸载失败
182 printf( " DeleteSrevice() Faild %d !\n " , GetLastError() );
183 }
184 else
185 {
186 // 卸载成功
187 printf( " DelServer:eleteSrevice() ok !\n " );
188 }
189 bRet = TRUE;
190 BeforeLeave:
191 // 离开前关闭打开的句柄
192 if (hServiceDDK)
193 {
194 CloseServiceHandle(hServiceDDK);
195 }
196 if (hServiceMgr)
197 {
198 CloseServiceHandle(hServiceMgr);
199 }
200 return bRet;
201 }
202
203 void TestDriver()
204 {
205 // 测试驱动程序
206 HANDLE hDevice = CreateFile( " \\\\.\\HelloDDK " ,
207 GENERIC_WRITE | GENERIC_READ,
208 0 ,
209 NULL,
210 OPEN_EXISTING,
211 0 ,
212 NULL);
213 if ( hDevice != INVALID_HANDLE_VALUE )
214 {
215 printf( " Create Device ok ! \n " );
216 }
217 else
218 {
219 printf( " Create Device faild %d ! \n " , GetLastError() );
220 }
221 CloseHandle( hDevice );
222 }
223
224 int main( int argc, char * argv[])
225 {
226 // 加载驱动
227 BOOL bRet = LoadNTDriver(DRIVER_NAME,DRIVER_PATH);
228 if ( ! bRet)
229 {
230 printf( " LoadNTDriver error\n " );
231 return 0 ;
232 }
233 // 加载成功
234
235 printf( " press any to create device!\n " );
236 getch();
237
238 TestDriver();
239
240 // 这时候你可以通过注册表,或其他查看符号连接的软件验证。
241 printf( " press any to unload the driver!\n " );
242 getch();
243
244 // 卸载驱动
245 UnloadNTDriver(DRIVER_NAME);
246 if ( ! bRet)
247 {
248 printf( " UnloadNTDriver error\n " );
249 return 0 ;
250 }
251
252 return 0 ;
253 }
254
255

 

关于WDM驱动的手工加载,见[1]

13NT驱动程序的基本结构

1)数据结构

DEVICE_OBJECT

The DEVICE_OBJECT structure is used by the operating system to represent a device object. A device object represents a logical, virtual, or physical device for which a driver handles I/O requests.

http://msdn.microsoft.com/en-us/library/ff543147%28VS.85%29.aspx

每个设备对象指针指向向一个设备对象,最后一个设备对象指向空。

wps_clip_image-28693

图 设备对象布局图 P92

设备扩展对象定义自己定义的结构体,驱动程序中尽是避免使用全局变量,因为会影响同步,而把全局变量存在设备扩展里。

driver object

Each driver object represents the image of a loaded kernel-mode driver. A pointer to the driver object is an input parameter to a driver's DriverEntry, AddDevice, and optional Reinitialize routines and to its Unload routine, if any.

每个驱动程序与唯一的驱动对象相对应,这个驱动对象是在驱动加载时候,被内核中的对象管理器创建。驱动对象为驱动和个实例被内核加载,并且一个驱动只能加载一个实例。

wps_clip_image-26564

图 驱动对象布局图 P88

其它参见:

http://www.cnblogs.com/mydomain/archive/2010/10/16/1853235.html

14DriverEntry主要是对系统进程创建的驱动对象进行初始化。

15WDM程序的基本结构

一般都是基于分层的,完成一个设备的操作,至少要由两个驱动设备共同完成。一个是PDO(物理设备对象),一个是FDO(功能设备对象),其关系是附加与被附加关系。当PC中插入某个设备时,总线驱动自动创建PDO,由PDO引出FDO

wps_clip_image-5558

FDOPDO P103

过滤驱动不是必须的。

NT的不同:

· 增加了对AddDevice函数的设备。操作系统加载PDO后,调用驱动的AddDevice例程,AddDevice例程负责创建FDO,并且附加到PDO之上。

·设备对象在AddDevice例程中创建。

·必须加入IRP_MJ_PNP的派遣回调函数。

PDO会通过AttachedDevice子域知道它上面的设备是FDO或过滤驱动,但是不知道下面的,可以通过定制自己的设备扩展来记录地址。

16、设备的层次结构

wps_clip_image-24338

图 设备对象堆栈 P104

wps_clip_image-4820

图 垂直结构 P111,P113

参考

1Windows 驱动开发技术详解

2http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx
3Windows驱动学习笔记,灰狐

目录
相关文章
|
11天前
|
安全 固态存储 文件存储
Windows 7纯净版重装教程|附微软原版镜像下载+驱动安装避坑技巧
本文详细介绍如何安全、高效地重装电脑系统,解决蓝屏、崩溃等问题。基于10年经验,涵盖从官方镜像获取、启动盘制作、数据备份到系统部署的全流程,并针对老旧机型优化。提供驱动一键安装工具和系统激活指南,确保无后门风险。文中还列出常见问题解决方案及操作禁忌,帮助用户顺利完成系统重装,让电脑重获新生。建议收藏并转发给有需要的朋友,欢迎留言咨询疑难问题。
|
3天前
|
算法 关系型数据库 测试技术
WHQL微软驱动签名方案,让驱动程序在Windows系统流畅运行
WHQL认证(Windows徽标认证)是微软设立的严格测试标准,旨在确保驱动程序的兼容性、稳定性和互通性。本文介绍了三种WHQL微软驱动签名方案:单系统签名、多系统签名和硬件兼容性测试方案,分别满足不同开发商的需求。通过WHQL认证,不仅能消除Windows安装警告,提升用户体验,还能获得“Designed for Windows”徽标授权,入列全球Windows Catalog及HCL产品表,提升品牌权威性和采购优先权。此外,访问微软OCA可获取错误反馈,助力产品质量改进。选择合适的签名方案,让驱动在Windows系统中流畅运行!
|
5月前
|
监控 Ubuntu Linux
视频监控笔记(五):Ubuntu和windows时区同步问题-your clock is behind
这篇文章介绍了如何在Ubuntu和Windows系统中通过设置相同的时区并使用ntp服务来解决时间同步问题。
133 4
视频监控笔记(五):Ubuntu和windows时区同步问题-your clock is behind
|
5月前
|
Ubuntu Linux Python
如何利用wsl-Ubuntu里conda用来给Windows的PyCharm开发
如何在WSL(Windows Subsystem for Linux)的Ubuntu环境中使用conda虚拟环境来为Windows上的PyCharm开发设置Python解释器。
479 1
|
5月前
|
监控 关系型数据库 MySQL
PowerShell 脚本编写 :自动化Windows 开发工作流程
PowerShell 脚本编写 :自动化Windows 开发工作流程
235 0
|
7月前
|
vr&ar C# 图形学
WPF与AR/VR的激情碰撞:解锁Windows Presentation Foundation应用新维度,探索增强现实与虚拟现实技术在现代UI设计中的无限可能与实战应用详解
【8月更文挑战第31天】增强现实(AR)与虚拟现实(VR)技术正迅速改变生活和工作方式,在游戏、教育及工业等领域展现出广泛应用前景。本文探讨如何在Windows Presentation Foundation(WPF)环境中实现AR/VR功能,通过具体示例代码展示整合过程。尽管WPF本身不直接支持AR/VR,但借助第三方库如Unity、Vuforia或OpenVR,可实现沉浸式体验。例如,通过Unity和Vuforia在WPF中创建AR应用,或利用OpenVR在WPF中集成VR功能,从而提升用户体验并拓展应用功能边界。
155 1
|
6月前
|
存储 安全 程序员
Windows任务管理器开发原理与实现
Windows任务管理器开发原理与实现
|
7月前
|
开发者 C# Windows
WPF与游戏开发:当桌面应用遇见游戏梦想——利用Windows Presentation Foundation打造属于你的2D游戏世界,从环境搭建到代码实践全面解析新兴开发路径
【8月更文挑战第31天】随着游戏开发技术的进步,WPF作为.NET Framework的一部分,凭借其图形渲染能力和灵活的UI设计,成为桌面游戏开发的新选择。本文通过技术综述和示例代码,介绍如何利用WPF进行游戏开发。首先确保安装最新版Visual Studio并创建WPF项目。接着,通过XAML设计游戏界面,并在C#中实现游戏逻辑,如玩家控制和障碍物碰撞检测。示例展示了创建基本2D游戏的过程,包括角色移动和碰撞处理。通过本文,WPF开发者可更好地理解并应用游戏开发技术,创造吸引人的桌面游戏。
372 0
|
Windows
23、Windows派遣函数(1)-Windows驱动开发详解笔记,IRP
驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的。用户模式下所有对驱动程序的I/O请求,全部由操作系统转化为一个叫做IRP的数据结构,不同的IRP数据会被“派遣”到不同的派遣函数(Dispatch Function)中。
932 0
|
Windows
24、Windows派遣函数(2)-Windows驱动开发详解笔记,直接读写方式
  1、直接读写方式 操作系统将用户模式下的缓冲区锁住,然后操作系统将这段缓冲区在内核模式地址再映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理地址。 操作系统将用户模式的地址锁定后,用内存描述符MDL记录这段内存。
1066 0