巧用ARP 探测网络中的黑客软件(转载)

 

嗅探器 (Sniffer) 一直以来都是一种让人恼火的黑客工具,因为它是一种静态的攻击软件,它的存在不会留下任何痕迹,因此人们很难将它揪出来。可是,它的危害性却又是相当大的 ( 它就像一个监视器,你的 一举一动 都在它的监视之下,你说危害大不大 ) 。所以,我们不能不要想个办法出来检查网络中是否存在 Sniffer ,这是非常必要的。
1. Sniffer 原理
所谓知己知彼方能百战不殆,要了解探测 Sniffer 的方法,就先得了解 Sniffer 的原理。首先,让我们来看一看局域网中是怎样传输数据的。当一个数据包的目的地是局域网内的某台计算机时,此数据包将以广播的形式被发送到网内每一台计算机上。而每台计算机的网卡将分析数据包中的目的 Mac 地址 ( 即以太网地址 ) ,如果此地址为本计算机 Mac 地址或为广播地址 (FF-FF-FF-FF-FF-FF) ,那么,数据包将被接收,而如果不是,网卡将直接将其丢弃。但是,这里有一个前提,就是接收端计算机的网卡是在正常模式下工作的。而如果网卡被设置为混杂模式,那么它就可以接收所有经过的数据包了 ( 当然也包括目的地不是本机的数据包 ) 。就是说,只要是发送到局域网内的数据包,都会被设置成混杂模式的网卡所接收 ! 这也就是 Sniffer 的基本原理了。至于 Sniffer 的具体实现和一些细节,这里就不多讲了,大家有兴趣可以参考相关资料。
2. 以太网中传输的 ARP 数据报
知道了 Sniffer 的基本原理,现在,我们就要想想怎么才能将局域网中隐藏的 Sniffer 揪出来,这才是本篇文章的主题。这里,我们需要自己构造 ARP 数据包,所以,就先简单介绍一下 ARP 请求和应答数据报的结构:
typedef struct _et_header // 以太网头部
{
unsigned char eh_dst[6];
unsigned char eh_src[6];
unsigned short eh_type;
}ET_HEADER;

{
unsigned short arp_hdr;
unsigned short arp_pro;
unsigned char arp_hln;
unsigned char arp_pln;
unsigned short arp_opt;
unsigned char arp_sha[6];
unsigned long arp_spa;
unsigned char arp_tha[6];
unsigned long arp_tpa;
}ARP_HEADER;

以上就是网络中传输的 ARP 数据包的结构了。至于结构中每个字段所表示的具体含义以及如何初始化,超出了本文章的讨论范围,大家有兴趣可以参看《 TCP-IP 协议详解》一书。
3. 探测局域网中的 Sniffer
终于进入主题了。既然 Sniffer 是一种静态的黑软,不会留下任何日志,那么我们就要主动的去探测它。鉴于 Sniffer 的原理是设置网卡为混杂模式,那么,我们就可以想办法探测网络中被设置为混杂模式的网卡,以此来判断是否存在 Sniffer
这里,让我们再来看看计算机接收数据包的规则。前面已经讲过,在正常模式下,首先由网卡判断数据包的目的 Mac 地址,如果为本机 Mac 地址或为广播地址,那么数据包将被接收进入系统核心,否则将被丢弃。而如果网卡被设置为混杂模式,那么所有的数据包都将直接进入系统核心。数据包到达系统核心后,系统还将进一步对数据包进行筛选:系统只会对目的 Mac 地址为本机 Mac 地址或广播地址的数据包做出响应 ―― 如果接收到的是 ARP 请求报文,那么系统将回馈一个 ARP 应答报文。但是,不同的是,系统核心和网卡对广播地址的判断有些不一样:以 Windows 系统为例,网卡会判断 Mac 地址的所有六位,而系统核心只判断 Mac 地址的前两位 (Win98 甚至只判断前一位 ) ,也就是说,对于系统核心而言,正确的广播地址 FF-FF-FF-FF-FF-FF 和错误的广播地址 FF-FF-FF-FF-FF-FE 是一样的,都被认为是广播地址,甚至 FF-FF-00-00-00-00 也会被系统核心认为是广播地址 !
写到这里,聪明的读者大概已经知道该怎么做了。如果我们构造一个目的 Mac
4. 主要源码分析
由以上分析可知,程序大概分为两个模块,一个是发送伪装广播地址的 ARP 请求报文,另一个是接收回馈的 ARP 应答报文并做出分析。我们就分别用两个线程来实现。主线程负责发送,监听线程负责接收。
首先是创建以太网头部和 ARP 头部的结构:
typedef struct _et_header // 以太网头部
  {
unsigned char eh_dst[6];
unsigned char eh_src[6];
unsigned short eh_type;
}ET_HEADER;

{
unsigned short arp_hdr;
unsigned short arp_pro;
unsigned char arp_hln;
unsigned char arp_pln;
unsigned short arp_opt;
unsigned char arp_sha[6];
unsigned long arp_spa;
unsigned char arp_tha[6];
unsigned long arp_tpa;
}ARP_HEADER;

