iT邦幫忙

2023 iThome 鐵人賽

DAY 5
3
Security

來自核心-烈日的 Windows系列 第 5

【第 05 話】傳送 IRP 到驅動程式

  • 分享至 

  • xImage
  •  

文章大綱

我們在【第 03 話】簡單的 WDM 驅動程式寫了十分簡短的驅動程式。這篇會以此為基礎,繼續多加處理 IRP 功能。從應用層呼叫 API 傳送 IRP 到 Kernel,並在驅動程式中處理接收的 IRP。

IRP

IRP(I/O Request Packet)是 Windows 中用於請求 I/O 操作的資料結構。驅動程式可以使用 IRP 來與 Kernel 溝通,例如讀取文件或進行網路傳輸。開發者也可以透過發 IRP 給 I/O Manager,並將 IRP 轉發給相應的驅動程式,從而執行對應的行為。

以下圖為例,上半部是應用層,下半部是 Kernel 層。在應用層的 Process 可以透過呼叫 API 傳送 IRP 到驅動程式。比如說 CreateFile 對應到 IRP_MJ_CREATECloseHandle 對應到 IRP_MJ_CLOSE,以此類推。

https://ithelp.ithome.com.tw/upload/images/20230919/20129318NWO6x9EF3I.png

建立 Device

要在 WDM 驅動程式中建立並設定 Device 大致有以下步驟。

  1. 呼叫 IoCreateDevice 建立一個 Device
  2. 呼叫 IoCreateSymbolicLink 建立一個 Symbolic Link 連結到上一步建立的 Device,如此應用程式就可以透過呼叫 CreateFile 取得這個 Device 的 Handle
  3. 設定處理每個 IRP 請求的函數

https://ithelp.ithome.com.tw/upload/images/20230919/20129318IILq7plic2.png

寫程式

程式會分為兩個部分,一個是應用程式,一個是驅動程式。完整的專案也放在我的 GitHub zeze-zeze/2023iThome

驅動程式

首先在 DriverEntry 定義好卸載函數、建立 Device、定義各 IRP 的處理函數。

extern NTSTATUS DriverEntry(PDRIVER_OBJECT driverObject, PUNICODE_STRING registryPath)
{
    UNREFERENCED_PARAMETER(registryPath);
    NTSTATUS status = STATUS_SUCCESS;

    // 要設定卸載函數驅動程式才能順利卸載
    driverObject->DriverUnload = DriverUnload;

    // 建立 Device 與對應的 Symbolic Link
    status = CreateDevice(driverObject);

    // 為需要用到的 IRP 定義處理函數
    driverObject->MajorFunction[IRP_MJ_CREATE] = Dispatcher;
    driverObject->MajorFunction[IRP_MJ_CLOSE] = Dispatcher;

    return STATUS_SUCCESS;
}

IoCreateDevice 建立一個 Device,再來用 IoCreateSymbolicLink 建立一個 Symbolic Link 連結到這個 Device。

#define SYMLINK_NAME L"\\??\\HandleIrp"
#define DEVICE_NAME L"\\device\\HandleIrp"

NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject)
{
    NTSTATUS status;
    UNICODE_STRING deviceName;
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&deviceName, DEVICE_NAME);
    RtlInitUnicodeString(&symLinkName, SYMLINK_NAME);

    // 建立一個 Device
    status = IoCreateDevice(pDriverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, 0, 1, &pDevice);
    if (NT_SUCCESS(status))
    {
        pDriverObject->DeviceObject = pDevice;

        // 建立一個 Symbolic Link 連結到這個 Device
        status = IoCreateSymbolicLink(&symLinkName, &deviceName);
    }
    return status;
}

在 IRP 的處理函數中,我們根據取得的 IRP 執行不同行為。

NTSTATUS Dispatcher(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
    PIO_STACK_LOCATION ioStackLocation = IoGetCurrentIrpStackLocation(pIrp);
    if (pDeviceObject != pDevice)
    {
        return STATUS_UNSUCCESSFUL;
    }

    // 根據收到的 IRP 做對應的處理
    switch (ioStackLocation->MajorFunction)
    {
        case IRP_MJ_CREATE:
            DbgPrint("[HandleIrpDrv] IRP_MJ_CREATE\n");
            break;
        case IRP_MJ_CLOSE:
            DbgPrint("[HandleIrpDrv] IRP_MJ_CLOSE\n");
            break;
        default:
            break;
    }

    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

最後別忘記在卸載驅動程式時,把之前建立的 Device 和 Symbolic Link 刪除。

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName, SYMLINK_NAME);

    // 在卸載時刪除之前建立的 Device 與 Symbolic Link
    IoDeleteSymbolicLink(&symLinkName);
    IoDeleteDevice(pDriverObject->DeviceObject);
}

應用程式

至於從應用層可以呼叫 CreateFile 發送 IRP_MJ_CREATE、呼叫 CloseHandle 發送 IRP_MJ_CLOSE 到驅動程式。

#define SymLinkName L"\\\\.\\HandleIrp"

int main(int argc, char* argv[])
{
    // CreateFile 等同於傳送 IRP_MJ_CREATE 到驅動程式
    HANDLE hDevice = CreateFile(SymLinkName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
    if (hDevice == INVALID_HANDLE_VALUE)
    {
        std::cout << "CreateFile error: " << GetLastError() << std::endl;
        return 1;
    }

    // CloseHandle 等同於傳送 IRP_MJ_CLOSE 到驅動程式
    CloseHandle(hDevice);

    return 0;
}

測試

開啟 VM,記得要在本機開啟 vmmon64.exe,並在 boot options 按 F8 選擇 Disable Driver Signature Enforcement

先開啟 DbgView64.exe,再載入編譯好的 HandleIrpDrv.sys,接著執行 HandleIrp.exe,可以看到輸出的 [HandleIrpDrv] IRP_MJ_CREATE[HandleIrpDrv] IRP_MJ_CLOSE

https://ithelp.ithome.com.tw/upload/images/20230919/20129318LlpcZuqOhL.png

參考資料


上一篇
【第 04 話】WinDbg 竄改 EPROCESS Token
下一篇
【第 06 話】IOCTL 與驅動程式溝通-實作竄改 EPROCESS TOKEN
系列文
來自核心-烈日的 Windows30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言