2011年7月25日星期一

很多句話讓XueTr卸載不了我們的驅動(ObjectType HOOK)

作 者: KiDebug 
時 間: 2011-05-24,13:34:43 
鏈接: http://bbs.pediy.com/showthread.php?t=134415

對於ObjectType HOOK,可以參考MJ0011和sudami的文章:

ObjectType HOOK干涉註冊表操作(bypass Icesword,gmer,NIAP,etc.)
http://www.xfocus.net/articles/200802/966.html

一種Object hook的思路和實現過程
http://forum.eviloctal.com/thread-33688-1-1.html

兩篇文章的共同點都是去HOOK系統已經生成的object type裡面的例程,系統怎麼去用這些例程呢?仍然以上篇的《一句話讓XueTr卸載不了我們的驅動》為例,驅動卸載時,函數調用如下:
kd> kp
ChildEBP RetAddr
ee5deb30 805b1bde nt!IopDeleteDriver
ee5deb4c 80523bf1 nt!ObpRemoveObjectRoutine+0xe0
ee5deb70 804f5778 nt!ObfDereferenceObject+0x5f
ee5dec14 8057a83d nt!IopUnloadDriver+0x28a
ee5dec24 8053e6d8 nt!NtUnloadDriver+0xf
ee5dec24 80500231 nt!KiFastCallEntry+0xf8
ee5deca0 804f55df nt!ZwUnloadDriver+0x11
ee5ded48 8057a83d nt!IopUnloadDriver+0xf1
ee5ded58 8053e6d8 nt!NtUnloadDriver+0xf

ObpRemoveObjectRoutine定義如下:
VOID
ObpRemoveObjectRoutine (
    IN PVOID Object,
    IN BOOLEAN CalledOnWorkerThread
    )

Object是我們的驅動對象DriverObject,
有如下的代碼:
    ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
    ObjectType = ObjectHeader->Type;

    if (ObjectType->TypeInfo.DeleteProcedure) {

#if DBG
        KIRQL SaveIrql;
#endif

        ObpBeginTypeSpecificCallOut( SaveIrql );

        if (!CalledOnWorkerThread) {

            ObjectHeader->Flags |= OB_FLAG_DELETED_INLINE;
        }

        (*(ObjectType->TypeInfo.DeleteProcedure))(Object);

        ObpEndTypeSpecificCallOut( SaveIrql, "Delete", ObjectType, Object );
    }

可以看到是從Object取ObjectHeader,然後從ObjectHeader取ObjectType,從ObjectType取TypeInfo.DeleteProcedure。沒有直接取系統已經生成的ObjectType
因此,我們可以自己構造一個ObjectType,裡面填充自己定義的例程,然後覆蓋Object上面的ObjectHeader裡面的ObjectType,這樣系統取到的TypeInfo.DeleteProcedure就是我們自己定義的例程了。

測試代碼如下:
/*
* 作者:KiDebug
* 空間:http://hi.baidu.com/KiDebug/
*/
#include <ntddk.h>

UCHAR My_OBJECT_TYPE[0x200];

void testUnload(IN PDRIVER_OBJECT DriverObject)
{
}

NTSTATUS testDefaultHandler(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  Irp->IoStatus.Information = 0;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);
  return Irp->IoStatus.Status;
}

void DoNothing()
{

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
  ULONG i;

  for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
    DriverObject->MajorFunction[i] = testDefaultHandler;

  DriverObject->DriverUnload = testUnload;

  __asm
  {
    mov eax,DriverObject
    mov esi,[eax-10h] //+0x008 Type +0x018 Body 兩者相差0x10
    lea edi,My_OBJECT_TYPE
    mov ecx,64h //_OBJECT_TYPE共0x190個字節,0x64個DWORD
    rep movsd //把Driver的系統自帶的_OBJECT_TYPE複製到My_OBJECT_TYPE裡面
    lea ebx,My_OBJECT_TYPE
    mov ecx,DoNothing
    mov [ebx+98h],ecx //+0x038 DeleteProcedure
    mov [eax-10h],ebx //把My_OBJECT_TYPE覆蓋成自己DriverObject頭上的Type
  };

  return STATUS_SUCCESS;
}
用InstDrv.exe加載編譯後的驅動,依次點擊安裝、啟動,然後用XueTr查看“驅動模塊”,可以看到test.sys,可以在右鍵菜單中點擊“卸載驅動(危險)”,但刷新後test.sys依然還在。
把My_OBJECT_TYPE覆蓋掉XueTr.sys驅動對像上面的ObjectHeader裡面的ObjectType,XueTr功能沒影響,能幹啥幹啥。
這樣的HOOK用XueTr查不到,可以推廣到其他的對像中去,比如說註冊表、進程等等。

既然能加載驅動,能進內核了,那麼神馬都是浮云了:看到什麼就可以HOOK什麼,反過來看到HOOK了什麼,就可以反HOOK什麼。

2011年7月24日星期日

散談遊戲保護那點事~就從_TP開始入手吧

散談遊戲保護那點事~就從_TP開始入手吧
作 者: crazyearl
時 間: 2010-12-20,02:37:22
鏈接: http://bbs.pediy.com/showthread.php?t=126802

聲明:本文只為研究技術,請所有童鞋切勿使用本文之方法做下那天理難容罪惡不捨之壞事。
         既是研究遊戲保護,那麼總要有一個研究對象。本文就以TMD_TP這款遊戲保護為例進行分析講解。請勿對號入座,如有雷同之處。純屬反彙編引擎之錯誤,不關我的事!
        轉載請註明出處
        關鍵字:DNF 驅動保護
        鑑於最近很多同學找上門來求解這那問題,反正這東西又不是絕密檔案,放在我手里大半個月了,還不如放出來讓大家一起進步算了。另外都是取之看雪還之看雪罷了。
索性我也就公佈一個全套的方案。絕無其他意思,所以還請同道中人嘴下留情。切勿背地使壞!
 
        在正式開篇之前我要感謝看雪ID:十年寒窗在我最困惑的時候,他給予了最大的幫助!另外還有一位和我同歲的神秘人物也給予了不小的幫助,感謝你們。
 
        廢話了半天,正式開始吧。
tmd_TP也就是國內比較流行的遊戲D_N*F的遊戲保護。
它在ring0層一共HOOK了幾個地方和一些其他的工作。來達到保護的目的
下面是簡報:

引用:
NtOpenThread //防止調試器在它體內創建線程
NtOpenProcess //防止OD等在進程列表看到它
KiAttachProcess //防止其他軟件附加它
NtReadVirtualMemory //防止別人讀取它的內存
NtWriteVirtualMemory //防止別人在它的內存裡面亂寫亂畫
KDCOM.dll:KdReceivePacket //這兩個是COM串口的接受和發送數據
KDCOM.dll:KdSendPacket //主要用來方式別人雙機調試
使用了KdDisableDebugger來禁用雙機調試

代碼:
.text:010025F0 jz short loc_1002622
.text:010025F2 call sub_10022A4
.text:010025F7 call ds:KdDisableDebugger
.text:010025FD push offset byte_10022EC
.text:01002602 push esi
.text:01002603 push offset byte_10022DC
.text:01002608 push edi
.text:01002609 push dword_100CF24
 
並對debugport進行了瘋狂的清零操作
甚至還包括EPROCESS+70\+74\+78等幾處位置

 
處理的手段通常都是向64端口寫入FE導致計算機被重啟

代碼:
.text:01001665 mov al, 0FEh
.text:01001667 out 64h, al ; AT Keyboard controller 8042.
.text:01001667 ; Resend the last transmission
.text:01001669 popa
.text:0100166A retn
 
下面簡單看下他關鍵的幾個HOOK:
KiAttachProcess

 
NtReadVirtualMemory

 
NtWriteVirtualMemory

 
NtOpenThread

 
NtOpenProcess

 

引用:
其中,前3個直接恢復即可。
第4個有監視,直接恢復即刻重啟
第5個和ring3有通信,直接恢復1分鐘內SX非法模塊
 
根據上面的分析,下面給出相應的解決方案
1.直接恢復 第1、2、3處HOOK
2.繞過4、5處HOOK
3.將debugport清零的內核線程幹掉
4.恢復硬件斷點
但是要有一個先後的邏輯順序
因為內核有一個線程負責監視幾個地方,必須要先乾掉它。
但是這個內容我寫在了處理debugport清零的一起,也就是第3步。所以大家在照搬源碼的時候
注意代碼執行次序
 
先從簡單的工作講起,恢復1、2、3處的HOOK
KiAttachProcess的處理

代碼:
////////////////////////////////////////////////// ////////////////////
// 名稱: Nakd_KiAttachProcess
// 功能: My_RecoveryHook_KiAttachProcess的中繼函數
// 參數:
// 返回:
////////////////////////////////////////////////// ////////////////////
static NAKED VOID Nakd_KiAttachProcess()
{
  __asm
  {
    mov edi,edi
    push ebp
    mov ebp,esp
    push ebx
    push esi
    mov eax,KiAttachProcessAddress //注意這個是全局變量BYTE*
    add eax,7
    jmp eax
  }
}
////////////////////////////////////////////////// ////////////////////
// 名稱: RecoveryHook_KiAttachProcess
// 功能: 解除遊戲保護對_KiAttachProcess函數的HOOK(DNF)
// 參數:
// 返回: 狀態
////////////////////////////////////////////////// ////////////////////
NTSTATUS My_RecoveryHook_KiAttachProcess()
{
  BYTE *KeAttachProcessAddress = NULL; //KeAttachProcess函數地址
  BYTE *p;
  BYTE MovEaxAddress[5] = {0xB8,0,0,0,0}; //
  BYTE JmpEax[2] = {0xff,0xe0};
  KIRQL Irql;
  //特徵碼
  BYTE Signature1 = 0x56, //p-1
      Signature2 = 0x57, //p-2
      Signature3 = 0x5F, //p-3
      Signature4 = 0x5E, //p+5
      Signature5 = 0xE8; //p第一個字節
 
  //獲得KeAttachProcess地址,然後通過特徵碼​​找到
  //KiAttachProcess的地址
  KeAttachProcessAddress = (BYTE*)MyGetFunAddress(L"KeAttachProcess");
  if (KeAttachProcessAddress == NULL)
  {
    KdPrint(("KeAttachProcess地址獲取失敗\n"));
    return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
  }
  //將p指向KeAttachProcess函數開始處
  p = KeAttachProcessAddress;
  while (1)
  {
    if ((*(p-1) == Signature1) &&
      (*(p-2) == Signature2) &&
      (*(p+5) == Signature3) &&
      (*(p+6) == Signature4) &&
      (*p == Signature5))
    {
      //定位成功後取地址
      KiAttachProcessAddress = *(PULONG)(p+1)+(ULONG)(p+5);
      break;
    }
 
    //推動指針
    p++;
  }
 
  //計算中繼函數地址
  *(ULONG *)(MovEaxAddress+1)=(ULONG)Nakd_KiAttachProcess;
 
  WPOFF(); //清除CR0
  //提升IRQL中斷級
  Irql=KeRaiseIrqlToDpcLevel();
  //寫入
  RtlCopyMemory(KiAttachProcessAddress,MovEaxAddress,5);
  RtlCopyMemory(KiAttachProcessAddress+5,JmpEax,2);
  //恢復Irql
  KeLowerIrql(Irql);
  WPON(); //恢復CR0
 
  return STATUS_SUCCESS;
}
 
NtReadVirtualMemory和
NtWriteVirtualMemory的處理
注意這裡,我對他們倆開頭的第2句PUSH的處理
我直接寫入了push 0x78563412
大家可以根據自己的地址來硬編碼一次。
或者乾脆這樣使用

代碼:
////////////////////////////////////////////////// ////////////////////
// 名稱: My_RecoveryHook_NtReadAndWriteMemory
// 功能: 解除遊戲保護對NtReadVirtualMemory和
// NtWriteVirtualMemory的HOOK
// 參數:
// 返回:
////////////////////////////////////////////////// ////////////////////
NTSTATUS My_RecoveryHook_NtReadAndWriteMemory()
{
  BYTE Push1Ch[2] = {0x6a,0x1c}; //0~2字節
  BYTE PushAdd[5] = {0x68,0x12,0x34,0x56,0x78}; //NtReadVirtualMemory[物理機]
  //BYTE PushAdd2[5] = {0x68,0xf0,0x6f,0x4f,0x80}; //NtWriteVirtualMemory[物理機]
  KIRQL Irql;
  BYTE *NtReadVirtualMemoryAddress = NULL; //NtReadVirtualMemory的地址
  BYTE *NtWriteVirtualMemoryAddress = NULL; //NtWriteVirtualMemory的地址
 
  //從SSDT表中獲取NtReadVirtualMemory函數地址
  NtReadVirtualMemoryAddress = (BYTE*)myGetCurrentAddress(0xBA);
  if (NtReadVirtualMemoryAddress == NULL)
  {
    KdPrint(("NtReadVirtualMemory函數地址獲取失敗! \n"));
    return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
  }
  //從SSDT表中獲取NtWriteVirtualMemory函數地址
  NtWriteVirtualMemoryAddress = (BYTE*)myGetCurrentAddress(0x115);
  if (NtWriteVirtualMemoryAddress == NULL)
  {
    KdPrint(("NtWriteVirtualMemory函數地址獲取失敗! \n"));
    return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
  }
 
  WPOFF(); //清除CR0
  //提升IRQL中斷級
  Irql=KeRaiseIrqlToDpcLevel();
  //寫入
  RtlCopyMemory(NtReadVirtualMemoryAddress,Push​​1Ch,2);
  RtlCopyMemory(NtReadVirtualMemoryAddress+2,Push​​Add,5);
 
  RtlCopyMemory(NtWriteVirtualMemoryAddress,Push​​1Ch,2);
  RtlCopyMemory(NtWriteVirtualMemoryAddress+2,Push​​Add,5);
  //恢復Irql
  KeLowerIrql(Irql);
  WPON(); //恢復CR0
 
  return STATUS_SUCCESS;
}
 
好了,下面來處理
NtOpenProcess和
NtOpenThread
這兩個函數的處理上不能太魯莽了。
手法要風騷一點細膩一點了
介於篇幅的原因,我只貼出來前者的處理方法,後者雷同
細微之處大家自行修改。我總不能真的給你方法又給你工具。眼看著自己變成教唆犯

代碼:
//NtOpenProcess用到的全局變量[為了方便堆棧平衡的處理使用全局變量]
PEPROCESS processEPROCESS = NULL; //保存訪問者的EPROCESS
ANSI_STRING p_str1,p_str2; //保存進程名稱
BYTE *ObOpenObjectByPointerAddress = NULL; //ObOpenObjectByPointer的地址
BYTE *p_TpHookAddress = NULL; //TP的HOOK函數地址
BYTE *p_ReturnAddress = NULL; //返回到的地址
BYTE *p_MyHookAddress = NULL; //我們的HOOK函數在哪寫入
#define DNF_EXE "DNF.exe" //要檢索的進程名
////////////////////////////////////////////////// ////////////////////
// 名稱: Nakd_NtOpenProcess
// 功能: My_RecoveryHook_NtOpenProcess的中繼函數
// 參數:
// 返回:
////////////////////////////////////////////////// ////////////////////
static NAKED VOID Nakd_NtOpenProcess()
{
  //獲得調用者的EPROCESS
  processEPROCESS = IoGetCurrentProcess();
  //將調用者的進程名保存到str1中
  RtlInitAnsiString(&p_str1,(ULONG)processEPROCESS+0x174);
  //將我們要比對的進程名放入str2
  RtlInitAnsiString(&p_str2,DNF_EXE);
  if (RtlCompareString(&p_str1,&p_str2,TRUE) == 0)
  {
    //說明是DNF進程訪問了這裡
    __asm
    {
      push dword ptr [ebp-38h]
      push dword ptr [ebp-24h]
      push p_ReturnAddress
      mov eax,p_TpHookAddress
      jmp eax
    }
  }
  else
  {
    __asm
    {
      push dword ptr [ebp-38h]
      push dword ptr [ebp-24h]
      push p_ReturnAddress
      mov eax,ObOpenObjectByPointerAddress
      jmp eax
    }
  }
}
 
