0x00 准备
- 宿主机 win10
- windbg preview
- vmware虚拟机 win7 x86
除默认安装外,还需要安装pykd。
PYKD是一个windbg的python扩展插件,让你在windbg调试过程中可以使用python脚本辅助调试。
有两种安装类型,通过 https://githomelab.ru/pykd/pykd 下载pykd.pyd加载;通过 https://githomelab.ru/pykd/pykd-ext 下载pykd.dll加载。
第一种适合编写python脚本的时候调用。
将pykd.dll放到相应目录加载,如果不放在如下路径,需要在.load的时候跟绝对路径
1 2 3 4 | # windbg preview %localappdata%\Packages\Microsoft.WinDbg_8wekyb3d8bbwe\LocalCache\Local\dbg\EngineExtensions # windbg winevt |
绿色版python无法正常运行,缺少注册表项,建议还是安装python2,不需要为他添加PATH。
1 2 3 4 5 6 7 8 | # 加载pykd .load pykd # 查看当然已安装python信息 !pykd.info # 切换python版本 !pykd.select 2.7 # 执行脚本 !py [script_path] |
双机调试配置
主要是针对虚拟机的配置,这里介绍手动和工具两种方法
手动配置
1、配置虚拟机端口
注意下vmware虚拟机配置中是否有打印机,有的话那他就占用串口号"1"了,新增的串口号得从2开始。如下在打印机存在的情况下,新增一个串行端口,默认编号为2,管道名称是以\\.\pipe\
开头,后面自定义没有要求。
2、虚拟机设置调试端口
1 2 3 4 5 6 7 8 9 10 | # 设置端口.使用115200传输,端口设置为2,这个根据上面新建的串口序号是多少来选择。 bcdedit /dbgsettings serial baudrate:115200 debugport:2 # 复制一个开机启动项,名字你自定义即可,执行后会生成一个ID如{91d470a8-6fe2-11eb-a1fa-80bb3fafb428} bcdedit /copy {current} /d "Win7 x86 with Kernel Debugging" # 增加一个开机引导,这里是设置引导项顺序,如下有两个,第一个是current,第二个为上面生成新的,注意ID 这个ID第二条指令返回的ID.一般为{xxxx-xxx-xxx-xxx} bcdedit /displayorder {current} {ID} bcdedit /displayorder {current} {91d470a8-6fe2-11eb-a1fa-80bb3fafb428} # 激活调试模式. ID同上. bcdedit /debug {ID} ON bcdedit /debug {91d470a8-6fe2-11eb-a1fa-80bb3fafb428} ON |
我对于上述配置的理解。设置kernel debug的端口,用于和宿主机通信;然后根据当前的启动加载器copy生成一个新的;根据新的启动加载器ID,将其加入到启动引导菜单里;并针对新的启动加载器开启debug模式。
可以通过bcdedit查看所有配置,displayorder引导菜单里就有两项了,并多了一个启动加载器。
然后在开机引导菜单里就会多出一个Win7 x86 with Kernel Debugging
,选择这个则可以进入内核调试模式,进入调试模式后就会卡住,等待com端口的连接,连接后才会继续运行。
3、windbg设置
使用前需要先配置符号表,设置保存路径和下载链接,这里通过环境变量的方式设置。
新建一个环境变量_NT_SYMBOL_PATH
值为: SRV*c:\mysymbol* [http://msdl.microsoft.com/download/symbols;C:\mysymbol\localsymbol](http://msdl.microsoft.com/download/symbols;C:%5Cmysymbol%5Clocalsymbol)
。PS: 如果不设置环境变量就需要在windbg里手动添加该参数值,效果一样。PS: 这里多加了一个本地路径C:\mysymbol\localsymbol用于存放第三方驱动如HEVD的pdb符号文件。
宿主机命令行输入windbgx可启动windbg preview,然后设置attach to kernel
,COM端口配置如下,波特率为第二步设置,端口名称为第一步设置的。
设置完后保存就会开始连接虚拟机,第一次连接会因为没有相关模块的符号文件,如下正在下载符号文件,建议挂代理下载,因为下载地址被墙了。
同时也可以在配置目录下看到下载的pdb符号文件
符号文件下载后会因为int3中断导致虚拟机仍未继续运行,输入g
即可继续运行。PS: 等待一会会再次进入中断需要再输入g
才真正让虚拟机正常运行,也就是需要输入两次。
开机后,然后通过下断点或者强制中断方式,就可以中断虚拟机进行调试了。
工具virtualKD(待补充)
virtualKD用来搭建双机调试环境,还是蛮好用的,在虚拟机安装target,就可以在宿主机使用windbg连接了。
HEVD驱动安装
osrloader工具包有四个目录,分别对应不同操作系统版本,如下标识。而win7 x86也选择WNET
运行osrloader如下安装驱动
查看服务状态,可看到HEVD驱动已运行。
在宿主机中强制中断输入lm m H*
,列举出以H开头的模块,其中就包含HEVD,点击HEVD即可查看该模块信息。
进一步浏览所有符号,这里会存在一个问题,因为HEVD的符号文件不存在,会在logs里提示符号文件不存在同时到微软下载失败,所以需要提前将HEVD驱动的pdb文件放到之前环境变量设置的目录C:\mysymbol\localsymbol
里,然后windbg里执行.reload
重新加载符号表。
然后重新查看则会发现符号文件加载成功,到此环境搭建完毕,后续就开始分析和调试内核漏洞了。
扩展知识
驱动开发
驱动程序其实也分运行在ring0和ring3的,ring0的驱动文件以sys后缀,ring3的驱动文件是以dll后缀。
一般来说,开发的驱动程序运行在ring0,需要操作硬件设备或者内核空间,对于我们来说主要是操作内核空间数据结构,以达到hook隐藏等功能。以下就驱动程序的几个模型
- 设备函数驱动程序
- 设备筛选器驱动程序
- 软件驱动程序
- 文件系统筛选器驱动程序
- 文件系统驱动程序
而其中我们用到的是软件驱动程序模型,因为该模型开发的驱动程序,可实现运行在内核模式下,访问受保护的操作系统数据,方便我们操作各类内核数据结构。
而软件驱动程序其实又细分为KMDF以及传统的windows NT 驱动程序模型。使用 KMDF 和传统 Windows NT 模型可以编写驱动程序,而无需考虑即插即用 (PnP) 和电源管理。 你可以改为专心于驱动程序的首要任务上。 使用 KMDF,你不必考虑 PnP 和电源,因为框架会为你处理 PnP 和电源。 如果使用传统 Windows NT 模型,无需考虑 PnP 和电源,因为内核模式服务在与 PnP 和电源管理完全无关的环境中运行。
如果你希望驱动程序与 PnP 和电源管理完全无关,请使用传统 Windows NT 模型。 如果需要编写考虑电源转换或 PnP 事件的软件,则不能使用传统 Windows NT 模型,而必须使用 KMDF。
驱动开发环境安装
需要安装以下两个程序:
- VS2015:IDE,不多说
- WDK10:windows driver kit windows驱动程序工具包
WDK10是一个在线安装程序,用于下载一个1G多的安装包,然后运行下载后的安装包才能正常安装。
简单的驱动程序开发
这部分简单看看,因为实际的使用会更类似于HEVD里写的,后续会针对HEVD驱动分析。
新建项目-->模板-Visual C++-->Windows Driver-->WDF,选择kernel mode driver即可。
项目结构如下
上面是根据模板生成的默认文件,也可以添加空模板项目,这时创建一个driver.c
在driver.c中,首先包含以下头文件:
123#include <ntddk.h>#include <wdf.h>// 如果无法添加 Ntddk.h,请打开“配置”->“C/C++”>“常规”>“其他包含目录”并添加 C:\Program Files (x86)\Windows Kits\10\Include\<build#>\km,将 <build#> 替换为 WDK 安装中的相应目录Ntddk.h 包含所有驱动程序的核心 Windows 内核定义,而 Wdf.h 包含基于 Windows 驱动程序框架 (WDF) 的驱动程序的定义。
接下来,为要使用的两个回调提供声明:
12DRIVER_INITIALIZE DriverEntry;EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;使用以下代码编写 DriverEntry:
1234567891011121314151617181920212223242526NTSTATUSDriverEntry(_In_ PDRIVER_OBJECT DriverObject,_In_ PUNICODE_STRING RegistryPath){// NTSTATUS variable to record success or failureNTSTATUS status = STATUS_SUCCESS;// Allocate the driver configuration objectWDF_DRIVER_CONFIG config;// Print "Hello World" for DriverEntryKdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Kmdf Hello World: DriverEntry\n" ));// Initialize the driver configuration object to register the// entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAddWDF_DRIVER_CONFIG_INIT(&config,KmdfHelloWorldEvtDeviceAdd);// Finally, create the driver objectstatus = WdfDriverCreate(DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&config,WDF_NO_HANDLE);return status;}DriverEntry 是所有驱动程序的入口点,就像
Main()
适用于许多用户模式应用程序一样。 DriverEntry 的任务是初始化驱动程序范围的结构和资源。 在此示例中,你针对 DriverEntry 打印了“Kmdf Hello World: DriverEntry”,并通过WDF_DRIVER_CONFIG_INIT
接口初始化WDF_DRIVER_CONFIG
结构体,并将KmdfHelloWorldEvtDeviceAdd
函数注册为_EvtDeviceAdd_
回调的入口点,然后使用WdfDriverCreate
创建了驱动程序对象并返回。接下来,使用以下代码编写
_KmdfHelloWorldEvtDeviceAdd_
:123456789101112131415161718192021NTSTATUSKmdfHelloWorldEvtDeviceAdd(_In_ WDFDRIVER Driver,_Inout_ PWDFDEVICE_INIT DeviceInit){// We're not using the driver object,// so we need to mark it as unreferencedUNREFERENCED_PARAMETER(Driver);NTSTATUS status;// Allocate the device objectWDFDEVICE hDevice;// Print "Hello World"KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Kmdf Hello World: EvtDeviceAdd\n" ));// Create the device objectstatus = WdfDeviceCreate(&DeviceInit,WDF_NO_OBJECT_ATTRIBUTES,&hDevice);return status;}系统在检测到你的设备已到达时,会调用 EvtDeviceAdd。 它的任务是初始化该设备的结构和资源。 在此示例中,你仅针对 EvtDeviceAdd 输出了“Kmdf Hello World: EvtDeviceAdd”消息、创建了设备对象并返回。 在你编写的其他驱动程序中,可以为硬件创建 I/O 队列,为特定于设备的信息设置设备上下文存储空间,或执行准备设备所需的其他任务。
1对于设备添加回调,请注意以驱动程序名称为前缀对回调命名的方式 (KmdfHelloWorld EvtDeviceAdd)。 通常,我们建议以这种方式命名驱动程序功能,以区别于其他驱动程序的功能。 DriverEntry 是固定入口函数名称。保存Driver.c
此示例说明了驱动程序的基本概念:驱动程序是一个“回调集合”,经初始化后,会在系统有需要时等待系统调用。 这可能是新设备到达事件、用户模式应用程序的 I/O 请求、系统电源关闭事件、另一个驱动程序的请求,或用户意外拔出设备时的意外删除事件。 幸运的是,就“Hello World”而言,只需操心驱动程序和设备的创建。
- 生成驱动程序
这一步就和其他C/C++程序没什么区别,不赘述,会生成这么几个文件如下:
- KmdfHelloWorld.sys - 内核模式驱动程序文件
- KmdfHelloWorld.inf - 在安装驱动程序时 Windows 使用的信息文件
- KmdfHelloWorld.cat - 安装程序验证驱动程序的测试签名所使用的目录文件
最后就是安装驱动程序,至于调试可用windbg或者VS也支持。
参考
https://bbs.pediy.com/user-home-825245.htm
https://bbs.pediy.com/thread-218838.htm
驱动开发
https://docs.microsoft.com/zh-cn/windows-hardware/drivers/gettingstarted/
https://www.mycode.net.cn/language/cpp/1771.html
驱动开发笔记(推荐看这个就行): https://bbs.pediy.com/thread-266038.htm