【驱动学习】开发相关API整理

文章目录
  1. 1. 链表
  2. 2. 安装卸载相关
    1. 2.0.1. WdfDriverCreate
    2. 2.0.2. WdfDeviceCreate
  • 3. 实用函数
    1. 3.1. 绕过签名检查
  • 4. 内存相关
    1. 4.1. 申请内存 ExAllocatePool
    2. 4.2. 填充内存 RtlZeroMemory
    3. 4.3. 拷贝内存 RtlCopyMemory
    4. 4.4. 释放内存 ExFreePool
  • 5. 进程相关
  • 概述:驱动开发过程中,相关API的学习整理

    [toc]

    链表

    定义链表结构体

    1
    2
    3
    4
    5
    6
    7
    typedef struct
    {
    DWORD Pid;
    UCHAR ProcessName[2048];
    DWORD Handle;
    LIST_ENTRY ListEntry;
    } ProcessList;

    初始化 InitializeListHead

    1
    2
    3
    4
    LIST_ENTRY linkListHead;

    // 初始化链表头部
    InitializeListHead(&linkListHead);

    插入链表 InsertTailList

    1
    2
    3
    pData = (ProcessList*)ExAllocatePool(PagedPool, sizeof(ProcessList));

    InsertTailList(&linkListHead, &pData->ListEntry);

    判断是否为空 IsListEmpty

    1
    2
    3
    4
    if(!IsListEmpty(&linkListHead))
    {
    DbgPrint("Empty");
    }

    移除链表 RemoveHeadList

    1
    LIST_ENTRY* pEntry = RemoveHeadList(&linkListHead);

    获取链表当中的结构

    1
    2
    pData = CONTAINING_RECORD(pEntry, ProcessList, ListEntry);
    DbgPrint("Pid[%d], ProcessName[%s], Handle[0x%x] \n", pData->Pid, pData->ProcessName, pData->Handle);

    安装卸载相关

    WdfDriverCreate

    WdfDriverCreate 是 Windows Driver Frameworks (WDF) 中用于创建驱动程序的函数。它用于初始化驱动程序,并将其与 Windows 操作系统进行集成。

    WdfDriverCreate 函数需要填写一个 WDFDRIVER 结构体来定义驱动程序的属性和行为。以下是一些常见的参数:

    • DriverName: 驱动程序的名称,用于在调试和日志记录中标识驱动程序。
    • DriverVersion: 驱动程序的版本号,用于标识驱动程序的版本。
    • DeviceAddVersion: 用于添加新设备的 WDF 函数版本。
    • DriverUnload: 驱动程序卸载时的回调函数,用于清理资源并执行其他必要的操作。
    • DispatchTable: 一个包含驱动程序处理 IRP 请求的回调函数的表。
    • RegistryPath: 驱动程序在注册表中的路径,用于存储配置信息和设置。

    WdfDeviceCreate

    WdfDeviceCreate 是 Windows Driver Frameworks (WDF) 中的一个函数,用于创建设备对象并初始化与设备相关的数据结构。它是 WDF 中非常重要和常用的函数之一。

    WdfDeviceCreate 函数需要填写一个 WDFDEVICE_INIT 结构体来定义设备的属性和行为。以下是一些常见的参数:

    • Driver: 指向驱动程序的指针,用于关联设备和驱动程序。
    • DeviceAttributes: 指向调用方分配的 WDF_OBJECT_ATTRIBUTES 结构的指针,该结构包含新设备的属性。
    • StackSize: 设备的堆栈大小,用于定义设备对象的执行环境。
    • DeviceInit: 指向 WDFDEVICE_INIT 结构的指针的地址,用于初始化设备的状态和行为。

    在调用 WdfDeviceCreate 之后,WDF 将使用提供的参数来创建并初始化一个设备对象。然后,设备可以使用 WDF 提供的其他函数来管理设备的状态、处理请求以及与其他设备或驱动程序进行交互。

    需要注意的是,WdfDeviceCreate 是一个高级别的函数,通常用于创建完整的设备对象。对于较简单的设备或需要更精细控制的情况,可能需要使用其他 WDF 函数来手动创建和配置设备对象。同时,在使用 WdfDeviceCreate 和其他 WDF 函数时,应注意遵循正确的使用方法和规范,以避免产生潜在的问题和风险。

    实用函数

    绕过签名检查

    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
    // 绕过签名检查
    BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
    {
    #ifdef _WIN64
    typedef struct _KLDR_DATA_TABLE_ENTRY
    {
    LIST_ENTRY listEntry;
    ULONG64 __Undefined1;
    ULONG64 __Undefined2;
    ULONG64 __Undefined3;
    ULONG64 NonPagedDebugInfo;
    ULONG64 DllBase;
    ULONG64 EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING path;
    UNICODE_STRING name;
    ULONG Flags;
    USHORT LoadCount;
    USHORT __Undefined5;
    ULONG64 __Undefined6;
    ULONG CheckSum;
    ULONG __padding1;
    ULONG TimeDateStamp;
    ULONG __padding2;
    } KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;
    #else
    typedef struct _KLDR_DATA_TABLE_ENTRY
    {
    LIST_ENTRY listEntry;
    ULONG unknown1;
    ULONG unknown2;
    ULONG unknown3;
    ULONG unknown4;
    ULONG unknown5;
    ULONG unknown6;
    ULONG unknown7;
    UNICODE_STRING path;
    UNICODE_STRING name;
    ULONG Flags;
    } KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;
    #endif

    PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
    pLdrData->Flags = pLdrData->Flags | 0x20;

    return TRUE;
    }

    如何使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 驱动入口地址
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
    {
    if (BypassCheckSign(Driver))
    DbgPrint("Bypass Sign Success.");

    DbgPrint("Driver loaded. \n");

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
    }

    内存相关

    申请内存 ExAllocatePool

    申请内存使用 ExAllocatePoolExAllocatePoolWithTagExAllocatePool2 接口

    原型

    1
    2
    3
    4
    PVOID ExAllocatePool(
    IN POOL_TYPE PoolType,
    IN SIZE_T NumberOfBytes
    );
    • PoolType:指定内存池的类型,可以是 NonPagedPool(非分页内存池)或 PagedPool(分页内存池)等。
    • NumberOfBytes:指定要分配的内存大小,最好是4的倍数。
    • 返回值:返回分配的内存地址,一定是内核模式地址。如果返回NULL,则代表分配失败。

    ExAllocatePool 与 ExAllocatePool2/ExAllocatePool3

    从 Windows 10 版本 2004 开始,推荐使用 ExAllocatePool2ExAllocatePool3 来替代 ExAllocatePool。新的 API 默认会将分配的内存初始化为零,有助于避免内存泄漏相关的bug。

    示例代码

    1
    2
    3
    4
    5
    6
    PVOID Allocation = ExAllocatePool(PagedPool, 100);
    if (Allocation == NULL) {
    // 处理内存分配失败的情况
    } else {
    // 使用分配的内存
    }

    在新的驱动中,应该使用 ExAllocatePool2 接口

    1
    2
    3
    4
    5
    6
    PVOID Allocation = ExAllocatePool2(POOL_FLAG_PAGED, 100, 'abcd');
    if (Allocation == NULL) {
    // 处理内存分配失败的情况
    } else {
    // 使用分配的内存
    }

    填充内存 RtlZeroMemory

    1
    2
    3
    // 分配内核堆空间
    pData = (ProcessList*)ExAllocatePool(PagedPool, sizeof(ProcessList));
    RtlZeroMemory(pData, sizeof(ProcessList));

    拷贝内存 RtlCopyMemory

    1
    2
    3
    4
    // 设置变量
    pData->Pid = (DWORD)PsGetProcessId(eproc);
    RtlCopyMemory(pData->ProcessName, PsGetProcessImageFileName(eproc), strlen(PsGetProcessImageFileName(eproc)));
    pData->Handle = (DWORD)PsGetProcessInheritedFromUniqueProcessId(eproc);

    释放内存 ExFreePool

    1
    ExFreePool(pData);

    进程相关

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <windef.h>

    // 函数返回给定进程的进程环境块(Process Environment Block,PEB)的指针
    extern PVOID PsGetProcessPeb(_In_ PEPROCESS Process);

    // 此函数通过给定的进程 ID 查找对应的进程,并将进程的 EPROCESS 结构指针存储在提供的指针变量中。
    NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS* Process);

    // 此函数返回给定进程的 Wow64 进程环境的指针。Wow64 进程环境是用于在 64 位 Windows 上运行 32 位应用程序的兼容性层。
    extern NTKERNELAPI PVOID PsGetProcessWow64Process(_In_ PEPROCESS Process);

    // 此函数返回给定进程的映像文件名,即执行该进程的可执行文件的名称。
    extern NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);

    // 此函数返回给定进程所继承的唯一进程 ID,即父进程ID。
    extern NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);