////////////////////////////////////////////////// ////////////////////
// 名稱: My_RecoveryHook_NtOpenProcess
// 功能: 解除遊戲保護對NtOpenProcess的HOOK
// 參數:
// 返回: 狀態
////////////////////////////////////////////////// ////////////////////
NTSTATUS My_RecoveryHook_NtOpenProcess()
{
  BYTE *NtOpenProcessAddress = NULL; //NtOpenProcess的地址
  BYTE *p = NULL; //臨時
  TOP5CODE *top5code = NULL; //保存5字節內容
  BYTE JmpAddress[6] = {0xE9,0,0,0,0,0x90};
  KIRQL Irql;
 
    //獲取NtOpenProcess的地址
    NtOpenProcessAddress = (BYTE*)MyGetFunAddress(L"NtOpenProcess");
    if (NtOpenProcessAddress == NULL)
    {
      KdPrint(("NtOpenProcess地址獲取失敗\n"));
      return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
    }
    //獲取ObOpenObjectByPointer的地址
    ObOpenObjectByPointerAddress = (BYTE*)MyGetFunAddress(L"ObOpenObjectByPointer");
    if (ObOpenObjectByPointerAddress == NULL)
    {
      KdPrint(("ObOpenObjectByPointer地址獲取失敗\n"));
      return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
    }
 
    //將p指向NtOpenProcess函數開始處
    p = NtOpenProcessAddress;
    //用一個無限循環來判斷給定的特徵碼來確定被HOOK位置
    while (1)
    {
      if ((*(p-7) == 0x50) &&
        (*(p-0xE) == 0x56) &&
        (*(p+0xd) == 0x50) &&
        (*(p+0x16) == 0x3b) &&
        (*(p+0x17) == 0xce) &&
        (*p == 0xE8) &&
        (*(p+5) == 0x8b) &&
        (*(p+6) == 0xf8))
      {
        KdPrint(("%0X \n",(ULONG)p));
        break;
      }
      //推動指針向前走
      p++;
    }
 
    //將top5code指向 p 的當前處
    //用以取出call [地址] 這5字節裡面的地址
    top5code = (TOP5CODE*)p;
    p_TpHookAddress = (BYTE*)((ULONG)p+5+top5code->address);
 
    //找到我們寫入自定義函數的地址
    p_MyHookAddress = p-6;
    //保存調用ObOpenObjectByPointer函數以後的返回地址
    p_ReturnAddress = p+5;
 
    //將一條JMP Nakd_NtOpenProcess寫入到數組中
    *(ULONG *)(JmpAddress+1)=(ULONG)Nakd_NtOpenProcess - ((ULONG)p_MyHookAddress+5);
 
    WPOFF(); //清除CR0
    //提升IRQL中斷級
    Irql=KeRaiseIrqlToDpcLevel();
    //寫入
    RtlCopyMemory(p_MyHookAddress,JmpAddress,6);
    //恢復Irql
    KeLowerIrql(Irql);
    WPON(); //恢復CR0
  
  return STATUS_SUCCESS;
}
處理之後:

簡而言之其原理就是,任何人調用了NtOpenProcess的時候會先進入
Nakd_NtOpenProcess函數,我們判斷。如果是遊戲進程訪問的話,就有可能是驗證之類的
我們轉到它自己的函數里面。讓它保持與ring3層的通信。否則的話,嘿嘿……
 
接下來是第3步處理debugport清零的這塊了。
我想絕大多數人關心的都是這裡了
網絡上能搜多到的辦法幾乎都失效了
有辦法的人又不肯放出來,急眼了就自己想了個土辦法
雖然不那麼時尚。但是絕對的奏效。
由於代碼凌亂不堪,簡單說下其原理。
我們定位內核模塊TxxxSxxx.sys的首地址
然後根據特徵碼遍歷整個模塊找到我們需要的地方,然後乾掉他們。
那麼我們又如何能夠通過人工的判斷出來到底是哪裡在作怪呢
利用syser或Start SoftICE對EPROCESS+BC處設置斷點。就可以一層一層的追溯上去了
到底如何用他們,我想大家自己多花點時間在看雪和GOOGLE或者BAIDU上面是不會吃虧的。
由於ZwQuerySystemInformation函數的使用非常繁瑣。而且篇幅有限。所以我只給出關鍵代碼,至於這個函數如何使用。大家可以自己在搜索引擎找“枚舉內核模塊”

代碼:
////////////////////////////////////////////////// ////////////////////
// 名稱: MyEnumKernelModule
// 功能: 枚舉內核模塊
// 參數: str:內核模塊名稱
// moduleadd:該模塊地址[傳出]
// modulesie:該模塊大小[傳出]
// 返回:
////////////////////////////////////////////////// ////////////////////
NTSTATUS MyEnumKernelModule(IN CHAR* str,OUT ULONG *moduleadd,OUT ULONG *modulesie)
{
  NTSTATUS status = STATUS_SUCCESS;
  ULONG n = 0;
  ULONG i = 0;
  PSYSTEM_MODULE_INFORMATION_ENTRY module = NULL;
  PVOID pbuftmp = NULL;
  ANSI_STRING ModuleName1,ModuleName2;
  BOOLEAN tlgstst= FALSE; //如果找到了指定模塊則設置為TRUE
 
  //利用11號功能枚舉內核模塊
  status = ZwQuerySystemInformation(11, &n, 0, &n);
 
  //申請內存
  pbuftmp = ExAllocatePool(NonPagedPool, n);
 
  //再次執行,將枚舉結果放到指定的內存區域
  status = ZwQuerySystemInformation(11, pbuftmp, n, NULL);
 
  module = (PSYSTEM_MODULE_INFORMATION_ENTRY)((PULONG )pbuftmp + 1 );
 
  //初始化字符串
  RtlInitAnsiString(&ModuleName1,str);
  //
  n = *((PULONG)pbuftmp );
  for ( i = 0; i < n; i++ )
  {
    RtlInitAnsiString(&ModuleName2,&module[i].ImageName);
    //DbgPrint("%d\t0x%08X 0x%08X %s\n",module[i].LoadOrderIndex,module[i].Base,module[i].Size,module[i].ImageName);
 
    if (RtlCompareString(&ModuleName1,&ModuleName2,TRUE) == 0)
    {
      DbgPrint("MyEnumKernelModule:%s:%0X \n",ModuleName2.Buffer,module[i].Base);
      *moduleadd = module[i].Base;
      *modulesie = module[i].Size;
      tlgstst = TRUE;
      break;
    }
  }
  ExFreePool(pbuftmp);
  if tlgstst == FALSE)
  {
    return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
  }
  return status;
}
 
////////////////////////////////////////////////// ////////////////////
// 名稱: My_Recovery_Debugport
// 功能: 恢復遊戲對debugport的清零操作
// 參數:
// 返回:
////////////////////////////////////////////////// ////////////////////
NTSTATUS My_Recovery_Debugport()
{
  NTSTATUS stats;
  BYTE *sd1 = NULL,*sd2 = NULL,*pd = NULL;
  ULONG ModuleSize,ModuleAddress,i,number = 0;
  BYTE *p;
  KIRQL Irql;
  BYTE C390[2] = {0xc3,0x90};
 
  //獲取指定的內核模塊地址和字節數
  stats = MyEnumKernelModule("\\??\\c:\\windows\\system32\\tessafe.sys",&ModuleAddress,&ModuleSize);
  if (stats == FAILED_TO_OBTAIN_FUNCTION_ADDRESSES)
  {
    return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
  }
  KdPrint(("Address:%0X Sie:%d \n",ModuleAddress,ModuleSize));
  //特徵碼
  /* sd1特徵
  p-1:18 p-2:87 p-3:DB p-4:33 p-5:07
  p-6:03 p :33 p+1:C0 p+7:3B p+8:D8
    sd2特徵
  p-1:07 p-2:87 p-3:c0 p-4:33 p+14:89
  p+15:1c p+16:38
  */
 
  //將P指向內核模塊開始處
  p = (BYTE*)ModuleAddress + 20;
  for (i = 0; i < ModuleSize - 20; i++,p++)
  {
    //sd1
    if ((*(p-1) == 0x18) &&
      (*(p-2) == 0x87) &&
      (*(p-3) == 0xDB) &&
      (*(p-4) == 0x33) &&
      (*(p-5) == 0x07) &&
      (*(p-6) == 0x03) &&
      (*p == 0x33) &&
      (*(p+1) == 0xC0) &&
      (*(p+7) == 0x3B) &&
      (*(p+8) == 0xD8) )
    {
      KdPrint(("--SD1 -- %0X \n",(ULONG)p));
      sd1 = p;
      number+=1; //記錄已經獲取一個特徵
    }
    //sd2
    if ((*(p-1) == 0x07) &&
      (*(p-2) == 0x87) &&
      (*(p-3) == 0xC0) &&
      (*(p-4) == 0x33) &&
      (*(p+14)== 0x89) &&
      (*(p+15)== 0x1C) &&
      (*(p+16)== 0x38) &&
      (*p == 0xA1))
    {
      KdPrint(("--SD2 -- %0X \n",(ULONG)p));
      sd2 = p;
      number+=1; //記錄已經獲取一個特徵
    }
    //pd
    if ((*(p-2) == 0xE3) &&
      (*(p-3) == 0xC1) &&
      (*(p-7) == 0xF3) &&
      (*(p-8) == 0x33) &&
      (*(p-10)== 0xEB) &&
      (*(p-11)== 0xC1) &&
      (*(p+1) == 0xF3) &&
      (*(p+2) == 0x42) &&
      (*(p+3) == 0x3B) &&
      (*(p+4) == 0xD1) &&
      (*p == 0x33))
    {
      KdPrint(("--PD -- %0X \n",(ULONG)p));
      pd = p;
      number+=1; //記錄已經獲取一個特徵
    }
    if (number >= 3)
    {
      KdPrint(("特徵 %d ---退出\n",number));
      break;
    }
  }
 
  //首先乾掉監視函數
  while (1)
  {
    if ((*(pd-1) == 0xcc) && (*(pd-2) == 0xcc))
    {
      KdPrint(("pd首地址:%0X \n",(ULONG)pd));
      WPOFF(); //清除CR0
      //提升IRQL中斷級
      Irql=KeRaiseIrqlToDpcLevel();
      //寫入
      RtlCopyMemory(pd,C390,2);
      //恢復Irql
      KeLowerIrql(Irql);
      WPON(); //恢復CR0
      break;
    }
    pd--;
  }
  //幹掉2個SD
  while (1)
  {
    if ((*(sd1-1) == 0xcc) && (*(sd1-2) == 0xcc))
    {
      KdPrint(("sd1首地址:%0X \n",(ULONG)sd1));
      WPOFF(); //清除CR0
      //提升IRQL中斷級
      Irql=KeRaiseIrqlToDpcLevel();
      //寫入
      RtlCopyMemory(sd1,C390,2);
      //恢復Irql
      KeLowerIrql(Irql);
      WPON(); //恢復CR0
      break;
    }
    sd1--;
  }
  while (1)
  {
    if ((*(sd2-1) == 0xcc) && (*(sd2-2) == 0xcc))
    {
      KdPrint(("sd2首地址:%0X \n",(ULONG)sd2));
      WPOFF(); //清除CR0
      //提升IRQL中斷級
      Irql=KeRaiseIrqlToDpcLevel();
      //寫入
      RtlCopyMemory(sd2,C390,2);
      //恢復Irql
      KeLowerIrql(Irql);
      WPON(); //恢復CR0
      break;
    }
    sd2--;
  }
 
  return STATUS_SUCCESS;
}
 
 
最後,處理一下硬件斷點就可以了
這裡我們使用到了SSDT HOOK
分別HOOK了SSDT 表中索引為0xD5和0x55的函數。由於這裡比較簡單
我想10個人有9個人懂得SSDT HOOK的。所以直接給出源碼,不做原理分析了

代碼:
//處理硬件斷點時
ULONG uNtSetContextThreadAddress;
ULONG uNtGetContextThreadAddress;
ULONG TenNtSetContextThread,
      TenNtGetContextThread;
////////////////////////////////////////////////// ////////////////////
// 名稱: _MyNtGetThreadContext
// 功能: 兩個SSDT HOOK偽造函數的中繼函數
// 參數:
// 返回:
////////////////////////////////////////////////// ////////////////////
static NAKED NTSTATUS Nakd_NtGetThreadContext(HANDLE hThread, PCONTEXT pContext)
{
  __asm
  {
    jmp dword ptr[TenNtGetContextThread]
  }
}
 
static NAKED NTSTATUS Nakd_NtSetThreadContext(HANDLE hThread, PCONTEXT pContext)
{
  __asm
  {
    jmp dword ptr[TenNtSetContextThread]
  }
}
////////////////////////////////////////////////// ////////////////////
// 名稱: MyNtGetThreadContext && MyNtSetThreadContext
// 功能: NtGetThreadContext與NtSetThreadContext函數被SSDT HOOK的偽造函數
// 參數:
// 返回:
////////////////////////////////////////////////// ////////////////////
NTSTATUS MyNtGetThreadContext(HANDLE hThread, PCONTEXT pContext)
{
  if ( _stricmp((const char*)PsGetProcessImageFileName(PsGetCurrentProcess()),DNF_EXE) )
  {
    return Nakd_NtGetThreadContext(hThread, pContext);
  }
  return STATUS_UNSUCCESSFUL;
}
 
 
NTSTATUS MyNtSetThreadContext(HANDLE hThread, PCONTEXT pContext)
{
  if ( _stricmp((const char*)PsGetProcessImageFileName(PsGetCurrentProcess()),DNF_EXE) )
  {
    return Nakd_NtSetThreadContext(hThread, pContext);
  }
  //DbgPrint("Dr7:%08X\n", pContext->Dr7);
  if ( pContext->Dr7 == 0x101 )
  {
    return Nakd_NtSetThreadContext(hThread, pContext);
  }
  return STATUS_UNSUCCESSFUL;
}
 
////////////////////////////////////////////////// ////////////////////
// 名稱: My_Recovery_HardwareBreakpoint
// 功能: 通過對set與get進行SSDT HOOK來恢復硬件斷點
// 參數:
// 返回:
////////////////////////////////////////////////// ////////////////////
NTSTATUS My_Recovery_HardwareBreakpoint()
{
  KIRQL Irql;
  //獲取地址
  uNtSetContextThreadAddress = (ULONG)KeServiceDescriptorTable->ServiceTableBase+0xD5 * 4;
  uNtGetContextThreadAddress = (ULONG)KeServiceDescriptorTable->ServiceTableBase+0x55 * 4;
 
  TenNtSetContextThread = *(ULONG*)uNtSetContextThreadAddress;
  TenNtGetContextThread = *(ULONG*)uNtGetContextThreadAddress;
 
  KdPrint(("Set地址:%0X\n",TenNtSetContextThread));
  KdPrint(("Get地址:%0X\n",TenNtGetContextThread));
 
  KdPrint(("Process:%0X \n",(ULONG)p_MyHookAddress));
  KdPrint(("Thread:%0X \n",(ULONG)t_MyHookAddress));
 
  WPOFF(); //清除CR0
  //提升IRQL中斷級
  Irql=KeRaiseIrqlToDpcLevel();
 
  //完成SSDT HOOK
  *(ULONG*)uNtGetContextThreadAddress = (ULONG)MyNtGetThreadContext;
  *(ULONG*)uNtSetContextThreadAddress = (ULONG)MyNtSetThreadContext;
 
  //恢復Irql
  KeLowerIrql(Irql);
  WPON(); //恢復CR0
 
  return STATUS_UNSUCCESSFUL;
}
 
