7.串口的過濾

設(shè)備綁定的內(nèi)核API之一

驅(qū)動 --> 生成多個 --> 設(shè)備對象 --> 對應(yīng) --> 真實的一個設(shè)備

windows --> 發(fā)送消息 --> 設(shè)備對象

消息 --> 驅(qū)動會攔截 --> 轉(zhuǎn)發(fā)給 --> 設(shè)備對象

驅(qū)動程序和驅(qū)動對象是一對一的關(guān)系枣耀,即一個.sys文件對應(yīng)一個DRIVER_OBJECT

驅(qū)動對象和設(shè)備對象是一對多的關(guān)系,即一個DRIVER_OBJECT中會有多個DEVICE_OBJECT

驅(qū)動對象中保存第一個設(shè)備對象的指針十酣,即DRIVER_OBJECT中的PDEVICE_OBJECT DeviceObject;指向第一個設(shè)備對象

設(shè)備對象中保存下一個設(shè)備對象的指針,形成設(shè)備鏈,即DEVICE_OBJECT中的struct _DEVICE_OBJECT *NextDevice;構(gòu)成一個設(shè)備鏈

設(shè)備對象中保存驅(qū)動對象的指針谣光,即DEVICE_OBJECT中的struct _DRIVER_OBJECT *DriverObject;指向驅(qū)動對象

一對多的數(shù)據(jù)關(guān)系瞬场,是多保存一的關(guān)鍵字段野瘦,即每個DEVICE_OBJECT中都有一個*DriverObject會找到自己所屬的驅(qū)動對象

NTSTATUS

IoAttachDevice(

_In_ _When_(return==0, __drv_aliasesMem)

PDEVICE_OBJECT SourceDevice,//調(diào)用者生成的 用來過濾的 虛擬設(shè)備

_In_ ?PUNICODE_STRING TargetDevice,//要綁定的設(shè)備,是設(shè)備的名字,所以沒有名字的設(shè)備不能用這個API

_Out_ PDEVICE_OBJECT *AttachedDevice//返回的指針 的指針,綁定成功后,被綁定的設(shè)備指針返回到這個地址

);

如果設(shè)備被多個其他設(shè)備綁定,其他設(shè)備會組成設(shè)備棧,后者居頂

綁定串口很容易,因為串口的名字是固定的

但是如果省內(nèi)沒有名字,則不是用上面這個API,可以用下面這個

IoAttachDeviceToDeviceStack

IoAttachDeviceToDeviceStackSafe <--這個更安全,但是要Windows xp 以上版本才能使用

上面這兩個函數(shù)都是根據(jù)設(shè)備對象的指針而不是名字進(jìn)行綁定的,所以可以綁定沒有名字的串口

NTSTATUS

IoAttachDeviceToDeviceStackSafe(

_In_ ?PDEVICE_OBJECT SourceDevice,//過濾設(shè)備

_In_ ?PDEVICE_OBJECT TargetDevice,//要綁定的設(shè)備(設(shè)備棧中的設(shè)備)是指針

_Outptr_ PDEVICE_OBJECT *AttachedToDeviceObject//返回最終被綁定的設(shè)備,實際上就是綁定之前設(shè)備棧最頂層的設(shè)備

);

上面這個函數(shù)在 ntddk.h 定義

綁定設(shè)備的最終過程:生成一個過濾設(shè)備(就是設(shè)備棧中的設(shè)備,之后它會變成隨便棧的最頂層)

生成設(shè)備

NTSTATUS

IoCreateDevice(

_In_ ?PDRIVER_OBJECT DriverObject,//本驅(qū)動的驅(qū)動對象,就是 DriverEntry傳入的

_In_ ?ULONG DeviceExtensionSize,//設(shè)備擴(kuò)展,填0

_In_opt_ PUNICODE_STRING DeviceName,//設(shè)備名字(過濾設(shè)備一般不要名字,所以填 NULL)

_In_ ?DEVICE_TYPE DeviceType,//設(shè)備類型(保持和被綁定的設(shè)備類型一致)

_In_ ?ULONG DeviceCharacteristics,//設(shè)備特征,填0

_In_ ?BOOLEAN Exclusive,

_Outptr_result_nullonfailure_

_At_(*DeviceObject,

__drv_allocatesMem(Mem)

_When_((((_In_function_class_(DRIVER_INITIALIZE))

||(_In_function_class_(DRIVER_DISPATCH)))),

__drv_aliasesMem))

PDEVICE_OBJECT *DeviceObject

);

//串口名

UNICODE_STRING com_name = RTL_CONSTANT_STRING(L"\\Device\\Serial0");

