4. 静态分析基础:反汇编基础、识别驱动入口点、定位 MajorFunction 与派遣函数表

静态分析是驱动安全测试的起点,不依赖实际运行环境,通过逆向工程手段解析驱动二进制文件的结构与逻辑。对于 Windows 内核驱动(.sys 文件),静态分析的核心任务包括:理解反汇编代码、定位驱动入口点(DriverEntry)、以及提取 MajorFunction 派遣函数表。本节将系统讲解这三项基础技能,并辅以实际案例与可视化图表。

4.1 反汇编基础:从二进制到控制流

反汇编是将机器码转换为汇编指令的过程。驱动静态分析通常使用 IDA Pro、Ghidra 或 WinDbg(配合反汇编引擎)。理解以下关键概念是分析驱动的前提:

  • 调用约定:Windows 内核驱动使用 __stdcall(参数从右向左压栈,由被调用者清理栈)。x64 平台则使用 __fastcall(前四个参数通过 RCX、RDX、R8、R9 传递)。
  • 常见指令:mov(数据传送)、call(函数调用)、jmp/jz/jnz(控制转移)、lea(有效地址加载)、push/pop(栈操作)。
  • 内核特有结构:DRIVER_OBJECT、DEVICE_OBJECT、IRP 等结构体在反汇编中以偏移量形式出现。
💡 分析技巧: 在 IDA 中加载驱动后,首先查看 NT_HEADER 中的 AddressOfEntryPoint,该字段指向驱动入口点(通常是 DriverEntry 或 GsDriverEntry)。对于使用 GS 安全检查的驱动,入口点可能是 GsDriverEntry,它会调用实际的 DriverEntry。
反汇编数据流与指令解码过程 反汇编数据流与指令解码过程 二进制 .sys 文件 机器码字节流 反汇编引擎 IDA / Ghidra / WinDbg 汇编指令流 mov / call / jmp 控制流图 (CFG) 基本块 / 跳转关系 结构识别 DRIVER_OBJECT / IRP ⚠️ 常见陷阱 混淆代码 / 花指令 可能干扰反汇编结果

4.2 识别驱动入口点:DriverEntry 与 GsDriverEntry

驱动入口点是静态分析的第一个关键锚点。Windows 驱动加载器在加载驱动后,会调用入口点函数。入口点通常具有以下特征:

入口点类型 特征 常见位置 安全意义
DriverEntry 标准入口点,接收 DRIVER_OBJECT 和注册表路径 PE 入口点直接指向 初始化设备、注册派遣函数
GsDriverEntry 由 /GS 编译选项生成,先调用安全 Cookie 初始化 PE 入口点指向 GsDriverEntry 存在缓冲区安全检查
自定义入口点 通过 /ENTRY 链接选项指定 非标准位置 可能用于隐藏或反逆向
🔍 定位方法: 在 IDA 中,按 G 键跳转到 DriverEntry 或 GsDriverEntry。如果符号被剥离,可通过搜索特征指令序列定位:mov [rcx+0B8h], rax(设置 MajorFunction 表)或 call WdfVersionBind(WDF 驱动)。

以下是一个典型的 DriverEntry 反汇编片段(x64 平台):

; DriverEntry 函数开始
DriverEntry proc near
    mov     [rsp+8], rbx          ; 保存非易失寄存器
    mov     [rsp+10h], rsi
    push    rdi
    sub     rsp, 20h              ; 分配局部栈空间
    
    ; 获取 DriverObject 指针 (RCX)
    mov     rdi, rcx              ; rdi = DriverObject
    
    ; 创建设备对象
    lea     rdx, aDeviceName      ; "\\Device\\MyDriver"
    mov     rcx, rdi              ; DriverObject
    call    IoCreateDevice        ; 调用 IoCreateDevice
    
    ; 检查返回值
    test    eax, eax
    js      short failure         ; 如果失败则跳转
    
    ; 设置 MajorFunction 表
    lea     rax, MyCreateClose    ; 函数地址
    mov     [rdi+70h], rax        ; IRP_MJ_CREATE (0x00)
    mov     [rdi+78h], rax        ; IRP_MJ_CLOSE (0x02)
    lea     rax, MyDeviceControl
    mov     [rdi+0B0h], rax       ; IRP_MJ_DEVICE_CONTROL (0x0E)
    
    ; 设置卸载例程
    lea     rax, MyUnload
    mov     [rdi+68h], rax        ; DriverUnload
    
    xor     eax, eax              ; 返回 STATUS_SUCCESS
    jmp     short epilogue
failure:
    ; 错误处理
    mov     eax, 0C0000001h       ; STATUS_UNSUCCESSFUL
epilogue:
    ; 恢复寄存器并返回
    add     rsp, 20h
    pop     rdi
    retn
DriverEntry endp

4.3 定位 MajorFunction 与派遣函数表

DRIVER_OBJECT 结构体中的 MajorFunction 数组是驱动安全分析的核心目标。该数组包含 28 个函数指针(IRP_MJ_XXX),每个指针对应一个 IRP 主功能码的处理函数。通过分析派遣函数表,可以快速了解驱动支持哪些操作,以及每个操作的处理逻辑。

MajorFunction 数组在 DRIVER_OBJECT 中的偏移(x64):