另外還有一些功能型的函數一併給出,省的大家迷糊
我也算服務到位了,再看上面代碼迷糊的時候。看這裡找找
看看有沒有能用到的,或者翻一下我以往的帖子。裡面應該有

代碼:
//保存5字節代碼的結構
#pragma pack(1)
typedef struct _TOP5CODE
{
  UCHAR instruction; //指令
  ULONG address; //地址
}TOP5CODE,*PTOP5CODE;
#pragma pack( )
 
//ssdt表結構
typedef struct _ServiceDescriptorTable {
  PVOID ServiceTableBase; //System Service Dispatch Table 的基地址
  PVOID ServiceCounterTable;
  //包含著SSDT 中每個服務被調用次數的計數器。這個計數器一般由sysenter 更新。
  unsigned int NumberOfServices;//由ServiceTableBase 描述的服務的數目。
  PVOID ParamTableBase; //包含每個系統服務參數字節數表的基地址-系統服務參數表
}*PServiceDescriptorTable;
//由SSDT索引號獲取當前函數地址
//NtOpenProcess [[KeServiceDescriptorTable]+0x7A*4]
extern PServiceDescriptorTable KeServiceDescriptorTable;
 
////////////////////////////////////////////////// ////////////////////
// 名稱: MyGetFunAddress
// 功能: 獲取函數地址
// 參數: 函數名稱字符串指針
// 返回: 函數地址
////////////////////////////////////////////////// ////////////////////
ULONG MyGetFunAddress( IN PCWSTR FunctionName)
{
  UNICODE_STRING UniCodeFunctionName;
  RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
  return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName );
}
 
////////////////////////////////////////////////// ////////////////////
// 名稱: myGetCurrentAddress
// 功能: 獲取SSDT表中指定函數的當前地址
// 參數: index:指定函數在表中的索引號
// 返回: 地址
////////////////////////////////////////////////// ////////////////////
ULONG myGetCurrentAddress(IN ULONG index)
{
  ULONG SSDT_Cur_Addr;
  __asm
  {
    push ebx
    push eax
    mov ebx,KeServiceDescriptorTable
    mov ebx,[ebx]
    mov eax,index
    shl eax,2
    add ebx,eax
    mov ebx,[ebx]
    mov SSDT_Cur_Addr,ebx
    pop eax
    pop ebx
  }
 
  return SSDT_Cur_Addr;
}
 
VOID WPOFF()
{
  __asm
  {
    cli
    mov eax,cr0
    and eax,not 10000h
    mov cr0,eax
  }
}
 
VOID WPON()
{
  __asm
  {
    mov eax,cr0
    or eax,10000h
    mov cr0,eax
    sti
  }
}
 
記在最後面的話:大家要善用搜索引擎(建議學習google hacking技巧),勤做筆記,最後要說的依然是感謝,感​​謝GOOGLE\BAIDU\PEDIY\DEBUGMAN。還有那些默默發帖的人~

如果有時間的話,我會將其他幾個遊戲保護的分析資料也放出來
什麼GPK\HP\HS的。大家不要催不要急,一定會放出來的。等到我覺得這些東西都沒有挑戰性的時候那麼也就不會再有資料陸續放出來了……
如果有好東西記得與我分享哈

2011年7月23日星期六

不HOOK任何函數,讓任務管理器、XueTr、360進程管理器無法結束保護進程

作 者: KiDebug
時 間: 2011-05-29,11:17:38
鏈接: http://bbs.pediy.com/showthread.php?t=134645

【提示】能加驅動進內核,就沒必要弄EXE了,所以我個人覺得沒啥實際用途,僅供觀看

Windows會給每一個進程建立一個EPROCESS結構,給每一個線程建立ETHREAD結構,EPROCESS結構第一個成員是KPROCESS結構,ETHREAD結構第一個成員是KTHREAD結構。每個進程的線程的ETHREAD結構都會按下圖所示鏈接起來:
無標題.png下載此附件需要消耗2Kx,下載中會自動扣除。

裡面有兩條鍊錶。通過遍歷這兩條鍊錶,可以得到一個進程所有的線程。 Windows在執行線程調度時,不會去管這兩條鍊錶。只有當新建一個線程時,會加入到鍊錶中,一個線程退出時,會從鍊錶中移除。

如果在EPROCESS中將這兩條鍊錶置空,會發生什麼事呢?



以下是驗證步驟:

【注意】請先關閉360安全衛士,以免加載驅動時提示。驅動代碼中用了一堆的硬編碼,所以請確保操作系統為Windows XP SP3 中文版,否則可能藍屏。

1.編譯以下代碼為haha.exe
/*
 * 【作者:KiDebug】
 * 【空間:http://hi.baidu.com/KiDebug/】
 */
#include <Windows.h>

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
  //這個循環是用來證明進程沒有被結束,不是重點,請無視!
  while (1)
  {
    MessageBoxA(NULL,"關不了,氣死你!~","哈哈!~",MB_OK);
  }
  return 0;
}

雙擊打開haha.exe,它會不斷地調用MessageBox彈出對話框,此時任務管理器、XueTr、360進程管理器都可以結束haha.exe

2.確保haha.exe可以彈出對話框,然後再編譯以下代碼為ProtectHaha.sys,利用InstDrv.exe加載ProtectHaha.sys,依次點擊安裝、啟動、停止、卸載
/*
 * 【作者:KiDebug】
 * 【空間:http://hi.baidu.com/KiDebug/】
 */
#include <ntddk.h>

void DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
  __asm
  {
    mov ebx,PsInitialSystemProcess
    mov ebx,[ebx]
    lea ebx,[ebx+88h] //+0x088 ActiveProcessLinks : _LIST_ENTRY
L1: mov ebx,[ebx] //遍歷ActiveProcessLinks,找haha.exe的EPROCESS
    cmp dword ptr [ebx+0ECh],61686168h
    jnz L1
    cmp dword ptr [ebx+0F0h],6578652eh //68 61 68 61 2e 65 78 65 haha​​.exe
    jnz L1

    //88h、50h、190h、194h都是硬編碼,適用於Windows XP SP3
    lea ebx,[ebx-88h] //EBX為haha.exe的EPROCESS
    lea eax,[ebx+50h] //nt!_KPROCESS
    mov [ebx+50h],eax //+0x050 ThreadListHead : _LIST_ENTRY
    mov [ebx+54h],eax //置為空鏈
    lea eax,[ebx+190h] //nt!_EPROCESS
    mov [ebx+190h],eax //+0x190 ThreadListHead : _LIST_ENTRY
    mov [ebx+194h],eax //置為空鏈
  }

  DriverObject->DriverUnload = DriverUnload;
  return STATUS_SUCCESS;
}
3.打開任務管理器,選中haha.exe,點​​擊“結束進程”,haha.exe無法結束,仍然可以不停地彈對話框;
4.打開XueTr,在“進程”窗口中選中haha.exe按右鍵,點擊“結束進程”,haha.exe無法結束,仍然可以不停地彈對話框;刷新,再次右鍵,點擊“強制結束進程” ,haha.exe無法結束,仍然可以不停地彈對話框;
5.打開360安全衛士(8.0.0.2001),啟動“功能大全”裡面的“進程管理器”,選中haha.exe,點​​擊“關閉程序”,haha.exe無法結束,仍然可以不停地彈對話框;

【注意】
1.有可能點擊對話框右上角的關閉按鈕後,haha.exe會假死。這時先隨便點下桌面或其他什麼地方,再點對話框的“確定”按鈕,就又活過來了。
2.如果要應用到其他進程上,請確保驅動啟動後這個進程不會新建線程,否則“強制結束進程”可以結束掉新建線程,可能會造成進程假死。
3.haha.exe中只有一個線程,對於其他進程,在EPROCESS中將兩條鍊錶置空後需要將鍊錶中第一個線程和最後一個線程的ThreadListEntry鏈接起來。
4.對於其他進程,置空鍊錶時需要考慮同步問題。

如何結束haha.exe呢?
在XueTr“進程”窗口中選中haha.exe按右鍵,點擊“查看進程線程”,把看到的線程全部結束即可。
WS點可以把線程棧空間設小點,然後創建成百上千個線程……

2011年7月22日星期五

XueTr更新到0.41版本,修正Bug[2011-06-25]




本工具目前初步實現如下功能:
1.進程、線程、進程模塊、進程窗口、進程內存、熱鍵、定時器信息查看,殺進程、殺線程、卸載模塊等功能
2.內核驅動模塊查看,支持內核驅動模塊的內存拷貝
3.SSDT、Shadow SSDT、FSD、KBD、TCPIP、Classpnp、Atapi、Acpi、SCSI、IDT信息查看,並能檢測和恢復ssdt hook和inline hook
4.CreateProcess、CreateThread、LoadImage、Shutdown、Lego等Notify Routine信息查看,並支持對這些Notify Routine的刪除
5.端口信息查看,目前不支持2000系統
6.查看消息鉤子
7.內核模塊的iat、eat、inline hook、patches檢測和恢復
8.磁盤、卷、鍵盤、網絡層等過濾驅動檢測,並支持刪除
9.註冊表編輯
10.進程iat、eat、inline hook、patches檢測和恢復
11.文件系統查看,支持基本的文件操作
12.查看(編輯)IE插件、SPI、啟動項、服務、Host文件、映像劫持、文件關聯、系統防火牆規則
13.ObjectType Hook檢測和恢復

14.DPC定時器檢測和刪除
15.WorkerThread枚舉
16.MBR Rootkit檢測及其修復

本版更新列表:
         1.修正Win7 SP1端口枚舉的Bug
         2.處理文件畸形路徑(包含對./..目錄以及RLO路徑的處理)
         3.修正NTFS流文件枚舉上的Bug(請開啟物理磁盤分析功能)
         4.修正驅動上的Bug(感謝dl123100、KiDebug等人直接或者間接意見)
         5.修改了卸載驅動時的提示,卸載驅動是很危險的,請不要輕易嘗試

官方網站:
http://www.xuetr.com/
下載:
http://filemarkets.com/file/newbing/03448da7/

NDIS中間層驅動開發在Win7系統下和Windows XP系統下的區別

來源網絡
NDIS中間層驅動的開發在Win7系統上和Windows XP系統上有差別。 

 我把NDIS中間層的討論分成2塊。 windows 7系統和Windows XP系統。 

(一)在 Windows XP系統上進行開發 
  平時很多朋友包括我在內,我們都在XP系統上使用NDIS5.1的框架來進行程序開發。我們都使用 
Microsoft WDK提供的NDIS 的Passthru例子,在這個例子上做進一步的修改,來達到我們的目地。 

在Passthru工程的DriverEntry函數里面,我們都看見如下的代碼: 

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) 
                NDIS_STATUS Status; 
  NDIS_PROTOCOL_CHARACTERISTICS PChars; 
  NDIS_MINIPORT_CHARACTERISTICS MChars; 
  PNDIS_CONFIGURATION_PARAMETER Param; 
  NDIS_STRING Name; 
  NDIS_HANDLE WrapperHandle; 


  UNICODE_STRING nameString, linkString; 
  UINT FuncIndex; 
  PDEVICE_OBJECT MyDeviceObject; 
  PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; 

  Status = NDIS_STATUS_SUCCESS; 
   
  //申請自旋鎖以到達資源共享的同步訪問 
  NdisAllocateSpinLock(&GlobalLock); 

  //調用NdisMInitializeWrapper函數來保存在NdisWrapperHandle返回的句柄 
  NdisMInitializeWrapper(&NdisWrapperHandle, DriverObject, RegistryPath, NULL); 

  do 
  { 
//調用NdisMInitializeWrapper函數來保存在WrapperHandle返回的句柄 
    NdisMInitializeWrapper(&WrapperHandle, DriverObject, RegistryPath, NULL); 

//對於MiniportCharacteristics組件,如果驅動程序不用導出MiniportXxx這樣的函數,則必須賦值為NULL。 
//如果要導出任何新版本的V4.0或V5.0的MiniportXxx函數,那麼中間層驅動程序的主版本必須是V4.0,並且提供4.0或5.0版本的MiniportCharacteristics組件. 
  NdisZeroMemory(&MChars, sizeof(NDIS_MINIPORT_CHARACTERISTICS)); 

  MChars.MajorNdisVersion = PASSTHRU_MAJOR_NDIS_VERSION; 
  MChars.MinorNdisVersion = PASSTHRU_MINOR_NDIS_VERSION; 

   /************************************************* ** 
      下面開始註冊中間層驅動程序的MiniportXxx函數 
    ************************************************** */ 
  MChars.HaltHandler = MPHalt; 
  MChars.InitializeHandler = MPInitialize; 
  MChars.QueryInformationHandler = MPQueryInformation; 
  MChars.SetInformationHandler = MPSetInformation; 
  MChars.ResetHandler = MPReset; 

  MChars.SendHandler = NULL; 
  MChars.SendPacketsHandler = MPSendPackets; 

  MChars.TransferDataHandler = MPTransferData; 
  MChars.ReturnPacketHandler = MPReturnPacket; 
  MChars.CheckForHangHandler = NULL; 

#ifdef NDIS51_MINIPORT 
  MChars.CancelSendPacketsHandler = MPCancelSendPackets; 
  MChars.PnPEventNotifyHandler = MPDevicePnPEvent; 
  MChars.AdapterShutdownHandler = MPAdapterShutdown; 
#endif // NDIS51_MINIPORT 


/* 
傳遞上一步保存的句柄,並調用NdisIMRegisterLayeredMiniport函數來註冊驅動程序的MiniportXxx系列函數。其中句柄NdisWrapperHandle是由先前的NdisMInitializeWrapper函數返回的。 

當驅動程序調用NdisIMInitializeDeviceInstance函數,請求中間層驅動程序的MiniportInitialize函數對虛擬NIC進行初始化時,需要把句柄NdisWrapperHandle傳入NDIS 

當中間層驅動程序成功地綁定到一個或者多個NIC驅動程序上的時候, 或者是綁定到一個非NIC驅動程序上的時候也會調用NdisIMInitializeDeviceInstance函數。這樣做使得中間層驅動程序可以初始化Miniport組件來接受虛擬NIC上的I/O請求 
*/ 
           Status = NdisIMRegisterLayeredMiniport(NdisWrapperHandle, &MChars, sizeof(MChars), &DriverHandle); 

            if (Status != NDIS_STATUS_SUCCESS) 
       { 
       break; 
             } 

#ifndef WIN9X 
  NdisMRegisterUnloadHandler(NdisWrapperHandle, PtUnload); 
#endif 

     
  //中間層驅動程序要做的事情: 
  //初始化NDIS_PROTOCOL_CHARACTERISTICS類型的結構. NDIS不再支持3.0版本的協議驅動. 
  //能夠使用4.0或5.0版本的ProtocolCharacteristic結構體,協議驅動必須支持PNP即插即用功能. 
  NdisZeroMemory(&PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); 
  PChars.MajorNdisVersion = PASSTHRU_PROT_MAJOR_NDIS_VERSION; 
  PChars.MinorNdisVersion = PASSTHRU_PROT_MINOR_NDIS_VERSION; 
  NdisInitUnicodeString(&Name, L"Passthru"); 
  PChars.Name = Name; 

  /************************************************* ****** 
          下面開始註冊中間層驅動程序的ProtocolXxx函數 
    這裡主要是註冊---下邊界面向無連接的中間層驅動程序的ProtocolXxx函數 
        ************************************************** *****/ 

  PChars.OpenAdapterCompleteHandler = PtOpenAdapterComplete; 
  PChars.CloseAdapterCompleteHandler = PtCloseAdapterComplete; 
  PChars.SendCompleteHandler = PtSendComplete; 
  PChars.TransferDataCompleteHandler = PtTransferDataComplete; 
   
  PChars.ResetCompleteHandler = PtResetComplete; 
  PChars.RequestCompleteHandler = PtRequestComplete; 

  PChars.ReceiveHandler = PtReceive; 
  PChars.ReceivePacketHandler = PtReceivePacket; 
  PChars.ReceiveCompleteHandler = PtReceiveComplete; 

  PChars.StatusHandler = PtStatus; 
  PChars.StatusCompleteHandler = PtStatusComplete; 

  PChars.BindAdapterHandler = PtBindAdapter; 
  PChars.UnbindAdapterHandler = PtUnbindAdapter; 
  PChars.UnloadHandler = PtUnloadProtocol; 
   
  PChars.PnPEventHandler= PtPNPHandler; 


  /* 
  如果驅動程序隨後要綁定到底層的NDIS驅動程序上,則調用NdisRegisterProtocol函數來註冊驅動程序的ProtocolXxx函數。 
  全局變量ProtHandle是在NDIS協議驅動裡面的NdisRegisterProtocol函數里面初始化的,然後中間層驅動必須保存protHandle的值,並在將來NDIS中間層驅動程序的協議部分的函數調用中作為輸入參數來傳遞. 
  */ 

  NdisRegisterProtocol(&Status, &ProtHandle,&PChars, 
       sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); 

  if (Status != NDIS_STATUS_SUCCESS) 
  { 
    NdisIMDeregisterLayeredMiniport(DriverHandle); 
                  break; 
  } 

//如果驅動程序導出了MiniportXxx和ProtocolXxx這一些列的函數,那麼就調用NdisIMAssociateMiniport函數向NDIS通告有關驅動程序的微端口低邊界和協議高邊界信息 
    NdisIMAssociateMiniport(DriverHandle, ProtHandle); 
  } 
  while (FALSE); 

