Welcome to 自由隨風'S Blog .
嗅探器(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地址为FF-FF-FF-FF-FF-FE的ARP请求报文,那么,对于在正常工作模式下的网卡,数据包将被丢弃,当然也就不会回馈任何报文;而对于在混杂模式下网卡,数据包将被接收进入系统核心。而系统核心会认为这个Mac地址是广播地址,因此就会回馈一个ARP应答报文。这样,我们就可以判断出这台机器上存在Sniffer了。

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_VALUE))
  {
  printf("Unable to open the driver, Error Code : %lx\n", GetLastError());
  return 0;
  }

  以太网头部和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");

  }

  }

  }


1, 作为职场新人,很容易放弃。这种放弃,作为刚毕业的新人,年纪轻轻,对于职业和未来不了解,一旦遇到挫折,首先想到的是放弃,放弃公司,放弃自己所从事的行业等等。


要知道世界上最容易的事情就是放弃,很多职场新人的想法就是,我还年轻,我不能浪费时间,我必须再去找更好的机会。在不断跳槽的过程中,在不断换公司的过程中,不管到哪里都是新人,任何一个公司招一个新人进来,要培训,要熟悉,要培养对这个新人的信任,需要很长一段时间,在寻求新的希望的同时,也放弃了很多机会。


曾见过以前的一个同事,一开始用C做电信的计费系统,后来用Java做ERP系统,后来又跳槽用PHP做网站,哪个行业都没有做深入,哪个编程语言都没有特别熟,职场上最不缺乏的就是万金油,需要的是对一个行业,对一类系统业务特别熟悉的人才。时间过得很快,等到几年时间过去,某人还在小不里子混做最底层的coding。


建议所有的职场新人,一开始对自己的职业道路要有一个规划,最好是锁定一个行业和偏重一类编程语言,做深做透。不要轻易放弃,就算公司很烂,不得已要跳槽,最好也是选择同一行业的公司,否则工作没有积累,很难往前进一步。


2, 职场新人,有些,架子还是蛮大,对公司对自己经常会有不切实际的想法。我以前招过一个计算机系的毕业生,公司一开始临时有个项目很忙,没有叫他写程序,而是让他负责到下面的客户收取报表数据。很简单但是有点枯燥,不过作为公司新人,这种活肯定是要轮到他干的,大概干一个月左右,公司说好一个月后就会把他调回来的。结果,就一个月,这么简单的工作他都干不好,整天抱怨,自己是名牌大学的学生,不让他写代码,怎么怎么样等等,把事情做的一塌糊涂,客户也不满意。最后没有办法让老员工去做,他被fire掉了。作为职场新人,应该要把自己的位置放低一点,如果对于公司安排不满意,可以和公司多交流,如果只是暂时的一些杂活,不应该过于嫌弃。上次我和朋友去参加邮通的会议,里面的一个老总说他大学毕业的时候,在车间里面发了半年的卫生纸,但是兢兢业业,做的很认真,很快就提上来了。


公司对于新人,一开始需要一个信任期,职场新人不要难的不会做,容易的不肯做,这样就很容易被人嫌弃了。


3,责任心。责任心非常重要,自己写的代码代表自己的人品,代表职业素养,有时候比能力更重要,一般做计算机的智商都不低,程序难到写不出的情况少,经常是程序写的不够强壮,出bug的多。以前公司里面,如果上了用户环境测试,一旦出现bug,不管是谁的错误,整个team要连夜加班把bug改掉,如果每次team被迫留下来加班,都是你出的bug,搞个几次,谁再会信任你呢,在这个公司也待不长了。


4,良好的习惯。学习的习惯,写代码,愿意去了解和研究,知其然也知其所以然,不停的学习和养成良好的习惯,才能不停进步。


5,和同事搞好关系。公司的同事一天要待在一起9-10个小时,甚至比和家人待在一起的时间还要多,花一些时间和同事良好的沟通,会使工作更加顺利。也不用奉承拍马那么严重,放低态度,勤奋诚恳,懂的就说懂,不懂多询问,就够了,一般老员工还是愿意帮助新人的,赶紧让新人上手,分担工作,让自己压力不那么大,新人需要抓紧时间,多学习,很快进步。

Pages: 1/71 First page 1 2 3 4 5 6 7 8 9 10 Next page Final page [ View by Articles | List ]