加载中...

驱动开发入门 – 之二:Win7-x64 + VMWare (Win7-x64) + WinDbg 双机调试环境搭建


1. 概述

1.1. 前言

适读人群:具备良好的C/C++开发经验,一定的逆向工程基础。

前置阅读《驱动开发入门 - 之一 :Win7 SP1 x64 驱动开发环境搭建》

为方便起见,在下文中使用【HostOS】指代【物理机/宿主机/调试机】,用【GuestOS】指代【虚拟机/客户机/被调试机】

WinDbg是一款双机调试工具,它安装在HostOS上,HostOS与GuestOS通过串口相连接,使用时需要在HostOS用WinDbg调试GuestOS上运行的内核程序。

由于使用两台物理机做双机调试的成本较高、可操作性较低,因此本文双机调试的环境是基于物理机(HostOS)与VMWare虚拟机(GuestOS)搭建的

之所以使用虚拟机搭建双机调试环境,主要出于以下几个方面考虑:

  • ① 驱动程序运行在系统内核,在调测时需要把操作系统设置到测试模式,极大降低安全性。
  • ② 经验不足的情况下编写内核程序很容易导致系统蓝屏,在HostOS调试风险过大。
  • ③ HostOS无法调试自己。到VS2010为止的IDE均不支持驱动程序的调试功能,只能在操作系统中加载驱动时调试,而加载驱动程序的操作系统是无法调试自身的(遇到INT3中断的时候整个系统会进入假死状态),因此需要借助GuestOS。

1.2. 名词解析

名词 英文原文 解析
HostOS Host Operating System 物理机/宿主机/调试机
GuestOS Guest Operating System 虚拟机/客户机/被调试机
COM Cluster Communication Port 串行通讯端口
KD Kernel Debug 内核调试

1.3 WinDbg简介

——部分内容摘于博文:http://blog.csdn.net/keidoekd2345/article/details/50125747

在安装微软Windows调试工具集后,可以在安装目录下发现四个调试器程序,分别是:cdb、ntsd、kd和WinDbg。

其中cdb和ntsd只能调试用户程序,kd主要用于内核调试(有时候也用于用户态调试)。这三者的一个共同特点是都只有控制台界面,以命令行形式工作。

而WinDbg在用户态、内核态下都能够发挥调试功能,特别地,它采用了可视化的用户界面。所以绝大部分情况下,我们在谈及Windows调试工具时都直接指向WinDbg,而不谈及其他三者。

WinDbg支持源码级的调试(类似于VC自带的调试器),而且在用户态和内核态下,都支持两种调试模式,即“实时调试模式(Living)”和“事后调试模式(Postmortem)”。

实时调试模式(Living):是被调试的目标对象(Target)当前正在运行当中,调试器可以实时分析、修改被调试目标的状态,如寄存器、内存、变量,调试exe可执行程序或双机实时调试都属于这种模式。

事后调试模式(Postmortem):是被调试的目标对象(Target)已经结束了,现在只是事后对它保留的快照进行分析,这个快照称为转储文件(Dump文件)。

2. 操作系统与预装组件

  • ① HostOS系统版本: Win7 SP1 x64 (必须升级到SP1版本)
  • ② GuestOS系统版本: Win7 SP1 x64 (必须升级到SP1版本)
  • ③ 在HostOS安装VS2010驱动开发环境的相关组件(详见《驱动开发入门 - 之一》,此处不再复述),之所以选择VS2010环境,是因为VS2008在x64环境下的混合汇编能力较弱,不便于后续的开发调试工作

3. 相关工具安装

表 1 工具清单
目标机器 工具 版本 用途 备注 下载
HostOS VS2010驱动开发
环境的相关组件
HostOS VMWare 11.1.2 安装GuestOS
的虚拟机
用于搭建隔离环境
调试驱动程序
 