//----------------- 創建設備對象與符號連接------------------------- --- 
  RtlInitUnicodeString(&nameString, L"\\Device\\MyPassthru" ); 
  RtlInitUnicodeString(&linkString, L"\\??\\MyPassthru"); 

  for(FuncIndex = 0; 
                    FuncIndex <=IRP_MJ_MAXIMUM_FUNCTION; 
                    FuncIndex++) 
  { 
    MajorFunction[FuncIndex] = NULL; 
  } 
       
  MajorFunction[IRP_MJ_CREATE] = MydrvDispatch; 
  MajorFunction[IRP_MJ_CLOSE] = MydrvDispatch; 
  MajorFunction[IRP_MJ_DEVICE_CONTROL] = MydrvDispatchIoctl; 


  Status = NdisMRegisterDevice( 
                    WrapperHandle, 
                    &nameString, 
                    &linkString, 
                    MajorFunction, 
                    &MyDeviceObject, 
                    &NdisDeviceHandle 
                    ); 

  if(Status != STATUS_SUCCESS) 
  { 
    DbgPrint("NdisMRegisterDevice failed!\n"); 
  } 


     
  return(Status); 


然後,我們就開始實現相應的回調函數。然而為了收、發網絡數據包,對它們進行控制,我們基本上只會去修改以下的回調函數: 
下面我把相關的函數的功能給羅列出來。 


/************************************************* ************** 
  MPSend函數的功能是: 發送單個數據包。 
    若中間層驅動不支持MiniportSendPackets,那麼MPSend函數必須實現。 
   
    參數說明: 
      MiniportAdapterContext 它是適配器。 
      PacketArray 它是包描述符指針。 
      Flags 它未被使用。 
         
 ************************************************** *************/ 
NDIS_STATUS 
MPSend( 
    IN NDIS_HANDLE MiniportAdapterContext, 
    IN PNDIS_PACKET Packet, 
    IN UINT Flags 
    ) 





/************************************************* ************** 
   MPSendPackets函數的功能是: 
  用於指定網絡上傳輸數據包的包描述符指針數組。該函數可以用來發送多個數據包。而不是只發送單個數據包。除非中間層驅動程序綁定到低層WAN NIC驅動程序上,並提供MiniportWanSend函數,否則驅動程序應提供對MPSendPackets函數的支持, 而不是對MPSend函數的支持。 
  
   參數說明: 
      MiniportAdapterContext 它是適配器。 
    PacketArray 它是包描述符數組。 
      NumberOfPackets 它是PacketArray的長度。 
  
每個數據包發送完成後無論是否成功都要調用NdisMSendComplete函數。讓上層的協議驅動能夠收到SendCompleteHandler來判斷數據包的完成狀態。 
 ************************************************** *************/ 
VOID 
MPSendPackets( 
    IN NDIS_HANDLE MiniportAdapterContext, 
    IN PPNDIS_PACKET PacketArray, 
    IN UINT NumberOfPackets 
    ) 





/************************************************* ************** 
  MPTransferData函數的功能: 
    該函數用於傳輸在前視緩衝區中沒有指示的接收數據包的剩餘部分。前視緩衝區由 
    中間層驅動程序傳遞給NdisMXxxIndicateReceive函數。這個被指示的數據包可以是 
    中間層驅動程序的ProtocolReceive函數或者是ProtocolReceivePackets處理程序接收的 
    轉換數據包。 
    如果中間層驅動程序通過調用NdisMXxxIndicateReceive函數向上層驅動程序指示接收 
    數據包,那麼MPTransferData函數必須提供。 
                                                                                   
    如果中間層通過調用驅動程序總是NdisMIndicateReceive函數向上層驅動程序指示接收 
    數據包,那麼MPTransferData函數可以不必提供。即 NdisMIndicateReceivePacket指示 
    接收數據包,那麼傳遞給ProtocoltReceive函數的前視緩衝區將總是獲得完整的數據包。 
************************************************** **************/ 
NDIS_STATUS 
MPTransferData( 
    OUT PNDIS_PACKET Packet, //目的數據包 
    OUT PUINT BytesTransferred,//複製了多少數據 
    IN NDIS_HANDLE MiniportAdapterContext,//適配器結構體 
    IN NDIS_HANDLE MiniportReceiveContext,//上下文 
    IN UINT ByteOffset,//指向拷貝數據包的偏移 
    IN UINT BytesToTransfer//實際傳輸了多少字節的數據 
    ) 





/************************************************* ************** 
PtTransferDataComplete函數的功能: 
   它是MPTransferData()的異步完成函數。 
  當ProtocolReceive函數需要調用NdisTransferData函數,那麼我們必須實現 
  PtTransferDataComplete的代碼。 

   當NdisTransferData調用之後,並且返回值是NDIS_STATUS_PENDING,在數據包傳輸完成了以後,會去調用PtTransferDataComplete函數用來表明一個數據包的傳輸完成。這時就會得到一個完整的數據包。 
************************************************** ************/ 
VOID 
PtTransferDataComplete( 
    IN NDIS_HANDLE ProtocolBindingContext, 
    IN PNDIS_PACKET Packet, 
    IN NDIS_STATUS Status, 
    IN UINT BytesTransferred 
    ) 




/************************************************* ************** 
PtReceive函數的功能是: 
    該函數以指向包含網絡接收數據的前視緩衝區的指針為參數被調用執行。如果這個前視緩衝區沒有包含完整的網絡數據包,那麼ProtocolReceive函數將以包描述符作為參數,調用NdisTransferData函數。 
   當NdisTransferData調用之後,數據包傳輸完成了以後,會去調用 
ProtocolTransferDataComplete函數以用來表明一個數據包的傳輸完成。這時就會得到一個完整的數據包。 
                   
     
   如果低層驅動程序調用了NdisMIndicateReceivePacket指示接收數據包,那麼傳遞給PtReceive函數的前視緩衝區將總是獲得完整的數據包。 
   如果中間層驅動不需要完整的數據包時,可以調用NdisMXxxIndicateReceice函數向上通知,這樣就省略了獲取完整數據包的過程。 
                             
 前視緩衝區:考慮數據包的接收,如果我們只需要看見數據內容的前幾個字節(如TCP頭)就可以決定這個包是否是本協議所需要處理的,那麼顯然下層驅動就沒有必要提交整個數據包,只提供一個包開始的幾個字節就可以了。 
                                                                                                           
  參數說明: 
 ProtocolBindingContext 在綁定是得到的綁定句柄,即它是在NdisOpenAdapter被調用時設置的。 
              
 MacReceiveContext 它指向一個不透明的環境變量,由底層NIC驅動傳入,它用此句柄與從網絡上收到的Packet關聯。當調用NdisGetReceivePacket函數取得低層驅動上面的包描述符時,需要用到這個參數。當NDIS協議驅動的ProtocolReceive函數的實現中,可能要調用NdisTransferData函數,那麼也會用到這個參數。 
 HeaderBuffer 它是一個以太網幀,就是以太網的包頭。它是個虛擬的地址,只能在當前函數中有效, 它不能被存儲而作為全局變量來使用。 
                                                            
 HeaderBufferSize 它是包頭的長度,它的值一般為14 
 LookAheadBuffer 它是前視緩衝區指針。 
 LookAheadBufferSize LookAheadBuffer的大小。 
                                                 
 PacketSize 它是完整的數據包長度(不包括包頭)。 
                                                      
  當PtReceive函數被調用了以後,系統將會去調用完成函數PtReceiveComplete 
                                                                                                                                    
  如果傳遞給PtReceive的數據是通過NdisMXxxIndicateReceive進行指示的,那麼傳遞給PtReceive的前視緩衝區的長度不會超過用OID_GEN_CURRENT_LOOKAHEAD調用NdisRequest返回的值。 
                           
  如果PtReceive的調用執行在NdisMIndicateReceivePacket之前進行,那麼底層驅動程序把包數組中的一個或者多個包狀態​​設置為NDIS_STATUS_RESOURCES,那麼前視緩衝區的大小總是等於整個網絡數據包的大小。所以中間層驅動程序可以不必調用NdisTransferData函數。 
************************************************** *************/ 

NDIS_STATUS 
PtReceive( 
    IN NDIS_HANDLE ProtocolBindingContext, 
    IN NDIS_HANDLE MacReceiveContext, 
    IN PVOID HeaderBuffer, 
    IN UINT HeaderBufferSize, 
    IN PVOID LookAheadBuffer, 
    IN UINT LookAheadBufferSize, 
    IN UINT PacketSize 
    ) 





/************************************************* ************* 
PtReceivePacket函數的功能是: 
   這是一個可選函數。如果中間層驅動程序所基於的NIC驅動程序指示的是數據包描述符指針數組,或者調用NdisMIndicateReceivePacket函數指示接收帶外數據,那麼驅動程序應該提供PtReceivePacket函數。 
 如果開發者不能夠確定中間層驅動程序的執行環境,也應該提供該函數,因為在能夠產生多包指示的底層NIC驅動程序上,中間層驅動程序將獲得更好的性能。 
************************************************** ************/ 
INT 
PtReceivePacket( 
    IN NDIS_HANDLE ProtocolBindingContext, 
    IN PNDIS_PACKET Packet 
    ) 


我們可以在這幾個回調函數里面來處理、過濾、分析捕捉到的數據包。防火牆軟件一般也都是在這幾個回調函數里面進行編程。 

其它的回調函數的代碼,我們基本上不用去修改。 

NDIS Passthru的例子很多,在網絡上搜索一下,就有很多源代碼例子,也有很多相關的技術說明。 



下面,我想重點的說明在Windows 7系統下,(當然是Win7 32/64位系統)如何做NDIS中間層驅動程序的開發,代碼和之前老的Windows操作系統提供的Passthru例子的區別。 





(二)在 Windows 7系統上進行開發 

 在Win7系統下開發驅動程序,需要數字證書,還需要驅動簽名認證。 

 由於Win7系統的內核做了大幅度的修改,它和XP系統的內核起了很大的變化,最顯著的就是剛才說的:需要簽名和證書。還有就是:不能隨意的HOOK SSDT了。 


在開發NDIS驅動程序的時候,WDK開發包提供了一個新的框架,叫著NDIS Filter 
NDIS Filter是一個例子工程。 
假入我把WDK安裝在E盤,那麼這個工程代碼就在: 
E:\WinDDK\7600.16385.1\src\network\ndis\filter目錄下。 

把這個例子工程和原來的Passthru工程代碼做比較,您會發現,原來需要導出來的2種類型的回調函數MiniportXXX和ProtocolXXX 在新的框架裡面被全部隱藏起來了。 
微軟提供了新的函數。一起來看看,微軟提供了什麼。 
在這裡,為了方便分析, 我把函數代碼都做了功能註釋,請大家一起看看。 
代碼如下: 


#pragma NDIS_INIT_FUNCTION(DriverEntry) 

#define LITTLE_ENDIAN (1) 
// 
// Global variables 
// 
NDIS_HANDLE FilterDriverHandle; // NDIS handle for filter driver 
NDIS_HANDLE FilterDriverObject; 
NDIS_HANDLE NdisFilterDeviceHandle = NULL; 
PDEVICE_OBJECT DeviceObject = NULL; 

FILTER_LOCK FilterListLock; 
LIST_ENTRY FilterModuleList; 
PWCHAR InstanceStrings = NULL; 

NDIS_FILTER_PARTIAL_CHARACTERISTICS DefaultChars = { 
{ 0, 0, 0}, 
      0, 
      FilterSendNetBufferLists, 
      FilterSendNetBufferListsComplete, 
      NULL, 
      FilterReceiveNetBufferLists, 
      FilterReturnNetBufferLists 
}; 

typedef struct in_addr { 
  union { 
    struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b; 
    struct { USHORT s_w1,s_w2; } S_un_w; 
    ULONG S_addr; 
  } S_un; 
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR; 


#pragma push(1) 
typedef struct IP_HEADER 

#if LITTLE_ENDIAN 
  unsigned char ip_hl:4; /* 頭長度 */ 
  unsigned char ip_v:4; /* 版本號 */ 
#else 
  unsigned char ip_v:4; 
  unsigned char ip_hl:4; 
#endif 

  unsigned char TOS; // 服務類型 

  unsigned short TotLen; // 封包總長度,即整個IP包的長度 
  unsigned short ID; // 封包標識,唯一標識發送的每一個數據報 
  unsigned short FlagOff; // 標誌 
  unsigned char TTL; // 生存時間,就是TTL 
  unsigned char Protocol; // 協議,可能是TCP、UDP、ICMP等 
  unsigned short Checksum; // 校驗和 
  struct in_addr iaSrc; // 源IP地址 
  struct in_addr iaDst; // 目的PI地址 

}IP_HEADER, *PIP_HEADER; 


typedef struct tcp_header 
  unsigned short src_port; //源端口號 
  unsigned short dst_port; //目的端口號 
  unsigned int seq_no; //序列號 
  unsigned int ack_no; //確認號 
#if LITTLE_ENDIAN 
  unsigned char reserved_1:4; //保留6位中的4位首部長度 
  unsigned char thl:4; //tcp頭部長度 
  unsigned char flag:6; //6位標誌 
  unsigned char reseverd_2:2; //保留6位中的2位 
#else 
  unsigned char thl:4; //tcp頭部長度 
  unsigned char reserved_1:4; //保留6位中的4位首部長度 
  unsigned char reseverd_2:2; //保留6位中的2位 
  unsigned char flag:6; //6位標誌 
#endif 
  unsigned short wnd_size; //16位窗口大小 
  unsigned short chk_sum; //16位TCP檢驗和 
  unsigned short urgt_p; //16為緊急指針 

}TCP_HEADER,*PTCP_HEADER; 


typedef struct udp_header 
  USHORT srcport; // 源端口 
  USHORT dstport; // 目的端口 
  USHORT total_len; // 包括UDP報頭及UDP數據的長度(單位:字節) 
  USHORT chksum; // 校驗和 

}UDP_HEADER,*PUDP_HEADER; 
#pragma push() 


#define IP_OFF​​SET 0x0E 

//IP 協議類型 
#define PROT_ICMP 0x01 
#define PROT_TCP 0x06 
#define PROT_UDP 0x11 


USHORT UTIL_htons( USHORT hostshort ) 
  PUCHAR pBuffer; 
  USHORT nResult; 

  nResult = 0; 
  pBuffer = (PUCHAR )&hostshort; 

  nResult = ( (pBuffer[ 0 ] << 8) & 0xFF00) | (pBuffer[ 1 ] & 0x00FF); 

  return( nResult ); 


