《Windows內核安全與驅動開發》 7.1&7.2&7.3 串口的過濾

《Windows內核安全與驅動開發》閱讀筆記 -- 索引目錄 html

《Windows內核安全與驅動開發》 7.1&7.2&7.3 串口的過濾編程

1、設備綁定的內核API數組

  1. 進行過濾的最主要的方法是對一個____進行綁定。
  2. 咱們能夠首先認爲:一個真實的設備對應一個_____。經過編程能夠生成一個__的____,並綁定到一個___的設備上。一旦綁定,則原本操做系統發送給____的請求,就會發送到____上。
  3. 一個簡單的API綁定函數是_____,必須是有____的設備,才能使用這個內核API進行綁定。
  4. 若是這個設備被其餘設備綁定了,它們在一塊兒組成一組設備,被稱爲____。實際上,IoAttachDevice老是會綁定___上最__層的那個設備。

2、生成過濾設備並綁定 與 從名字得到設備對象安全

  1. 在綁定一個設備以前,首先要知道如何生成一個用於____的設備,函數_____被用於生成過濾設備。
  2. 在綁定一個設備以前,應該把這個設備對象的多個____和要綁定的目標對象一致,包括標誌和特徵。
  3. 在知道一個設備名字的狀況下,可使用函數____來得到這個設備對象的指針。該函數的FileObject是一個返回參數,在得到這個設備對象的同時會得到一個____,就打開串口而言沒什麼用,但使用這個函數以後必須把這個對象____,不然會引發____。

3、綁定全部串口函數

  1. 串口x的設備名爲____。

4、請求的區分測試

  1. 每一個驅動程序只有__個驅動對象。
  2. 每一個驅動對象能夠生成___個驅設備對象。
  3. 若干個設備(它們能夠屬於__的驅動)依次綁定造成一個____,老是最__端的設備先收到請求。

5、請求的結局spa

  1. 對請求的過濾,有三種結局:____、_____、_____。
  2. 處理第一種最簡單,首先調用函數____跳過當前棧空間;而後調用____把這個請求發送給___的設備。請注意,由於____的設備已經被過濾設備所綁定,所以首先接收到IRP的是過濾設備的對象。

6、寫請求的數據操作系統

  1. 一個寫請求保存在那裏呢?IRP有三種,一種是___,一種是___,一種是____。不一樣的__類別,IRP的緩衝不一樣。
  2. ____是最有效率的解決方案。應用層的緩衝區地址直接放在___裏,在內核層去訪問。在____和_____一致的狀況下,是徹底正確的。但一旦內核進程進行____,這個訪問就結束了。
  3. 一種更簡單的解決的方案是把__層的地址空間映射到__空間,這須要在__中增長一個映射。固然這不須要編程者手動去修改,經過構造__就能實現這個功能。其能夠翻譯爲____。

 答案
翻譯

1、設備綁定的內核API指針

  1. 設備對象
  2. 設備對象  虛擬 設備對象  真實 真實設備 虛擬設備
  3. IoAttchDevice 名稱
  4. 設備棧 設備棧 頂

2、生成過濾設備並綁定 與 從名字得到設備對象

  1. 過濾 IoCreateDevice
  2. 子域
  3. IoGetDeviceObjectPointer 文件對象 關閉 內存泄漏

3、綁定全部串口

  1. \Device\Serialx

4、請求的區分

  1. 一個
  2. 若干
  3. 設備棧 頂

5、請求的結局

  1. 請求被容許經過了 請求直接被否決了 過濾完成了這個請求
  2. IoSkipCurrentIrpStackLocation IoCallDriver 真實

6、寫請求的數據

  1. irp->MDLAddress irp->UserBuffer irp->AssociatedIrp.SystemBuffer
  2. UserBuffer UserBuffer 當前進程  發送請求進程   進程切換
  3. 應用層 內核層 頁表 MDL 內存描述符鏈

 


 三個基本概念

1、過濾的概念

  過濾的概念本質就是在一個驅動中對每一個串口生成一個過濾設備,將每一個過濾設備附加到對應的設備棧中。

  消息自上而下發送,在到達串口前必須通過過濾設備,這樣來對此進行過濾。

  

2、設備棧的概念

   拿一個函數來舉個例子

NTSTATUS IoAttachDeviceToDeviceStackSafe( IN PDEVICE_OBJECT SourceDevice, IN PDEVICE_OBJECT TargetDevice, OUT PDEVICE_OBJECT *AttachedToDeviceObject );

  其中之因此會輸出 AttchedToDeviceObject,是由於當將過濾設備SourceDevice卸載時須要這個參數。

  

 

 

