tcp filter例子
我先描述一下2000/nt下的tcp/ip協議的一些情況。2000/nt下,ip,tcp,udp是在?一個驅動程序里實現的,叫做tcp.sys,這個驅動程序創建了3個設備,就是ip,tcp,udp?
。?
?首先描述一下driverentry。首先當然是iocreatedevice,用file_device_unknown,?
因為tcp設備就是用的這個參數。代碼如下:?
????RtlInitUnicodeString(?&usDeviceName,?FILTER_NAME?);?
????status?=?IoCreateDevice(?DriverObject,?
????????????????????????????sizeof(DEVICE_EXTENSION),?
????????????????????????????&usDeviceName,?
????????????????????????????FILE_DEVICE_UNKNOWN,?
????????????????????????????0,?
????????????????????????????FALSE,?
????????????????????????????&pDevObj?);?
然后就調用iogetdeviceobjectpointer來得到tcp設備的指針。?
代碼如下:?
????RtlInitUnicodeString(?&usTargetName,?TARGET_NAME?);?
????status?=?IoGetDeviceObjectPointer(?&usTargetName,?
??????????????????????????????????????FILE_ALL_ACCESS,?
??????????????????????????????????????&pTargetFileObj,?
??????????????????????????????????????&pTargetDevObj?);?
注意TARGET_NAME是大小寫區分的,我是用#define?TARGET_NAME?L"\\Device\\Tcp",?
不能寫成#define?TARGET_NAME?L"\\Device\\tcp"。?
然后我們就開始調用IoAttachDeviceToDevieStatck插入到tcp設備。?
調用完成后,我們要讓我們的設備表現的和tcp一樣,于是把它的所有?
特性都從tcpobj復制(pdevobj->xxx=ptcpobj->xxx)。?
再掃描他tcpdriverobject的majorfunction,我們的driver必須都?
支持。最后設置driverunload,因為為了方便調試,我們必須寫一個?unload
前面已經講過driverentry了,經過driverentry,所有的原來應該發送到?
tcp設備的irp現在都發送到我們的設備的處理函數了,如果什么事情都不做,?
那么可以簡單的調用iocalldriver把這個irp發到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設備的前期處理?
就在這個函數里。后期處理的函數可以放在completionroution里。?
好了,我們已經得到了發向tcp設備的所有irp了,這個時候我們的任務就是要了解?
tcp設備到底是如何工作的.?
這種情況下數據不經過tcp設備,filter也就無從得到了。?
為了了解如何從發向tcp設備的irp中得到信息,首先我先描述一下tdi?client是如何?
與tcp通訊以及tdi?client一般是如何工作的。?
driverstdio里面有好幾個例子都是關于tdi?client的,但是這些例子都是基于它自己?
的類庫的,不過這些例子功能都很強大,其中一個usb+web的溫度計的創意簡直讓我目瞪?
口呆。?
因為我也做過pci溫度計的driver,但是我從來就沒有想到還可以加上一個web?server在?
里面。?
因為driverstdio里的例子太復雜,我在這里簡單描述一下,給一個小例子。?
因為發送過程較為簡單,先描述發送。?
首先調用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,然后調用?
??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?
不用我說也應該知道,數據是放在一個buf里面,調用這個函數之前,要先構件一個mdl?
,把這個buf?
放進去。?
然后...irp已經好了,只要IoCallDriver(pUDPObject,PIrp)就行了...?
上面用的是UDP的例子(Datagram),但是tcp也是一樣,雖然函數有點差別,但是也是大?
同小異(TdiBuildSend)。?
為了搞清楚上面的兩個函數到底做了些什么(到底構建的irp是什么樣子),沒有必要去?
跟蹤,實際上,tdibuildxxx?
都不過是一個宏,你可以在tdikrnl.h里找到。打開tdikrnl.h看看,發現每個宏里都有?
這么一句:?
????????_IRPSP->MajorFunction?=?IRP_MJ_INTERNAL_DEVICE_CONTROL;?
于是我們知道tdi?client就是通過majorfunction=IRP_MJ_INTERNAL_DEVICE_CONTROL,m?
inorfunction=send/recv/...?
和tcp通訊的。這就使得我們確信,這種方法是可行的。(不是直接調用tdi函數,而是?
發irp)?
接收的過程較為復雜(tdi?client的選擇較多,因此要考慮各種情況)?
開始的時候,我在ddk?document里看到這么一個宏:tdibuildreceive,我想ok,?
和send一樣,我當時想的很簡單,以為tdi?client要接受數據了,它就向tcp發一個?
irp,然后返回pending,當有數據來的時候,tcp處理完這個irp,然后tdi?client只要在?
這個irp的?
irp_complete里處理得到的數據就行了。這的確是tdi?client的一個選擇,但是?
當我試驗的時候,我發現至少wsock不是這樣做的,因為我打開了一個ie,瀏覽了一個網?
頁,?
但是我發現我發出的數據都很好的捕獲到了,但是接收的數據一個也沒有,而且事實上?
,?
tcp似乎就沒有受到minorfunction=tdi_receive的irp。我因為這件事苦惱了一段時間,?
?
后來我仔細的讀了讀ddk?document,發現tdi?client還有第2種選擇,首先向tcp發送一?
個?
irp,minorfunction=tdi_sethandler,設置一些回調函數,然后當事情發生的時候,tc?
p?
就會調用這些回調函數,這些函數名字是clientEventxxx。這個過程可以仔細看ddk?do?
cument,?
看TdiBuildSetEventHandler,就知道哪些過程可以用這個方法。receive也是其中的一?
個,于是?
我們的方法得到了。首先我們得到了irp,如果是set_eventhandler,那么就修改tdi?c?
lient?
向tcp設置的回調函數的入口,把它指向我們自己寫的一個函數,同時保留原來的入口,?
?
然后在我們自己寫的函數里里調用它。?
代碼如下:?
???????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里接受數據?
,?
這個tdi_client的選擇很多,還有ClientEventChainedReceive?什么的,詳情可看ddk,?
?
winsock似乎都是沒有用過這個。不過這個不影響,我們可以把ddk里面所有可能的情況?
?
全部列出來,一一判斷,然后分別處理。對于clienteventreceive,這里還有幾句話要?
說?
清楚,并不是每次調用這個函數就得到了數據,這還要根據ReceiveFlags參數,tdi?cl?
ient?
要根據這個參數決定是否要發tdi_receive?irp得到數據。于是我們的處理函數就要判斷?
這個?
參數,以便做出相應的處理,我自己的代碼在實際的環境中運行了一段時間,似乎沒有?
問題,但是?
我也不敢說,對于各種情況我都考慮到了。?
評論
查看更多