/*UTIL_ntohs把網絡字節順序轉換成主機字節順序*/ 
USHORT UTIL_ntohs( USHORT netshort ) 
  return( UTIL_htons( netshort ) ); 



NTSTATUS 
DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) 
    NDIS_STATUS Status; 
    NDIS_FILTER_DRIVER_CHARACTERISTICS FChars; 
    NDIS_STRING ServiceName; 
    NDIS_STRING UniqueName; 
    NDIS_STRING FriendlyName; 
    BOOLEAN bFalse = FALSE; 

    UNREFERENCED_PARAMETER(RegistryPath); 
     
    DEBUGP(DL_TRACE,("===>DriverEntry...\n")); 
    
    RtlInitUnicodeString(&ServiceName, FILTER_SERVICE_NAME); 
    RtlInitUnicodeString(&FriendlyName, FILTER_FRIENDLY_NAME); 
    RtlInitUnicodeString(&UniqueName, FILTER_UNIQUE_NAME); 
    FilterDriverObject = DriverObject; 
     
    do 
    { 
        NdisZeroMemory(&FChars, sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS)); 

/* 
大多數的NDIS6.0數據結構中包含的對像頭結構的成員,即NDIS_OBJECT_HEADER結構。 
對像頭有三個成員:類型,大小和修改。如果頭信息是不正確的,那麼調用NDIS6.0函數將失敗。 
*/ 
        FChars.Header.Type = 
        NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS; 
        FChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS); 
        FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1; 

        FChars.MajorNdisVersion = FILTER_MAJOR_NDIS_VERSION; 
        FChars.MinorNdisVersion = FILTER_MINOR_NDIS_VERSION; 
        FChars.MajorDriverVersion = 1; 
        FChars.MinorDriverVersion = 0; 
        FChars.Flags = 0; 

        FChars.FriendlyName = FriendlyName; 
        FChars.UniqueName = UniqueName; 
        FChars.ServiceName = ServiceName; 

             
/************************************************* ***** 
NDIS_FILTER_DRIVER_CHARACTERISTICS結構中Mandatory例程 
************************************************** ****/ 
        FChars.AttachHandler = FilterAttach; 
        FChars.DetachHandler = FilterDetach; 
        FChars.RestartHandler = FilterRestart; 
        FChars.PauseHandler = FilterPause; 


/************************************************* *********** 
NDIS_FILTER_DRIVER_CHARACTERISTICS結構中Optional且不能在運行時變更的例程 
************************************************** ***********/ 
        FChars.SetOptionsHandler = FilterRegisterOptions; 
        FChars.SetFilterModuleOptionsHandler = FilterSetModuleOptions; 
        FChars.OidRequestHandler = FilterOidRequest; 
        FChars.OidRequestCompleteHandler = FilterOidRequestComplete; 
        FChars.StatusHandler = FilterStatus; 
       FChars.DevicePnPEventNotifyHandler = FilterDevicePnPEventNotify; 
       FChars.NetPnPEventHandler = FilterNetPnPEvent; 
      FChars.CancelSendNetBufferListsHandler = FilterCancelSendNetBufferLists; 



/************************************************* ************* 
DIS_FILTER_DRIVER_CHARACTERISTICS結構中Optional且能在運行時變更的例程。 
         
下面這4個例程也被定義在NDIS_FILTER_PARTIAL_CHARACTERISTICS中,這個結構指定的 
例程可以在運行時的FilterSetModuleOptions例程中調用NdisSetOptionHandles來改變。 
如果過濾驅動要在例程中修改自身的一個特性,那麼必須提供FilterSetModuleOptions例程。 
************************************************** **************/ 
        FChars.SendNetBufferListsHandler = FilterSendNetBufferLists; 
   FChars.SendNetBufferListsCompleteHandler = FilterSendNetBufferListsComplete; 
        FChars.ReturnNetBufferListsHandler = FilterReturnNetBufferLists; 
        FChars.ReceiveNetBufferListsHandler = FilterReceiveNetBufferLists; 

       /// 
        FChars.CancelOidRequestHandler = FilterCancelOidRequest; 
       
        DriverObject->DriverUnload = FilterUnload; 
     
        FilterDriverHandle = NULL; 

        FILTER_INIT_LOCK(&FilterListLock); 

        InitializeListHead(&FilterModuleList); 
         
  // 把Filter驅動註冊給NDIS 
        Status = NdisFRegisterFilterDriver(DriverObject, 
                                           (NDIS_HANDLE)FilterDriverObject, 
                                           &FChars, 
                                           &FilterDriverHandle); 
        if (Status != NDIS_STATUS_SUCCESS) 
        { 
            DEBUGP(DL_WARN, ("MSFilter: Register filter driver failed.\n")); 
            break; 
        } 
        // 
        // Initilize spin locks 
        // 

        Status = FilterRegisterDevice(); 

        if (Status != NDIS_STATUS_SUCCESS) 
        { 
            NdisFDeregisterFilterDriver(FilterDriverHandle); 
            FILTER_FREE_LOCK(&FilterListLock); 
            DEBUGP(DL_WARN, ("MSFilter: Register device for the filter driver failed.\n")); 
            break; 
        } 

         
    } 
    while(bFalse); 
     
     
    DEBUGP(DL_TRACE, ("<===DriverEntry, Status = %8x\n", Status)); 
    return Status; 
     




//過濾驅動註冊可選服務 
NDIS_STATUS 
FilterRegisterOptions( 
        IN NDIS_HANDLE NdisFilterDriverHandle, //它指向了這個過濾驅動 
        IN NDIS_HANDLE FilterDriverContext //它是這個驅動的上下文 
        ) 
    DEBUGP(DL_TRACE, ("===>FilterRegisterOptions\n")); 
     
    ASSERT(NdisFilterDriverHandle == FilterDriverHandle); 
    ASSERT(FilterDriverContext == (NDIS_HANDLE)FilterDriverObject); 

    if ((NdisFilterDriverHandle != (NDIS_HANDLE)FilterDriverHandle) || 
        (FilterDriverContext != (NDIS_HANDLE)FilterDriverObject)) 
    { 
        return NDIS_STATUS_INVALID_PARAMETER; 
    } 

    DEBUGP(DL_TRACE, ("<===FilterRegisterOptions\n")); 

    return (NDIS_STATUS_SUCCESS); 

     

/************************************************* ************** 
FilterAttach函數的功能: 
   Attaching狀態表示:一個Filter Driver正準備附加一個Filter Module到一個驅動棧上。 
   一個過濾驅動進入Attaching狀態下不能進行發送請求、接收指示、狀態指示、OID請求操作。 

   當一個過濾驅動進入Attaching狀態時,它可以: 
   (1)創建一個環境上下文區域並且初始化一個緩衝區池以及其Filter Module特點的資源。 
  (2)用NDIS 傳來給Filter Attach的NdisFilterHandle作為輸入來調用NdisFSetAttributes例程。 


   Attach is complete 
   當Filter Module在Attaching狀態下並且Filter Driver初始化了所有的Filter Module所需要的 
   所有資源時,Filter Module進入Paused狀態。 


   參數說明: 
    NdisFilterHandle 它用於所有過濾驅動中對Ndisxxx類例程的調用時引用指示這個過濾模塊。 
    FilterDriverContext 它由NdisFRegisterFilterDriver的FilterDriverContext來指定。 
    AttachParameters 它是過濾模塊的初始化參數結構體。 
 ************************************************** ************/ 
NDIS_STATUS 
FilterAttach( 
    IN NDIS_HANDLE NdisFilterHandle, 
    IN NDIS_HANDLE FilterDriverContext, 
    IN PNDIS_FILTER_ATTACH_PARAMETERS AttachParameters 
    ) 

    PMS_FILTER pFilter = NULL; 
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS; 
    NDIS_FILTER_ATTRIBUTES FilterAttributes; 
    ULONG Size; 
    BOOLEAN bFalse = FALSE; 
     
    DEBUGP(DL_TRACE, ("===>FilterAttach: NdisFilterHandle %p\n", NdisFilterHandle)); 

    do 
    { 
        ASSERT(FilterDriverContext == (NDIS_HANDLE)FilterDriverObject); 
        if (FilterDriverContext != (NDIS_HANDLE)FilterDriverObject) 
        { 
            Status = NDIS_STATUS_INVALID_PARAMETER; 
            break; 
        } 
         
        if ((AttachParameters->MiniportMediaType != NdisMedium802_3) 
                && (AttachParameters->MiniportMediaType != NdisMediumWan)) 
        { 
           DEBUGP(DL_ERROR, ("MSFilter: Doesn't support media type other than NdisMedium802_3.\n")); 
         
           Status = NDIS_STATUS_INVALID_PARAMETER; 
           break; 
        } 
         
        Size = sizeof(MS_FILTER) + 
               AttachParameters->FilterModuleGuidName->Length + 
               AttachParameters->BaseMiniportInstanceName->Length + 
               AttachParameters->BaseMiniportName->Length; 
         
        pFilter = (PMS_FILTER)FILTER_ALLOC_MEM(NdisFilterHandle, Size); 
        if (pFilter == NULL) 
        { 
            DEBUGP(DL_WARN, ("MSFilter: Failed to allocate context structure.\n")); 
            Status = NDIS_STATUS_RESOURCES; 
            break; 
        } 
         
        NdisZeroMemory(pFilter, sizeof(MS_FILTER)); 

        pFilter->FilterModuleName.Length = pFilter->FilterModuleName.MaximumLength = At​​tachParameters->FilterModuleGuidName->Length; 
        pFilter->FilterModuleName.Buffer = (PWSTR)((PUCHAR)pFilter + sizeof(MS_FILTER)); 

        NdisMoveMemory(pFilter->FilterModuleName.Buffer, 
                        AttachParameters->FilterModuleGuidName->Buffer, 
                        pFilter->FilterModuleName.Length); 

         
        pFilter->MiniportFriendlyName.Length = pFilter->MiniportFriendlyName.MaximumLength = At​​tachParameters->BaseMiniportInstanceName->Length; 
        pFilter->MiniportFriendlyName.Buffer = (PWSTR)((PUCHAR)pFilter->FilterModuleName.Buffer + pFilter->FilterModuleName.Length); 
         
  NdisMoveMemory(pFilter->MiniportFriendlyName.Buffer, 
                       AttachParameters->BaseMiniportInstanceName->Buffer, 
                        pFilter->MiniportFriendlyName.Length); 

         
        pFilter->MiniportName.Length = pFilter->MiniportName.MaximumLength = At​​tachParameters->BaseMiniportName->Length; 
        pFilter->MiniportName.Buffer = (PWSTR)((PUCHAR)pFilter->MiniportFriendlyName.Buffer + 
                                                   pFilter->MiniportFriendlyName.Length); 
        NdisMoveMemory(pFilter->MiniportName.Buffer, 
                        AttachParameters->BaseMiniportName->Buffer, 
                        pFilter->MiniportName.Length); 

        pFilter->MiniportIfIndex = At​​tachParameters->BaseMiniportIfIndex; 
       
        pFilter->TrackReceives = TRUE; 
        pFilter->TrackSends = TRUE; 
        pFilter->FilterHandle = NdisFilterHandle; 


        NdisZeroMemory(&FilterAttributes, sizeof(NDIS_FILTER_ATTRIBUTES)); 
        FilterAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1; 
        FilterAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES); 
        FilterAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES; 
        FilterAttributes.Flags = 0; 


        Status = NdisFSetAttributes(NdisFilterHandle, 
                                    pFilter, //pFilter參數的功能是,為過濾模塊指定環境上下文 
                                    &FilterAttributes); 

        if (Status != NDIS_STATUS_SUCCESS) 
        { 
            DEBUGP(DL_WARN, ("MSFilter: Failed to set attributes.\n")); 
            break; 
        } 
         
        pFilter->State = FilterPaused; 

        FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse); 
        InsertHeadList(&FilterModuleList, &pFilter->FilterModuleLink); 
        FILTER_RELEASE_LOCK(&FilterListLock, bFalse); 
         
    } 
    while (bFalse); 
     
    if (Status != NDIS_STATUS_SUCCESS) 
    { 
        if (pFilter != NULL) 
        { 
            FILTER_FREE_MEM(pFilter); 
        } 
    } 
     
    DEBUGP(DL_TRACE, ("<===FilterAttach: Status %x\n", Status)); 
    return Status; 




/************************************************* ************* 
FilterPause函數的功能: 
   Paused狀態:在這種狀態下,Filter Driver不能執行接收和發送操作。 
   當FilterDriver執行FilterPause全程時它就進入了Pausing狀態。 

   Pausing狀態:在這種狀態下,Filter Driver要為一個Filter Module完成停止發送和接收 
   處理所需要的所有準備工作。 

   一個在Pausing狀態的Filter Driver有如下的約束: 

   (1)Filter Module不能發起任何新的接收指示,但可以傳遞下層驅動的接收指示。 
   (2)如果有Filter Module發起的接收指示還沒有完成,那麼必須等到它們全部完成。有隻當 
      FilterReturnNetBufferLists完成所有外部接收指示後,暫停操作才能完成。 
   (3)要返回任何未處理的由下層驅動引發的接收指示給NDIS,只有等到NdisFReturnNetBufferLists返回了所有未處理的接收指示後暫停操作才能完成。這裡也可以排隊緩衝這些未完成的接收指示。 
   (4)立即用NdisFReturnNetBufferLists返回所有下層驅動新傳來的接收指示,如果需要可以在返回之前製和排隊這些接收指示。 
   (5)不能發起任何新的發送請求。 
   (6)如果有Filter Driver引的但NDIS還未完成的發送操作,必須等待它們完成。 
   (7)應該在FilterSendNetBufferLists例程中立即調用NdisFSendNetBufferListsComplete返回那些新達到的發送請求。並且為每一個NET_BUFFER_LIST設置NDIS_STATUS_PAUSED返回狀態。 
   (8)這時可以使用NdisFIndicateStatus提供狀態指示。 
   (9)可以在FilterStatus中處理狀態指示。 
   (10)可以在FilterOidRequest裡面處理OID請求。 
   (11)可以發起一個OID操作。 
   (12)不能釋放分配的相關資源,和Filter Module相關的資源最好放在FilterDetach例程裡面來釋放。 
   (13)如果有用於發送和接收的定時器那麼要停止它。 

   當成功停止發送和接收操作後就必須完成暫停操作。暫停操作的完成可以是同步的也可以是異步的。 
   若返回值是NDIS_STATUS_SUCCESS則,是同步。 
   若是異步,則返回NDIS_STATUS_PENDING。那麼還必須調用NdisFPauseComplete函數。 

   暫停操作完成了以後,Filter Module進入了Paused狀態。這裡它有如下的約束: 

   (1)不能發起任何接收指示,但可以傳遞底層驅動發來的接收指示。 
   (2)需要立即調用NdisFReturnNetBufferLists返回底層驅動的接收指示給NDIS,如果需要可以在返回之前複製和排隊這些接收指示。 
   (3)不能引發任何新的發送指示。 
   (4)需要立即調用NdisFSendNetBufferListsComplete完成那些在FilterSendNetBufferLists中收到的發送請求,並為每一個NET_BUFFER_LIST設置NDIS_STATUS_PAUSED返回狀態。 
   (5)這時可以使用NdisFIndicateStatus發起狀態指示。 
   (6)可以在FilterStatus中進行狀態指示處理。 
   (7)可以在FilterOidRequest裡面處理OID請求。 
   (8)可以發起一個OID請求。 


   在Filter Module進行Pausing狀態時NDIS不會發起其它PnP操作,比如:附加、分離、重啟。只有當Filter Module進入了Paused狀態後NDIS才能對它進行分離和重啟操作。 
 ************************************************** *************/ 
