《WIN64驱动编程基础教程.docx》由会员分享,可在线阅读,更多相关《WIN64驱动编程基础教程.docx(269页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、当确定你的硬件没有问题,那就开始配置“软”的方面。首先,你要有一台运行 WINDOWS 7 X64 的电脑,然后还要安装有 VS2010 和 WDK7。VS2010 和 WDK7 的下载地址分别如下:Ed2k:/|file|cn_visual_studio_2010_ultimate_x86_dvd_532347.iso|2685982720|4AE6228933DDE49D9BFA4C3467C831C2|/AE6850409A78/GRMWDK_EN_7600_1.ISO如果您不会选择,在安装时选择“完全安装”即可:至于驱动开发环境的配置,网上有很多的教程,本论坛的网友也发了一个“科普贴”
2、( VS 开发驱动,而是喜欢直接用记事本进行开发、输入命令进行编译。我个人用的驱动开发工具是 NOTEPAD+(官网:http:/notepad-plus- plus.org),大家可以上去下载最新版,下载并后安装完毕后,设置一下语言格式(“设置”- “语言格式设置”)。Notepad+支持代码高亮,这对驱动开发来说就足够了。NOTEPAD+有个中国人写的代码格式化的开源插件,叫做 NppAStyle。官方首页: 。下载完毕后解压缩, 把 UNICODE 版的NppAStyle.dll 复制到 C:Program Files (x86)Notepad+plugins 目录下。 点开NOTEPA
3、D+的“插件”-“NppAStyle”-“Options”,选择你想要的格式化风格(我喜欢的是 ANSI 风格)即可。要格式化的时候,按下 Alt+F 就能格式化了。(本文提到的工具或源码,除有版权问题的之外,均可以在论坛上下载。)当配置好开发环境后,就要配置测试环境了。不瞒大家说,写这章的心里压力最大,如果后面的章节没写好,大家也就是一个知识点没学会,如果这章没写好,大家肯定要骂我了。因为这章的内容是讲解如何在 WIN64 系统上玩驱动,如果没搞好,就彻底没法玩了。废话不多说,先说今天的各个主角:1. VMWARE。VMWARE 是虚拟机软件,相信这个大家都知道,因为版权的关系,我不能把它的
4、下载地址和注册码等信息放在文章里,这个可以大家去各种知名软件站点搜索。建议下载VMWARE 9.0(虽然最近已经出到了 10.0)。2. WINDBG。WINDBG 是微软出品的调试器,比起 OD 等常用调试器,就是支持内核调试。它的下载地址是: http:/www.windbg.org/X64%20Debuggers%20And%20Tools-x64_en- us.msi(不过其实大家不用下载,因为安装 WDK 时已经自带了)。3. VirtualKD。方便与进行虚拟机双机调试的工具,免去手动设置的麻烦。下载地址: http:/virtualkd.sysprogs.org接下来分别说安装虚
5、拟机、进行双机内核调试和进行本地内核调试的步骤。请大家一步一步进行操作,顺序绝对不能弄乱,否则必定失败。安装虚拟机的步骤: 1.安装 VMWARE。2. 安装 WIN7X64 虚拟机(当然你也可以把其他 WIN64 系统都安装好)。3. 在虚拟机里关闭 UAC(方便测试驱动!这步一定要做,否则后续步骤会失败)。4.在虚拟机里安装 VMware Tools。5. 在虚拟机里安装.NET FRAMEWORK4(如果是 WIN7X64 以后的系统则跳过这步)。6. 备份虚拟机的当前状态。如果不了解虚拟机的使用,请自行百度,讲解虚拟机的使用方法不是课程重点。进行双机内核调试的步骤:1. 把 Virtu
6、alKD 的 target 文件夹弄进虚拟机里。2. 在虚拟机里安装 VirtualKD(打开 VirtualKD 目录下的 target 文件夹,以管理员权限运行 vminstall.exe)。【见下图】3. 在真机运行 VMMON64.exe,设置调试器路径。点击 VMMON 界面下面的Debugger path按钮,选择 WINDBG 路径。一般路径是:C:WinDDK7600.16385.1Debuggerswindbg.exe。【见下图】4. 重启虚拟机,进入这个内核启动项。【见下图】5. 进入不久之后,WINDBG 会自动启动(实际上是 VMMON.EXE 启动的),出现类似这样的
7、画面(见下图)。按下 F5,继续让系统运行。6. 当虚拟机进入系统后,在真机 WINDBG 获得焦点的时候,按下 Ctrl+Break,记得随时调试虚拟机。按下 Ctrl+Break 后,会出现类似的画面:7. 这个时候先不急着调试,先在真机安装符号包。WINDOWS 系统符号的官方下载地址是: WIN7X64,所以去页面上选择Windows 7 RTM x64 retail symbols, all languages或Windows 7 Service Pack 1 x64 retail symbols, all languages下载(根据你安装了 WIN7X64SP1 还是 WIN7X
8、64RTM)。下载完毕后安装。安装过程就是一路 NEXT,记得在安装过程中选择把符号安装到 c:symbols,否则会影响后面的符号加载!8. 设 置WINDBG的 符 号 。 点 击File-SymbolFilePath , 输 入【SRV*C:Symbols* Reload 复选框,WINDBG 则会自动重载符号。9. 让 WINDBG 重载符号。在 WINDBG 的命令行上输入.reload 即可(注意 reload 前面有个点)。10. 测试 WINDBG 是否加载符号成功。输入 u KiInsertQueueApc,如果出现类似以下的结果, 就证明今天的课程学完了!如果没有,请上论坛
9、提问或反馈问题。进行本地内核调试的步骤:1. 开启 CMD,输入 bcdedit debug on。2. 重启计算机。3. 打开 WINDBG,按下 Ctrl+K,选择 Local,按下确定。4. 设置调试符号路径(参照前面的方法)。配置好驱动测试环境后,就可以正式编写驱动了。市面上讲解驱动开发的书籍汗牛充栋, 但讲得较为太复杂,让初学者不好理解。本文从一个简单的 hello,world 驱动(驱动模板) 讲起,力求讲解得简单明了,让大家好理解。本文主角:1. DbgView。DbgView 是查看程序调试输出的工具,由美国高富帅 Mark Russinovich 编写(不得不说,此人长得帅,
10、编程技术又牛,让多少男人羡慕妒忌,让多少女人一见倾心)。下 载 地 址 : 2.KmdMgr。KmdMgr 是一个由俄国人编写的驱动加载工具。比起国内那些乱七八糟的驱动加载工具,它的特点是可以与驱动进行通信(虽然无法设置 I/O 缓冲区)。下载地址: mat=raw&rev=13. WIN64AST。作者自行开发的 64 位 ARK 类工具。在本章中用来查看驱动是否加载成功。在后续章节还有其他的用途。下载地址:。4. WIN64UDL。作者自行开发的驱动加载工具,能在正常模式下加载没有签名的驱动。因为这个工具,被人举报滥用签名,最终导致价值 15000 人民币的数字签名被吊销。下载地址: 编写
11、驱动:/【0】包含的头文件,可以加入系统或自己定义的头文件#include #include #include /【1】定义符号链接,一般来说修改为驱动的名字即可#define DEVICE_NAMELDeviceKrnlHW64#define LINK_NAMELDosDevicesKrnlHW64#define LINK_GLOBAL_NAMELDosDevicesGlobalKrnlHW64/【2】定义驱动功能号和名字,提供接口给应用程序调用#define IOCTL_IO_TESTCTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED,
12、 FILE_ANY_ACCESS)#define IOCTL_SAY_HELLOCTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)/【3】驱动卸载的处理例程VOID DriverUnload(PDRIVER_OBJECT pDriverObj)UNICODE_STRING strLink; DbgPrint(KrnlHW64DriverUnloadn); RtlInitUnicodeString(&strLink, LINK_NAME); IoDeleteSymbolicLink(&strLink);以下
13、是一个我写的 WIN64 驱动模板(代码中已经加了详细的注释,完整工程文件可以在论坛上下载):IoDeleteDevice(pDriverObj-DeviceObject);/【4】IRP_MJ_CREATE对应的处理例程,一般不用管它NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)DbgPrint(KrnlHW64DispatchCreaten); pIrp-IoStatus.Status = STATUS_SUCCESS; pIrp-IoStatus.Information = 0; IoCompleteRequest(p
14、Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;/【5】IRP_MJ_CLOSE对应的处理例程,一般不用管它NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)DbgPrint(KrnlHW64DispatchClosen); pIrp-IoStatus.Status = STATUS_SUCCESS; pIrp-IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_SUCCESS;
15、/【6】IRP_MJ_DEVICE_CONTROL对应的处理例程,驱动最重要的函数之一,一般走正常途径调 用驱动功能的程序,都会经过这个函数NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;PIO_STACK_LOCATION pIrpStack; ULONG uIoControlCode;PVOID pIoBuffer; ULONG uInSize; ULONG uOutSize;DbgPrint(KrnlHW64DispatchIo
16、ctln); pIrpStack = IoGetCurrentIrpStackLocation(pIrp);/控制码uIoControlCode = pIrpStack-Parameters.DeviceIoControl.IoControlCode;/输入输出缓冲区pIoBuffer = pIrp-AssociatedIrp.SystemBuffer;/输入区域大小uInSize = pIrpStack-Parameters.DeviceIoControl.InputBufferLength;/输出区域大小uOutSize = pIrpStack-Parameters.DeviceIoCon
17、trol.OutputBufferLength; switch(uIoControlCode)/在这里加入接口 case IOCTL_IO_TEST: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(KrnlHW64IOCT
18、L_SAY_HELLOn); status = STATUS_SUCCESS;break;if(status = STATUS_SUCCESS)pIrp-IoStatus.Information = uOutSize;elsepIrp-IoStatus.Information = 0; pIrp-IoStatus.Status = status;IoCompleteRequest(pIrp, IO_NO_INCREMENT); return status;/【7】驱动加载的处理例程,里面进行了驱动的初始化工作NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver
19、Obj, PUNICODE_STRING pRegistryString)NTSTATUS status = STATUS_SUCCESS;UNICODE_STRING ustrLinkName; UNICODE_STRING ustrDevName; PDEVICE_OBJECT pDevObj;/初始化驱动例程pDriverObj-MajorFunctionIRP_MJ_CREATE = DispatchCreate; pDriverObj-MajorFunctionIRP_MJ_CLOSE = DispatchClose; pDriverObj-MajorFunctionIRP_MJ_D
20、EVICE_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)RtlInitU
21、nicodeString(&ustrLinkName, LINK_GLOBAL_NAME);elseRtlInitUnicodeString(&ustrLinkName, LINK_NAME);/创建符号链接status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName); if(!NT_SUCCESS(status)IoDeleteDevice(pDevObj); return status;/走到这里驱动实际上已经初始化完成,下面添加的是功能初始化的代码DbgPrint(KrnlHW64DriverEntryn);return STATU
22、S_SUCCESS;如果你懒得认真看完上面的代码,也没问题,我总结几句:1.DriverEntry 就是驱动的main 函数,驱动加载后会从 DriverEntry 开始执行。2.驱动类似 DLL,可以提供接口给应用程序调用,不过以导出函数的方式,而是用一套专门的通信函数 DeviceIoControl。关于应用程序与驱动程序通信,后面会讲。编译驱动:1. 打开x64 Free Build Environment:2. 切换到源码目录(假设源码目录是:z:sys),并输入 BUILD 编译:3. 如果看到1 executable built字眼,则证明编译成功。4. 驱动的编译跟目录下的 so
23、urce 文件有关系,比如本例中,它的内容如下(注意不要手贱把空行去掉了,否则可能会导致无法编译):TARGETNAME=KrnlHW64-驱动的文件的名称,一般来说修改这个就行了TARGETTYPE=DRIVER-编译的类型TARGETPATH=obj INCLUDES=.SOURCES = MyDriver.c创建驱动服务(获得服务句柄,如果服务已经存在,此步则变成打开服务)-启动服务-停止服务-移除服务-关闭服务句柄-关闭 SCM 句柄。如果要与驱动通信,则用 CreateFile 打开驱动的符号链接(可以理解成获得一个“通信句柄”),然后使用 DeviceIoControl 与驱动进行
24、信息交互。如果曾经打开过驱动的符号链接,则必须在卸载驱动前关闭“通信句柄”,否则再次加载相同的驱动时会有一些麻烦。DeviceIoControl 的参数也很好理解,只有 5 个重要参数:控制码,输入缓冲区,输入长度,输出缓冲区,输出长度。但这个控制码(以下简称 IOCTL)则是大有玄机的,这里则必须讲清楚。仔细看回 KrnlHW64.sys 的代码,里面关于 IOCTL 的定义是这么写的:#define IOCTL_IO_TESTCTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)#define IOCTL
25、_SAY_HELLOCTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED,FILE_ANY_ACCESS)这里面有一个宏,叫做 CTL_CODE,它用函数来表示就是: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
26、_ACCESS * 16384) | (lngFunction * 4)| METHOD_BUFFERED;之所以要这么计算,是因为这个 32 位的 IOCTL 的每一位都有不同的含义(IOCTL 每一位的具体含义如下图所示)。如果不遵守这个规则,随意指派控制码,那么在与驱动进行通信时将会蓝屏。为了方便大家,我已经把这套东西整合成了一个类。#include #pragma comment(lib,”advapi32.lib”)class cDrvCtrlpublic:cDrvCtrl()/初始化各个变量m_pSysPath = NULL; m_pServiceName = NULL; m_pD
27、isplayName = 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;
28、/显示名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, D
29、WORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen); /IO控制;/打开已经存在的服务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
30、(m_hSCManager,m_pServiceName,SERVICE_ALL_ACCESS); if (NULL = m_hService)CloseServiceHandle(m_hSCManager); return FALSE;elsereturn TRUE;/安装服务服务BOOL cDrvCtrl:Install(PCHAR pSysPath,PCHAR pServiceName,PCHAR pDisplayName)m_pSysPath = pSysPath; m_pServiceName = pServiceName; m_pDisplayName = pDisplayName
31、;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,N
32、ULL,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;elseCloseServiceHandle(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_dwLastErro