HostOS WinDbg 6.11 Windows平台下的
驱动程序调试工具
用于配合GuestOS
双机调试驱动程序
官方地址
CSDN
HostOS windbg-双机调试.bat 6.11 使得windbg连接到
GuestOS的启动脚本
配合WinDbg使用,
已固化启动参数
CSDN
HostOS Win7符号文件 Win7
SP1x64
WinDbg调试代码用 相当于Win系统内核
程序的源码文件
官方地址
GuestOS 开启win测试环境.bat
关闭win测试环境.bat
调测环境变更脚本 把操作系统永久切换到测试
模式并关闭驱动签名校验
CSDN
GuestOS DriverMonitor 3.2.0 驱动程序装载器 XP x86用于安装、启动内核
程序的调试工具,
Win7 x64可能不兼容
CSDN
GuestOS InstDrv 1.3.0 驱动程序装载器 Win7用于安装、启动
内核程序的调试工具
CSDN
GuestOS DebugView 4.81 内核驱动消息捕获器 配合DriverMonitor或InstDrv
使用,用于捕获内核程序的
DbgPrint / KdPrint语句
所打印消息
CSDN
HostOS
GuestOS
VirtualKD 3.0 内核调试加速器 配合WinDbg使用,提高
HostOS与GuestOS传输速率
官方地址

4. 安装VMware虚拟机及GuestOS

由于VMWare11之后的虚拟机和系统安装都比较简单,此处就不详述了,仅说明一下步骤:

  • ① 在HostOS安装VMWare虚拟机
  • ② 在虚拟机安装Win7 x64 SP1操作系统(GuestOS)
  • ③ 为了便于HostOS与GuestOS的文件交互,安装VMWare Tools(VMWare 已自带:【菜单】 –> 【虚拟机】 –> 【安装VMWare Tools】)
  • ④ 在GuestOS中执行【开启Windows测试环境.bat】脚本使GuestOS关闭驱动签名校验,并进入测试模式
  • ⑤ 在GuestOS中安装 DriverMonitor / InstDrv 和 DebugView

【注意】
若第 ④ 步的脚本下载地址已失效,可手动在GuestOS的DOS控制台输入以下命令:
【开启Windows测试环境.bat】脚本主要是两个指令:
bcdedit -set loadoptions DDISABLE_INTEGRITY_CHECKS // 关闭驱动数字签名校验
bcdedit /set testsigning on // 开启系统测试模式
 
【关闭Windows测试环境.bat】脚本是配套的两个还原指令:
bcdedit -set loadoptions DENABLE_INTEGRITY_CHECKS // 启动驱动数字签名校验
bcdedit /set testsigning off // 关闭系统测试模式

5. 配置VMWare的虚拟管道串口

当双机都是物理机时,HostOS与GuestOS是用物理串口连接的。

但是在GuestOS是虚拟机的情况下,就不可能使用物理串口了,此时需要在GuestOS上设置一个用虚拟的管道串口,步骤如下:

① 在虚拟机关机状态下,选择【编辑虚拟机设置】:

编辑虚拟机设置

② 在【硬件】选项卡【移除打印机】:

这是因为打印机默认占用了串口COM_1,为了使得下文的配置无需修改,这里建议删除打印机(不删除打印机也可以,但后面的配置请自行修改为COM_2作为管道串口命名)

移除打印机

③ 添加一个串行端口:【添加】 –> 【串行端口】 –> 【下一步】 –> 【输出到命名管道】 –> 【下一步】:

添加串行端口

设置为管道串口

④ 设置如下值,点击【完成】按钮。

命名管道:\.\pipe\com_1 (会自动填上编号最小的可用串口)

  • 【该端是服务器。】
  • 【另一端是应用程序。】
  • 【勾上】启动时连接。

设置虚拟管道串口参数

⑤ 最后回到如下所示的界面,选中刚才新建的【串行端口】,在I/O模式【勾选】轮询时主动放弃 CPU(Y),点击【确定】即可。

设置串口IO模式

6. 在GuestOS增设调试模式的操作系统

首先区分GuestOS中操作系统的几种模式:

  • 正常模式:初装操作系统时的默认状态
  • 测试模式:利用bcdedit命令打开系统的TestSigning开关,使系统处于可以装载未认证驱动程序的状态(若已按上文所述操作,当前的GuestOS已处于此状态)
  • 调试模式:利用bcdedit命令打开系统的debug和bootdebug 开关,使系统上运行的程序处于可以被另一个系统调试的状态