3、設備、驅動與IRP請求的概念

  如圖,一個驅動對象能夠只綁定一個分發函數來處理全部設備的請求。

  由於分發函數的第一個參數就是對應的設備對象,咱們能夠經過該成員來進行區分是來自該驅動對象的哪一個設備的。

  


 利用過濾設備實現監控的代碼

該代碼實現了一個驅動過濾的例子。

1、實驗效果

使用超級終端,端口2來創建鏈接(windbg每每使用端口1,再測試時沒法接收信息,後來端口2則能夠收到請求)

 

2、源代碼

/********* 做者:OneTrianee 編寫時間:2019/12/8 編譯環境:Win10+vs2019+"Empty WDM Driver" 代碼做用:生成過濾設備,綁定端口監視輸入數據 參考資料:《Windows內核安全與驅動開發》 注意事項: 1. 虛擬機中windbg每每會佔用COM1,此時通訊應該使用COM2(若是沒有,關閉去虛擬機設置中開一個。) 2. 開機時綁定端口2成功,可是卸載以後再綁定就只能綁定端口1,暫時不清楚緣由(接觸綁定沒發現問題..) 效果(DebugView): 分發函數接收到請求了!! comcap: Send Data: 61 分發函數接收到請求了!! comcap: Send Data: 73 ***********/ #include <ntddk.h> #include <ntstrsafe.h>

// sleep 時用到的宏
#define  DELAY_ONE_MICROSECOND  (-10)
#define  DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
#define  DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)

#define  CCP_MAX_COM_ID 32 // 假設有32個端口

//
// 函數聲明 // NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp); // 設備分發函數
void ccpUnload(PDRIVER_OBJECT drv); // 動態卸載驅動函數
void ccpAttachAllComs(PDRIVER_OBJECT driver); // 綁定全部串口函數
PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS* status); // 打開端口設備對象 
NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT* fltobj, PDEVICE_OBJECT* next ); // 將設備與驅動對象進行綁定 //
// 設備棧: // 
// IRP消息 ↓ // ---------- // |過濾設備| // ---------- // |端口設備| // ---------- // static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID] = { 0 }; // 過濾設備
static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = { 0 }; // 端口設備(綁定後會返回)

/* 函數名:ccpDispatch 函數做用:分發函數,分發設備的irp請求 參數1 - PDEVICE_OBJECT device:發送請求的目標設備 參數2 - PIRP irp: IRP請求內容 */ NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; ULONG i, j; //
    // 判斷是發送給哪一個過濾設備 // 若是設備存在,則打印出設備內容 //     DbgPrint("分發函數接收到請求了!!\n"); for (i = 0; i < CCP_MAX_COM_ID; i++) { if (s_fltobj[i] == device) { // 全部電源操做,所有直接放過。
            if (irpsp->MajorFunction == IRP_MJ_POWER) { // 直接發送,而後返回說已經被處理了。
 PoStartNextPowerIrp(irp); IoSkipCurrentIrpStackLocation(irp); return PoCallDriver(s_nextobj[i], irp); } // 咱們只考慮寫請求,若是爲寫請求,則打印出來
            if (irpsp->MajorFunction == IRP_MJ_WRITE) { // 獲取長度
                ULONG len = irpsp->Parameters.Write.Length; //
                // 獲取緩衝區(三中一個) //                 PUCHAR buf = NULL; if (irp->MdlAddress != NULL) buf = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority); else buf = (PUCHAR)irp->UserBuffer; if (buf == NULL) buf = (PUCHAR)irp->AssociatedIrp.SystemBuffer; // 打印內容
                for (j = 0; j < len; j++) { DbgPrint("comcap: Send Data: %2x\r\n", buf[j]); } } // 這些請求直接下發便可。咱們並不由止或改變它。
 IoSkipCurrentIrpStackLocation(irp); return IoCallDriver(s_nextobj[i], irp); } } // 若是根本就不在被綁定的設備中,那是有問題的,直接返回錯誤參數。
    irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } /* 函數名:ccpAttchAllComs 函數功能:生成過濾設備, 參數1 - dirver:生成過濾設備須要指明其在哪一個驅動中。 */
