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