------------VS 2013驱动开发 + Windbg + VM双机调试(亲测+详解)-------------
WIN10已上线,随之而来的是VS2015;微软在 “WDK7600” 以后就不再提供独立的内核驱动开发包了,而是必须首先安装微软集成开发环境VS,然后再从微软官网下载集成的WDK驱动程序开发包、或者离线安装的开发包。
地址:
https://msdn.microsoft.com/zh-cn/windows/hardware/hh852365.aspx
离线下载地址:百度云链接:http://pan.baidu.com/s/1i3KdUSH 密码:vp41
安装WDK后,本人使用的是VS2013 +WDK8.1,无脑下一步安装完成后,建立新工程,即可看到新的驱动工程项目。
其中提供了很多的模板,(WDM WDF,KWDM等等),大家可能感觉这些框架形式很复杂,其实这些框架的区别,主要在于设备驱动开发,例如WDM相对NT增加了即插即用电源管理,WDF封装了WDM接口变成类形式,隐藏驱动设备等对象,其他的类似应用层驱动框架等(其实就是dll)。
因新框架的封装隐藏了底层对象信息等,有时候不够*,让我们实现一些功能的时候不太方便,所以我们还是主要使用NT式驱动。
编写驱动:
第一个问题来了,WDK8.1提供的模板中根本没有提供NT驱动模板,我们如何创建NT驱动呢?
解决:其实虽然没有提供NT模板,但是我们可以建立WDM空模板工程,然后再自己添加文件,编译,得到的也就是NT驱动了。
建立工程后,首先会有两个工程,一个就是驱动工程,另外一个是package工程(这个是测试驱动安装的一个工程,对于我们来说其实没有什么用处,反正本人是没有使用过得,可以直接删除)。
驱动工程中会帮你建立一个inf文件,NT是使用不到的,当然新一代的过滤驱动,例如 minifilter 是使用的,VS2013支持直接创建minifilter工程,所以如你建的是 NT驱动,这个可以直接删除,如不想删除,就需要在inf文件中填写一些信息,避免生成程序时报错,可以按以下模板填写:
<strong><span style="font-size: 18px;">
;这个 DDK 文档是一个优秀的INF参考 ;
;MyWDMDriverA.inf
; ;----------------------- 版本部分 ------------------------------------ [Version]
Signature="$YHMADE$" ;签名
Provider=YanHui ;供应商,可选填写
DriverVer=11/14/2015,20.46.24.01 ;驱动程序版本,格式自定义,可选填写
CatalogFile=myWDMDriverA.cat ;此处重点。生成的inf中没有填写,此处需要自己添加:名字.cat ; 如果设备符合其中一个标准类,填写使用的名称和GUID,否则要创建自己的设备类和GUID。如本例所示。 Class=YH ;随便改个就行
ClassGUID={EF2934D45-1DD8-F672-5656-H7G1EE8A35FF} ;随便改个就行,不要与已有的冲突class和calssguid都有个对应关系.网上可查 ;--------- 源名称和源磁盘文件部分 -----------------------
;这部分确定安装源磁盘和文件,他们在这里显示作为一个例子,但可以注释掉。 [SourceDisksNames]
1 = %DiskName%,,,"" [SourceDisksFiles]
myWDMDriverA.sys = 1,, ;--------- 类安装/32位类部分 -------------------------------
;如果使用一个标准的类就不要 ; 9X 风格
[ClassInstall]
Addreg=Class_AddReg ; NT 风格
[ClassInstall32]
Addreg=Class_AddReg [Class_AddReg]
HKR,,,,%DeviceClassName%
HKR,,Icon,,"-5" ;--------- 终点节目录------------------------------------------- [DestinationDirs]
Default_Files_Driver = 12,System32\Drivers ;---------制造商和模型部分 ---------------------------------- [Manufacturer]
%ManufacturerName%=Standard,NT$ARCH$ [Standard.NT$ARCH$] ; PCI硬件id使用表单
; PCI\VEN_aaaa
; DEV_bbbb
; SUBSYS_cccccccc
; REV_dd
; 改成你自己的ID
%DeviceDesc%=Default_DDI, PCI\VEN_2DF4
%DeviceDesc%=Default_DDI, PCI\DEV_6677
%DeviceDesc%=Default_DDI, PCI\SUBSYS_7D9F873K
%DeviceDesc%=Default_DDI, PCI\REV_HK ;----------Windows 9X DD安装部分 ----------------------------------------------- ; 实验表明,DD 安装的根是大于19字符的名称
; 在Windows 98造成问题 [Default_DDI]
CopyFiles=Default_Files_Driver
AddReg=Default_9X_AddReg [Default_9X_AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,myWDMDriverA.sys
HKR, "Parameters", "BreakOnEntry", 0x00010001, 0 ; --------- Windows NT ----------------- [Default_DDI.NT]
CopyFiles=Default_Files_Driver
AddReg=Default_NT_AddReg [Default_DDI.NT.Services]
Addservice = MyWDMDriverA, 0x00000003, Default_AddService [Default_AddService]
DisplayName = %SvcDesc%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %10%\System32\Drivers\myWDMDriverA.sys [Default_NT_AddReg]
HKLM, "System\CurrentControlSet\Services\MyWDMDriverA\Parameters",\
"BreakOnEntry", 0x00010001, 0 ; --------- 文件 (常见) ------------- [Default_Files_Driver]
myWDMDriverA.sys ;--------- 截面字符串 --------------------------------------------------- [Strings]
ProviderName="YanHui"
ManufacturerName="YanHuiDriver"
DeviceDesc="QQ14526854";设备管理器中的显示的实例的名字.在类的下边显示
DeviceClassName="QT35623608";此为显示在设备管理器中的类的名字,修改一次类的名字,就需要更改一次classguid的值.
SvcDesc="YanHui" ;inf结束. inf中没有的节的可以参照我的inf自己添加.
</span></strong>
如图:
我们直接添加一个myWDMDriverA.cpp(我使用C++开发驱动,但我还是建议大家使用c开发比较适合,因为微软内核使用的也是C,而且C是能够直接操作内存,汇编之上的个人感觉最好的语言)
在编译驱动前,这里对 Windows 驱动程序做一个简要的说明:
1.Windows驱动程序分为两类,一类是不支持即插即用功能的NT式的驱动程序;另一类是支持即插即用功能的WDM式的驱动程序。
2.NT式的驱动程序要导入的头文件时ntddk.h,而WDM式的驱动要导入的头文件为wdm.h
3.DriverEntry需要放在INIT标志的内存中。INIT标志指明该函数只是在加载的时候需要载入内存,而当驱动程序加载成功后,该函数可以从内存中卸载掉。
4.C++编写驱动需要注意下面的编写格式:
#ifdef _cplusplus
extern "C"
{
#endif
#include <ntddk.h>
#ifdef _cplusplus
}
#endif #define PAGEDCODE code_seg("PAGE") #pragma PAGEDCODE
VOID Unload(IN PDRIVER_OBJECT pDriverObject);
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
KdPrint(("......"));
return "......." ;
}
如果在 C++ 中编写驱动入口时,不按照这样的格式写,当加载或卸载驱动时会出现蓝屏
并且在编译代码是要特别注意字母的大小写,C 和 C++ 对大小写字母特别敏感!!
添加一些简单的代码。
下面,编译,报错。。。。。没有关系,查看出错原因,无外乎一些警告被当做错误,或者一些函数参数没有被使用,导致编译不过,这些都是因为安全警告等级太高了,我们可
其实以前的旧版本,在编写驱动前需要对项目进行属性配置,才能正确的生成。
但微软已在 VS2013 上做了很大的改进,让我们几乎不用改写属性都可以顺利生成,以前的就以前yi以如果出现编译错误,可以修改两个地方来解决:
(一) 将所有的警告和安全措施,全部都做到,例如没有使用的参数使用宏UNREFERENCED_PARAMETER,等等,当然做到这些,有时候基本没有办法写程序。
(二) 降低警告等级哦,
如果你实在不放心,我可以参照下面的说明来修改项目属性来达到因程序配置问题而影响驱动的生成问题:这里只记录一些配置和操作步骤!
1.设置VC++路径
<我把wdk安装在E盘下>
(1). 配置可执行文件目录:E:\WinDDK\7600.16385.1\bin\x86;
(2). 配置包含目录:
E:\WinDDK\7600.16385.1\inc\ddk
E:\WinDDK\7600.16385.1\inc\crt
E:\WinDDK\7600.16385.1\inc\api
(3). 配置库目录:
E:\WinDDK\7600.16385.1\lib\win7\i386
新建C/C++文件 不然无C/C++设置选项 <刚开始我们创建了一个空的项目所以项目里没有c++文件,现在要做的就是在空的项目-源文件-添加一个新建项c++文件>
常规
目标文件扩展名:.sys //必选
2设置C/C++选项
常规选项卡
(1) 调试信息格式(C7 兼容(/Z7) //可选
(2) 警告等级 (2 级(/W2) //可选
(3 )将警告视为错误 (是(/wx) //可选
优化选项卡
优化(禁用/Od) //可选
预处理器
预处理器定义:WIN32=100;_X86_=1;WINVER=0x501;DBG=1 //必选
代码生成
(1) 启用最小重新生成: 否 //可选
(2 )基本运行时检查: 默认值 //可选
(3) 运行时库: 多线程调试(/MTd) 或 多线程(/MT) //建议选
(4 )缓冲区安全检查: 否 //可选 (可避免出现 LINK : error LNK2001: 无法解析外部符号 __security_cookie)
高级
调用约定: __stdcall(/Gz) //必选
3. 链接器设置
常规 :
启用增量链接:否(/INCREMENTAL:NO) //建议 选上
忽略导入库:是 // 可选 (设置为此值时,必须在附加库目录中加: E:\WinDDK\7600.16385.1\lib\win7\i3865这样项目就不会依赖 IDE 环境的设置)
输入 :
附加依赖项 :ntoskrnl.lib;Hal.lib;wdm.lib;wdmsec.lib;wmilib.lib;ndis.lib;MSVCRT.LIB;LIBCMT.LIB //必选 //NT式驱动ntoskrnl.lib;WDM式驱动wdm.lib ( HalXXX 函数在Hal.lib, WmiXXX 函数在wmilib.lib,NdisXXX函数在ndis.lib ) (必要时需要增加微软的标准库MSVCRT.LIB MSVCRTD.LIB(调试库)LIBCMT.LIBIBCMTD.LIB(调试库) ) (如果源码中有source文件,那么该文件的TARGETLIBS字段会列出该项 目需要的库)
忽略所有默认库:是 (/NODEFAULTLIB) //必选
清单文件: 启用用户账户控制(UAC) 否 //必选不然会出现>LINK : fatal error LNK1295: “/MANIFESTUAC”与“/DRIVER”规范不兼容;链接时不使用“/MANIFESTUAC”
调试:
生成调试信息: 是(/DEBUG) //可选
生成映像文件:是(/MAP) //可选
映像文件名:$(TargetDir)$(TargetName).map //可选
系统(System):
子系统: 控制台(/SUBSYSTEM:CONSOLE //必选
堆栈保留大小:4194304 //可选
堆栈提交大小: 4096 //可选
驱动程序: 驱动程序(/DRIVER) //必选
高级:
入口点:DriverEntry //必选
随机基址: 清空 //把框里的数据删掉。(yes也不是no也不是就是要一个干干净净的文本框)//必选 不然会出现 e:\xxx.sys : fatal error LNK1295: “/DYNAMICBASE”与“/DRIVER”规范不兼容;链接时不使用“/DYNAMICBASE”
数据执行保护(DEP): 清空 //把框里的数据删掉。(yes也不是no也不是就是要一个干干净净的文本框) //必选不然会出现e:\xxx.sys : fatal error LNK1295:“/NXCOMPAT:NO”与“/DRIVER”规范不兼容;链接时不使用“/NXCOMPAT:NO”
设置效应和:是(/RELEASE) //可选
基址:0x10000 //建议选上
命令行:/SECTION:INIT,D /IGNORE:4078 (建议不要写进去,会报错!)
好了,我们再次尝试编译生成,生成过了。
现在来设置虚拟机的端口:至于虚拟机的系统安装我就不解释了,网上一搜一大把!
按照网上的解说,虚拟机中添加一个虚拟串口(\\.\pipe\com_1),因为虚拟机中自带的虚拟打印机,占用虚拟串口一.
所以,你有两个选择,一个删除该虚拟打印机,一个,添加别的虚拟串口(例如:\\.\pipe\com_2),这里,我选择的是删除虚拟打印机。
好了,虚拟机已经开启了串口监听。
调试驱动:
VS2013提供集成IDE调试驱动,网上面有很多配置调试环境的帖子,在这里我先啰嗦解释两句:
(一) VS2013 调试启动,首先需要建立一个调试机工程
(二) 选择添加一个目标计算机(Add New Computer),这里的目标计算机就是按装用 Windbg VS2013 等调试软件的主机,我们基本上都用本机为目标机,点击下一步,能够选择多种目标计算机类型.
例如:
第一项Prevision Computer and Automatically Configura Debuggers:选择一个远程的真机,VS会在远程计算机上安装调试工具,设置调试环境,会重启几次。
第二项Provision Computer and choose debugger setting和第三项:可以自己选择调试方式(网络,串口,1394,USB)。即使不是使用网络进行调试,也要使用网线连接调试机和被调试机,因为VS在配置环境的时候需要使用网络进行配置和传输文件。
在这里我选择使用第三项,并且配置串口调试,因为,以前我们使用Windbg的时候也是配置串口调试的。
按上图配置即可:
Connection Type选为Serial (串口)
(波特率)Baud Rate 115200
(管道)Pipe勾选
(重连接)Reconnect勾选
(管道名)Pipe name \\.\pipe\com_1
这几个选项根据你虚拟机设置来设置,最后点击完成。
OK,到这里,总结一下,我们能够编译驱动,然后直到调试驱动需要创建一个目标计算机,目标计算机可以是真机,也可能是虚拟机,当然现在不能证明。因为微软告诉我们说,VS2012以后,不支持xp平台驱动编译,这个我们在选择驱动编译环境的时候,我们也看到了。完全就没有xp,
不要紧,那我们先使用win7 32来调试实验下。
打开虚拟机进入系统后,设置系统的启动项为:(调试模式),设置完后,重启虚拟机。
我也可以直接在系统启动的时候按F8键进入启动模式选择界面,选中“调试模式”回车:
选择后,虚拟机的系统就正常进入,速度会有点慢,需耐心等待。
然后VS2013工程中我们选择要上面设置的COM1端口的配置电脑:
选择菜单上的“调试”选择“附加到进程”选项:
传输: Windows Kernel Mode Debugger
限定符: 就是我们添加的计算机名
点击附加 VS2013会自动进入调试状态,如果没有我们点击“Debuging Tools for windows”选项:
这时会弹出确认界面:
选择:“I Know”,然后 VS2013 自带的 Windbg 软件就会启动,进入到等待连接的状态:
这时检测虚拟机的系统,看是否正常进入了桌面,(这里速度有点慢,需稍微等待一下)。
有反应了,说明系统正在登陆….。稍等片刻后,再此查看,如果进入了系统,我们点击VS2013 编译器的暂停按钮,中断与虚拟机的连接:
点击后这里可能会有点卡,速度有点慢,等一下就好了。如果真的卡死了,我们用任务管理器把 VS2013 编译器结束掉,再重新打开,并打开驱动工程项目,且按自己的需要下好断点。
再重新选择菜单上的“调试”->”附加到进程”按上次的设置来开启驱动调试。注意,开启的虚拟机不能关闭!
当重新与虚拟机连接,并运行到: int 3 这里后:
好了,一切准备就绪,现在我们在命令框中输入 “g” 按回车,让虚拟机不受控制,自己接受主权,自动跑起来…
在这之前,切记我们已在代码中下了断点,不然后面加载驱动后,调试器无法拦截上….
现在可以把生成的驱动文件 DriverName.sys 考到虚拟系统的桌面上,这里我的驱动文件是MyWDMDriverA.sys。
然后我们用驱动加载工具DriverMonitor 打开要加载驱动文件,然后点击 go 继续加载:
看到了吧!当驱动被加载到系统后,VS 2013 编译器和Windbg调试器便立刻有了反应,且驱动调试截取到断点的位置!
这时VS2013 可能会弹出“帧不在模块中的警告界面(是个人情况而定),而我的两台电脑就只有一点出现这种情况:
原因是我们没对Windbg 设置源文件和符号路径。
网上说:出现该错误就有可能是下面中的一种,读者可以一一试试:
1. 在项目中引用另一个项目,而修改了被引用项目的代码后,引用的dll没有更新,所以出现上述错误。
2. 试图调试托管代码的转储文件。Visual Studio 仅支持使用 SOS 工具,在“即时”窗口中对托管的小型转储进行调试。
3. 在解决方案属性页中,可以更改调试器查找源文件的目录,并通知调试器忽略选定的源文件。请检查"解决方案属性页"对话框中的- >"通用属性"- >"调试源文件"- >"不查找这些源文件",看你要调试的源文件是不是在该列表中,如果在,请删除后确定。
4. 在工具- >选项- >调试中禁用源代码不可用时显示反汇编。
5. 由于代码的上下文件限制了要调试的源代码的加载(特别是在调试JavaScript代码时出现该错误时此种可能性最大)。
6. 你要调试的源文件处在一个封装好的dll文件中,所以按F11是跟不进去的。
7. 如果你有备份文件,试试你的备份文件。
8. 重写你要调试的源代码。(当然不推荐这种方法了,不过这也是最后没有办法的办法。)
我们可以尝试对源文件进行第3、4点设置, Windbg 符号的路径我们点击“选项”对话框:
我们选择“调试”->符号->勾选符号文件里面的两个选项,让它自动在微软的服务器上下载所需要的符号。
我们再选缓存符号的目录,如你自己有下载安装 Windbg Symbols 符号文件,就可以在这里直接指定到安装目录,如果没有安装就,随便指定到一个空文件夹,点击确定后,Windbg 会继续运行,在服务器上下载符号,并自动选择调试模式并进入。
我们来调出VS2013的寄存器,就可以看到驱动动态指针地址:
如果源码地址正确,我们就能在反汇编调试窗中看到指针指向和运行跳转步骤!
到这,就说明 VS2013 的部署+VM双机调试测试成功!
这里多说点废话!!
在2013中驱动项目的符号文件是不用特殊指定的,编译器会自动在刚刚勾选的微软服务器上找到符号文件并下载到指定的位置。
但我也要知道另一个方法是用命令的形式来加载或下载符号文件。
如果系统中没有单独安装符号文件,在Debugger Immediate Windows窗口中输入命令:
.sympath srv*c:\Symbols*http://msdl.microsoft.com/download/symbols
*c:\Symbols*之间的路径就是你要保存的符号文件的路径,如果你已经下载并单独安装了符号文件可以用如下形式的命令来指定:
.sympath d:\Symbols 回车后显示:
我们再 .reaload 一下(重新加载符号文件)
哈哈,这里输入命令还有智能提示哦!
部分符号文件没有找到也属于正常情况,指定目录下没有这些驱动的符号文件!可以指定到官方网站去下载符号文件
.sympath srv*c:\MyServerSymbols*[url=http://msdl.microsoft.com/download/symbols]http://msdl.microsoft.com/download/symbols
---------------------- Windgb + VM 进行双机调试---------------------
如果用上述 VS2013 自带的 Windbg 调试器无法连接,那我们就自己在网上下载 Windbg 调试器,安装在本地,并进行设定调试!
下载地址:https://msdn.microsoft.com/zh-cn/windows/hardware/hh852365/
看到了吧!Windbg 调试器已包含着驱动模块包中,我们上面开始部署 VS2013 驱动开发+VM调试时,已经安装了这个驱动包,所以,Windbg也安装在系统中了,具体位置在这:
我们把 Windbg 发送个快捷方式到桌面。
如何配置Windbg调试虚拟机呢,好吧,我就多说一些。
网上有人说,VS2013调试虚拟机中还需要安装,X:\Windows Kits\8.1\Remote\x64目录下的WDK Test Target Setup x64-x64_en-us(视虚拟机系统位数判断,32位系统请到x32下找相应文件)。真需要装吗?这样,我们先不安装试试看!下
下面调试主机的windbg选项,右键Windbg,选属性,在目标这里
"C:\Program Files\Windows Kits\8.1\Debuggers\x86\windbg.exe" -b -k com:pipe,port=\\.\pipe\com_1,baud=115200,reconnect –y
加上后面红色字的这些选项,就能连上了。
虚拟机中添加一个虚拟串口(\\.\pipe\com_1),因为虚拟机中自带的虚拟打印机,占用虚拟串口一,所以,你有两个选择,一个删除该虚拟打印机,一个,添加别的虚拟串口(例如:\\.\pipe\com_2),这里,我选择的是删除虚拟打印机
怎么添加?其实上面也已经添加过一次了,也有添加图解,如果添加过了的,这就不用重复添加了,没有添加的,可按上面的步骤来添加。
好了,打开虚拟机,和上面一样,按 F8 进入启动选项界面,选中“调试模式”按回车键:
打开主机的Windbg,就能看到连接上的信息了,默认连上之后会断下来:
在命令窗口中输入“g”回车,就能让虚拟机继续跑了。
速度有点慢,需要等一会。
我们看看 Windbg 调试器:
系统登陆中,稍等下,等系统进入桌面:
进入桌面了,那我们切换到 Windbg 调试器,按 Ctrl+Break 键,让它和虚拟机中断连接,好让我们对Windbg操作:
WinDbg是安装WinDDk时附加安装的,没有symbols文件,所以可能会要连接网络进行下载,设置符号路径目录方点击 File,选择Symbol File Path输入我们存放WinDbg符号的路径,我们可以设置在 C:\Symbols 下,所以我这样,按 “Ctrl+S” 打开设置窗体,符号路径一般有两个(可以有多个),一个是你设置保存系统DLL的PDB文件的路径,另一个是你驱动的PDB的路径,以分号隔开。这里我们只设一个,如下:
.sympath srv*c:\Symbols*http://msdl.microsoft.com/download/symbols
当然,目录可以自己随便填写,只要符号Windows 格式即可,但后面跟的微软服务器的Windbg 符号下载地址不能改!
记得勾选“Reload”,OK后稍等片刻,等WinDBG把这些东西加载完毕后再操作。
注:当我们本机缺少或没有某个symbol时从上面微软地址自动下载到C:\symbols目录中。
我们还要设置Source File Path目录(源文件)路径,也就是DriverName.cpp文件的路径。
将编译好的项目下的 xx.cpp文件拷贝到Source File Path目录路径下,这里指定路径为:C:\CommonFramework。
好了,Windbg 调试器的配置基本完成。
现在打开源文件,源文件在本地系统里。“Ctrl+O”找到驱动的源文件打开,如图:
这时,我们可以在WinDBG的命令栏输入命令来添加源文件的断点了,如在这里输入:
“bu drivername!driverentry”
表示在驱动的DriverEntry函数的入口点下一个延迟断点(大小写不限),其实BU的意思就是Set Unresolved Breakpoint,WinDBG会记住这个断点,当这个驱动被加载了并且执行到这个地方,WinDBG会暂停VM Win7系统让你进行调试操作。drivername是你的驱动名字,比如我在这里的驱动名字是myWDMDriverA,那我在这里输入的命令如下:
回车后即下好断点了。
下断点完后,我们输入“g”,回车,让VM win7系统跑起来。
在VM win7系统里运行驱动加载工具 DriverMonitor.exe 把我们的驱动加载起来open->go。
可以看到,已经断下来了,我们可以源码调试驱动了:
我们按 F11 或 F8 往下单步调试:
如图,可清晰看到指针的指向。
每一步的走向一幕了然….大功告成!!
注:打开源文件和设置符号路径可以在一开始的时候就做,这是没有严格先后顺序的。