void ccpAttachAllComs(PDRIVER_OBJECT driver) { ULONG i; PDEVICE_OBJECT com_ob; NTSTATUS status; for (i = 0; i < CCP_MAX_COM_ID; i++) { // 獲取端口設備對象
        com_ob = ccpOpenCom(i, &status); if (com_ob == NULL) // 獲取失敗,繼續獲取下一個
            continue; else DbgPrint("綁定端口號%u成功!", i); // 在這裏綁定,並無論綁定是否成功
        ccpAttachDevice(driver, com_ob, &s_fltobj[i], &s_nextobj[i]); } } /* 函數名:ccpOpenCom 函數功能:打開驅動端口設備 參數1 - ULONG id:打開的端口號ID,函數內自動給轉換爲設備名,而後打開 參數2 - NTSTATUS* status:操做狀態 返回值 - PDEVICE_OBJECT:若是打開成功,返回端口設備句柄指針 */ PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS* status) { UNICODE_STRING name_str; static WCHAR name[32] = { 0 }; PFILE_OBJECT fileobj = NULL; PDEVICE_OBJECT devobj = NULL; //
    // 將 ID 轉換爲相應的端口設備名UNICODE_STRING //     memset(name, 0, sizeof(WCHAR) * 32); RtlStringCchPrintfW( (NTSTRSAFE_PWSTR)name, 32, (NTSTRSAFE_PWSTR)L"\\Device\\Serial%d", id); RtlInitUnicodeString(&name_str, name); //
    // 打開設備對象 // 若是打開成功,刪除文件對象,防止內存泄漏 //     *status = IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &devobj); if (*status == STATUS_SUCCESS) ObDereferenceObject(fileobj); // 返回端口設備句柄
    return devobj; } /* 函數名: 函數功能:生成過濾設備,並綁定串口設備,保存過濾設備fltobj和原棧頂設備next 參數1 - PDRIVER_OBJECT driver:生成過濾設備時所在的驅動對象 參數2 - PDEVICE_OBJECT oldobj:已生成的端口設備 參數3 - PDEVICE_OBJECT* fltobj:生成的過濾設備(保存在數組中) 參數4 - PDEVICE_OBJECT* next:原設備棧頂的設備 */ NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT* fltobj, PDEVICE_OBJECT* next ) { NTSTATUS status; PDEVICE_OBJECT topdev = NULL; //
    // 生成過濾設備, // 注意生成設備的屬性與被綁定設備一致 //     status = IoCreateDevice(driver, 0, NULL, oldobj->DeviceType, 0, FALSE, fltobj); if (status != STATUS_SUCCESS) return status; //
    // 拷貝重要的標誌位 //     if (oldobj->Flags & DO_BUFFERED_IO) (*fltobj)->Flags |= DO_BUFFERED_IO; if (oldobj->Flags & DO_DIRECT_IO) (*fltobj)->Flags |= DO_DIRECT_IO; if (oldobj->Flags & DO_BUFFERED_IO) (*fltobj)->Flags |= DO_BUFFERED_IO; if (oldobj->Characteristics & FILE_DEVICE_SECURE_OPEN) (*fltobj)->Characteristics |= FILE_DEVICE_SECURE_OPEN; (*fltobj)->Flags |= DO_POWER_PAGABLE; //
    // 綁定過濾設備到端口的設備棧中 //     topdev = IoAttachDeviceToDeviceStack(*fltobj, oldobj); if (topdev == NULL) { // 若是綁定失敗,則銷燬設備,以後從新來過
        IoDeleteDevice(*fltobj); *fltobj = NULL; status = STATUS_UNSUCCESSFUL; return status; } *next = topdev; // 存儲原來棧頂的設備 //
    // 設置這個設備已京啓動 //     (*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } /* 函數名:ccpUnload 函數做用:動態卸載驅動對象 參數1 - PDRIVER_OBJECT drv:須要卸載的驅動對象指針 */
void ccpUnload(PDRIVER_OBJECT drv) { ULONG i; LARGE_INTEGER interval; // 首先解除綁定
    for (i = 0; i < CCP_MAX_COM_ID; i++) { if (s_nextobj[i] != NULL) /* |--------| |過濾設備| |--------| |next設備| // 將該層上面的過濾設備給卸載掉 |--------| |xxxx設備| |--------| |端口設備| |--------| */ IoDetachDevice(s_nextobj[i]); } // 睡眠5秒,等待全部irp處理結束
    interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND); KeDelayExecutionThread(KernelMode, FALSE, &interval); // 刪除這些設備
    for (i = 0; i < CCP_MAX_COM_ID; i++) { if (s_fltobj[i] != NULL) { IoDeleteDevice(s_fltobj[i]); } } } NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { size_t i; DbgPrint("過濾設備安裝成功!"); // 全部分發函數都設置成同樣的
    for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { driver->MajorFunction[i] = ccpDispatch; } // 支持動態卸載
    driver->DriverUnload = ccpUnload; // 綁定全部串口
 ccpAttachAllComs(driver); // 直接返回成功便可
    return STATUS_SUCCESS; }

 

原文出處:https://www.cnblogs.com/onetrainee/p/12006226.html

相關文章
相關標籤/搜索