NDIS_STATUS 
FilterPause( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNDIS_FILTER_PAUSE_PARAMETERS PauseParameters 
        ) 

    PMS_FILTER pFilter = (PMS_FILTER)(FilterModuleContext); 
    NDIS_STATUS Status; 
    BOOLEAN bFalse = FALSE; 

    UNREFERENCED_PARAMETER(PauseParameters); 
     
    DEBUGP(DL_TRACE, ("===>NDISLWF FilterPause: FilterInstance %p\n", FilterModuleContext)); 

      
    FILTER_ASSERT(pFilter->State == FilterRunning); 
     
    FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); 
    pFilter->State = FilterPausing; 
    FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); 


    Status = NDIS_STATUS_SUCCESS; 

    pFilter->State = FilterPaused; 
     
    DEBUGP(DL_TRACE, ("<===FilterPause: Status %x\n", Status)); 
    return Status; 




/************************************************* ************** 
  FilterRestart函數的功能: 
   在Restarting狀態下,一個Filter Driver必須為一個Filter Module完成重啟發送和接收數據時 
   所需要的所有準備工作。 

   Restart is complete 
    當Filter Module在Restarting狀態下並且完成所有發送和接收所需要的準備工作時,Filter Module就進入了Running狀態。 

要啟動一個Paused狀態的Filter Module,如果有FilterSetModuleOptions就先調用它,接著調用FilterRestart。 

  當Filter Module在Restarting狀態下,它可以: 
  (1)完成任何正常的發送和接收所需要的準備工作。 
  (2)可讀寫Filter Module的配置參數。 
  (3)可以接收網絡數據指示,拷貝和排隊數據稍後只是給上層驅動或者丟棄數據。 
  (4)不能發起任何新的接收指示。 
  (5)應該立即調用NdisFSendNetBufferListsComplete例程來拒絕FilterSendNetBufferLists傳來的發送的請求。應該設置每一個NET_BUFFER_LIST的完成狀態為NDIS_STATUS_PAUSED 
                (6)可以使用NdisFIndicateStatus例程進行狀態指示。 
  (7)可以控制OID請求操作。 
  (8)不能發起任何新的發送請求。 
  (9)應該調用NdisFReturnNetBufferLists返回所有新的接收指示。如果需要的話可以在接收指示返回之前拷貝它們。 
  (10)可以製作OID請求發送給下層驅動設置或查詢配置信息。 
  (11)可以在FilterStatus中控制狀態指示。 
  (12)返回時指示NDIS_STATUS_SUCCESS 或失敗狀態,如果不Filter Module不能啟動返回了失敗,而它又是一個Mandatory的Filter Driver 那個NDIS將會結束這個驅動棧 
在一個Filter Driver完成對發送和接收的重啟後必須指示完成這個重啟操作。 
Filter Driver的重啟操作的完成可以是同步也可以是異步的,同步時返回NDIS_STATUS_SUCCESS 異步時返回NDIS_STATUS_PENDING。如果返回的是NDIS_STATUS_PENDING 就必須調用NdisFRestartComplete 例程在重啟操作完成後。 
在這種情況下,驅動需要傳遞給NdisFRestartComplete 一個固定狀態(標識重啟結果的成功或失敗狀態)。 

重啟操作完成Filter Module就進入了Running狀態,恢得一切正常的發送和接收外理。 
在Filter Driver的FilterRestart例程執行的時候NDIS不會發起任即插即用操作,如附加,分離,暫停請求等等。 Ndis可以在Filter Module進入Running狀態後發起一個暫停請求。 
************************************************** *************/ 
NDIS_STATUS 
FilterRestart( 
    IN NDIS_HANDLE FilterModuleContext, 
    IN PNDIS_FILTER_RESTART_PARAMETERS RestartParameters 
    ) 

    NDIS_STATUS Status; 
    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; // BUGBUG, the cast may be wrong 
    NDIS_HANDLE Con​​figurationHandle = NULL; 


    PNDIS_RESTART_GENERAL_ATTRIBUTES NdisGeneralAttributes; 
    PNDIS_RESTART_ATTRIBUTES NdisRestartAttributes; 
    NDIS_CONFIGURATION_OBJECT ConfigObject; 
     
    DEBUGP(DL_TRACE, ("===>FilterRestart: FilterModuleContext %p\n", FilterModuleContext)); 
     
    FILTER_ASSERT(pFilter->State == FilterPaused); 

    ConfigObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT; 
    ConfigObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1; 
    ConfigObject.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT); 
    ConfigObject.NdisHandle = FilterDriverHandle; 
    ConfigObject.Flags = 0; 
     
    Status = NdisOpenConfigurationEx(&ConfigObject, &ConfigurationHandle); 
    if (Status != NDIS_STATUS_SUCCESS) 
    { 
         
#if 0 
        // 
        // The code is here just to demonstrate how to call NDIS to write an eventlog. If drivers need to write 
        // an event log. 
        // 
        PWCHAR ErrorString = L"Ndislwf"; 
         
        DEBUGP(DL_WARN, ("FilterRestart: Cannot open configuration.\n")); 
        NdisWriteEventLogEntry(FilterDriverObject, 
                                EVENT_NDIS_DRIVER_FAILURE, 
                                0, 
                                1, 
                                &ErrorString, 
                                sizeof(Status), 
                                &Status); 
#endif 
                                 
    } 


    if (Status == NDIS_STATUS_SUCCESS) 
    { 
        NdisCloseConfiguration(ConfigurationHandle); 
    } 

    NdisRestartAttributes = RestartParameters->RestartAttributes; 

   
    if (NdisRestartAttributes != NULL) 
    { 
        PNDIS_RESTART_ATTRIBUTES NextAttributes; 
         
        ASSERT(NdisRestartAttributes->Oid == OID_GEN_MINIPORT_RESTART_ATTRIBUTES); 
     
        NdisGeneralAttributes = (PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data; 
     
        NdisGeneralAttributes->LookaheadSize = 128; 

        NextAttributes = NdisRestartAttributes->Next; 

        while (NextAttributes != NULL) 
        { 

            NextAttributes = NextAttributes->Next; 
        } 
     
    } 

    
    pFilter->State = FilterRunning; 


    Status = NDIS_STATUS_SUCCESS; 
     
    if (Status != NDIS_STATUS_SUCCESS) 
    { 
        pFilter->State = FilterPaused; 
    } 
     
     
    DEBUGP(DL_TRACE, ("<===FilterRestart: FilterModuleContext %p, Status %x\n", FilterModuleContext, Status)); 
    return Status; 



/************************************************* ************* 
FilterDetach函數的功能: 
   Detach狀態:當Filter Driver從一個驅動棧上分離一個Filter Module時,將發生該事件。 
   在驅動棧上分離一個過濾模塊時,NDIS會暫停這個驅動棧。這意味著NDIS已經使過濾模塊進入 
   了Parse狀態。即FilterPause函數先被調用了。 

   在這個例程中釋放和這個過濾模塊相關的環境上下文和其它資源。這個過程不能失敗。 
   當FilterDetach函數返回以後,NDIS會重新啟動被暫停的驅動棧。 
    
   參數說明: 
    FilterDriverContext 它由NdisFRegisterFilterDriver的FilterDriverContext來指定。 
 ************************************************** **********/ 
VOID 
FilterDetach( 
        IN NDIS_HANDLE FilterModuleContext 
        ) 

    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    BOOLEAN bFalse = FALSE; 


    DEBUGP(DL_TRACE, ("===>FilterDetach: FilterInstance %p\n", FilterModuleContext)); 

   
    FILTER_ASSERT(pFilter->State == FilterPaused); 
         
    
    if (pFilter->FilterName.Buffer != NULL) 
    { 
        FILTER_FREE_MEM(pFilter->FilterName.Buffer); 
    } 


    FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse); 
    RemoveEntryList(&pFilter->FilterModuleLink); 
    FILTER_RELEASE_LOCK(&FilterListLock, bFalse); 


    FILTER_FREE_MEM(pFilter); 

   
    DEBUGP(DL_TRACE, ("<===FilterDetach Successfully\n")); 
     
    return; 




/************************************************* ************* 
 系統只會在調用FilterDetach()分離了所有和本Filter Driver相關的Filter Module以後,才會調用FilterUnload例程。 
************************************************** **************/ 
VOID 
FilterUnload( 
        IN PDRIVER_OBJECT DriverObject 
        ) 

#if DBG 
    BOOLEAN bFalse = FALSE; 
#endif 

    UNREFERENCED_PARAMETER(DriverObject); 

    DEBUGP(DL_TRACE, ("===>FilterUnload\n")); 
     
    
    FilterDeregisterDevice(); 
    NdisFDeregisterFilterDriver(FilterDriverHandle); 
     
#if DBG 
    FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse); 
    ASSERT(IsListEmpty(&FilterModuleList)); 

    FILTER_RELEASE_LOCK(&FilterListLock, bFalse); 
     
#endif 
     
    FILTER_FREE_LOCK(&FilterListLock); 
     
    DEBUGP(DL_TRACE, ("<===FilterUnload\n")); 

    return; 
     



/************************************************* ************** 
 FilterOidRequest函數的功能: 
  Filter Module可以在Runnig狀態、Restarting狀態、Paused狀態和Pauseing狀態進行OID的控制和處理。 
  Filter Driver可以處理上層驅動引發的OID請求,NDIS調用Filter Driver的FilterOidRequest例程來處理OID請求,Filter Driver需要調用NdisFOidRequest例程來轉發請求給下層驅動。 

  Filter Driver可以從FilterOidRequest同步和​​異步完成一個OID請求,分別返回NDIS_STATS_SUCCESS和NDIS_STATUS_PENDING即可。 FilterOidRequest可以用同步的直接完成一個OID請求並返回一個錯誤狀態。 

  如果FilterOidRequest返回NDIS_STATUS_PENDING,就必須在OID請求完成後調用 
  NdisFOidRequestComplete來通知上層驅動求請求完成。在這種情況下,請求的結果通過 
  NdisFOidRequestComplete的OidRequest參數返回給上層驅動,並通過Status參數返回請求完成的最終狀態。 
    

  如果FilterOidRequest返回NDIS_STATUS_SUCCESS,通過FilterOidRequest的OidRequest參數返回一個查詢結果到上層。這時不調用 NdisFOidRequestComplete例程。 


  要轉發OID請求到下層驅動,Filter Driver必須調用NdisFOidRequest。如果一個OID請求不能被轉發到下層驅動應該當立即返回。要完成一個請求且不轉發可以直接返回NDIS_STATUS_SUCCESS或其它錯誤狀態或返回NDIS_STATUS_PENDING 後調用NdisFOidRequestComplete。 


  如果NdisFOidRequest返回NDIS_STATUS_PENDING,NDIS在OID請求完成後調用FilterOidRequestComplete來通知求請求完成在這種情況下,請求的結果通過NdisFOidRequestComplete的OidRequest參數返回給上層驅動,並通過Status 參數返回請求完成的最終狀態。 
    

  如果NdisFOidRequest返回NDIS_STATUS_SUCCESS,通過NdisFOidRequest的OidRequest參數返回一個查詢結果到上層。這時不調用FilterOidRequestComplete例程。 
  一個Filter Driver可以調用NdisFOidRequest引發OID請求在Restarting、Running、Pausing和Paused 狀態。 
   
  注意:Fil​​ter Driver必須跟踪這個請求確保不在FilterOidRequestComplete中調用NdisFOidRequestComplete(因為請求是自己引發的不能傳到上層)。 
 ************************************************** **************/ 
NDIS_STATUS 
FilterOidRequest(IN NDIS_HANDLE FilterModuleContext,IN PNDIS_OID_REQUEST Request) 

    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    NDIS_STATUS Status; 
    PNDIS_OID_REQUEST ClonedRequest=NULL; 
    BOOLEAN bSubmitted = FALSE; 
    PFILTER_REQUEST_CONTEXT Context; 
    BOOLEAN bFalse = FALSE; 

     
    DEBUGP(DL_TRACE, ("===>FilterOidRequest: Request %p​​.\n", Request)); 

    do 
    { 
        Status = NdisAllocateCloneOidRequest(pFilter->FilterHandle, 
                                            Request, 
                                            FILTER_TAG, 
                                            &ClonedRequest); 
        if (Status != NDIS_STATUS_SUCCESS) 
        { 
            DEBUGP(DL_WARN, ("FilerOidRequest: Cannot Clone Request\n")); 
            break; 
        } 

        Context = (PFILTER_REQUEST_CONTEXT)(&ClonedRequest->SourceReserved[0]); 
        *Context = Request; 

        bSubmitted = TRUE; 

        
        ClonedRequest->RequestId = Request->RequestId; 

        pFilter->PendingOidRequest = ClonedRequest; 


//Filter Driver可以調用NdisFOidRequest引發一個OID查詢和設置請求給下層驅動。 
        Status = NdisFOidRequest(pFilter->FilterHandle, ClonedRequest); 

        if (Status != NDIS_STATUS_PENDING) 
        { 

            FilterOidRequestComplete(pFilter, ClonedRequest, Status); 
            Status = NDIS_STATUS_PENDING; 
        } 

         
    }while (bFalse); 

    if (bSubmitted == FALSE) 
    { 
        switch(Request->RequestType) 
        { 
            case NdisRequestMethod: 
                Request->DATA.METHOD_INFORMATION.BytesRead = 0; 
                Request->DATA.METHOD_INFORMATION.BytesNeeded = 0; 
                Request->DATA.METHOD_INFORMATION.BytesWritten = 0; 
                break; 

            case NdisRequestSetInformation: 
                Request->DATA.SET_INFORMATION.BytesRead = 0; 
                Request->DATA.SET_INFORMATION.BytesNeeded = 0; 
                break; 

            case NdisRequestQueryInformation: 
            case NdisRequestQueryStatistics: 
            default: 
                Request->DATA.QUERY_INFORMATION.BytesWritten = 0; 
                Request->DATA.QUERY_INFORMATION.BytesNeeded = 0; 
                break; 
        } 

    } 
    DEBUGP(DL_TRACE, ("<===FilterOidRequest: Status %8x.\n", Status)); 

    return Status; 




/************************************************* ************ 
 FilterCancelOidRequest函數的功能: 
  NDIS調用FilterCancelOidRequest來取消一個OID請求,當NDIS調用FilterCancelOidRequest時, 
  Filter Driver應該盡可能快的調用NdisFCancelOidRequest。 
 ************************************************** ***********/ 
VOID 
FilterCancelOidRequest( 
    IN NDIS_HANDLE FilterModuleContext, 
    IN PVOID RequestId 
    ) 
    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    PNDIS_OID_REQUEST Request = NULL; 
    PFILTER_REQUEST_CONTEXT Context; 
    PNDIS_OID_REQUEST OriginalRequest = NULL; 
    BOOLEAN bFalse = FALSE; 
      
    FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); 
     
    Request = pFilter->PendingOidRequest; 

    if (Request != NULL) 
    { 
        Context = (PFILTER_REQUEST_CONTEXT)(&Request->SourceReserved[0]); 
     
        OriginalRequest = (*Context); 
    } 

    if ((OriginalRequest != NULL) && (OriginalRequest->RequestId == RequestId)) 
    { 
        FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); 
         
        NdisFCancelOidRequest(pFilter->FilterHandle, RequestId); 
    } 
    else 
    { 
        FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); 
    } 
         


     