NTSTATUS status = IoAttachDevice();

分發(fā)函數(shù),處理所有串口的寫請求 串口--------------應(yīng)用程序

NTSTATUS ccpDispatch(PDEVICE_OBJECT device,PIRP irp){

PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);

NTSTATUS status;

ULONG i,j;

//判斷發(fā)送給哪個設(shè)備

for(i=0;i

if(s_fltobi[i] == device){

//所有的電源操作直接放過,直接發(fā)送,然后返回說已經(jīng)處理

if(irpsp->MajorFunction == IRP_MJ_POWER){

PoStartNextPowerIrp(irp);

IoSkipCurrentIrpStackLocation(irp);

return PoCallDriver(s_nextobj[i],irp);

}

//只過濾寫操作,獲得寫請求的緩沖區(qū)和長度

if(irpsp->MajorFunction == IRP_MJ_WRITE){

//如果是寫,獲得長度

ULONG len = irpsp->Parameters.Write.Length;

//然后獲得緩沖區(qū)

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;

}

//打印內(nèi)容

for(j=0;j

DbgPrint("comcap:send data: %2x\r\n",buf[j]);

}

}

//這些請求直接下發(fā)執(zhí)行即可,不處理

IoSkipCurrentIrpStackLocation(irp);

return IpCallDriver(s_nextobj[i],irp);

}

}

//如果請求不在所綁定的設(shè)備中.則有問題,直接返回參數(shù)錯誤

irp->IpStatus.Information = 0;

irp->IpStatus.Status = STATUS_INVALID_PARAMETER;

IpCompleteRequest(irp,IO_NO_INCREMENT);

return STATUS_SUCCESS;

}

動態(tài)卸載

IoDetachDevice 解除設(shè)備綁定

IoDeleteDevice 刪除生成的設(shè)備

KeDelayExecutionThread 延時

卸載過濾參數(shù)有一個問題,我們要終止這個過濾程序,但是一些IRP可能還在這個過濾程序的處理過程中

要取消這些請求很麻煩,而且不一定成功,所以這里的解決方案是等待5秒在卸載

代碼:

#include

#include

//串口過濾驅(qū)動

//綁定過濾設(shè)備

NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *next){

NTSTATUS status;

PDEVICE_OBJECT topdev = NULL;

//生成設(shè)備并綁定

status = IoCreateDevice(driver, 0, NULL, oldobj->DeviceType, 0, FALSE, fltobj);

if (status != STATUS_SUCCESS){ return status; }

//拷貝主要標(biāo)志位

if (oldobj->Flags & DO_BUFFERED_IO){ (*fltobj)->Flags |= DO_BUFFERED_IO; }

if (oldobj->Flags & DO_DIRECT_IO){ (*fltobj)->Flags |= DO_DIRECT_IO; }

if (oldobj->Characteristics & FILE_DEVICE_SECURE_OPEN){ (*fltobj)->Characteristics |= FILE_DEVICE_SECURE_OPEN; }

(*fltobj)->Flags |= DO_POWER_PAGABLE;

//講一個設(shè)備綁定到另一個設(shè)備上

topdev = IoAttachDeviceToDeviceStack(*fltobj, oldobj);

if (topdev == NULL){

IoDeleteDevice(*fltobj);

*fltobj = NULL;

status = STATUS_UNSUCCESSFUL;

return status;

}

*next = topdev;

//設(shè)置這個設(shè)備已經(jīng)啟動

(*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING;

return STATUS_SUCCESS;

}

//打開串口

PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS *status){

UNICODE_STRING name_str;

static WCHAR name[32] = { 0 };

PFILE_OBJECT fileobj = NULL;

PDEVICE_OBJECT devobj = NULL;

//根據(jù)ID轉(zhuǎn)換成串口的名字

memset(name, 0, sizeof(WCHAR)* 32);

RtlStringCchPrintfW(name, 32, L"\\Device\\Serial%d", id);

RtlInitUnicodeString(&name_str, name);

//打開設(shè)備對象

*status = IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &devobj);

//如果打開成功,把文件對象引用解除

if (*status == STATUS_SUCCESS){ ObDereferenceObject(fileobj); }

return devobj;

}

//綁定所有的串口

#define CCP_MAX_COM_ID 32

static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID] = { 0 };

static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = { 0 };

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; }

ccpAttachDevice(driver, com_ob, &s_fltobj[i], &s_nextobj[i]);

}

}

//分發(fā)函數(shù)

NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp){

PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);

NTSTATUS status;

ULONG i, j;

