x86驱动开发
WDK下载链接:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/download-the-wdk
,根据页面对应内容下载即可。
x86驱动打开只能使用<=wdk8.1版本,对应的可用vs版本为<=vs2017。因此X86篇章的驱动开发环境如下:
- 系统:window7 x64 sp1
- IDE:VS2013
- WDK:8.1
一、DriverEntry
1、第一个驱动
安装WDK后,VS新建项目中会多出Windows Driver
项。
KMDF和WDM的区别不大,KMDF的驱动支持即插即用设备,例如U盘。在测试中发现x86下安装KMDF会失败,这里只写使用WDM进行编写驱动。
1 |
|
其中需要对VS设置如下:
1、关闭C++将警告视为错误
。
2、关闭WppTrapingRun Wpp Tracing
。
然后编译即可。在测试平台上使用KmdManager
进行加载,首先点击Register
,然后点击Run
。使用PcHunter可以看到驱动已经加载。
在加载驱动的时候首先需要将驱动的基本信息写入注册表中,写入的路径为DriverEntry的第二个参数。使用KdPrint
进行输出。
KdPrint和DbgPrint区别是:KdPrint实际上是一个宏,会对Debug和Release进行判断,如果为Debug编译的驱动则会调用DbgPrint,否则不调用。
1 | KdPrint(("%wZ\r\n", pReg)); |
%wZ
表示的是输出内核字符串。内核中一旦出现内存溢出或者空指针则会引发蓝屏,因此微软使用了更安全的UNICODE_STRING结构来表示字符串.
1 | typedef struct _UNICODE_STRING { |
加载驱动,使用DbgView捕获输出Capture->Cpature Kernel
,Capture->Enable Verbose Kernel Output
然后打开到对应的注册表查看。
- DisplayName:驱动对外显示的名字,比如PCHunter枚举驱动模块时显示的名字
- ErrorControl:母鸡
- ImagePath:驱动模块的路径,
\??\
为全路径磁盘就是挂载这个上面 - Start:驱动加载的类型,<=2为开启自启,数字越低自启动时机与早。
- Type:类型,1为驱动模块,0好像是服务。
驱动加载时,系统会将这些信息写入注册表,然后在拉起驱动模块。
驱动卸载时,系统首先会调用驱动的卸载函数(如果不设置则无法卸载),然后删除注册表。
当一个正常的内核模块加载完成时,注册表就保存着他的信息,通过枚举注册表即可获取系统加载的驱动模块,因此绕过这中枚举方法就是隐藏自己的注册表,例如驱动加载完毕后删除注册表。
二、蓝屏调试
当产生蓝屏时可以通过执行!analyze -v
来获取详细信息。大概流程为:
1、执行!analyze -v
获取详细信息。
2、根据信息给出的蓝屏原因进行搜索。
根据给出的原因到https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/
搜索
然后根据windbg给出的参数进行大致排查原因。
3、根据栈回溯查找原因
可以大致看到最后一次自己模块的调用位置。还有异常所处的模块是什么。
- k、kv:查看堆栈
三、驱动断链
PDRIVER_OBJECT中的DriverSection实际上是一个链表,结构为:KLDR_DATA_TABLE_ENTRY
1 | typedef struct _NON_PAGED_DEBUG_INFO { |
如果遍历可以得到非处理的驱动模块(隐藏的不行)。
1 | NTSTATUS DriverEntry(PDRIVER_OBJECT pDrv, PUNICODE_STRING pReg) |
其中可以看到第一个是自身,调用RemoveEntryList
断链自身即可隐藏,但比较弱。