VOID 
FilterOidRequestComplete( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNDIS_OID_REQUEST Request, 
        IN NDIS_STATUS Status 
        ) 

    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    PNDIS_OID_REQUEST OriginalRequest; 
    PFILTER_REQUEST_CONTEXT Context; 
    BOOLEAN bFalse = FALSE; 
     
    DEBUGP(DL_TRACE, ("===>FilterOidRequestComplete, Request %p​​.\n", Request)); 

    Context = (PFILTER_REQUEST_CONTEXT)(&Request->SourceReserved[0]); 
    OriginalRequest = (*Context); 

    
    if (OriginalRequest == NULL) 
    { 
        filterInternalRequestComplete(pFilter, Request, Status); 
        return; 
    } 


    FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); 
     
    ASSERT(pFilter->PendingOidRequest == Request); 
    pFilter->PendingOidRequest = NULL; 

    FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); 

    switch(Request->RequestType) 
    { 
        case NdisRequestMethod: 
            OriginalRequest->DATA.METHOD_INFORMATION.OutputBufferLength = Request->DATA.METHOD_INFORMATION.OutputBufferLength; 
            OriginalRequest->DATA.METHOD_INFORMATION.BytesRead = Request->DATA.METHOD_INFORMATION.BytesRead; 
            OriginalRequest->DATA.METHOD_INFORMATION.BytesNeeded = Request->DATA.METHOD_INFORMATION.BytesNeeded; 
            OriginalRequest->DATA.METHOD_INFORMATION.BytesWritten = Request->DATA.METHOD_INFORMATION.BytesWritten; 
            break; 

        case NdisRequestSetInformation: 
            OriginalRequest->DATA.SET_INFORMATION.BytesRead = Request->DATA.SET_INFORMATION.BytesRead; 
            OriginalRequest->DATA.SET_INFORMATION.BytesNeeded = Request->DATA.SET_INFORMATION.BytesNeeded; 
            break; 

        case NdisRequestQueryInformation: 
        case NdisRequestQueryStatistics: 
        default: 
            OriginalRequest->DATA.QUERY_INFORMATION.BytesWritten = Request->DATA.QUERY_INFORMATION.BytesWritten; 
            OriginalRequest->DATA.QUERY_INFORMATION.BytesNeeded = Request->DATA.QUERY_INFORMATION.BytesNeeded; 
            break; 
    } 


    (*Context) = NULL; 

    NdisFreeCloneOidRequest(pFilter->FilterHandle, Request); 


/* 
如果FilterOidRequest返回NDIS_STATUS_PENDING,就必須在OID請求完成後調用NdisFOidRequestComplete 來通知上層驅動求請求完成。在這種情況下,請求的結果通過NdisFOidRequestComplete的OidRequest參數返回給上層驅動,並通過Status參數返回請求完成的最終狀態。 
要轉發OID請求到下層驅動,Filter Driver必須調用NdisFOidRequest。 
如果一個OID請求不能被轉發到下層驅動應該當立即返回。 
要完成一個請求且不轉發可以直接返回NDIS_STATUS_SUCCESS或其它錯誤狀態 
或返回NDIS_STATUS_PENDING後調用NdisFOidRequestComplete。 
*/ 

    NdisFOidRequestComplete(pFilter->FilterHandle, OriginalRequest, Status); 
     
    DEBUGP(DL_TRACE, ("<===FilterOidRequestComplete.\n")); 




/************************************************* ************ 
  FilterStatus函數的功能: 
    當下層驅動報告狀態的時候NDIS會調用它。此外,Filter Driver還可以自己引發一個狀態指示。 

當下層驅動調用一個狀態指示例程時(NdisMIndicateStatusEx或NdisFIndicateStats),NDIS 
會調用Filter Driver的FilterStatus例程。 

Filter Driver在FilterStatus中調用NdisFIndicateStatus傳遞一個狀態指示給上層驅動。此外,還可以過濾狀態指示(不用調用NdisFIndicateStatus)或在調用NdisFIndicateStatus之前修改狀態信息。 
   
    
Filter Driver要自己引發一個狀態報告,可以在NDIS未調用FilterStatus的情況下調用NdisFIndicateStatus。在這種情況下,Filter Driver要設置SourceHandle 成員為FilteAttech 參數提供的NdisFilterHandle句柄。 
如果一個狀態指示是一個OID請求相關的(下層請求一個OID 下層要做相應的狀態指示),那麼狀態的DestinationHandle和RequestId成員要設置成上層的OID請求包攜帶的數據。 
   

Filter Driver調用NdisFIndicateStatus後NDIS會調用相鄰上層的狀態指示函數(ProtocolStatusEx或FilterStatus)。 
************************************************** **************/ 
VOID 
FilterStatus( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNDIS_STATUS_INDICATION StatusIndication 
        ) 

    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
#if DBG 
    BOOLEAN bFalse = FALSE; 
#endif 

    DEBUGP(DL_TRACE, ("===>FilterStaus, IndicateStatus = %8x.\n", StatusIndication->StatusCode)); 
    
#if DBG 
    FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); 
    ASSERT(pFilter->bIndicating == FALSE); 
    pFilter->bIndicating = TRUE; 
    FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); 
#endif 
     
    
    NdisFIndicateStatus(pFilter->FilterHandle, StatusIndication); 

#if DBG 
    FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); 
    ASSERT(pFilter->bIndicating == TRUE); 
    pFilter->bIndicating = FALSE; 
     
    FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); 


#endif 
     
    DEBUGP(DL_TRACE, ("<===FilterStaus.\n")); 





/* 
Filter Driver提供FilterPnpEventNotify來接收NDIS傳遞的PnP和電源管理事件 
*/ 
VOID 
FilterDevicePnPEventNotify( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNET_DEVICE_PNP_EVENT NetDevicePnPEvent 
        ) 

    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    NDIS_DEVICE_PNP_EVENT DevicePnPEvent = NetDevicePnPEvent->DevicePnPEvent; 
#if DBG 
    BOOLEAN bFalse = FALSE; 
#endif 

    DEBUGP(DL_TRACE, ("===>FilterDevicePnPEventNotify: NetPnPEvent = %p.\n", NetDevicePnPEvent)); 

    switch (DevicePnPEvent) 
    { 

        case NdisDevicePnPEventQueryRemoved: 
        case NdisDevicePnPEventRemoved: 
        case NdisDevicePnPEventSurpriseRemoved: 
        case NdisDevicePnPEventQueryStopped: 
        case NdisDevicePnPEventStopped: 
        case NdisDevicePnPEventPowerProfileChanged: 
        case NdisDevicePnPEventFilterListChanged: 
                 
            break; 
             
        default: 
            DEBUGP(DL_ERROR, ("FilterDevicePnPEventNotify: Invalid event.\n")); 
            FILTER_ASSERT(bFalse); 
             
            break; 
    } 

  //Filter Driver要下層驅動轉發收到的事件,轉發事件要用到NdisFDevicePnPEventNotify例程 
    NdisFDevicePnPEventNotify(pFilter->FilterHandle, NetDevicePnPEvent); 
                               
    DEBUGP(DL_TRACE, ("<===FilterDevicePnPEventNotify\n")); 






/* 
Filter Driver提供了FilterNetPnpEvent例程來處理網絡Pnp和電源管理事件通知。 
*/ 
NDIS_STATUS 
FilterNetPnPEvent( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNET_PNP_EVENT_NOTIFICATION NetPnPEventNotification 
        ) 
    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS; 

//Filter Driver需要轉發網絡PnP和電源管理事件給上層驅動。轉發這些事件是通NdisFNetPnpEvent來完成的。 
    Status = NdisFNetPnPEvent(pFilter->FilterHandle, NetPnPEventNotification); 
     
    return Status; 




/************************************************* ************* 
 FilterSendNetBufferListsComplete函數的功能: 
  NDIS調用FilterSendNetBufferListsComplete 把發送的結構和數據返還給Filter Driver。 NDIS可以收集多次NdisFSendNetBufferLists發送的結構和數據形成一個單鍊錶傳遞給FilterSendNetBufferListsComplete。除非到NDIS調用FilterSendNetBufferListsComplete,否則一個發送請求的當前狀態總是未知的。 

  一個過濾驅動是不能在NDIS調用FilterSendNetBufferListsComplete返回結構之前對NET_BUFFER_LIST和其關聯的數據做檢查的。 FilterSendNetBufferListsComplete要完成一個發送請求完成後的任何必要的後繼處理。當NDIS調用FilterSendNetBufferListsComplete時,Filter Driver就重新獲地對結構及結構相關資源的所有權。可以在FilterSendNetBufferListsComplete中釋放相關的資源和準備下一個NdisFSendNetBufferLists調用。 
  
  NDIS總是按照過濾驅動調用NdisFSendNetBufferLists提交的順序傳遞給下層驅動,但是回返FilterSendNetBufferListsComplete 的順序則是任意的。 Filter Driver可以請求一個回環發送請求,只要把NdisFSendNetBufferLists的SendFlags設置成NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK就行了。 NDIS會引發一個包含發送數據的接收包指示。 
   
    
  一個Filter Driver應該對自己引發的發送請求保持跟踪並確保在完成時不調用NdisFSendNetBufferComplete例程。 
 ************************************************** ************/ 
VOID 
FilterSendNetBufferListsComplete( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNET_BUFFER_LIST NetBufferLists, 
        IN ULONG SendCompleteFlags 
        ) 

    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    ULONG NumOfSendCompletes = 0; 
    BOOLEAN DispatchLevel; 
    PNET_BUFFER_LIST CurrNbl; 
    
    DEBUGP(DL_TRACE, ("===>SendNBLComplete, NetBufferList: %p.\n", NetBufferLists)); 

    
    if (pFilter->TrackSends) 
    { 
        CurrNbl = NetBufferLists; 
        while (CurrNbl) 
        { 
            NumOfSendCompletes++; 
            CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl); 
             
        } 
        DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendCompleteFlags); 
        FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); 
        pFilter->OutstandingSends -= NumOfSendCompletes; 
        FILTER_LOG_SEND_REF(2, pFilter, PrevNbl, pFilter->OutstandingSends); 
        FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 
    } 

    NdisFSendNetBufferListsComplete(pFilter->FilterHandle, NetBufferLists, SendCompleteFlags); 

    DEBUGP(DL_TRACE, ("<===SendNBLComplete.\n")); 




/************************************************* ************ 
 FilterSendNetBufferLists函數的功能: 
  NDIS調用一個Filter Driver的FilterSendNetBufferLists例程來過濾上層驅動的發送請求。 Filter Driver不能改變其它驅動傳來的NET_BUFFER_LIST結構中的SourceHandle成員的值。它可以過濾數據並發送過濾的數據到下層驅動。 
  對每一個提交到FilterSendNetBufferLists的NDIS_BUFFER_LIST,我們可做下面的操作。 
    
  1)可以把緩衝區通過NdisFSendBufferLists 傳遞給下層驅動,NDIS 保證上下文空間對FilterDriver的有效性。過濾驅動可以在發送前修改緩衝區的內容。可以像處理自己引發的發送請求的緩衝區一樣處理這個緩衝區。 
  2)可以調用NdisFSendNetBufferListsComplete 拒絕傳遞這個包 
  3)排隊緩衝區內容到本地的供以後處理。例如要在一定超時後處理或要接收到特定包後才處理等。如果支持這種處理方式就要支持取消請求的操作。 
  4)可以拷貝緩衝區並引發一個發送請求。它類似自己引發一個發送請求,但必須先調用NdisFSendNetBufferComplete返回上層驅動的緩衝區。 
       
  發送請求在驅動棧繼續完成,當一個微端口驅動調用NdisMSendNetBufferListsComplete完成一個發送請求時,NDIS會調用微端口 
  驅動之上最近的Filter Driver的FilterSendNetBufferLists例程。 
   
  在一個發送操作完成後,Filter Driver可以做在FilterSendNetBufferLists中所有修改的相反操作。 FilterSendNetBufferListsComplete返回一個NET_BUFFER_LIST結構的單鍊錶和發送請求的最終狀態給上層的驅動。當最頂層的Filter Module的FilterSendNetBufferListsComplete被調用完成後NDIS會調用引發發送請求的協議驅動的ProtocolSendNetBufferListsComplete。如果Filter Driver不提供FilterSendNetBufferLists它還是可以引發一個發送操作的,但它必須提供一個FilterSendNetBufferListsComplete並且不能在這個例程裡把這個事件傳遞給上層驅動。 

  一個Filter Driver可以傳遞或過濾一個上層驅動的回環請求,要傳遞一個回環請求,NDIS會設置FilterSendNetBufferLists的SendFlags參數為NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK,Filter Driver在調用NdisFSendNetBufferLists時把這個標記傳給它即可。在回環請求的情況下NDIS會指示一個包含發送數據的接收包。 

  通常情況下,如果一個Filter Driver修改的任何行為不是NDIS提供的標準服務,那麼它應該當自己為NDIS提供相應的服務。例如,如果一個Filter Driver修改了一個硬件地址請求,就必須處理直接到這個新地址回環包。在這種情況下, 因為Filter Driver已經更改了地址NDIS是不能提供一個回環服務的。 
  還有就是如果Filter Driver設置了混雜模式那它就不能傳遞額外的數據給上層接收。 
************************************************** ************/ 

VOID 
FilterSendNetBufferLists( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNET_BUFFER_LIST NetBufferLists, 
        IN NDIS_PORT_NUMBER PortNumber, 
        IN ULONG SendFlags 
        ) 


    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    PNET_BUFFER_LIST CurrNbl; 
    BOOLEAN DispatchLevel; 
    BOOLEAN bFalse = FALSE; 
     
    DEBUGP(DL_TRACE, ("===>SendNetBufferList: NBL = %p.\n", NetBufferLists)); 

    do 
    { 

       DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags); 

#if DBG 
         
        FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); 
         
        if (pFilter->State != FilterRunning) 
        { 
            FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 
             
            CurrNbl = NetBufferLists; 
            while (CurrNbl) 
            { 
                NET_BUFFER_LIST_STATUS(CurrNbl) = NDIS_STATUS_PAUSED; 
                CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl); 
            } 
            NdisFSendNetBufferListsComplete(pFilter->FilterHandle, 
                        NetBufferLists, 
                        DispatchLevel ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0); 
            break; 
             
        } 
        FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 
#endif 

/************************************************* *****/ 

             //在這裡添加我們的代碼 

/************************************************* *****/ 

        if (pFilter->TrackSends) 
        { 
            FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); 
            CurrNbl = NetBufferLists; 
            while (CurrNbl) 
            { 
                pFilter->OutstandingSends++; 
                FILTER_LOG_SEND_REF(1, pFilter, CurrNbl, pFilter->OutstandingSends); 
                 
                CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl); 
            } 
            FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 
        } 
        

        NdisFSendNetBufferLists(pFilter->FilterHandle, NetBufferLists, PortNumber, SendFlags); 
         
         
    } 
    while (bFalse); 
     
    DEBUGP(DL_TRACE, ("<===SendNetBufferList. \n")); 




/************************************************* ************ 
 FilterReturnNetBufferLists函數的功能: 
  如果Filter Driver設置了NdisFIndicateReceiveNetBufferLists的狀態為NDIS_STATUS_SUCCESS, NDIS通過驅動的FilterReturnNetBufferLists 
  返回指示數據。在這種情況下Filter Driver失去了對NET_BUFFER_LIST的所有權,直到FilterReturnNetBufferLists被調用。 

  Filter Driver調用NdisFIndicateNetBufferLists 傳遞接收指示給驅動棧上的上層驅動,如果上層驅動保留了對緩衝區(NET_BUFFER_LIST)的所有權,NDIS會調用Filter Driver的FilterReturnNetBufferLists 例程。 
   
  在FilterReturnNetBufferLists中應該撤消在接收路徑上(如在FilterReciveNetBufferLists中做的一些處理)的操作。當最底層的Filter Module完成對緩衝區(NET_BUFFER_LIST)的處理後,NDIS把緩衝區返回給微端口驅動。如果FilterReceiveNetBufferLists的ReceiveFlags沒有設置NDIS_RECEIVE_FLAGS_RESOURCES標記, FilterDriver調用NdisFReturnNetBufferList返回這個緩衝區數據,如果設置了FilterReceiveNetBufferLists直接返回時就把緩衝區返還給了下層微端口驅動。 
 ************************************************** *************/ 
