阅读:2975回复:8
如何实现局域网内IP数据包的分析和过滤?
用VC 或Delphi 哪个更容易实现些 win 2k下
对IP数据包分析和过滤 需要安装什么? |
|
沙发#
发布于:2005-05-18 21:13
Win2K下的Sniffer工具源代码 详细信息 < 局域网 >
Win2K下的Sniffer源代码。 [代码性质] VC完整应用程序代码 [代码作者] zw [文件大小] 130K http://www.vckbase.com/code/downcode.asp?id=1692 IP包监听程序(For 9x)源代码 详细信息 < 局域网 > IP包监听程序源代码(包含VXD源代码) [代码性质] VC完整应用程序代码 [代码作者] HiHint [文件大小] 158K http://www.vckbase.com/code/downcode.asp?id=1508 从事网络安全的技术人员和相当一部分准黑客(指那些使用现成的黑客软件进行攻击而不是根据需要去自己编写代码的人)都一定不会对网络嗅探器(sniffer)感到陌生,网络嗅探器无论是在网络安全还是在黑客攻击方面均扮演了很重要的角色。通过使用网络嗅探器可以把网卡设置于混杂模式,并可实现对网络上传输的数据包的捕获与分析。此分析结果可供网络安全分析之用,但如为黑客所利用也可以为其发动进一步的攻击提供有价值的信息。可见,嗅探器实际是一把双刃剑。 虽然网络嗅探器技术被黑客利用后会对网络安全构成一定的威胁,但嗅探器本身的危害并不是很大,主要是用来为其他黑客软件提供网络情报,真正的攻击主要是由其他黑软来完成的。而在网络安全方面,网络嗅探手段可以有效地探测在网络上传输的数据包信息,通过对这些信息的分析利用是有助于网络安全维护的。权衡利弊,有必要对网络嗅探器的实现原理进行介绍。 嗅探器设计原理 嗅探器作为一种网络通讯程序,也是通过对网卡的编程来实现网络通讯的,对网卡的编程也是使用通常的套接字(socket)方式来进行。但是,通常的套接字程序只能响应与自己硬件地址相匹配的或是以广播形式发出的数据帧,对于其他形式的数据帧比如已到达网络接口但却不是发给此地址的数据帧,网络接口在验证投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取到达的数据包。而网络嗅探器的目的恰恰在于从网卡接收所有经过它的数据包,这些数据包即可以是发给它的也可以是发往别处的。显然,要达到此目的就不能再让网卡按通常的正常模式工作,而必须将其设置为混杂模式。 具体到编程实现上,这种对网卡混杂模式的设置是通过原始套接字(raw socket)来实现的,这也有别于通常经常使用的数据流套接字和数据报套接字。在创建了原始套接字后,需要通过setsockopt()函数来设置IP头操作选项,然后再通过bind()函数将原始套接字绑定到本地网卡。为了让原始套接字能接受所有的数据,还需要通过ioctlsocket()来进行设置,而且还可以指定是否亲自处理IP头。至此,实际就可以开始对网络数据包进行嗅探了,对数据包的获取仍象流式套接字或数据报套接字那样通过recv()函数来完成。但是与其他两种套接字不同的是,原始套接字此时捕获到的数据包并不仅仅是单纯的数据信息,而是包含有 IP头、 TCP头等信息头的最原始的数据信息,这些信息保留了它在网络传输时的原貌。通过对这些在低层传输的原始信息的分析可以得到有关网络的一些信息。由于这些数据经过了网络层和传输层的打包,因此需要根据其附加的帧头对数据包进行分析。下面先给出结构.数据包的总体结构: 数据包 IP头 TCP头(或其他信息头) 数据 数据在从应用层到达传输层时,将添加TCP数据段头,或是UDP数据段头。其中UDP数据段头比较简单,由一个8字节的头和数据部分组成,具体格式如下: 16位 16位 源端口 目的端口 UDP长度 UDP校验和 而TCP数据头则比较复杂,以20个固定字节开始,在固定头后面还可以有一些长度不固定的可选项,下面给出TCP数据段头的格式组成: 16位 16位 源端口 目的端口 顺序号 确认号 TCP头长 (保留)7位 URG ACK PSH RST SYN FIN 窗口大小 校验和 紧急指针 可选项(0或更多的32位字) 数据(可选项) 对于此TCP数据段头的分析在编程实现中可通过数据结构_TCP来定义: typedef struct _TCP{ WORD SrcPort; // 源端口 WORD DstPort; // 目的端口 DWORD SeqNum; // 顺序号 DWORD AckNum; // 确认号 BYTE DataOff; // TCP头长 BYTE Flags; // 标志(URG、ACK等) WORD Window; // 窗口大小 WORD Chksum; // 校验和 WORD UrgPtr; // 紧急指针 } TCP; typedef TCP *LPTCP; typedef TCP UNALIGNED * ULPTCP; 在网络层,还要给TCP数据包添加一个IP数据段头以组成IP数据报。IP数据头以大端点机次序传送,从左到右,版本字段的高位字节先传输(SPARC是大端点机;Pentium是小端点机)。如果是小端点机,就要在发送和接收时先行转换然后才能进行传输。IP数据段头格式如下: 16位 16位 版本 IHL 服务类型 总长 标识 标志 分段偏移 生命期 协议 头校验和 源地址 目的地址 选项(0或更多) 同样,在实际编程中也需要通过一个数据结构来表示此IP数据段头,下面给出此数据结构的定义: typedef struct _IP{ union{ BYTE Version; // 版本 BYTE HdrLen; // IHL }; BYTE ServiceType; // 服务类型 WORD TotalLen; // 总长 WORD ID; // 标识 union{ WORD Flags; // 标志 WORD FragOff; // 分段偏移 }; BYTE TimeToLive; // 生命期 BYTE Protocol; // 协议 WORD HdrChksum; // 头校验和 DWORD SrcAddr; // 源地址 DWORD DstAddr; // 目的地址 BYTE Options; // 选项 } IP; typedef IP * LPIP; typedef IP UNALIGNED * ULPIP; 在明确了以上几个数据段头的组成结构后,就可以对捕获到的数据包进行分析了。 嗅探器的具体实现 根据前面的设计思路,不难写出网络嗅探器的实现代码,下面就给出一个简单的示例,该示例可以捕获到所有经过本地网卡的数据包,并可从中分析出协议、IP源地址、IP目标地址、TCP源端口号、TCP目标端口号以及数据包长度等信息。由于前面已经将程序的设计流程讲述的比较清楚了,因此这里就不在赘述了,下面就结合注释对程序的具体是实现进行讲解,同时为程序流程的清晰起见,去掉了错误检查等保护性代码。主要代码实现清单为: // 检查 Winsock 版本号,WSAData为WSADATA结构对象 WSAStartup(MAKEWORD(2, 2), &WSAData); // 创建原始套接字 sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)); // 设置IP头操作选项,其中flag 设置为ture,亲自对IP头进行处理 setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)); // 获取本机名 gethostname((char*)LocalName, sizeof(LocalName)-1); // 获取本地 IP 地址 pHost = gethostbyname((char*)LocalName)); // 填充SOCKADDR_IN结构 addr_in.sin_addr = *(in_addr *)pHost->h_addr_list[0]; //IP addr_in.sin_family = AF_INET; addr_in.sin_port = htons(57274); // 把原始套接字sock 绑定到本地网卡地址上 bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in)); // dwValue为输入输出参数,为1时执行,0时取消 DWORD dwValue = 1; // 设置 SOCK_RAW 为SIO_RCVALL,以便接收所有的IP包。其中SIO_RCVALL // 的定义为: #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) ioctlsocket(sock, SIO_RCVALL, &dwValue); 前面的工作基本上都是对原始套接字进行设置,在将原始套接字设置完毕,使其能按预期目的工作时,就可以通过recv()函数从网卡接收数据了,接收到的原始数据包存放在缓存RecvBuf[]中,缓冲区长度BUFFER_SIZE定义为65535。然后就可以根据前面对IP数据段头、TCP数据段头的结构描述而对捕获的数据包进行分析: while (true) { // 接收原始数据包信息 int ret = recv(sock, RecvBuf, BUFFER_SIZE, 0); if (ret > 0) { // 对数据包进行分析,并输出分析结果 ip = *(IP*)RecvBuf; tcp = *(TCP*)(RecvBuf + ip.HdrLen); TRACE(\"协议: %s\\r\\n\",GetProtocolTxt(ip.Protocol)); TRACE(\"IP源地址: %s\\r\\n\",inet_ntoa(*(in_addr*)&ip.SrcAddr)); TRACE(\"IP目标地址: %s\\r\\n\",inet_ntoa(*(in_addr*)&ip.DstAddr)); TRACE(\"TCP源端口号: %d\\r\\n\",tcp.SrcPort); TRACE(\"TCP目标端口号:%d\\r\\n\",tcp.DstPort); TRACE(\"数据包长度: %d\\r\\n\\r\\n\\r\\n\",ntohs(ip.TotalLen)); } } 其中,在进行协议分析时,使用了GetProtocolTxt()函数,该函数负责将IP包中的协议(数字标识的)转化为文字输出,该函数实现如下: #define PROTOCOL_STRING_ICMP_TXT \"ICMP\" #define PROTOCOL_STRING_TCP_TXT \"TCP\" #define PROTOCOL_STRING_UDP_TXT \"UDP\" #define PROTOCOL_STRING_SPX_TXT \"SPX\" #define PROTOCOL_STRING_NCP_TXT \"NCP\" #define PROTOCOL_STRING_UNKNOW_TXT \"UNKNOW\" …… CString CSnifferDlg::GetProtocolTxt(int Protocol) { switch (Protocol){ case IPPROTO_ICMP : //1 /* control message protocol */ return PROTOCOL_STRING_ICMP_TXT; case IPPROTO_TCP : //6 /* tcp */ return PROTOCOL_STRING_TCP_TXT; case IPPROTO_UDP : //17 /* user datagram protocol */ return PROTOCOL_STRING_UDP_TXT; default: return PROTOCOL_STRING_UNKNOW_TXT; } 最后,为了使程序能成功编译,需要包含头文件winsock2.h和ws2tcpip.h。在本示例中将分析结果用TRACE()宏进行输出,在调试状态下运行,得到的一个分析结果如下: 协议: UDP IP源地址: 172.168.1.5 IP目标地址: 172.168.1.255 TCP源端口号: 16707 TCP目标端口号:19522 数据包长度: 78 …… 协议: TCP IP源地址: 172.168.1.17 IP目标地址: 172.168.1.1 TCP源端口号: 19714 TCP目标端口号:10 数据包长度: 200 …… 从分析结果可以看出,此程序完全具备了嗅探器的数据捕获以及对数据包的分析等基本功能。 小结 本文介绍的以原始套接字方式对网络数据进行捕获的方法实现起来比较简单,尤其是不需要编写VxD虚拟设备驱动程序就可以实现抓包,使得其编写过程变的非常简便,但由于捕获到的数据包头不包含有帧信息,因此不能接收到与 IP 同属网络层的其它数据包, 如 ARP数据包、RARP数据包等。在前面给出的示例程序中考虑到安全因素,没有对数据包做进一步的分析,而是仅仅给出了对一般信息的分析方法。通过本文的介绍,可对原始套接字的使用方法以及TCP/IP协议结构原理等知识有一个基本的认识。本文所述代码在Windows 2000下由Microsoft Visual C++ 6.0编译调试通过。 |
|
板凳#
发布于:2005-05-19 08:11
先谢谢楼上的啦
下载下来,研究一把 |
|
地板#
发布于:2005-05-19 09:52
运行ipmen 2k的那个代码为什么会出现:
Compiling... ipmonDlg.cpp C:\\WINDOWS\\Desktop\\ipMon_zw_2K\\ipmonDlg.cpp(8) : fatal error C1083: Cannot open include file: \'iphlpapi.h\': No such file or directory Error executing cl.exe. 环境是win me vc++6.0, 还需要安装DDK NDIS ,设置注册表? |
|
地下室#
发布于:2005-05-19 10:08
你要装VS2003或新版本的Platform SDK.
|
|
|
5楼#
发布于:2005-05-19 10:51
楼上的,我现在的系统是win me 那我需要安装哪个版的SDK
|
|
6楼#
发布于:2005-05-19 11:17
你在WINDOWS ME下研究IP数据包分析和过滤,真有你的!
MICROSOFT每三个月出一期MSDN,你选择一个较新的版本装. 这样编译可以通过,但运行有问题. |
|
|
7楼#
发布于:2005-05-19 11:22
书写NDIS过滤钩子驱动实现ip包过滤
作者:no 在普通的WINDOWS 2000下实现实现包过滤的方法主要是书写NDIS过滤驱动程序,需要的技巧比较高,而且烦琐,需要考虑很多细节。但是对于很多应用而言,只需要能更方便的对ip包进行过滤处理,其实NDIS对于ip包的过滤提供一种书写过滤钩子驱动的方式,主要方法是: 驱动中建立一个普通的设备,然后通过IOCTL_PF_SET_EXTENSION_POINTER操作将你的内核模式的过滤钩子挂接到系统默认的ip过滤驱动上,这样你就可以在自己的过滤钩子里面实现完整的基于包的各种分析和过滤的处理了。 下面就是一个完整的NDIS过滤钩子驱动的代码拒绝所有外来的TCP带S的建立连接的请求。 注意事项: 1。需要在DDK环境中编译 2。需要修改注册表中LMHK\\System\\\\CurrentControlSet\\\\Services\\\\IPFILTERDRIVER的START类型为3,让他随系统启动而启动 3。编译生成了sys文件后需要拷贝到winnt\\system32\\drivers目录下 4。需要运行一个程序后手动生成注册表项 5。使用时用net start f *** ilthook启动驱动,用net stop f *** ilthook停止驱动 6。此方法只能对ip包进行过滤,其他的协议不会经过这个过滤钩子进行处理。 //驱动程序的头文件 #include \"ntddk.h\" #include \"ntddndis.h\" #include \"pfhook.h\" #ifndef __NTHANDLE_H #define __NTHANDLE_H #define NT_DEVICE_NAME L\"\\\\Device\\\\F *** ilthook\" #define DOS_DEVICE_NAME L\"\\\\DosDevices\\\\F *** ilthook\" #define PROT_TCP 6 #include \"ntddk.h\" #include \" *** ilthook.h\" typedef struct IPHeader { UCHAR iph_verlen; // Version and length UCHAR iph_tos; // Type of service USHORT iph_length; // Total datagram length USHORT iph_id; // Identification USHORT iph_offset; // Flags, fragment offset UCHAR iph_ttl; // Time to live UCHAR iph_protocol; // Protocol USHORT iph_xsum; // Header checksum ULONG iph_src; // Source address ULONG iph_dest; // Destination address } IPHeader; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath); NTSTATUS CreateFilterHook (IN PDRIVER_OBJECT DriverObject); VOID DriverUnload (IN PDRIVER_OBJECT DriverObject); PF_FORWARD_ACTION IpFilterHook( IN unsigned char *PacketHeader, IN unsigned char *Packet, IN unsigned int PacketLength, IN unsigned int RecvInterfaceIndex, IN unsigned int SendInterfaceIndex, IN IPAddr RecvLinkNextHop, IN IPAddr SendLinkNextHop); #endif //驱动程序的c文件 #define PROT_TCP 6 #include \"ntddk.h\" #include \"ntddndis.h\" #include \"pfhook.h\" #include \"f *** ilthook.h\" PDEVICE_OBJECT deviceObject; UNICODE_STRING win32DeviceName; //住驱动入口点 NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING ntDeviceName; RtlInitUnicodeString(&ntDeviceName,NT_DEVICE_NAME); //建立一个过滤钩子驱动设备 status = IoCreateDevice (DriverObject,0,&ntDeviceName,FILE_DEVICE_UNKNOWN,0,TRUE,&deviceObject); if (!NT_SUCCESS (status)) { goto ERROR; } RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); //建立一个过滤钩子驱动设备符号连接 status = IoCreateSymbolicLink( &win32DeviceName, &ntDeviceName ); if (!NT_SUCCESS(status)) // If we couldn\'t create the link then { // abort installation. goto ERROR; } //申明卸载例程 DriverObject->DriverUnload = DriverUnload; //建立钩子挂接 status = CreateFilterHook(DriverObject); if (!NT_SUCCESS(status)) // If we couldn\'t create the link then { // abort installation. IoDeleteSymbolicLink(&win32DeviceName); goto ERROR; } return(STATUS_SUCCESS); ERROR: if(deviceObject) IoDeleteDevice(deviceObject); //DbgPrint( \"Leave DriverEntry failed\\n\" ); return status; } NTSTATUS CreateFilterHook(IN PDRIVER_OBJECT DriverObject) { PIRP nirp; NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT filtfileob; UNICODE_STRING ntDeviceName; PDEVICE_OBJECT filtdeviceob; PF_SET_EXTENSION_HOOK_INFO filthook; IO_STATUS_BLOCK filtstatus; RtlInitUnicodeString(&ntDeviceName,L\"\\\\Device\\\\IPFILTERDRIVER\"); //将钩子挂接函数放入结构中 filthook.ExtensionPointer = IpFilterHook; //获得系统ipfilterdriver驱动的设备指针 status = IoGetDeviceObjectPointer(&ntDeviceName,FILE_GENERIC_READ|FILE_GENERIC_WRITE,&filtfileob,&filtdeviceob); if(status!=STATUS_SUCCESS) return status; //绑定过滤钩子到系统ipfilterdriver驱动的设备指针 nirp = IoBuildDeviceIoControlRequest( IOCTL_PF_SET_EXTENSION_POINTER, filtdeviceob, &filthook, sizeof(PF_SET_EXTENSION_HOOK_INFO), NULL, 0, FALSE, NULL, &filtstatus); if(nirp==NULL) return filtstatus.Status; //调度系统ipfilterdriver设备重新操作irp return (IoCallDriver(filtdeviceob,nirp)); } VOID DriverUnload(IN PDRIVER_OBJECT DriverObject) { //与加载一样,只是钩子函数结构中放NULL,让系统ipfilterdriver卸载加载的钩子函数 PIRP nirp; NTSTATUS status = STATUS_SUCCESS; PDEVICE_OBJECT filtdeviceob; PFILE_OBJECT filtfileob; PF_SET_EXTENSION_HOOK_INFO filthook; IO_STATUS_BLOCK filtstatus; UNICODE_STRING ntDeviceName; RtlInitUnicodeString(&ntDeviceName,L\"\\\\Device\\\\IPFILTERDRIVER\"); filthook.ExtensionPointer = NULL; status = IoGetDeviceObjectPointer(&ntDeviceName,FILE_GENERIC_READ|FILE_GENERIC_WRITE,&filtfileob,&filtdeviceob); if(status==STATUS_SUCCESS) { nirp = IoBuildDeviceIoControlRequest( IOCTL_PF_SET_EXTENSION_POINTER, filtdeviceob, &filthook, sizeof(PF_SET_EXTENSION_HOOK_INFO), NULL, 0, FALSE, NULL, &filtstatus); if(nirp!=NULL) IoCallDriver(filtdeviceob,nirp); } IoDeleteSymbolicLink(&win32DeviceName); IoDeleteDevice(deviceObject); return; } PF_FORWARD_ACTION IpFilterHook( unsigned char *PacketHeader, unsigned char *Packet, unsigned int PacketLength, unsigned int RecvInterfaceIndex, unsigned int SendInterfaceIndex, IPAddr RecvLinkNextHop, IPAddr SendLinkNextHop ) { //过滤钩子函数,这儿只简单判断属于TCP协议且数据是抵达而且带SYN标志则过滤。大家可以根据需要修改自己的过滤判断和处理。 if(((IPHeader *)PacketHeader)->iph_protocol == PROT_TCP) { //Packet[13]==0x2就是TCP中SYN的标志 //SendInterfaceIndex==INVALID_PF_IF_INDEX说明包是抵达而不是发送的,因此这样过滤就不会影响自己的包出去,但是外来带SYN请求的包则会拒绝。 if(Packet[13]==0x2 && SendInterfaceIndex==INVALID_PF_IF_INDEX) return PF_DROP; } return PF_FORWARD; } //简单的建立注册表项的程序 unsigned char sysdir[256]; unsigned char drivcedir[256]; int RegHandelDev(char * exename) { //修改注册表启动一个NTHANDLE驱动程序 char subkey[200]; int buflen; HKEY hkResult; char Data[4]; DWORD isok; buflen = sprintf(subkey,\"System\\\\CurrentControlSet\\\\Services\\\\%s\",exename); subkey[buflen]=0; isok = RegCreateKey(HKEY_LOCAL_MACHINE,subkey,&hkResult); if(isok!=ERROR_SUCCESS) return FALSE; Data[0]=3; Data[1]=0; Data[2]=0; Data[3]=0; isok=RegSetvalueEx(hkResult,\"Start\",0,4,(const unsigned char *)Data,4); Data[0]=1; isok=RegSetvalueEx(hkResult,\"Type\",0,4,(const unsigned char *)Data,4); isok=RegSetvalueEx(hkResult,\"ErrorControl\",0,4,(const unsigned char *)Data,4); GetSystemDirectory(sysdir,256); buflen = sprintf(drivcedir,\"%s\\\\Drivers\\\\F *** iltHook.sys\",sysdir); buflen = sprintf(subkey,\"\\\\??\\\\%s\",drivcedir); subkey[buflen]=0; isok=RegSetvalueEx(hkResult,\"ImagePath\",0,1,(const unsigned char *)subkey,buflen); RegCloseKey(hkResult); buflen = sprintf(subkey,\"\\\\Registry\\\\Machine\\\\System\\\\CurrentControlSet\\\\Services\\\\%s\",exename); subkey[buflen]=0; return TRUE; } int main(int argc,char *argv[]) { //注册驱动程序 if(RegHandelDev(\"F *** ilthook\")==FALSE) return FALSE; return TRUE; } 俺对这个好多不懂: DDK是作什么用的? |
|
8楼#
发布于:2005-05-19 11:28
有没有只要在VC++6.0 win2k/98下就能实现对局域网内IP数据包的监控的方法啊?
|
|