//發(fā)送給哪些設(shè)備(設(shè)備最多有 CCP_MAX_COM_ID 個)

for (i = 0; i < CCP_MAX_COM_ID; i++){

if (s_fltobj[i] == device){

//如果是電源操作,放過.直接發(fā)送,然后返回

if (irpsp->MajorFunction == IRP_MJ_POWER){

PoStartNextPowerIrp(irp);

IoSkipCurrentIrpStackLocation(irp);

return PoCallDriver(s_nextobj[i], irp);

}

//處理寫請求操作.獲得緩沖區(qū)及其長度,然后打印

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("misaka: send data %2x\r\n",buf[j]); }

}

//其他請求不處理,直接下發(fā)

IoSkipCurrentIrpStackLocation(irp);

return IoCallDriver(s_nextobj[i], irp);

}

}

//如果不在綁定的設(shè)備中,則可能有問題,直接返回參數(shù)錯誤

irp->IoStatus.Information = 0;

irp->IoStatus.Status = STATUS_INVALID_PARAMETER;

IoCompleteRequest(irp, IO_NO_INCREMENT);

return STATUS_SUCCESS;

}

//動態(tài)卸載

#define DELAY_ONE_MICROSECOND (-10)

#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)

#define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)

void ccpUnload(PDRIVER_OBJECT drv){

ULONG i;

LARGE_INTEGER interval;

//解除綁定

for (i = 0; i < CCP_MAX_COM_ID; i++){

if (s_nextobj[i] != NULL){ IoDetachDevice(s_nextobj[i]); }

}

//睡眠5秒,等待所有的 IRP 處理結(jié)束

interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND);

KeDelayExecutionThread(KernelMode, FALSE, &interval);

//刪除設(shè)備

for (i = 0; i < CCP_MAX_COM_ID; i++){

if (s_fltobj[i] != NULL){ IoDeleteDevice(s_fltobj[i]); }

}

DbgPrint("misaka: bye ,driver unload successfully.");

}

//驅(qū)動入口

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path){

DbgPrint("misaka: hello ,wecome to use misaka driver service .");

size_t i;

//所有的分發(fā)函數(shù)都設(shè)置為一樣的

for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++){ driver->MajorFunction[i] = ccpDispatch; }

//動態(tài)卸載

driver->DriverUnload = ccpUnload;

//綁定所有串口

ccpAttachAllComs(driver);

return STATUS_SUCCESS;

}

說明:因為wein7系統(tǒng)沒有找到教程的超級終端,隨意沒辦法測試,不過驅(qū)動打開是正常的.關(guān)閉也是正常的.串口控制大概也應(yīng)該正常!

misaka: hello ,wecome to use misaka driver service .

misaka: bye ,driver unload successfully.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市查剖,隨后出現(xiàn)的幾起案子钾虐,更是在濱河造成了極大的恐慌,老刑警劉巖梗搅,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件禾唁,死亡現(xiàn)場離奇詭異,居然都是意外死亡无切,警方通過查閱死者的電腦和手機(jī)荡短,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哆键,“玉大人掘托,你說我怎么就攤上這事〖冢” “怎么了闪盔?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辱士。 經(jīng)常有香客問我泪掀,道長,這世上最難降的妖魔是什么颂碘? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任异赫,我火速辦了婚禮,結(jié)果婚禮上头岔,老公的妹妹穿的比我還像新娘塔拳。我一直安慰自己,他們只是感情好峡竣,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布靠抑。 她就那樣靜靜地躺著,像睡著了一般适掰。 火紅的嫁衣襯著肌膚如雪颂碧。 梳的紋絲不亂的頭發(fā)上荠列,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機(jī)與錄音稚伍,去河邊找鬼弯予。 笑死,一個胖子當(dāng)著我的面吹牛个曙,可吹牛的內(nèi)容都是我干的锈嫩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼垦搬,長吁一口氣:“原來是場噩夢啊……” “哼呼寸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起猴贰,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤对雪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后米绕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瑟捣,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年栅干,在試婚紗的時候發(fā)現(xiàn)自己被綠了迈套。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡碱鳞,死狀恐怖桑李,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窿给,我是刑警寧澤贵白,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站崩泡,受9級特大地震影響禁荒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜角撞,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一圈浇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧靴寂,春花似錦、人聲如沸召耘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽污它。三九已至剖踊,卻和暖如春庶弃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背德澈。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工歇攻, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梆造。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓缴守,卻偏偏與公主長得像,于是被迫代替她去往敵國和親镇辉。 傳聞我的和親對象是個殘疾皇子屡穗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容