本节的最终目标就是在 [测试模式] 的基础上再增加 [调试模式],更具体地描述,就是在开机的操作系统列表中新增一个被标识为【启用调试程序】的操作系统(其中“Win-7双击调试模式”是自定义的系统名称,“启用调试程序”表示该系统处于调试模式

在XP时代,通过修改C:\boot.ini配置文件可以实现此目的。但Win7系统已经没有这个文件了,需要通过bcdedit命令进行配置。

系统列表

详细的配置方式如下:

① 虚拟机开机后,正常登陆到Win7系统桌面(若已按照上文配置,当前所登陆到的Win7系统正处于【测试模式】,可以通过桌面右下角的水印验证。某些系统会屏蔽这个水印,此时可以通过命令【bcdedit /enum】确认testsigning的值是否为Yes以判断系统当前是否处于测试模式)。

测试模式水印

② 以管理员身份运行DOS控制台在控制台中依次输入以下命令:

bcdedit /copy {current} /d "Win7-双机调试模式"    // 这是系统副本的名字,任意即可
bcdedit /timeout 10

这两条命令的作用是把当前所登陆的操作系统,复制一份副本,命名为【Win7-双机调试模式】。新的系统会出现在开机时的操作系统列表中,该列表的呈现时间为10秒。

使用管理员运行DOS

复制当前操作系统

③ 重启GuestOS,此时可以看到在操作系统列表中多出第一项【Win7-双机调试模式】,这就是刚才复制的系统副本。选择这个系统登陆到桌面。

操作系统列表

④ 以管理员身份运行DOS控制台,在控制台中依次输入以下命令使得系统进入调试模式:

bcdedit /debug ON
bcdedit /bootdebug ON

再输入以下命令可查看操作系统在调试模式下使用的串口配置:

bcdedit /dbgsettings

如下图所示为调试模式的配置参数,其中【debugtype=Serial】表示使用串口做调试,【debugport=1】表示串口端口为COM_1,【baudrate=115200】为波特率,表示HostOS与GuestOS通过此虚拟串口交换数据的传输速率(约等效于10KB/s,暂时保持默认值即可)。

设置当前系统为调试模式

这里需要注意的是,若上文小节【5 配置VMWare的虚拟的管道串口】中所配置的串口不是COM_1,这里可以使用以下命令 修改串口号等 调试模式的参数:

bcdedit /dbgsettings serial debugport:1 baudrate:115200

⑤ 到此就已经成功添加了一个调试模式的操作系统了,重启后可在系列列表中看见【Win-7双击调试模式 [启用调试程序]】,这个就是之后要用来调试驱动用的系统。

操作系统列表

⑥ 为了便于之后开机后都处于调试模式,可以通过右键【计算机】 –> 【属性】 –> 【高级系统设置】 –> 【高级】 –> 【启动和故障恢复】 –> 【设置】 修改默认的操作系统、以及操作系统列表的显示时间:

修改默认的操作系统

7. WinDbg的安装与配置

只有HostOS需要安装WinDbg,步骤如下:

  • ① 安装dbg_x86_6.11.1.404.msi到HostOS任意位置
  • ② 复制windbg_cn.exe到安装目录(此为汉化界面入口)
  • ③ 复制 [windbg_cn-双机调试.bat] 和 [windbg-双机调试.bat] 脚本到安装目录(这两个脚本功能相同,区别只是一个汉化版、一个英文版)
  • ④ 根据上文设置的虚拟管道串口,右键编辑bat脚本的启动参数(若使用COM_1串口则无需修改):

start “” windbg_cn.exe -b -k com:pipe,port=\.\pipe\com_1,baud=115200,reconnect -y   // 中文脚本
start “” windbg.exe -b -k com:pipe,port=\.\pipe\com_1,baud=115200,reconnect -y   // 英文脚本

8. 验证WinDbg配置(连接GuestOS)

  • ① 启动虚拟机,进入【win7-双机调试模式 [启动调试程序]】系统(注:调试模式的系统在启动过程有两个系统断点,若不通过WinDbg跳过断点,系统会陷入中断状态无法进入桌面)。
  • ② 运行 [windbg_cn-双机调试.bat] 脚本,若脚本配置的启动参数与GuestOS设置的虚拟管道串口参数一致,则可以明显看到宿主系统和虚拟系统连接成功的提示。
  • ③ 在系统启动过程中,会出现多次【int 3】系统中断,需要在WinDbg的【kd>】中输入命令【g】以跳过中断,直到登陆到桌面。
  • ④ 成功进入桌面后,就可以开始使用WinDbg做双机调试。

WinDbg连接GuestOS

9. 双机调试

9.1. 什么是符号文件

类似于在C/Java的IDE中调试源码,WinDbg在调试驱动程序时也需要用到源码,在Windows中把这种源码称之为符号文件(Symbol Files)

符号文件是一个数据信息文件,以*.pdb扩展名标识。它包含了应用程序二进制文件(如*.sys、*.dll、*.exe)的调试信息(如程序中所有变量信息),专用于调试。符号文件与二进制文件的编译版本密切,当二进制文件被重新编译时,上次编译所产生的pdb文件就过时了,不能再用于调试工作。

用 Visual C++ 和 WinDbg 调试程序时都要用到符号文件,但最终生成的可执行文件在运行时并不需要符号文件。

例如每个 Windows 操作系统下有一个 GDI32.dll 文件,编译器在编译该 DLL 的时候会产生一个 GDI32.pdb 文件。一旦我们拥有了这个GDI32.pdb文件,那么便可以用它来调试并跟踪到 GDI32.dll 内部。

9.2. 符号文件路径设置

WinDbg是支持在调试时自动在线下载对应符号文件的。但是符号文件内容也不少(某些符号文件在线下载需要几分钟,不便于调试),建议把离线安装包下载到本地先预安装。

这个是微软提供的各个Windows版本的符号路径下载的站点:

https://developer.microsoft.com/en-us/windows/hardware/download-symbols

以本文所采用的操作系统版本为例,选择【Windows 7 Service Pack 1 x64 retail symbols, all languages】下载:

选择合适的符号文件版本

下载成功后,将其安装到HostOS的任意位置即可。

本文所选择的符号文件安装目录为:E:\04_work\re\Symbol

安装成功后,配置环境变量:

  • ① 右击【计算机】–>【属性】–>【高级系统设置】–>【高级选项卡】–>【环境变量】
  • ② 在【系统变量】中新建四个变量:
变量名 变量值 备注
_NT_SYMBOL_DIR E:\04_work\re\Symbol 符号文件安装目录
_NT_SYMBOL_PATH %_NT_SYMBOL_DIR%;symsrv*symsrv.dll*
%_NT_SYMBOL_DIR%*http://msdl.microsoft.com/download/symbols
符号文件检索位置
_NT_SOURCE_PATH 根据实际情况按需配置(可不配) 编译驱动生成的符号文件目录
_NT_ALT_SYMBOL_PATH 根据实际情况按需配置(可不配) 其他符号文件目录

注:
在环境变量 _NT_SYMBOL_PATH 指定了两个路径:第一个是本地符号文件安装目录,第二个是在线符号文件站点。 它表示如果在本地安装目录%_NT_SYMBOL_DIR%下找不到所需的Symbol File,就从微软的Symbol Server上下载并保存到%_NT_SYMBOL_DIR%目录。

重新运行WinDbg连接到GuestOS,从控制台的连接信息可以看到WinDbg已成功找到符号文件路径位置。点击【文件】 –> 【符号文件路径】 可以发现系统变量 _NT_SYMBOL_PATH 所指定的符号文件路径已被加载到WinDbg:

符号文件路径设置

9.3. 调试一个驱动程序

以之前的VS2010_WDK_Demo驱动项目为例(该项目通过EasySYSchs生成,已生成模板代码,编译后可直接被系统加载运行,详见《驱动开发入门 - 之一》,此处不再复述)。

在VS2010_WDK_Demo.cpp中找到入口函数DriverEntry,使用DbgPrint函数打印一句内核调试消息:

添加内核调试消息语句

重新编译并签名VS2010_WDK_Demo驱动项目,拷贝VS2010_WDK_Demo.sys到GuestOS中,并使用DriverMonitor / InstDrv装载并启动该驱动,则可以在WinDbg中捕获到刚才添加的内核消息“Hello, Driver By VS2010”:

调试一个简单的驱动程序

9.4. 加入断点调试

由于驱动程序中没有断点,驱动程序被装载后转瞬就运行完了。

为了便于调试,这次在驱动程序的入口函数DriverEntry中加入INT 3系统中断的汇编代码(此处只演示效果,暂不说明如何在x64系统中嵌入汇编代码,若有需要了解可见附录):

添加系统中断

重新编译并签名VS2010_WDK_Demo驱动项目。

把HostOS编译目录中生成的符号文件VS2010_WDK_Demo.pdb设置到WinDbg中(【文件】 –> 【源文件路径】),使得WinDbg可以在调试过程中查看驱动程序的源码。

然后拷贝驱动程序VS2010_WDK_Demo.sys到GuestOS中,签名后使用DriverMonitor / InstDrv装载并启动该驱动,此时GuestOS因为系统中断会陷入假死状态,按【Ctrl+Alt】释放鼠标到HostOS,发现WinDbg已运行到断点位置并暂停。

WinDbg上方的工具栏提供了对驱动程序做调试的按钮,左侧为源码区,右侧则是控制台,如下所示为单步调试的过程。

熟悉WinDbg的使用后,其他驱动程序的调试方式也是大同小异,此处就不再一一叙述了。

设置源文件路径

驱动程序陷入中断

驱动程序调试

10. 附录1:利用VirtualKD提高调试的传输速率

在调试的过程中可能会发现WinDbg经常会不断打印一堆意义不明XML stream消息刷屏,在打印期间WinDbg与GuestOS均处于无法操作的假死状态

SXS.DLL: Read 0 bytes from XML stream; HRESULT returned = 0x00000001
SXS.DLL: Creating 8828 byte file mapping
C:\Users\Administrator\Desktop\DriverGenius\security\kxescan\kfc_dps.datSXS.DLL: Read 756 bytes from XML stream; HRESULT returned = 0x00000000
 00000000: 3c-3f-78-6d-6c-20-76-65-72-73-69-6f-6e-3d-22-31 (<?xml version="1)
 00000010: 2e-30-22-20-65-6e-63-6f-64-69-6e-67-3d-22-55-54 (.0" encoding="UT)
 00000020: 46-2d-38-22-20-73-74-61-6e-64-61-6c-6f-6e-65-3d (F-8" standalone=)
 00000030: 22-79-65-73-22-3f-3e-0d-0a-3c-21-2d-2d-20-43-6f ("yes"?>..<!-- Co)
 00000040: 70-79-72-69-67-68-74-20-28-63-29-20-4d-69-63-72 (pyright (c) Micr)
 00000050: 6f-73-6f-66-74-20-43-6f-72-70-6f-72-61-74-69-6f (osoft Corporatio)
 00000060: 6e-20-2d-2d-3e-0d-0a-3c-61-73-73-65-6d-62-6c-79 (n -->..<!assembly)

HostOS与GuestOS交互数据刷屏

这种消息实际上是HostOS与GuestOS在交互数据,当在GuestOS打开某些进程时就可能会触发。当需要交互的数据量很大的时候,就会造成WinDbg与GuestOS双双陷入假死状态,直到数据交互完成。那么要解决假死状态的方法自然就是想办法使得HostOS与GuestOS尽快完成数据交互。

在前面配置虚拟管道串口的时候,有一个波特率参数,默认值就是【baudrate=115200】,该值直接制约了HostOS与GuestOS交互数据的传输速率(115200 baudrate等效于115200 bit per secon,约为10KB/s)。但是串口波特率的取值范围为300、600、1200、2400、4800、9600、19200、38400、43000、56000、57600、115200,可以发现默认值已经是最大值了,无法再上调波特率。

为此需要借助MS提供的另一个工具VirtualKD:其原理是先在HostOS与GuestOS预装后门进程,然后利用KD的扩展DLL功能向GuestOS附加到这个进程产生一条虚拟管道,WinDbg可以借助该管道在HostOS与GuestOS之间进行调试。相比于使用串口,VirtualKD的数据交互速率有极大的提升(微软官方测试速率最高为6MB/S,即使在VMWare平台也可达到150KB/s)。

VirtualKD的安装和使用都比较简单,具体步骤如下:

  • ① 把从官方下载的VirtualKD.exe在HostOS与GuestOS中分别拷贝一份。
  • ② 分别在HostOS与GuestOS中运行VirtualKD.exe解压
  • ③ 在GuestOS中运行解压目录中的 \target\vminstall.exe,由于是非Win10系统,必须取消勾选【Replace kdcom.dll】,其他参数可以不修改,点击【install】按钮安装,安装成功后会提示重启
  • ④ 重启后在操作系统列表会新增一个系统副本,副本名称就是刚才定义的名称,使用它进入系统
  • ⑤ 在HostOS中运行解压目录中的vmmon64.exe,点击【Debugger path…】按钮选择WinDbg安装目录下的【windbg.exe】(此方式不支持选择汉化版的入口),最后点击【Run debugger】,VirtualKD会自动启动WinDbg并连接到GuestOS。
  • ⑥ 在WinDbg调试期间不能关闭VirtualKD进程

在GuestOS安装VirtualKD

操作系统列表新增VirtualKD调试系统

在HostOS配置VirtualKD运行WinDbg

11. 附录2: WinDbg常用命令/操作

可跳转到此页面查询:

WinDbg 命令手册

12. 附录3:WDK(x64)+汇编的混合编译

——部分内容摘于博文:http://blog.csdn.net/cosmoslife/article/details/9071949

使用WinDbg调试驱动程序的时候,已经涉及代码编写了。而其中一个泛用的特例就是在C/C++代码中嵌套汇编的INT3系统中断指令。

因此此节详细说明一下如何在x64的C/C++驱动程序中嵌入汇编代码,并将其编译到一起。

x86的编译平台中是支持使用__asm{ … }语法进行内联汇编的,亦即可以直接把汇编代码直接写到C/C++代码之中,例如:

NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING reg_path) 
{ 
    DbgPrint("This is x86 C/C++"); 

    __asm {
        int 3         ; 系统中断
    }

    DbgPrint("This is x86 C/C++"); 
    return STATUS_SUCCESS; 
}

但是在x64编译平台是不支持内联汇编的

x64平台要求把汇编代码统一写到 *.asm 文件中,通过汇编编译器(VS默认为ml64.exe)编译成 *.obj 目标文件,最后与 C/C++ 代码的目标文件link到一起生成*.exe可执行文件(或*.sys驱动文件)。

在WDK驱动项目中,详细的操作方式如下(这里还是以在之前的VS2010_WDK_Demo驱动项目中添加INT3系统中断为例)

① 在WDK项目中新建文件夹asm,再新建文件fun.asm(文件名任意,后缀必须为*.asm

② 在fun.asm中编写INT3系统中断汇编代码:

.CODE            ; 声明为代码段,类似的还有.DATA数据段

int_3 PROC        ; 函数/过程,红色部分是其名称,可被C/C++声明后直接调用
    int 3
    ret
int_3 ENDP

END

③ 在VS2010_WDK_Demo.h 头文件的 extern “C” { … } 代码块中添加汇编函数的外部声明,使其可以被调用C/C++代码(至于为什么要在extern “C”修饰符内做函数声明,可见附录4的【13.1 [error LNK 2019] 】

extern "C" { 
    ......
    void int_3(void);
    ......
} 

在*.h头文件声明*.asm的外部函数

④ 之后就可以在VS2010_WDK_Demo.cpp 源文件中直接调用 int_3() 函数了:

在*.cpp源文件调用*.asm的外部函数

⑤ 但是现在WDK工程还不能编译成功,原因是WDK不知道如何编译fun.asm文件。为此需要再为*.asm文件指定一套编译规则,方法如下:

右键【项目】 –> 【生成自定义】 –> 勾选【masm】,这组操作意为为WDK项目增加汇编文件的编译规则MASM:

为WDK工程添加新的编译规则(MASM)

⑥ 右键【fun.asm】 –> 【属性】 –> 【配置属性】 –> 【常规】 –> 【项类型】 –> 【 Microsoft Macro Assembler】(此选项只有在执行第⑤步后才会出现) –> 【应用】,这组操作意为使用默认的MASM编译器编译*.asm文件:

为*.asm汇编文件指定MASM编译器

⑦ 之后左侧会增加一项【Microsoft Macro Assembler】,这就是MASM编译器的配置项。

点击【Command Line】查看编译脚本: x64平台的编译脚本为ml64.exe, Win32平台的编译脚本为ml.exe。

由于当前工程是64位WDK项目,因此若编译脚本不是ml64.exe,需要在配置管理器新增x64平台。

确认MASM编译器的汇编编译脚本是否正确

(重要)点击【Object File】查看*.asm文件编译所生成的目标文件*.obj的输出位置。由于配置值使用了宏变量,可【点击配置值】 –> 【下拉】 –> 【编辑】 –> 【宏】,找到对应的变量查看其真实值。

如下图所示,配置值为【$(IntDir)%(FileName).obj】。

其中$(IntDir)为宏变量,查得其值为x64\amd64</font>

%(FileName)泛指所编译的*.asm文件名称(不含后缀),如当前所编译的fun.asm文件,%(FileName)的值则为fun

因此对于fun.asm汇编文件而言,$(IntDir)%(FileName).obj的真实值为x64\amd64\fun.obj,记下这个值。

确认*.asm汇编文件编译所生成的目标文件位置

⑨ 驱动工程是通过WDK的build命令进行编译的(在VS2010中是驱动工程目录下的BuildDrv.bat脚本),但是build命令不会自动LINK到*.asm汇编文件的目标文件,直接影响就是在编译*.cpp源文件时,找不到*.asm汇编文件所声明的函数。

为此需要告诉build脚本,在编译*.cpp的时候要和哪些*.asm的目标文件进行LINK。

事实上,*.asm是先于*.cpp被编译成目标文件的,亦即在编译*.cpp的时候,可以把*.asm的目标文件作为*.cpp的库文件,而*.asm的目标文件位置,在第⑧步的时候已经查到了。

尔后只需要修改WDK的sources文件,添加属性项【TARGETLIBS】,指定值为*.asm的目标文件(在本例中就是x64\amd64\fun.obj):

为驱动工程指定*.asm的目标文件作为链接库文件

⑩ 最后编译项目:【生成】–>【(重新)生成解决方案】,如无意外则编译成功:

成功编译含*.asm汇编文件的驱动工程

注:
在64位驱动工程中混合汇编编译的方式,暂时只适用于VS2010。
在VS2008的驱动工程中,即使使用相同步骤配置,其DDK的build.bat脚本却无法调用MASM编译器ml64.exe对\.asm汇编文件进行编译,亦即在编译*.cpp时无法自动得到*.asm的目标文件并进行LINK。*

13. 附录4:常见异常解决方案

13.1. [error LNK 2019] - unresolved external symbol _DriverEntry@8 referenced in function _GsDriverEntry@8

#include<ntddk.h> 

VOID DriverUnload(PDRIVER_OBJECT driver) 
{ 
    DbgPrint("load Driver\r\n"); 
}

NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING reg_path) 
{ 
    DbgPrint("Hello, WDK Demo!"); 
    driver->DriverUnload=DriverUnload; 
    return STATUS_SUCCESS; 
}

以上述驱动程序源码为例,在使用VS2008 / VS2010 编译时报错如下:

1>DDKBLD: ================ Build warnings =======================
1>1>BufferOverflowK.lib(gs_support.obj) : error LNK2019: unresolved external symbol _DriverEntry@8 referenced in function _GsDriverEntry@8
1>1>d:\01_wor~1\c\vs2008\re\helloddk\helloddk\bufferoverflowk.lib(gs_support.obj) : error LNK2019: unresolved external symbol _DriverEntry@8 referenced in function _GsDriverEntry@8
1>1>d:\01_wor~1\c\vs2008\re\helloddk\helloddk\objchk_win7_x86\i386\HelloDDK.sys : fatal error LNK1120: 1 unresolved externals
1>1>d:\01_wor~1\c\vs2008\re\helloddk\helloddk\objchk_win7_x86\i386\helloddk.sys : error LNK1120: 1 unresolved externals
1>DDKBLD: =======================================================

异常原因:

这是一个链接错误,表示系统在链接时找不到入口函数_DriverEntry@8。

在VS2008 / VS2010中默认的编译方式是采用C++方式的,而这个错误意思是C编译器对DriverEntry进行编译后的结果,前缀“_”是C编译器特有的,后缀“@8”是所有参数的长度。所以C++编译器一定是把DriverEntry编译成了系统无法认识的另一副模样了(实际上,C++编译器会把它编译成以“?DriverEntry@@”开头的一串很长的符号)。

解决方案:

在报错的函数DriverEntry前面加上extern “C”修饰符即可。

extern “C”提醒编译器要使用C编译格式编译DriverEntry函数,这样编译生成的函数名称为“_DriverEntry@8”,链接器即可正确地识别出符号了。

需要注意的是,若报错的不是函数DriverEntry ,而是其他函数XXX,则也需要在前面添加extern “C”修饰符。

修改后的代码如下:

#include<ntddk.h> 

VOID DriverUnload(PDRIVER_OBJECT driver) 
{ 
    DbgPrint("load Driver\r\n"); 
} 

extern "C"
NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING reg_path) 
{ 
    DbgPrint("Hello, WDK Demo!"); 
    driver->DriverUnload=DriverUnload; 
    return STATUS_SUCCESS; 
}

13.2. GuestOS安装VirtualKD后若退出TestSigning模式则无法启动Windows

GuestOS安装VirtualKD后,若再使用以下命令关闭系统的测试模式,则会出现如下图所示的情况,无法登入系统,错误代码为0xc0000428

bcdedit /set testsigning off    // 关闭系统测试模式

无法启动Windows

异常原因:

GuestOS安装VirtualKD后,C:\Windows\System32\kdcom.dll会被替换成供VirtualKD使用的另一个同名文件kdcom.dll,原kdcom.dll会被备份为kdcom_old.dll

需知kdcom.dll是Windows中极其重要的文件,负责调试驱动程序以及内核模块,它在windows开机时就会被加载到内存中,如果损坏会导致蓝屏。

而由于VirtualKD的kdcom.dll签名可能是过期失效(或未被认可)的,此后若关闭系统的测试模式,系统在启动时会重新发起对kdcom.dll文件的签名校验,最终因校验不通过导致系统启动失败。

解决方案:

在系统关闭测试模式之前,恢复原有的kdcom.dll文件即可 (若很不幸地你的GuestOS在恢复kdcom.dll前已经关闭了测试模式,可以尝试还原VM快照或者用PE登陆系统替换kdcom.dll文件)。

打开C:\Windows\System32目录,可以发现有两个文件:kdcom.dll与kdcom_old.dll

右键查看kdcom.dll属性,在详细信息一栏可知这是VirtualKD使用的文件,而另一个kdcom_old.dll原文件。只需备份kdcom.dll,然后把kdcom_old.dll重命名为kdcom.dll即可。

需要注意的是,恢复kdcom.dll文件后VirtualKD就会失效了,若下次需要使用VirtualKD则还得把这两个文件再次交换。

C:\Windows\System32\kdcom.dll

VirtualKD的kdcom.dll

14. 资源下载

本文全文下载


文章作者: EXP
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 EXP !
  目录