tcp filter例子
我先描述一下2000/nt下的tcp/ip協(xié)議的一些情況。2000/nt下,ip,tcp,udp是在?一個驅(qū)動程序里實現(xiàn)的,叫做tcp.sys,這個驅(qū)動程序創(chuàng)建了3個設備,就是ip,tcp,udp?
。?
?首先描述一下driverentry。首先當然是iocreatedevice,用file_device_unknown,?
因為tcp設備就是用的這個參數(shù)。代碼如下:?
????RtlInitUnicodeString(?&usDeviceName,?FILTER_NAME?);?
????status?=?IoCreateDevice(?DriverObject,?
????????????????????????????sizeof(DEVICE_EXTENSION),?
????????????????????????????&usDeviceName,?
????????????????????????????FILE_DEVICE_UNKNOWN,?
????????????????????????????0,?
????????????????????????????FALSE,?
????????????????????????????&pDevObj?);?
然后就調(diào)用iogetdeviceobjectpointer來得到tcp設備的指針。?
代碼如下:?
????RtlInitUnicodeString(?&usTargetName,?TARGET_NAME?);?
????status?=?IoGetDeviceObjectPointer(?&usTargetName,?
??????????????????????????????????????FILE_ALL_ACCESS,?
??????????????????????????????????????&pTargetFileObj,?
??????????????????????????????????????&pTargetDevObj?);?
注意TARGET_NAME是大小寫區(qū)分的,我是用#define?TARGET_NAME?L"\\Device\\Tcp",?
不能寫成#define?TARGET_NAME?L"\\Device\\tcp"。?
然后我們就開始調(diào)用IoAttachDeviceToDevieStatck插入到tcp設備。?
調(diào)用完成后,我們要讓我們的設備表現(xiàn)的和tcp一樣,于是把它的所有?
特性都從tcpobj復制(pdevobj->xxx=ptcpobj->xxx)。?
再掃描他tcpdriverobject的majorfunction,我們的driver必須都?
支持。最后設置driverunload,因為為了方便調(diào)試,我們必須寫一個?unload
前面已經(jīng)講過driverentry了,經(jīng)過driverentry,所有的原來應該發(fā)送到?
tcp設備的irp現(xiàn)在都發(fā)送到我們的設備的處理函數(shù)了,如果什么事情都不做,?
那么可以簡單的調(diào)用iocalldriver把這個irp發(fā)到tcp設備去讓它處理。?
當然,我們是要做些事情的,于是代碼如下:?
????UCHAR?MajorFunction,MinorFunction;?
????PDEVICE_EXTENSION??pDevExt?=?(PDEVICE_EXTENSION)?DeviceObject->DeviceExt?
ension;?
????PIO_STACK_LOCATION?pIrpStack?=?IoGetCurrentIrpStackLocation(?Irp?);?
????MajorFunction?=?pIrpStack->MajorFunction;?
????MinorFunction?=?pIrpStack->MinorFunction;?
????//DBGPRINT(?...)?
????ParseIrp(Irp);?
????IoCopyCurrentIrpStackLocationToNext(?Irp?);?
????IoSetCompletionRoutine(?Irp,?
????????????????????????????CompletionRoutine,?
????????????????????????????NULL,???//?context?
????????????????????????????TRUE,???//?InvokeOnSuccess?
????????????????????????????TRUE,???//?InvokeOnError?
????????????????????????????TRUE?);?//?InvokeOnCancel?
????return?IoCallDriver(?pDevExt->TargetDevObj,?Irp);?
代碼很簡單,除了parseirp之外,其他都是例行公事。對于tcp設備的前期處理?
就在這個函數(shù)里。后期處理的函數(shù)可以放在completionroution里。?
好了,我們已經(jīng)得到了發(fā)向tcp設備的所有irp了,這個時候我們的任務就是要了解?
tcp設備到底是如何工作的.?
這種情況下數(shù)據(jù)不經(jīng)過tcp設備,filter也就無從得到了。?
為了了解如何從發(fā)向tcp設備的irp中得到信息,首先我先描述一下tdi?client是如何?
與tcp通訊以及tdi?client一般是如何工作的。?
driverstdio里面有好幾個例子都是關于tdi?client的,但是這些例子都是基于它自己?
的類庫的,不過這些例子功能都很強大,其中一個usb+web的溫度計的創(chuàng)意簡直讓我目瞪?
口呆。?
因為我也做過pci溫度計的driver,但是我從來就沒有想到還可以加上一個web?server在?
里面。?
因為driverstdio里的例子太復雜,我在這里簡單描述一下,給一個小例子。?
因為發(fā)送過程較為簡單,先描述發(fā)送。?
首先調(diào)用pIrp?=?TdiBuildInternalDeviceControlIrp?(?
?????????????????????TDI_SEND_DATAGRAM,??????????????????????????????//?sub??
function?
?????????????????????pDeviceObject,??????????????????????????????????//?poin?
ter?to?device?object?
?????????????????????pTransportObject,???????????????????????????????//?poin?
ter?to?udp?object?
?????????????????????NULL,???????????????????????????????????????????//?poin?
ter?to?event?
?????????????????????NULL?);?????????????????????????????????????????//?poin?
ter?to?return?buffer?
分配一塊irp,然后調(diào)用?
??TdiBuildSendDatagram?(?
???????????????????????????pIrp,?????????????????????????????????????//?poin?
ter?to?irp?
???????????????????????????pDeviceObject,????????????????????????????//?poin?
ter?to?device?object?
???????????????????????????pTransportObject,?????????????????????????//?poin?
ter?to?file?object?
???????????????????????????NULL,?????????????????????????????????????//?comp?
letion?routine?
???????????????????????????NULL,?????????????????????????????????????//?comp?
letion?context?
???????????????????????????pMdl,?????????????????????????????????????//?poin?
ter?to?data?
???????????????????????????dBufferSize,??????????????????????????????//?size?
?of?data?
???????????????????????????pConnectInfo?);???????????????????????????//?conn?
ection?information?
不用我說也應該知道,數(shù)據(jù)是放在一個buf里面,調(diào)用這個函數(shù)之前,要先構件一個mdl?
,把這個buf?
放進去。?
然后...irp已經(jīng)好了,只要IoCallDriver(pUDPObject,PIrp)就行了...?
上面用的是UDP的例子(Datagram),但是tcp也是一樣,雖然函數(shù)有點差別,但是也是大?
同小異(TdiBuildSend)。?
為了搞清楚上面的兩個函數(shù)到底做了些什么(到底構建的irp是什么樣子),沒有必要去?
跟蹤,實際上,tdibuildxxx?
都不過是一個宏,你可以在tdikrnl.h里找到。打開tdikrnl.h看看,發(fā)現(xiàn)每個宏里都有?
這么一句:?
????????_IRPSP->MajorFunction?=?IRP_MJ_INTERNAL_DEVICE_CONTROL;?
于是我們知道tdi?client就是通過majorfunction=IRP_MJ_INTERNAL_DEVICE_CONTROL,m?
inorfunction=send/recv/...?
和tcp通訊的。這就使得我們確信,這種方法是可行的。(不是直接調(diào)用tdi函數(shù),而是?
發(fā)irp)?
接收的過程較為復雜(tdi?client的選擇較多,因此要考慮各種情況)?
開始的時候,我在ddk?document里看到這么一個宏:tdibuildreceive,我想ok,?
和send一樣,我當時想的很簡單,以為tdi?client要接受數(shù)據(jù)了,它就向tcp發(fā)一個?
irp,然后返回pending,當有數(shù)據(jù)來的時候,tcp處理完這個irp,然后tdi?client只要在?
這個irp的?
irp_complete里處理得到的數(shù)據(jù)就行了。這的確是tdi?client的一個選擇,但是?
當我試驗的時候,我發(fā)現(xiàn)至少wsock不是這樣做的,因為我打開了一個ie,瀏覽了一個網(wǎng)?
頁,?
但是我發(fā)現(xiàn)我發(fā)出的數(shù)據(jù)都很好的捕獲到了,但是接收的數(shù)據(jù)一個也沒有,而且事實上?
,?
tcp似乎就沒有受到minorfunction=tdi_receive的irp。我因為這件事苦惱了一段時間,?
?
后來我仔細的讀了讀ddk?document,發(fā)現(xiàn)tdi?client還有第2種選擇,首先向tcp發(fā)送一?
個?
irp,minorfunction=tdi_sethandler,設置一些回調(diào)函數(shù),然后當事情發(fā)生的時候,tc?
p?
就會調(diào)用這些回調(diào)函數(shù),這些函數(shù)名字是clientEventxxx。這個過程可以仔細看ddk?do?
cument,?
看TdiBuildSetEventHandler,就知道哪些過程可以用這個方法。receive也是其中的一?
個,于是?
我們的方法得到了。首先我們得到了irp,如果是set_eventhandler,那么就修改tdi?c?
lient?
向tcp設置的回調(diào)函數(shù)的入口,把它指向我們自己寫的一個函數(shù),同時保留原來的入口,?
?
然后在我們自己寫的函數(shù)里里調(diào)用它。?
代碼如下:?
???????PIO_STACK_LOCATION?pIrpStack?=?IoGetCurrentIrpStackLocation(?Irp?);?
?MajorFunction?=?pIrpStack->MajorFunction;?
?MinorFunction?=?pIrpStack->MinorFunction;?
?FileObject?=?pIrpStack->FileObject?;?
???????....?
?switch(MajorFunction)?
?{?
??...?
??case?IRP_MJ_INTERNAL_DEVICE_CONTROL??:?
??{?
???switch(MinorFunction)?
???{?
????...?
????case?TDI_SET_EVENT_HANDLER:?
????pRequestSetEvent?=?(PTDI_REQUEST_KERNEL_SET_EVENT)(&(pIrpStack->??????Pa?
rameters.DeviceIoControl))?;?
????EventType=pRequestSetEvent->EventType;?
????EventHandler=pRequestSetEvent->EventHandler;?
??????????EventContext=pRequestSetEvent->EventContext;?
????...?
????switch(EventType)?
????{?
?????...?
?????case?TDI_EVENT_RECEIVE?:?
??????pRequestSetEvent->EventHandler?=?OurClientEventReceive;?
??????g_EventReceive?=?EventHandler;?
??????break;?
?????...?
值得注意的是,就算是set_eventhandler,也不一定就是tdi_event_receive里接受數(shù)據(jù)?
,?
這個tdi_client的選擇很多,還有ClientEventChainedReceive?什么的,詳情可看ddk,?
?
winsock似乎都是沒有用過這個。不過這個不影響,我們可以把ddk里面所有可能的情況?
?
全部列出來,一一判斷,然后分別處理。對于clienteventreceive,這里還有幾句話要?
說?
清楚,并不是每次調(diào)用這個函數(shù)就得到了數(shù)據(jù),這還要根據(jù)ReceiveFlags參數(shù),tdi?cl?
ient?
要根據(jù)這個參數(shù)決定是否要發(fā)tdi_receive?irp得到數(shù)據(jù)。于是我們的處理函數(shù)就要判斷?
這個?
參數(shù),以便做出相應的處理,我自己的代碼在實際的環(huán)境中運行了一段時間,似乎沒有?
問題,但是?
我也不敢說,對于各種情況我都考慮到了。?
評論
查看更多