<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    驅動開發:通過Async反向與內核通信

    VSole2022-10-20 11:03:23

    一、前言

    這次學習的是通過運用Async異步模式實現的反向通信,反向通信機制在開發中時常被用到,例如一個殺毒軟件如果監控到有異常進程運行或有異常注冊表被改寫后,該驅動需要主動的通知應用層進程讓其知道,這就需要用到驅動反向通信的相關知識點,如下將循序漸進的實現一個反向通信案例。

    在開始學習Async反向通信之前先來研究一個Sync正向通信案例,不論是正向反向通信其在通信模式上與《驅動開發:通過ReadFile與內核層通信》所介紹的通信模式基本一致,都是通過ReadFile觸發驅動中的IRP_MJ_READ讀取派遣。

    唯一的區別是在傳輸數據時使用了MmGetSystemAddressForMdl方式,它將給定MDL描述的物理頁面映射到系統空間,并調用RtlCopyMemory()將全局字符串復制到這個空間內,這樣客戶端就可以循環讀取內核傳出的數據。

    我們來看驅動端代碼是如何實現的這個功能,代碼并沒有什么特殊的無法理解的點,只是需要注意我們在驅動入口調用IoCreateDevice()時傳入了第二個參數FILE_DEVICE_EXTENSION,該參數的作用是,創建設備時,指定設備擴展內存的大小,傳一個值進去,就會給設備分配一塊非頁面內存。

    #include <ntddk.h>#include <stdio.h> // 保存一段非分頁內存,用于給全局變量使用#define FILE_DEVICE_EXTENSION 4096 // 定義全局字符串static int global_count = 0;static char global_char[5][128] = { 0 }; // 驅動綁定默認派遣函數NTSTATUS _DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp){    _pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;    _pIrp->IoStatus.Information = 0;    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);    return _pIrp->IoStatus.Status;} // 驅動創建后觸發NTSTATUS _SyncCreateCloseDispatch(PDEVICE_OBJECT _pDevcieObject, PIRP _pIrp){    _pIrp->IoStatus.Status = STATUS_SUCCESS;    _pIrp->IoStatus.Information = 0;    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);    return _pIrp->IoStatus.Status;} // 應用層讀數據后觸發NTSTATUS _SyncReadDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp){    NTSTATUS status = STATUS_SUCCESS;    PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(_pIrp);    PVOID pBuffer = NULL;    ULONG uBufferLen = 0;     do    {        // 讀寫請求使用的是直接I/O方式        pBuffer = MmGetSystemAddressForMdl(_pIrp->MdlAddress);        if (pBuffer == NULL)        {            status = STATUS_UNSUCCESSFUL;            break;        }        uBufferLen = pIrpStack->Parameters.Read.Length;        DbgPrint("讀字節長度: %d \n", uBufferLen);         // 最大支持20字節讀請求        uBufferLen = uBufferLen >= 20 ? 20 : uBufferLen;         // 輸出五次字符串        if (global_count < 5)        {            RtlCopyMemory(pBuffer, global_char[global_count], uBufferLen);            global_count = global_count + 1;        }     } while (FALSE);     // 填寫返回狀態及返回大小    _pIrp->IoStatus.Status = status;    _pIrp->IoStatus.Information = uBufferLen;     // 完成IRP    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);    return status;} // 卸載驅動VOID _UnloadDispatch(PDRIVER_OBJECT _pDriverObject){    // 刪除創建的設備    UNICODE_STRING  Win32DeviceName;    RtlInitUnicodeString(&Win32DeviceName, L"\\DosDevices\\LySharkSync");    IoDeleteDevice(_pDriverObject->DeviceObject);} // 驅動入口NTSTATUS DriverEntry(PDRIVER_OBJECT _pDriverObject, PUNICODE_STRING _pRegistryPath){    UNICODE_STRING DeviceName, Win32DeivceName;    PDEVICE_OBJECT pDeviceObject = NULL;    NTSTATUS status;    HANDLE hThread;    OBJECT_ATTRIBUTES ObjectAttributes;     // 設置符號名    RtlInitUnicodeString(&DeviceName, L"\\Device\\LySharkSync");    RtlInitUnicodeString(&Win32DeivceName, L"\\DosDevices\\LySharkSync");     // 循環初始化IRP函數    for (ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)    {        _pDriverObject->MajorFunction[i] = _DefaultDispatch;    }     // 再次覆蓋派遣函數    _pDriverObject->MajorFunction[IRP_MJ_CREATE] = _SyncCreateCloseDispatch;    _pDriverObject->MajorFunction[IRP_MJ_CLOSE] = _SyncCreateCloseDispatch;    _pDriverObject->MajorFunction[IRP_MJ_READ] = _SyncReadDispatch;    _pDriverObject->DriverUnload = _UnloadDispatch;     // 分配一個自定義擴展 大小為sizeof(DEVEXT)    // By: LyShark.com    status = IoCreateDevice(_pDriverObject, sizeof(FILE_DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);    if (!NT_SUCCESS(status))        return status;     if (!pDeviceObject)        return STATUS_UNEXPECTED_IO_ERROR;     // 為全局變量賦值    strcpy(global_char[0], "hi,lyshark A");    strcpy(global_char[1], "hi,lyshark B");    strcpy(global_char[2], "hi,lyshark C");    strcpy(global_char[3], "hi,lyshark D");    strcpy(global_char[4], "hi,lyshark E");     // 指定讀寫方式為 直接I/O MDL模式    pDeviceObject->Flags |= DO_DIRECT_IO;     // 數據傳輸時地址校驗大小    pDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;    status = IoCreateSymbolicLink(&Win32DeivceName, &DeviceName);     pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;    return STATUS_SUCCESS;}
    

    對于應用層來說并沒有什么特別的,同樣調用ReadFile讀取內核中的參數,同樣for循環讀取五次,代碼如下:

    #include <stdio.h>#include <Windows.h> int main(int argc, char *argv[]){    HANDLE hFile;    char Buffer[10] = { 0 };    DWORD dwRet = 0;    BOOL bRet;     hFile = CreateFileA("\\\\.\\LySharkSync", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);    if (hFile == INVALID_HANDLE_VALUE)        return 0;     for (int x = 0; x < 5; x++)    {        bRet = ReadFile(hFile, Buffer, 20, &dwRet, NULL);        if (!bRet)        {            CloseHandle(hFile);            return 0;        }        printf("讀入數據: %s -> 讀取長度: %d \n", Buffer, dwRet);    }    return 0;}
    

    這段代碼運行效果如下:

    與同步模式不同,異步模式雖然同樣使用ReadFile實現通信,但在通信中引入了Event事件通知機制,這也是異步與同步最大的區別所在,用戶層可以分別創建多個Event事件,等待內核依次做出相應并最終一并返回。

    首先驅動內定義了_DeviceExtension自定義接口,該接口用于保存此次事件所對應的Irp以及其所對應的DPC時間等。

    異步分發函數_AsyncReadDispatch同樣是被IRP_MJ_READ派遣函數觸發的,觸發后其內部會首先IoGetCurrentIrpStackLocation得到當前IRP的堆棧信息,然后設置IoMarkIrpPending()并最終將該IRP通過InsertTailList()插入到IRP鏈表內等待被處理。

    IoMarkIrpPending

    用于標記指定的IRP,標志著某個驅動的分發例程(分發函數)因需要被其他的驅動程序進一步處理最終返回STATUS_PENDING狀態。

    函數_CustomDpc則是定時器內部要執行的具體操作,在DriverEntry驅動入口處做了如下初始化,初始化了鏈表,并初始化了一個定時器,最后啟動這個定時器每隔1秒都會執行一次_CustomDpc如果我們的IRP鏈表內IsListEmpty() 檢測存在數據,則會主動拷貝內存RtlCopyMemory并推送到應用層。

    // 初始化IRP鏈表InitializeListHead(&pDevExt->IrpList);// 初始化定時器KeInitializeTimer(&(pDevExt->timer));// 初始化DPC pDevExt是傳給_CustomDpc函數的參數KeInitializeDpc(&pDevExt->dpc, (PKDEFERRED_ROUTINE)_CustomDpc, pDevExt); // 設置定時時間位1spDevExt->liDueTime = RtlConvertLongToLargeInteger(-10000000);// 啟動定時器KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);
    

    驅動層完成代碼如下所示:

    #include <ntddk.h> // 自定義接口擴展typedef struct _DeviceExtension{    LIST_ENTRY IrpList;    KTIMER timer;    LARGE_INTEGER liDueTime;    KDPC dpc;}DEV_EXT, *PDEV_EXT; // 默認派遣函數NTSTATUS _DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp){    _pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;    _pIrp->IoStatus.Information = 0;    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);    return _pIrp->IoStatus.Status;} // 創建派遣函數NTSTATUS _AsyncCreateCloseDispatch(PDEVICE_OBJECT _pDevcieObject, PIRP _pIrp){    _pIrp->IoStatus.Status = STATUS_SUCCESS;    _pIrp->IoStatus.Information = 0;    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);    return  _pIrp->IoStatus.Status;} // 讀取派遣函數NTSTATUS _AsyncReadDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp){    NTSTATUS status;    PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(_pIrp);    PDEV_EXT pDevExt = (PDEV_EXT)_pDeviceObject->DeviceExtension;     IoMarkIrpPending(_pIrp);     // 將IRP插入自定義鏈表中插入的是ListEntry    InsertTailList(&pDevExt->IrpList, &_pIrp->Tail.Overlay.ListEntry);     // 返回pending 主要返回給I/O管理器的值必須和IRP的Pending標志位一致    // By: LyShark.com    // 即調用iomarkirppending和返回值要一致    return STATUS_PENDING;} // DPC線程VOID _CustomDpc(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2){    PIRP pIrp;    PDEV_EXT pDevExt = (PDEV_EXT)DeferredContext;    PVOID pBuffer = NULL;    ULONG uBufferLen = 0;    PIO_STACK_LOCATION pIrpStack = NULL;     do    {        if (!pDevExt)        {            break;        }         // 檢查尾端IRP鏈表是否為空 為空則跳出        if (IsListEmpty(&pDevExt->IrpList))        {            break;        }         // 從IRP鏈表中取出一個IRP并完成該IRP 取出的是ListEntry的地址        PLIST_ENTRY pListEntry = (PLIST_ENTRY)RemoveHeadList(&pDevExt->IrpList);        if (!pListEntry)            break;         pIrp = (PIRP)CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry);        pIrpStack = IoGetCurrentIrpStackLocation(pIrp);         DbgPrint("當前DPC Irp: 0x%x\n", pIrp);         // 驅動程序的讀寫方式位直接I/O        pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress);        if (pBuffer == NULL)        {            pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;            pIrp->IoStatus.Information = 0;            IoCompleteRequest(pIrp, IO_NO_INCREMENT);             break;        }        uBufferLen = pIrpStack->Parameters.Read.Length;        DbgPrint("讀取DPC長度: %d\n", uBufferLen);         // 支持5字節以下的讀請求        uBufferLen = uBufferLen > 13 ? 13 : uBufferLen;         // 復制請求內容        RtlCopyMemory(pBuffer, "hello lyshark", uBufferLen);         pIrp->IoStatus.Status = STATUS_SUCCESS;        pIrp->IoStatus.Information = uBufferLen;         // 完成該IRP        IoCompleteRequest(pIrp, IO_NO_INCREMENT);    } while (FALSE);     // 重新設置定時器    KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);} // 卸載驅動VOID _UnloadDispatch(PDRIVER_OBJECT _pDriverObject){    UNICODE_STRING Win32DeviceName;    PDEV_EXT pDevExt = (PDEV_EXT)_pDriverObject->DeviceObject->DeviceExtension;     RtlInitUnicodeString(&Win32DeviceName, L"\\DosDevices\\LySharkAsync");     // 刪除定時器    // LyShark    KeCancelTimer(&pDevExt->timer);    // 刪除創建的設備    IoDeleteDevice(_pDriverObject->DeviceObject);} // 驅動入口NTSTATUS DriverEntry(PDRIVER_OBJECT _pDriverObject, PUNICODE_STRING _pRegistryPath){    UNICODE_STRING DeviceName, Win32DeivceName;    PDEVICE_OBJECT pDeviceObject = NULL;    NTSTATUS status;    PDEV_EXT pDevExt = NULL;    HANDLE hThread;    OBJECT_ATTRIBUTES ObjectAttributes;    CLIENT_ID CID;     RtlInitUnicodeString(&DeviceName, L"\\Device\\LySharkAsync");    RtlInitUnicodeString(&Win32DeivceName, L"\\DosDevices\\LySharkAsync");     for (ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)    {        _pDriverObject->MajorFunction[i] = _DefaultDispatch;    }     _pDriverObject->MajorFunction[IRP_MJ_CREATE] = _AsyncCreateCloseDispatch;    _pDriverObject->MajorFunction[IRP_MJ_CLOSE] = _AsyncCreateCloseDispatch;    _pDriverObject->MajorFunction[IRP_MJ_READ] = _AsyncReadDispatch;    _pDriverObject->DriverUnload = _UnloadDispatch;     // 分配自定義擴展    status = IoCreateDevice(_pDriverObject, sizeof(DEV_EXT), &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);    if (!NT_SUCCESS(status))        return status;    if (!pDeviceObject)        return STATUS_UNEXPECTED_IO_ERROR;     pDeviceObject->Flags |= DO_DIRECT_IO;    pDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;    status = IoCreateSymbolicLink(&Win32DeivceName, &DeviceName);     pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;    pDevExt = (PDEV_EXT)pDeviceObject->DeviceExtension;     // 初始化IRP鏈表    InitializeListHead(&pDevExt->IrpList);    // 初始化定時器    KeInitializeTimer(&(pDevExt->timer));    // 初始化DPC pDevExt是傳給_CustomDpc函數的參數    KeInitializeDpc(&pDevExt->dpc, (PKDEFERRED_ROUTINE)_CustomDpc, pDevExt);     // 設置定時時間位1s    pDevExt->liDueTime = RtlConvertLongToLargeInteger(-10000000);    // 啟動定時器    KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);     return STATUS_SUCCESS;}
    

    驅動層說完了,接下來是應用層,對于應用層來說,需要使用CreateEvent打開通知事件,或者叫做事件對象,然后當有通知時,則直接使用ReadFile讀取對應的緩沖區,當所有讀取全部結束WaitForMultipleObjects等待結束即輸出結果。

    #include <stdio.h>#include <Windows.h> int main(int argc, char *argv[]){    HANDLE hFile;    char Buffer[3][32] = { 0 };    DWORD dwRet[3] = { 0 };    OVERLAPPED ol[3] = { 0 };    HANDLE hEvent[3] = { 0 };     // By:LyShark    hFile = CreateFileA("\\\\.\\LySharkAsync", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,        NULL);    if (INVALID_HANDLE_VALUE == hFile)        return  0;     // event用來通知請求完成    hEvent[0] = CreateEvent(NULL, TRUE, FALSE, NULL);    ol[0].hEvent = hEvent[0];     hEvent[1] = CreateEvent(NULL, TRUE, FALSE, NULL);    ol[1].hEvent = hEvent[1];     hEvent[2] = CreateEvent(NULL, TRUE, FALSE, NULL);    ol[2].hEvent = hEvent[2];     // 讀取事件內容到緩存    ReadFile(hFile, Buffer[0], 13, &dwRet[0], &ol[0]);    ReadFile(hFile, Buffer[1], 13, &dwRet[1], &ol[1]);    ReadFile(hFile, Buffer[2], 13, &dwRet[2], &ol[2]);     // 等待三個事件執行完畢    WaitForMultipleObjects(3, hEvent, TRUE, INFINITE);     // 輸出結果    printf("緩存LyShark A: %s \n", Buffer[0]);    printf("緩存LyShark B: %s \n", Buffer[1]);    printf("緩存LyShark C: %s \n", Buffer[2]);     CloseHandle(hFile);    return  0;}
    

    這段代碼最終運行效果如下:

    通信char函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    本文更多的是根據調試Windows Server 2003,分析漏洞成因。但是AD域并沒有對其進行強校驗。通過建立與域控同名卻不以\$結尾的機器賬戶,即DC,對域控進行欺騙。至此便得到了高權限ST。從上圖中可以很明確的看到域控的機器名為WINSRVSERVER$,之后會使用WINSRVSERVER作為機器賬戶名進行欺騙。攻擊準備工作相關準備工作不是本文重點,可以在noPac項目中學習。
    項目的bug和不足作者只實現了TCP-client的代碼,并且x86下測試通過,但是在x64模式下連接到服務端時出現了錯誤。
    在開始學習Async反向通信之前先來研究一個Sync正向通信案例,不論是正向反向通信其在通信模式上與《驅動開發:通過ReadFile與內核層通信》所介紹的通信模式基本一致,都是通過ReadFile觸發驅動中的IRP_MJ_READ讀取派遣。唯一的區別是在傳輸數據時使用了MmGetSystemAddressForMdl方式,它將給定MDL描述的物理頁面映射到系統空間,并調用RtlCopyMemory()將全局字符串復制到這個空間內,這樣客戶端就可以循環讀取內核傳出的數據。
    MITM Fuzz下圖是用戶層與內核層實現通信的過程,可以看到,最后是通過NtDeviceIoControlFile來分發給相應驅動對象的派遣函數的,因此,可以通過對該函數進行HOOK操作。如果將修改以后的數據發送給NtDeviceIoControlFile函數以后,發生了內核崩潰或藍屏,往往預示著該驅動程序可能存在內核漏洞。
    由于RPC的規范非常復雜,且相關內容很少,所以我也只是根據文檔和調式盡可能地將我的理解貼上來,如果有誤歡迎指正。MyRPCServer向外只導出一個函數,并打印出4個傳入的參數內容.MyRPCClient是一個最簡單的RPC客戶端,它調用MyRPCServer向外導出的HelloProc函數,并傳入4個參數。
    BEServer - BattlEye服務器,收集上傳的信息,并判定作弊行為。本次分析的是BEDaisy,也就是BE內核驅動中的各種檢測。上傳的內容主要是一些黑名單特征,這些特征應該是從服務器下發的,因此可以在不重新編譯驅動的情況下,動態調整檢測的特征。沒有給定偏移量的特征,BE在檢測時會按子串匹配的方式嘗試所有位置進行匹配。
    一看題目給出的sys文件,可以確定是本人最擅長的Windows內核和注入相關題目,必須要把這個驅動安排的明明白白的。
    linux跟蹤技術之ebpf
    2022-12-30 10:51:15
    eBPF是一項革命性的技術,起源于 Linux 內核,可以在操作系統內核等特權上下文中運行沙盒程序。它可以安全有效地擴展內核的功能,而無需更改內核源代碼或加載內核模塊。比如,使用ebpf可以追蹤任何內核導出函數的參數,返回值,以實現kernel hook 的效果;通過ebpf,還可以在網絡封包到達內核協議棧之前就進行處理,這可以實現流量控制,甚至隱蔽通信
    windows消息機制詳解
    2022-06-10 16:15:55
    要想深入理解windows,消息機制的知識是必不可少的。
    在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类