然后是发送 ARP 请求报文的主线程,取得所有适配器的名字。其中, “adapter_name” 表示一个用于存放适配器名字的缓冲区,而这些适配器名字将以 UNICODE 编码方式存入此缓冲区中。 UNICODE 编码方式就是用一个字的空间 ( 两个字节 ) 来存放一个字符。这样,每个字符间自然会出现一个 '/0' 。而两个适配器名字之间将会有一个字为 '/0' 作为间隔。 adapter_length: 这个缓冲区的大小:
if(PacketGetAdapterNames((char*)adapter_name, &adapter_length)==FALSE)
{
printf("PacketGetAdapterNames error:%d/n",GetLastError());
return 0;
}

打开适配器,此处我默认打开第一块适配器:
lpAdapter=(LPADAPTER)PacketOpenAdapter((LPTSTR)adapter_list[0]);
if (!lpAdapter||(lpAdapter->hFile==INVALID_HANDLE_
以太网头部和 ARP 头部结构赋值, StrToMac 函数是笔者自定义的字符串转换为 Mac 地址的函数:
StrToMac("00E06E41508F",s_Mac); //"00E06E41508F" 是笔者测试程序所用的本地机的网卡地址 , 测试者应将其改为测试机网卡地址
memcpy(et_header.eh_src,s_Mac,6);
StrToMac("FFFFFFFFFFFE",d_Mac); // 目的物理地址设置为 FFFFFFFFFFFE
memcpy(et_header.eh_dst,d_Mac,6);
et_header.eh_type=htons(0x0806); // 类型为 0x0806 表示这是 ARP
arp_header.arp_hdr=htons(0x0001); // 硬件地址类型以太网地址
arp_header.arp_pro=htons(0x0800); // 协议地址类型为 IP 协议
arp_header.arp_hln=6; // 硬件地址长度为 6
arp_header.arp_pln=4; // 协议地址长度为 4
arp_header.arp_opt=htons(0x0001); // 标识为 ARP 请求
arp_header.arp_spa=inet_addr("172.24.21.10"); //"172.24.21.10" 是我测试程序所用的本地机的 IP, 测试者应将其改为测试机 IP
memcpy(arp_header.arp_sha,et_header.eh_src,6);
arp_header.arp_tpa=inet_addr(argv[1]);
memcpy(arp_header.arp_tha,et_header.eh_dst,6);
发送数据包:
lpPacket=PacketAllocatePacket(); // PACKET 结构指针分配内存
PacketInitPacket(lpPacket,buffer,512); // 初始化 PACKET 结构指针
PacketSetNumWrites(lpAdapter,5); // 设置发送次数
PacketSendPacket(lpAdapter,lpPacket,TRUE);// 发送 ARP 请求包
最后别忘了扫尾工作:
PacketFreePacket(lpPacket); // 释放 PACKET 结构指针
PacketCloseAdapter(lpAdapter); // 关闭适配器
最后是监听线程,设置接收数据包的系列参数:
PacketSetHwFilter(lpAdapter, NDIS_PACKET_TYPE_DIRECTED); // 设置网卡为直接模式
PacketSetBuff(lpAdapter,1024); // 设置网卡接收数据包的缓冲区大小
PacketSetReadTimeout(lpAdapter,2); // 设置接收到一个包后的 休息 时间
接收数据包:
PacketReceivePacket(lpAdapter, lpPacket, TRUE); // 接收数据包
对数据包进行分析,以得出结论:
char *buf;
bpf_hdr *lpBpfhdr;
ET_HEADER *lpEthdr;
in_addr addr={0};
buf=(char *)lpPacket->Buffer;
lpBpfhdr=(bpf_hdr *)buf;
lpEthdr=(ET_HEADER *)(buf+lpBpfhdr->bh_hdrlen);
if(lpEthdr->eh_type==htons(0x0806)) // 判断是否为 ARP
{
ARP_HEADER *lpArphdr=(ARP_HEADER*)(buf+lpBpfhdr->bh_hdrlen+sizeof(ET_HEADER));
char source_ip[20]={0},dest_ip[20]={0};
addr.S_un.S_addr=lpArphdr->arp_spa;
memcpy(source_ip,inet_ntoa(addr),strlen(inet_ntoa(addr)));
memset(&addr,0,sizeof(in_addr));
addr.S_un.S_addr=lpArphdr->arp_tpa;
memcpy(dest_ip,inet_ntoa(addr),strlen(inet_ntoa(addr)));
if(!strcmp(source_ip,ip) && !strcmp(dest_ip,"172.24.21.10")) // 判断接收到的包的源 IP 与目的 IP 是否正确 ( 字符串变量 ip 是从主线程传递过来的被探测机的 ip)
{
if(lpArphdr->arp_opt==htons(0x0002)) // 判断是否为 ARP 应答
{
printf("There is a Sniffer!/n");
}
}
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值