Windows Driver 初探

再毕设的压力下,学习了一下驱动开发,下文部分为微软官方文档的翻译,仅当成驱动开发小白摸索。。大佬可绕路


Windows驱动开发初探

开发背景

主要是为了实现一个 当创建新的进程的时候,获取其创建时的函数调用链 这个方案。(迫于时间,最后直接HOOK函数CreateProcess,然后利用了AppCertDLLS的简单思路。。。 。然后就开始自己瞎找,找到了一个利用驱动实现检测的思路。

关键函数

驱动中有一个函数叫做:PsSetCreateProcessNotifyRoutine。这个函数原型

1
2
3
4
NTSTATUS PsSetCreateProcessNotifyRoutine( 
IN PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
IN BOOLEAN Remove
);

这个函数能够再我们创建进程/删除进程的时候,调用回调函数NotifyRoutine

1
2
3
4
5
6
7
PCREATE_PROCESS_NOTIFY_ROUTINE PcreateProcessNotifyRoutine;

void PcreateProcessNotifyRoutine(
HANDLE ParentId,
HANDLE ProcessId,
BOOLEAN Create
)
  • ParentId: 父进程ID
  • ProcessId: 子进程ID
  • Create: 当前进行的是创建还是删除工作

利用这个函数,我们就能够直接检测每一个进程的创建过程,感觉简单了很多。

开发准备

  • Visual Studio 2017
  • 驱动开发的库不是自带的,得自己去官网上下:WDK

安装好 WDK 之后,我们就能够在VS里面直接看到开发界面找到驱动开发界面:
!()[Windows-Driver-初探/driver00.png]
驱动主要分为两种:

  • Kernel-Mode Driver
  • User-Mode Driver

由于教程上Kernel-Mode Driver的教程比较多,这里选择用这个进行入门。

PS:驱动起名字的时候注意,不要超过32个字符,这个是规定

简单入门

首先选择对应版本:

之后,我们修改当前的项目属性,在配置中的Driver Settings -> General 中可以配置我们要发布的驱动对应的平台。

然后再项目处添加一个叫做Driver.c的文件

这个位置的 Driver.c 注意不要用cpp做后缀,似乎和编译器有关

然后键入下列代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 原先使用的是MSDN上提供的源代码,但是编译后运行的时候,一直提示说“找不到设备”,所以只能够
// 改成了网路上常见的Hello World 形式

#include<ntddk.h>

VOID DriverUnload(PDRIVER_OBJECT driver){
DbpPrint("[-] Unload Driver\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING path){
DbgPrint("[+] Driver Loading!\n");

driver->DriverUnload = DriverUnload;
return NTSTATUS_SUCCESS;
}

编写完成后,我们修改一下我们工程的配置(为了准备发布),求改模式为DEBUG,并且发布设置为x64

并且不使用Wpp Tracing

之后来到对应文件目录下(x64\Debug\KmdfHelloWorld)能够找到以下文件

  • KmdfHelloWorld.sys: 这个就是kernel-mode的驱动文件
  • KmdfHelloWorld.inf: 里面记录当sys文件安装的时候需要的信息
  • KmdfHelloWorld.cat: 这是一个目录文件,记录了安装程序用来验证驱动程序包的测试签名

测试入门

驱动要运行起来没有别的程序那么简单,得需要另一台独立的机子才能够运行。我们一般把运行调试器的机子叫做host computer,用于运行驱动的机子叫做target computer

配置流程

这里我们用Vmware来辅助测试。首先我们装好了一个64bit 的 Windows7。为了实现联机调试,我们要给当前虚拟机添加串口设备:

  • 右键当前虚拟机,点击设备,然后位置添加串口设备
  • 在串口设备中,选择使用命名的管道,添加管道\\.\pipe\DriverDbgPipe
  • 注意将命名管道下面的内容选择为:该端为服务器,另一端为应用程序

最终配置情况如下:

Windows中管道的命名规则必须为: \\.\pipe\

然后在管理员权限下,执行以下语句:

1
2
3
4
bcdedit /set {current} debug yes
bcdedit /set {current} debugtype serial
bcdedit /set {current} debugport 2
bcdedit /set {current} baudrate 115200

这里的debugport用于指定我们的COM口。我们创建设备的时候,写作串口设备2,所以这里设置为2。

之后使用windbg,加上下列参数即可进行调试

1
.\windbg.exe -b -k com:pipe,port=\\.\pipe\DriverWinDbg,resets=0,reconnect

Visual Studio 配置

这里使用的是VS2017(后来改成VS2015,配置一致)

  1. 首先在 host computer 上找到文件WDK Test Target Setup x86-x86_en-us.msi
  2. 然后 target computer 中安装这个文件。
  3. 在 Visual Studio 中打开Driver->Test->Configure Devices
  4. Add New Device 添加测试主机,由于我们之前填写debug用的是Serial(串口),那么这里我们也继续使用串口作为调试。首先我们配置主机的IP等如下:

    我这边自动安装总是失败,所以这边打算利用VS单纯来调试,安装驱动的时候使用InstDrv
  5. 下一个界面确认如下:
  6. 最后,如果我们看到这个画面,就说明我们配置成功:

之后我们就能够拿VS来调试啦!

动态调试

  1. 首先在调试中找到附加到进程
  2. 连接类型调整成Windows Kernel Mode Debugger,连接目标调整成我们的主机,然后选择内核进程:
  3. 连接之后,会进入如下画面,此时我们按全部中断,让整个内核进程停下来
  4. 如果之前配置没错误的话,就能够挂载进行内核调试。(找不到成功的图了)

然后我们利用工具InstDrv进行驱动安装,安装好之后启动服务,就能够下调试驱动啦!

PS:用VS调试的时候,可以直接在源代码中下断点进行调试,不过要保证

  • 发布的是DEBUG版本的
  • 当我们修改了任何源代的内容,都要重新发布并且重新安装。

踩坑

  1. 如果要实现VS远程调试的话,一定要打开
1
bcdedit /debug on

设置完之后,一定要重启,最好是关闭虚拟机再打开。
(上面也提过别的打开方式,但是一定要打开,否则windbg会一直处于reconnect wait...的状态)

  1. 同样,为了实现VS远程调试,一定要安装:
    WDK Test Target Setup x86-x86_en-us.msi
    或者
    WDK Test Target Setup x64-x64_en-us.msi
    否则的话配置HOST那部分无法通过。

  2. 调试的时候,对于驱动,调试的时候如果要替换当前的驱动,一定要记得卸载,不然的话会卡死

  3. 之前用VS2017的时候,不知原因一直爆炸。。改成VS2015之后突然可以了。。。理解不能

  4. 如果要用VS下断点,则此时的调试顺序为:

  • 使用VS连接上target 主机
  • 当前 Target 载入驱动

如果先载入了驱动,再让VS连上target,此时下载vs里面的断点(指下在代码上的那种)会断不下来。(估计原因是因为载入了驱动后,VS会找不到符号所以无法调试)

  1. 如果调试内核的时候下了断点(包括用了windbg的那个暂停按钮),需要再调试结束的时候将断点删除,否则的话会造成电脑重新启动的时候显示驱动损坏。原因是因为下断点的时候,一般用的是软中断,也就是会将当前指令替换成0xcc的一种断点。因为内核调试的时候一般退出调试会直接关闭调试模式等,导致此断点的软中断中的指令没有被替换会原来的指令,导致驱动加载错误。
  2. 有时候,如果target computer打开了防火墙,也会导致无法连接(显示waiting to reconnect