LOADING

加载过慢请开启缓存 浏览器默认开启

windows内核调试

先搭建内核开发环境

windbg驱动调试的环境搭建。

推荐阅读微软官方文档开始使用 WinDbg(内核模式) - Windows drivers | Microsoft Learn

windows系统内核调试 环境搭建(保姆级)_dsigntool-CSDN博客

下载 Windows 驱动程序工具包 (WDK) - Windows drivers | Microsoft Learn

Windows驱动程序逆向工程方法 - 🔰雨苁ℒ🔰

驱动开发:WinDBG 配置内核双机调试 - lyshark - 博客园

一些要用到的命令。

串行接口

\\.\pipe\com_2

符号表环境变量

_NT_SYMBOL_PATH         SRV*D:\Myself_Software\Windows_soft\symbols*http://msdl.microsoft.com/download/symbols

windbg内核调试操作

开启调试服务(需要关闭安全引导)

bcdedit /debug on
bcdedit /dbgsettings serial debugport:1 baudrate:115200
.reload #加载内核符号

windbg常用快捷键

F5 继续

F10 逐过程

F11 逐语句

Shift+F11 单步跳出

F7 运行到行

Ctrl + F9 在突出显示的行上切换启用断点状态

Shift + F9 添加断点

Ctrl+Shift+O 打开脚本

Ctrl+Shift+Enter 执行脚本

Ctrl+S 保存脚本

Alt+S、N 新脚本

控制台常用命令

.hh 命令访问参考命令帮助
g 运行
.symfix 设置符号表或添加本地符号(.symfix+ path)
.reload 加载符号表 (/f添加路径)
lm 列出所有模块
lm m name v 特性模块信息
x [Options] Module!Symbol 检查符号
x /D model!name 查找符号可用通配符*
bl 查看断点
bp	设置一个将一直处于活动状态的断点,直到其所在模块被卸载。
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+ 设置本地源路径

驱动开发环境配置

微软的教程非常的好用,下面我只提出几点需要记住的比较重要的事项。

编写 Hello World Windows 驱动程序(内核模式) - Windows drivers | Microsoft Learn

我这里使用的是windows11 + vs2022的主机和用于调试的windows 10的虚拟机。开发框架采用KWDF。

KWDF是以调用回调函数的来执行操作的,每一个函数都对应一个事件,通过事件的触发来调用回调函数进行数据处理。由于封装的方法较多,开发比较简单。

根据微软的教程,配置好环境,我们在编写完后可以用vs直接把驱动部署在虚拟机上,其中的驱动文件位置在

%systemdrive%\drivertest\drivers

把devcon.exe放到虚拟机的驱动位置下,通过命令来进行驱动程序的安装。(好像需要管理员权限)

devcon install XXX.inf root\XXX

root\后面的内容可以在inf文件中找到(驱动程序路径)

[Standard.NT$ARCH$]
%KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld

源码调试时用,把pdb文件所在位置设置为本地源路径,.reload后会自动加载源码的符号。

 .lsrcpath+ 设置本地源路径

在windbg的左上角点击文件,选择source file添加源代码(直接显示源代码窗口),或者用lsf- [filename] 添加源码(无窗口)。根据微软文档的解释,如果使用 WinDbg,只要程序计数器位于调试器具有其源信息的代码中,就会立即显示“源”窗口。即用源代码进行调试。在源模式下调试 - Windows drivers | Microsoft Learn

跟随官方文档,我们的环境差不多安装完成,接下来正式进行代码编写。

驱动开发结构分析与学习

下面通过一个以diviceiocontrol进行通信的程序为例子分析一个驱动程序的一些主要要素。

Deviceiocontrol:应用程序与驱动程序通信 DeviceIoControl - 沉疴 - 博客园

#include <ntddk.h>
#include <wdf.h>
#include <initguid.h>

// 自定义 IOCTL 控制码,用于deviceiocontrol通信执行操作 参数(irp生成函数,设备对象类型,ioct控制码(0x800--0xfff),
// 访问权限)
#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; //deviceiocontrol消息处理函数

// 驱动入口 参数(驱动对象,注册表路径)
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    WDF_DRIVER_CONFIG config; //WDF_DRIVER_CONFIG结构体,保存了驱动的一些信息
    WDF_DRIVER_CONFIG_INIT(&config, HelloKMDFEvtDeviceAdd); //初始化回调函数(把回调函数注册到驱动的信息中)

    KdPrint(("HelloKMDF: DriverEntry\n")); //输出信息
    //KMDF创建并注册驱动对象,这代表我们已经完成所有回调函数的部署,KMDF框架自动帮我们注册驱动。
    return WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
}

// 设备添加时回调(由WDF_DRIVER_CONFIG_INIT()注册)
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;

    // 创建默认队列处理 DeviceIoControl
    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);
}

// 处理用户态 IOCTL 请求
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);
}