偏移量 IRP 主功能码 名称 常见用途
0x70 IRP_MJ_CREATE (0x00) Create 打开设备句柄
0x78 IRP_MJ_CLOSE (0x02) Close 关闭设备句柄
0x80 IRP_MJ_READ (0x03) Read 从设备读取数据
0x88 IRP_MJ_WRITE (0x04) Write 向设备写入数据
0xB0 IRP_MJ_DEVICE_CONTROL (0x0E) DeviceControl 设备 I/O 控制(IOCTL)
0xB8 IRP_MJ_INTERNAL_DEVICE_CONTROL (0x0F) InternalDeviceControl 内部 I/O 控制
0xE0 IRP_MJ_PNP (0x1B) PnP 即插即用管理
0xE8 IRP_MJ_POWER (0x1C) Power 电源管理
⚠️ 安全关注点: 派遣函数表中的 IRP_MJ_DEVICE_CONTROL 是最常被攻击的目标。恶意驱动或存在漏洞的驱动可能在此处实现任意内存读写、权限提升或拒绝服务。分析时应重点关注该派遣函数的 IOCTL 分发逻辑。
MajorFunction 派遣函数表结构 MajorFunction 派遣函数表结构 DRIVER_OBJECT +0x00 Type (short) +0x08 DriverStart +0x10 DriverSize +0x68 DriverUnload MajorFunction[28] [0x00] IRP_MJ_CREATE [0x02] IRP_MJ_CLOSE [0x03] IRP_MJ_READ [0x04] IRP_MJ_WRITE [0x0E] IRP_MJ_DEVICE_CONTROL MyCreateClose 处理 Create/Close MyReadWrite 处理 Read/Write MyDeviceControl 处理 IOCTL (高危) 🔍 分析要点 • 检查每个派遣函数地址 • 重点关注 DeviceControl • 识别未注册的派遣函数 • 对比符号表与反汇编

4.4 实战案例:分析一个简单的驱动派遣函数表

以下是一个实际驱动中 DriverEntry 设置派遣函数表的反汇编片段(x86 平台,IDA 反汇编结果):

.text:00011000 DriverEntry proc near
.text:00011000
.text:00011000 var_4           = dword ptr -4
.text:00011000 arg_0           = dword ptr  8
.text:00011000 arg_4           = dword ptr  0Ch
.text:00011000
.text:00011000                 push    ebp
.text:00011001                 mov     ebp, esp
.text:00011003                 push    ebx
.text:00011004                 push    esi
.text:00011005                 push    edi
.text:00011006                 mov     edi, [ebp+arg_0]    ; edi = DriverObject
.text:00011009                 push    0                   ; DeviceName = NULL
.text:0001100B                 push    0                   ; Exclusive = FALSE
.text:0001100D                 push    0                   ; DeviceType = FILE_DEVICE_UNKNOWN
.text:0001100F                 push    edi                 ; DriverObject
.text:00011010                 call    IoCreateDevice
.text:00011015                 test    eax, eax
.text:00011017                 jl      short loc_1102F
.text:00011019                 mov     [edi+4], eax        ; DeviceObject
.text:0001101C                 mov     eax, offset sub_11100 ; Create/Close 函数
.text:00011021                 mov     [edi+34h], eax      ; MajorFunction[0] = Create
.text:00011024                 mov     [edi+3Ch], eax      ; MajorFunction[2] = Close
.text:00011027                 mov     eax, offset sub_11200 ; DeviceControl 函数
.text:0001102C                 mov     [edi+70h], eax      ; MajorFunction[14] = DeviceControl
.text:0001102F                 xor     eax, eax
.text:00011031                 pop     edi
.text:00011032                 pop     esi
.text:00011033                 pop     ebx
.text:00011034                 pop     ebp
.text:00011035                 retn    8
.text:00011035 DriverEntry endp

分析步骤:

  1. 定位 DriverEntry:通过入口点或特征指令 call IoCreateDevice 找到该函数。
  2. 识别 MajorFunction 赋值:查找 mov [edi+offset], reg 指令,其中 offset 对应 DRIVER_OBJECT.MajorFunction 数组的索引偏移。
  3. 提取派遣函数地址:记录每个 offset 对应的函数地址(如 sub_11100、sub_11200)。
  4. 跳转到派遣函数:在 IDA 中按 G 跳转到这些地址,分析其内部逻辑。
📊 派遣函数表提取结果:
IRP 主功能码 偏移 (x86) 函数地址 函数名称(推测)
IRP_MJ_CREATE (0x00) 0x34 0x11100 sub_11100
IRP_MJ_CLOSE (0x02) 0x3C 0x11100 sub_11100(复用)
IRP_MJ_DEVICE_CONTROL (0x0E) 0x70 0x11200 sub_11200

4.5 常见陷阱与注意事项

  • 符号剥离:发布版驱动通常剥离符号,函数名变为 sub_XXXXX,需通过上下文推断功能。
  • 内联函数:部分驱动使用内联汇编或宏来设置派遣函数表,可能不直接出现 mov [edi+offset] 模式。
  • WDF 驱动:基于 Windows Driver Framework (WDF) 的驱动通过 WdfDeviceCreate 和 WdfDeviceInitSetXxx 注册派遣函数,需跟踪框架回调。
  • 派遣函数复用:多个 IRP 主功能码可能指向同一个函数(如 Create 和 Close 共用),分析时需注意函数内部的分支逻辑。
  • 动态注册:部分驱动在运行时动态修改 MajorFunction 表(如通过 IoRegisterDriverReinitialization),静态分析可能遗漏。
🛠️ 工具推荐: 使用 IDA Pro 的 DriverWorks 插件或 KdCore 脚本可以自动解析 DRIVER_OBJECT 结构并标注派遣函数表。对于 Ghidra,可使用 WindowsDriverAnalyzer 扩展。

通过本节的学习,你应该能够独立完成一个驱动二进制文件的静态分析入门工作:从反汇编基础到定位入口点,再到提取完整的派遣函数表。这是后续进行漏洞挖掘、恶意行为分析和安全加固的基石。