《Windows內核安全與驅動開發》閱讀筆記 -- 索引目錄 html
《Windows內核安全與驅動開發》 7.1&7.2&7.3 串口的過濾編程
1、設備綁定的內核API數組
2、生成過濾設備並綁定 與 從名字得到設備對象安全
3、綁定全部串口函數
4、請求的區分測試
5、請求的結局spa
6、寫請求的數據操作系統
答案
翻譯
1、設備綁定的內核API指針
2、生成過濾設備並綁定 與 從名字得到設備對象
3、綁定全部串口
4、請求的區分
5、請求的結局
6、寫請求的數據
三個基本概念
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