適配器的打開與關閉(下)
先介紹和適配器相關結構體:
_ADAPTER用來描述一個已打開的適配器實例
具體定義如下:
typedef struct_ADAPTER
{
//指向一個NPF驅動程序實例的句柄
HANDLE hFile;
//保存當前打開網路適配器的名稱
CHAR SymbolicLink[MAX_LINK_NAME_LENGTH];
//每個數據包重複發送的次數
int NumWrites;
//與適配器讀調用關聯的通知事件
HANDLE ReadEvent;
//讀超時時間
UINT ReadTimeOut;
CHAR Name[ADAPTER_NAME_LENGTH];
PWAN_ADAPTER pWanAdapter;
//適配器標識
UINT Flags;
}ADAPTER,*LPADAPTER;
_PACKET從驅動程序處得到的一組數據包
具體定義如下:
typedef struct_PACKET
{
//儲存數據包的緩衝區
PVOID Buffer;
//緩衝區的長度
UINT Length;
//緩衝區中的有效位元組數
DWORD ulBytesReceived;
}PACKET,*LPPACKET;
_PACKET_OID_DATA包含一個OID請求
具體定義如下:
struct_PACKET_OID_DATA
{
//OID碼,獲得一個有效編碼的完整列表
ULONG Oid;
//Data成員的長度
ULONG Length;
//變長成員,包含傳遞到適配器或從適配器接收的信息
UCHAR Data[1];
};
typedef struct_PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA;
由上節已知適配器的打開主要依靠pcap_open函數實現,而在pcap_open函數中,調用pcap_create函數創建pcap_t結構體,而pcap_create函數中又是通過調用pcap_create_common來實現創建pcap_t結構體的,同時調用pcap_active_win32實現對網路適配器實例的激活,pcap_active_win32函數又通過調用Packet.dll的PacketOpenAdapter函數打開適配器,並通過調用PacketSetHwFilter設置捕獲器的過濾模式,最後調用PacketAllocatePacket和PacketSetBuff對數據包結構體進行分配空間及初始化操作.
PacketOpenAdapter函數先獲得g_AdaptersInfoMutex互斥信號,然後更新鏈表並查找適配器,接著通過調用更為底層的PacketOpenAdapterNPF函數試圖打開正常的NPF適配器,並釋放互斥信號。
而PacketOpenAdapterNPF函數同通過連到服務控制管理器(SCM),檢查NPF驅動程序是否存在,若不存在,調用PacketInstallDriver函數安裝驅動程序,接下來分配並初始化_ADAPTER對象,根據設備的原始名稱創建NPF設備名稱,並試圖通過調用CreateFile函數打開該適配器,若成功,則調用PacketSetReadEvt函數通過IOCTL系統調用將讀事件信號分配捕獲並傳遞到內核,調用PacketSetMaxLookaheadsize函數為驅動程序NPF_tap函數設置前視緩衝區,調用PacketGetNetType函數獲取適配器鏈路層信息。
內核中有一個最重要的結構體:_OPEN_INSTANCE描述NPF驅動程序運行實例狀態,幾乎被所有驅動程序函數使用,由於代碼太長,這裡就不展示它的具體定義。
內核驅動程序NPF,因此相關函數基本都以NPF開頭或結尾,Packet.dll中的PacketOpenAdapterNPF調用CreateFileA函數,調用NPF_Open函數打開驅動的一個新實例,而NPF_Open函數又調用NdisOpenAdapter函數打開網路設備,在NdisOpenAdapter函數操作完成時NPF_OpenAdapterCmplete函數被調用結束一個適配器的打開操作。
上層的設置緩衝區大小和讀寫請求的操作都是由底層的NdisRequest實現的,用來執行OID(對象標識符)請求或設置,在完成一個OID請求操作時,NPF_RequestComplete函數被調用來結束一個OID請求。通常僅網路驅動程序執行OID操作,但NPF會通過調用NPF_IoControl執行IOCTL調用,當DeviceIoControl系統函數被調用時,該函數響應IRP_MJ_DEVICE_CONTROL而被調用,常見的IOCTL命令碼如下:
BIOCSMINTOCOPY
設置內核緩衝區中最小數據量大小
BIOCISETLOBBEH
用來設置環回的行為,處理接收自己發送包的方式;
BOPCSETBUFFERSIZE
用來設置內核緩衝區的大小
BIOCSETEVENTHANDLE
將用戶所分配的事件句柄傳遞給內核空間
接下來介紹關閉適配器的實現,通過pcap_close函數釋放pcap_t結構體及其相關所獲得的資源,在該函數中調用pcap_cleanup_win32函數關閉適配欸,釋放儲存數據包的內存,最後調用pcap_cleanup_live_common函數清除過濾器
而pcap_cleanup_win32函數調用PacketCloseAdapter關閉並釋放一個ADAPTER結構體資源,在調用CloseHandle時,NPF_Cleanup函數被調用來停止捕獲/監視/轉儲過程並釋放資源,通過調用NPF_CloseOpenInstance函數釋放與Open實例相關的資源,通過調用NPF_CloseBinding函數解除調用NdisOpenAdapter庫函數所建立的綁定和分配的資源,在NPF_CloseBinding中調用NdisCloseAdapter時,NPF_CloseAdapterComplete函數被調用來結束適配器的關閉,通過調用NPF_ReleaseOpenInstanceResources函數釋放在NPF_Open函數中所分配的部分資源。
通過這一階段的學習,發現難點在於熟練語法的基礎上理解其中的機制運行,並熟悉內核空間與用戶空間的介面操作流程。
TAG:全球大搜羅 |