先搭建内核开发环境 windbg驱动调试的环境搭建。 推荐阅读微软官方文档开始使用 WinDbg(内核模式) - Windows drivers | Microsoft Learn
windows系统内核调试 环境搭建(保姆级)_dsigntool-CSDN博客
下载 Windows 驱动程序工具包 (WDK) - Windows drivers | Microsoft Learn
Windows驱动程序逆向工程方法 - 🔰雨苁ℒ🔰
驱动开发:WinDBG 配置内核双机调试 - lyshark - 博客园
备忘录
反调试:
内核下的调试与反调试 - iBinary - 博客园
总结
一些要用到的命令。
推荐串行接口进行调试
符号表环境变量
1 _NT_SYMBOL_PATH SRV*D:\Myself_Software\Windows_soft\symbols*http://msdl.microsoft.com/download/symbols
windbg内核调试操作
开启调试服务(需要关闭安全引导)
1 2 bcdedit /debug on bcdedit /dbgsettings serial debugport:1 baudrate:115200
windbg attach kernel后重启即可
windbg常用快捷键 F5 继续
F10 逐过程
F11 逐语句
Shift+F11 单步跳出
F7 运行到行
Ctrl + F9 在突出显示的行上切换启用断点状态
Shift + F9 添加断点
Ctrl+Shift+O 打开脚本
Ctrl+Shift+Enter 执行脚本
Ctrl+S 保存脚本
Alt+S、N 新脚本
控制台常用命令 其实控制台可以用鼠标点击选项进行操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 .hh 命令访问参考命令帮助 g 运行 pa [addr] 执行到 .symfix 设置符号表或添加本地符号(.symfix+ path) .reload 加载符号表 (/f添加路径) lm 列出所有模块 lm m name v 特性模块信息 x [Options] Module!Symbol 检查符号 x /D model!name 查找符号可用通配符* sxe ld [name] 拦截模块的加载 e{b|d|q|a|f|D|a} Address [Values] 写入值到内存中 d{a|b|c|d|D|f|p|q|u|w|W} [addr] 打印内存中的值 bl 查看断点 bp 设置一个将一直处于活动状态的断点,直到其所在模块被卸载。 bp /w "@esp < 0x6ff9f8" [addr] 条件断点 bu 设置一个断点,该断点在卸载模块时未解析,并在重新加载模块时重新启用。 bm 为符号设置一个断点。 此命令适当地使用 bu 或 bp,并允许使用通配符(*)对匹配的每个符号(如类中的所有方法)设置断点。 bc 清除列表中的断点。 使用 bc * 清除所有断点。 bd 禁用断点。 使用 bd * 禁用所有断点。 be 启用断点。 使用 be * 启用所有断点。 ba <access> <size> <address> {options} 设置在访问内存位置时触发的断点(e 执行:当 CPU 从地址中提取指令时。r 读/写:当 CPU 读取或写入地址时。w write:当 CPU 写入地址时) .bpcmds (显示断点命令) dv 命令来显示给定帧的所有局部变量的名称和值 kp 显示堆栈和参数的完整列表。 kn 允许您查看堆栈以及旁边的帧信息。 !process 调试器扩展来显示或设置进程信息 dv dename!name 查与例程关联的区域设置变量 !process 0 0 显示所有进程的摘要信息。(0 27)输出全部 !thread 命令查看线程 .thread 设置当前线程 r 查看寄存器 !lmi 扩展显示有关模块的详细信息 !dh 扩展显示标头信息 lsf- [filename] 添加源码 .lsrcpath+ 设置本地源路径 .load [dllpath] 加载dll (加载位置待验证) .chain 查看已加载的dll 脚本: .scriptproviders(列出脚本提供程序) .scriptload [jspath](加载脚本) .scriptload(卸载脚本) .scriptrun [jspath](运行脚本) .scriptlist(列出已加载的脚本)
windbg脚本(摘自官方文档JavaScript 调试器脚本 - Windows drivers | Microsoft Learn )
用vscod编写时把C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\JsProvider.d.ts放到当前目录,并在脚本文件前添加
js脚本可调用api调试器数据模型 - 代码命名空间 - Windows drivers | Microsoft Learn
一个框架,来自官方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 host.diagnostics .debugLog ("***>; Code at the very top (root) of the script is always run \n" ); function initializeScript ( ) { host.diagnostics .debugLog ("***>; initializeScript was called \n" ); } function invokeScript ( ) { host.diagnostics .debugLog ("***>; invokeScript was called \n" ); } function uninitializeScript ( ) { host.diagnostics .debugLog ("***>; uninitialize was called\n" ); } function main ( ) { host.diagnostics .debugLog ("***>; main was called \n" ); }
条件断点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 "use strict" ; function invokeScript ( ) { var ctl = host.namespace .Debugger .Utility .Control ; var address = host.evaluateExpression ("pszCaption" ); if (host.memory .readWideString (address) == "Open" ) { ctl.ExecuteCommand ("gc" ); } else { } }
windbg的脚本开发还是挺困难的,教程也比较少微软自己提供了一个项目用于学习microsoft/WinDbg-Samples: Sample extensions, scripts, and API uses for WinDbg.
另外一个使用脚本hugsy/windbg_js_scripts: Toy scripts for playing with WinDbg JS API
进阶使用windbgtips (@windbgtips) / X
时间旅行调试TTD
[InsightEngineering/Time Travel Debugging (TTD) at main · DebugPrivilege/InsightEngineering](https://github.com/DebugPrivilege/InsightEngineering/tree/main/Time Travel Debugging (TTD))
时间旅行调试 - 概述 - Windows drivers | Microsoft Learn
[原创]TTD调试与ttd-bindings逆向工程实践-软件逆向-看雪-安全社区|安全招聘|kanxue.com
驱动开发环境配置 微软的教程非常的好用,下面我只提出几点需要记住的比较重要的事项。
编写 Hello World Windows 驱动程序(内核模式) - Windows drivers | Microsoft Learn
我这里使用的是windows11 + vs2022的主机和用于调试的windows 10的虚拟机。开发框架采用KWDF。
KWDF是以调用回调函数的来执行操作的,每一个函数都对应一个事件,通过事件的触发来调用回调函数进行数据处理。由于封装的方法较多,开发比较简单。
根据微软的教程,配置好环境,我们在编写完后可以用vs直接把驱动部署在虚拟机上,其中的驱动文件位置在
1 %systemdrive%\drivertest\drivers
把devcon.exe放到虚拟机的驱动位置下,通过命令来进行驱动程序的安装。(需要管理员权限)
1 devcon install XXX.inf root\XXX
root\后面的内容可以在inf文件中找到(驱动程序路径)
1 2 [Standard.NT$ARCH$] %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
源码调试时用,把pdb文件所在位置设置为本地源路径,.reload后会自动加载源码的符号。
在windbg的左上角点击文件,选择source file添加源代码(直接显示源代码窗口),或者用lsf- [filename] 添加源码(无窗口)。根据微软文档的解释,如果使用 WinDbg,只要程序计数器位于调试器具有其源信息的代码中,就会立即显示“源”窗口。即用源代码进行调试。在源模式下调试 - Windows drivers | Microsoft Learn
跟随官方文档,我们的环境差不多安装完成,接下来正式进行代码编写。
驱动安装 一般驱动 工具安装:Downloads:Driver Loader
自注册服务:
1 2 3 4 sc create [name] type= kernel start= demand binPath= [path.sys] sc start [name] sc stop [name] sc delete [name]
inf文件:
这里令驱动名字为Driver1,文件为Driver1.sys和Driver1.inf(由vs生成)
devcon安装
1 devcon.exe install XXX.inf root\[name]
右键文件安装也可行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 ; ; Driver1.inf ; [Version] Signature = "$WINDOWS NT$" Class = System ; TODO: specify appropriate Class ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318} ; TODO: specify appropriate ClassGuid Provider = %ManufacturerName% CatalogFile = Driver1.cat DriverVer = ; TODO: set DriverVer in stampinf property pages PnpLockdown = 1 [DestinationDirs] DefaultDestDir = 13 [SourceDisksNames] 1 = %DiskName%,,,"" [SourceDisksFiles] Driver1.sys = 1,, ;***************************************** ; Install Section ;***************************************** [Manufacturer] %ManufacturerName% = Standard,NT$ARCH$.10.0...16299 ; %13% support introduced in build 16299 [Standard.NT$ARCH$.10.0...16299] %Driver1.DeviceDesc% = Driver1_Device, Root\Driver1 ; TODO: edit hw-id [Driver1_Device.NT] CopyFiles = File_Copy [File_Copy] Driver1.sys ;-------------- Service installation [Driver1_Device.NT.Services] AddService = Driver1,%SPSVCINST_ASSOCSERVICE%, Driver1_Service_Inst ; -------------- Driver1 driver install sections [Driver1_Service_Inst] DisplayName = %Driver1.SVCDESC% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %13%\Driver1.sys [Driver1_Device.NT.Wdf] KmdfService = Driver1, Driver1_wdfsect [Driver1_wdfsect] KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE = 0x00000002 ManufacturerName = "huanghunr" ;TODO: Replace with your manufacturer name DiskName = "Driver1 Installation Disk" Driver1.DeviceDesc = "Driver1 Device" Driver1.SVCDESC = "Driver1 Service"
CTF中的驱动开发结构分析与学习 下面通过一个以diviceiocontrol进行通信的程序为例子分析一个驱动程序的一些主要要素。
Deviceiocontrol:应用程序与驱动程序通信 DeviceIoControl - 沉疴 - 博客园
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <ntddk.h> #include <wdf.h> #include <initguid.h> #define Driver_IOCT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD HelloKMDFEvtDeviceAdd; EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL HelloKMDFEvtIoDeviceControl; NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { WDF_DRIVER_CONFIG config; WDF_DRIVER_CONFIG_INIT(&config, HelloKMDFEvtDeviceAdd); KdPrint(("HelloKMDF: DriverEntry\n" )); return WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE); } NTSTATUS HelloKMDFEvtDeviceAdd (WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit) { UNREFERENCED_PARAMETER(Driver); WDFDEVICE device; NTSTATUS status; DECLARE_CONST_UNICODE_STRING(devName, L"\\Device\\HelloKMDF" ); WdfDeviceInitAssignName(DeviceInit, &devName); status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device); if (!NT_SUCCESS(status)) return status; WDF_IO_QUEUE_CONFIG ioQueueConfig; WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential); ioQueueConfig.EvtIoDeviceControl = HelloKMDFEvtIoDeviceControl; return WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE); } VOID HelloKMDFEvtIoDeviceControl (WDFQUEUE Queue, WDFREQUEST Request, size_t OutputBufferLength, size_t InputBufferLength, ULONG IoControlCode) { UNREFERENCED_PARAMETER(Queue); UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); if (IoControlCode == IOCTL_HELLOKMDF) { KdPrint(("HelloKMDF: 收到 IOCTL_HELLOKMDF 指令!\n" )); } WdfRequestComplete(Request, STATUS_SUCCESS); }
之后再去了解一下其他通信,总结点模板。