VOID 
FilterReturnNetBufferLists( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNET_BUFFER_LIST NetBufferLists, 
        IN ULONG ReturnFlags 
        ) 

    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    PNET_BUFFER_LIST CurrNbl = NULL; 
    UINT NumOfNetBufferLists = 0; 
    BOOLEAN DispatchLevel; 
    ULONG Ref; 
     
    DEBUGP(DL_TRACE, ("===>ReturnNetBufferLists, NetBufferLists is %p.\n", NetBufferLists)); 

    if (pFilter->TrackReceives) 
    { 
        while (CurrNbl) 
        { 
             
            NumOfNetBufferLists ++; 
            CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl); 
        } 
    } 
    NdisFReturnNetBufferLists(pFilter->FilterHandle, NetBufferLists, ReturnFlags); 
  
    if (pFilter->TrackReceives) 
    { 
        DispatchLevel = NDIS_TEST_RETURN_AT_DISPATCH_LEVEL(ReturnFlags); 
        FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); 

        pFilter->OutstandingRcvs -= NumOfNetBufferLists; 
        Ref = pFilter->OutstandingRcvs; 
        FILTER_LOG_RCV_REF(3, pFilter, NetBufferLists, Ref); 
        FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 
    } 


    DEBUGP(DL_TRACE, ("<===ReturnNetBufferLists.\n")); 
     




/************************************************* ************** 
 FilterReceiveNetBufferLists函數的功能: 
  Filter Driver調用NdisFIndicateReceiveNetBufferLists來指示發送數據。這個函數通過NET_BUFFER_LIST結構給上層驅動指示數據。 Filter Driver可以從池中分配這個結構。如果Filter Driver設置了NdisFIndicateReceiveNetBufferLists的狀態為NDIS_STATUS_SUCCESS, NDIS通過驅動的FilterReturnNetBufferLists返回指示數據。在這種情況下Filter Driver失去了對NET_BUFFER_LIST的所有權直到FilterReturnNetBufferLists被調用。如果Filter Driver在調用NdisFIndicateReceiveNetBufferLists時設置ReceiveFlags為ND​​IS_RECEIVE_FLAGS_RESOURCES,在函數返回後Filter Driver會立即恢復對NET_BUFFER_LIST的所有權,這時Filter Driver必須立即處理這個NET_BUFFER_LIST的返回,因為NDIS在這種情況下是不會調用FilterReturnNetBufferLists返回NET_BUFFER_LIST結構的。 

  注意: 一個Filter Driver應該跟踪自己引發的接收指示確保它在FilterReturnNetBufferLists 
  中不調用NdisFReturnNetBufferLists。 
 ************************************************** *************/ 
VOID 
FilterReceiveNetBufferLists( 
        IN NDIS_HANDLE FilterModuleContext, 
        IN PNET_BUFFER_LIST NetBufferLists, 
        IN NDIS_PORT_NUMBER PortNumber, 
        IN ULONG NumberOfNetBufferLists, 
        IN ULONG ReceiveFlags 
         ) 


    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
    BOOLEAN DispatchLevel; 
    ULONG Ref; 
    BOOLEAN bFalse = FALSE; 


#if DBG 
    ULONG ReturnFlags; 
#endif 
     
    DEBUGP(DL_TRACE, ("===>ReceiveNetBufferList: NetBufferLists = %p.\n", NetBufferLists)); 
    do 
    { 

        DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags); 
#if DBG 
        FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); 
  
        if (pFilter->State != FilterRunning) 
        { 
            FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 

            if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags)) 
            { 
                ReturnFlags = 0; 
                if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags)) 
                { 
                    NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); 
                } 
                 
                NdisFReturnNetBufferLists(pFilter->FilterHandle, NetBufferLists, ReturnFlags); 
            } 
            break; 
        } 
        FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 
#endif 

        ASSERT(NumberOfNetBufferLists >= 1); 


/*------------------------------------------------ --------------------------------------*/ 

                  //在這裡添加我們的代碼 


/*------------------------------------------------ ---------------------------------------*/ 


        if (pFilter->TrackReceives) 
        { 
            FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); 
            pFilter->OutstandingRcvs += NumberOfNetBufferLists; 
            Ref = pFilter->OutstandingRcvs; 
             
            FILTER_LOG_RCV_REF(1, pFilter, NetBufferLists, Ref); 
            FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 
        } 


/************************************************* *********** 
調用NdisFIndicateReceiveNetBufferLists來指示發送數據。 

如果Filter Driver設置了NdisFIndicateReceiveNetBufferLists的狀態為NDIS_STATUS_SUCCESS, NDIS通過驅動的FilterReturnNetBufferLists 返回指示數據。 
     
如果Filter Driver設置了NdisFIndicateReceiveNetBufferLists的ReceiveFlags值為 
NDIS_RECEIVE_FLAGS_RESOURCES,那麼在函數返回後Filter Driver會立即恢復對 
NET_BUFFER_LIST的所有權,這時Filter Driver必須立即處理這個NET_BUFFER_LIST的返回。 
在這種情況下是不會調用FilterReturnNetBufferLists返回NET_BUFFER_LIST結構的。 
************************************************** **********/ 
        NdisFIndicateReceiveNetBufferLists( 
                   pFilter->FilterHandle, 
                   NetBufferLists, 
                   PortNumber, 
                   NumberOfNetBufferLists, 
                   ReceiveFlags); 


        if (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags) && pFilter->TrackReceives) 
        { 
            FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); 
            pFilter->OutstandingRcvs -= NumberOfNetBufferLists; 
            Ref = pFilter->OutstandingRcvs; 
            FILTER_LOG_RCV_REF(2, pFilter, NetBufferLists, Ref); 
            FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); 
        } 

    } while (bFalse); 
     
    DEBUGP(DL_TRACE, ("<===ReceiveNetBufferList: Flags = %8x.\n", ReceiveFlags)); 
     



/************************************************* ************* 
 FilterCancelSendNetBufferLists函數的功能: 
 過濾驅動調用NDIS_SET_NET_BUFFER_LIST_CANCEL_ID宏為每一個NET_BUFFER_LIST標記一個取消Id。 
 在為網絡數據分配取消ID之前,必須先調用NdisGenratePartialCanceId獲得取消ID的高字節。 
 這是為了確保不是驅動不會把一個取消ID分配給兩個驅動驅動通常在DriverEntry調用 
 NdisGenratePartialCanceld,但是驅動可以在不同的時間多次調用它來獲得多個取消ID。 
 要取消被標記過取消ID且正在傳輸的數據,驅動可以調用NdisFCancelSendNetBufferLists例程 
 來完成。要獲得取消ID可以用NDIS_GET_NET_BUFFER_LIST_CANCEL_ID宏來完成。 

 如果一個Filter Driver對所有發送的NET_BUFFER_LIST標記了相同的取消ID那它可以用一個 
 NdisFCancelSendNetBufferLists來取消所有的發送請求。如果把一部發送請求的NET_BUFFER_LIST標記相同的取消ID那麼就可以調用一次NdisFCancelSendNetBufferLists來取消這部分發送請求。 

 在實現這個功能時NDIS會調用下層驅動的取消發送功能。中斷正在執行的發送任務後,下層驅動會 
 調用發送完成全程(如:NdisMSendNetBufferListComplete)返回指定的NET_BUFFER_LIST結構並指定返回狀態為NDIS_STATUS_CANCELLED, NDIS依次調用Filter Driver的FilterSendNetBufferListsComplete例程。在FilterSendNetBufferListsComplete中要用NDIS_SET_NET_BUFFER_LIST_CANCEL_ID設置取消的NET_BUFFER_LIST 
 的取消ID 為NULL,這樣是為了防止這個ID,在NET_BUFFER_LIST被再次分配時使用。 


上層驅動在取消一個未完成的發送請求時也必須對這個發送請求的NET_BUFFER_LIST結構設定取消ID。 
 NDIS會傳遞給Filter Driver的FilterCancelSendNetBufferLists一個取消ID來取消發送請求的 
 NET_BUFFER_LIST發送。 FilterCanCelSendNetBufferLists下執行下列操作。 
  1)遍歷Filter Driver的發送隊列,用NDIS_GET_NET_BUFFER_LSIT_CANCEL_ID獲得隊列中NET_BUFFER_LIST的取消ID與FilterCancelSendBufferLists的取消ID比較。 
  2)移除隊列中取消ID 和FilterCancelSentBufferLists中取消ID相同的元素。 
  3)調用NdisFSendNetBufferListsComplete來完成這些NET_BUFFER_LIST並設定返回狀 
    態為NDIS_STATUS_CANCELLED。 
  4)調用NdisFCancelSendNetBufferLists傳遞取消發送請求給下層驅動。傳遞取消ID給下層驅動就Filter Driver取消自己引發的發關請求一樣。 
 ************************************************** ***********/ 
VOID 
FilterCancelSendNetBufferLists( 
    IN NDIS_HANDLE FilterModuleContext, 
    IN PVOID CancelId 
    ) 

    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 

    NdisFCancelSendNetBufferLists(pFilter->FilterHandle,CancelId); 




/************************************************* ************* 
 FilterSetModuleOptions函數的功能: 
  必須要在初始化時為驅動註冊FilterSetModuleOptions例程,驅動可以在這個例程中初始化 
  NDIS_FILTER_PARTIAL_CHARACTERISTICS結構來調用NdisSetOptionalHandlers來完成必變。 
  這個例程如果存在那麼在調用Filter Driver的FilterRestart例程之前調用它。 
 ************************************************** *************/ 
NDIS_STATUS 
FilterSetModuleOptions( 
    IN NDIS_HANDLE FilterModuleContext 
    ) 
     
   PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; 
   NDIS_FILTER_PARTIAL_CHARACTERISTICS OptionalHandlers; 
   NDIS_STATUS Status = NDIS_STATUS_SUCCESS; 
   BOOLEAN bFalse = FALSE; 

  
   if (bFalse) 
   { 
       UINT i; 

       
       pFilter->CallsRestart++; 

       i = pFilter->CallsRestart % 8; 

       pFilter->TrackReceives = TRUE; 
       pFilter->TrackSends = TRUE; 

       NdisMoveMemory(&OptionalHandlers, &DefaultChars, sizeof(OptionalHandlers)); 
       OptionalHandlers.Header.Type = NDIS_OBJECT_TYPE_FILTER_PARTIAL_CHARACTERISTICS; 
       OptionalHandlers.Header.Size = sizeof(OptionalHandlers); 
       switch (i) 
       { 
            
            case 0: 
                OptionalHandlers.ReceiveNetBufferListsHandler = NULL; 
                pFilter->TrackReceives = FALSE; 
                break; 

            case 1: 
                 
                OptionalHandlers.ReturnNetBufferListsHandler = NULL; 
                pFilter->TrackReceives = FALSE; 
                break; 

            case 2: 
                OptionalHandlers.SendNetBufferListsHandler = NULL; 
                pFilter->TrackSends = FALSE; 
                break; 

            case 3: 
                OptionalHandlers.SendNetBufferListsCompleteHandler = NULL; 
                pFilter->TrackSends = FALSE; 
                break; 

            case 4: 
                OptionalHandlers.ReceiveNetBufferListsHandler = NULL; 
                OptionalHandlers.ReturnNetBufferListsHandler = NULL; 
                break; 

            case 5: 
                OptionalHandlers.SendNetBufferListsHandler = NULL; 
                OptionalHandlers.SendNetBufferListsCompleteHandler = NULL; 
                break; 

            case 6: 
                 
                OptionalHandlers.ReceiveNetBufferListsHandler = NULL; 
                OptionalHandlers.ReturnNetBufferListsHandler = NULL; 
                OptionalHandlers.SendNetBufferListsHandler = NULL; 
                OptionalHandlers.SendNetBufferListsCompleteHandler = NULL; 
                break; 
                 
            case 7: 
                break; 
       } 
       Status = NdisSetOptionalHandlers(pFilter->FilterHandle, (PNDIS_DRIVER_OPTIONAL_HANDLERS)&OptionalHandlers ); 
   } 
   return Status; 



NDIS_STATUS 
filterDoInternalRequest( 
    IN PMS_FILTER FilterModuleContext, 
    IN NDIS_REQUEST_TYPE RequestType, 
    IN NDIS_OID Oid, 
    IN PVOID InformationBuffer, 
    IN ULONG InformationBufferLength, 
    IN ULONG OutputBufferLength, OPTIONAL 
    IN ULONG MethodId, OPTIONAL 
    OUT PULONG pBytesProcessed 
    ) 

    FILTER_REQUEST FilterRequest; 
    PNDIS_OID_REQUEST NdisRequest = &FilterRequest.Request; 
    NDIS_STATUS Status; 
    BOOLEAN bFalse; 


    bFalse = FALSE; 
    NdisZeroMemory(NdisRequest, sizeof(NDIS_OID_REQUEST)); 

    NdisInitializeEvent(&FilterRequest.ReqEvent); 
     
    NdisRequest->Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; 
    NdisRequest->Header.Revision = NDIS_OID_REQUEST_REVISION_1; 
    NdisRequest->Header.Size = sizeof(NDIS_OID_REQUEST); 
    NdisRequest->RequestType = RequestType; 

    switch (RequestType) 
    { 
        case NdisRequestQueryInformation: 
             NdisRequest->DATA.QUERY_INFORMATION.Oid = Oid; 
             NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer =InformationBuffer; 
                                     
             NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength; 
                                    
            break; 

        case NdisRequestSetInformation: 
             NdisRequest->DATA.SET_INFORMATION.Oid = Oid; 
             NdisRequest->DATA.SET_INFORMATION.InformationBuffer =InformationBuffer; 
                                     
             NdisRequest->DATA.SET_INFORMATION.InformationBufferLength =InformationBufferLength; 
                                     
            break; 

        case NdisRequestMethod: 
             NdisRequest->DATA.METHOD_INFORMATION.Oid = Oid; 
             NdisRequest->DATA.METHOD_INFORMATION.MethodId = MethodId; 
             NdisRequest->DATA.METHOD_INFORMATION.InformationBuffer = InformationBuffer; 
                                     
             NdisRequest->DATA.METHOD_INFORMATION.InputBufferLength = InformationBufferLength; 
                                     
             NdisRequest->DATA.METHOD_INFORMATION.OutputBufferLength = OutputBufferLength; 
             break; 
              
                 

        default: 
            FILTER_ASSERT(bFalse); 
            break; 
    } 

    NdisRequest->RequestId = (PVOID)FILTER_REQUEST_ID; 
     
    Status = NdisFOidRequest(FilterModuleContext->FilterHandle,NdisRequest); 
     

    if (Status == NDIS_STATUS_PENDING) 
    { 
         
        NdisWaitEvent(&FilterRequest.ReqEvent, 0); 
        Status = FilterRequest.Status; 
    } 


    if (Status == NDIS_STATUS_SUCCESS) 
    { 
        if (RequestType == NdisRequestSetInformation) 
        { 
            *pBytesProcessed = NdisRequest->DATA.SET_INFORMATION.BytesRead; 
        } 

        if (RequestType == NdisRequestQueryInformation) 
        { 
            *pBytesProcessed = NdisRequest->DATA.QUERY_INFORMATION.BytesWritten; 
        } 

        if (RequestType == NdisRequestMethod) 
        { 
            *pBytesProcessed = NdisRequest->DATA.METHOD_INFORMATION.BytesWritten; 
        } 
         
        if (RequestType == NdisRequestMethod) 
        { 
            if (*pBytesProcessed > OutputBufferLength) 
            { 
                *pBytesProcessed = OutputBufferLength; 
            } 
        } 
        else 
        { 
             
            if (*pBytesProcessed > InformationBufferLength) 
            { 
                *pBytesProcessed = InformationBufferLength; 
            } 
        } 
    } 


    return (Status); 



VOID 
filterInternalRequestComplete( 
    IN NDIS_HANDLE FilterModuleContext, 
    IN PNDIS_OID_REQUEST NdisRequest, 
    IN NDIS_STATUS Status 
    ) 


    PFILTER_REQUEST FilterRequest; 

    UNREFERENCED_PARAMETER(FilterModuleContext); 
    
    FilterRequest = CONTAINING_RECORD(NdisRequest, FILTER_REQUEST, Request); 

    FilterRequest->Status = Status; 

    NdisSetEvent(&FilterRequest->ReqEvent); 
}