16 + 3 发表于 2020-5-25 19:07

申请ID:win32的无君

1.申请ID:win32的无君
2.个人邮箱: wujunxxw@126.com
3.本科大一学生,原创首发文章:使用winpcap遍历网络设备,并在链路层直接发送TCP/SYN包

winpcap是一个功能强大的API,它为我们提供了直接访问计算机网络底层的能力。最近因为某些特殊需求,要发送一些篡改过的TCP/SYN包,首先想到的就是使用winpcap来实现。现在我已经完成了这个项目,那就趁热打铁给大家分享一下我的经验与方法吧。
如果要完整地讲解winpcap,以一篇帖子的篇幅是远远不够的。下面只以最简洁扼要的方式,讲述实现这个功能(发包)所要明白的知识。
本文仅供学习研究使用,禁止用作非法用途,如有不妥之处请告之,本人会在第一时间修改删除。

1、winpcap环境的建立
               winpcap官网地址:https://www.winpcap.org/
                  首先,我们要从官网上下载两个zip,分别是 winpcap的驱动程序 和 winpcap SDK。前者解压直接安装,后者包含了编译winpcap程序所必需的头文件、库文件和一些程序示例。
                  安装完驱动程序后,我们还得将 头文件 和 库文件 放置在IDE正确的路径,否则就include不到这些函数了。我使用的是VS2019,路径可以在项目->属性->VC++目录中找到。
                                                                                          

               双击“包含目录”条目,会给出这个目录在你计算机内的绝对路径。把sdk中的头文件拖动到这个路径下就可以啦。库文件同理。
                  注意:winpcap只支持32位程序。将             这个东西设置为x86,以避免可能出现的问题。

2、遍历网络设备,打开一个接口
                   在传输什么东西之前,winpcap必须使用某个网络设备的名称(一串最长为512的char字符串)来打开这个网络设备的接口。
                   这串字符当然不建议手动输入,我们可以使用pcap_findalldevs();来遍历系统上的所有网络设备的属性。
                   这个函数通过指针的方式,将系统上第一个网络设备的属性传递到一个名为pcap_if_t的结构里。
                   然后通过pcap_if_t->next这种调用,我们又可以找到下一个网络设备的pcap_if_t。这样就实现了设备的遍历。直到pcap_if_t->next为NULL时,那么它就是系统上最后一个设备了。
                   当遍历完成后,我们使用pcap_freealldevs();关掉winpcap对网络设备的遍历。
                   话不多说,我们直接上代码。
struct DEVS_NAME
{
        char szDevName;
};
int GetAllDevs(DEVS_NAME devsList[])
{      int nDevsNum = 0;
      pcap_if_t* alldevs;
      char errbuf;
      if (pcap_findalldevs(&alldevs, errbuf) == -1)
      {
                return -1;
                printf("error in pcap_findalldevs_ex: %s\n", errbuf);
      }
      for (pcap_if_t* d = alldevs; d != NULL; d = d->next)
      {
                strcpy(devsList.szDevName, d->name);
                nDevsNum++;
      }
      pcap_freealldevs(alldevs);

      return nDevsNum;
}
                   这个自定义函数能自动对系统上所有网络设备进行遍历,并将它们的名称通过指针输出到一个自定义结构中。返回值为系统上网络设备的数量。

                   然后我们可以使用pcap_open_live();函数打开某个设备。代码示例:
char szError;
pcap_t* handleD = pcap_open_live(szDevName, 65536, 1, 1000, szError);
                   pcap_open_live();接受5个参数,分别是设备名称、接口传输的最大字节数、是否处于混杂模式、超时时间(毫秒为单位),以及错误警告文本。
                   如果设备打开成功,这个函数将返回一个pcap_t句柄。否则返回NULL。


3、发送第一个TCP包
                  我们使用wireshark抓一个网站的TCP/SYN包,如图
                                                                  
                  我们先尝试用自己的代码把这个包原封不动地发出去。
                  将TCP包的全部原文以16进制的形式复制下来,然后粘贴到我们的程序里,使用pcap_sendpacket();这个函数发包。代码如下:
byte packet = {
0xb8,0xf8,0x83,0xbf,0xc5,0x7c,0x84,0x3a,
0x4b,0x6d,0xd3,0x88,0x08,0x00,0x45,0x00,
0x00,0x34,0x73,0xb4,0x40,0x00,0x80,0x06,
0x1e,0xfd,0xc0,0xa8,0x00,0x73,0x68,0x81,
0x3e,0x76,0x1a,0x1e,0x01,0xbb,0x84,0x93,
0x20,0x10,0x00,0x00,0x00,0x00,0x80,0x02,
0x20,0x00,0x26,0x87,0x00,0x00,0x02,0x04,
0x05,0xb4,0x01,0x03,0x03,0x02,0x01,0x01,
0x04,0x02 };
pcap_sendpacket(handleD, packet, sizeof(packet));
                  pcap_sendpacket();的三个参数分别为打开的设备的句柄、要发送的包、包的大小。
                  数据包将不经过任何加工,直接由链路层发送到路由器上。
                  用wireshark开始抓包,并运行这个程序。
                                                            
                   我们看到程序已经发包成功了,并收到了这个网站的回复。


4、IP头、TCP头、两个校验和
                   由于篇幅所限,关于TCP/IP协议,本文不进行过多赘述。想要对它们深入了解的朋友可以在互联网上收集资料,或等我另起一篇文章对此进行详细解释。
                   总而言之,我们从计算机发出去的TCP包由“以太网报头”、“IP报头”、“TCP报头”三个部分组成。为了防止长途传输中可能的错误,“IP报头”、“TCP报头”都是要计算"校验和"的。既然我们要篡改TCP包中的数据,那么校验和也必须得重新计算了,否则网站那边的路由器一算,诶,校验和不对,这个包就直接被丢掉了。
                   校验和的算法:
                                    1.把校验和字段置为0。
                                    2.把整个报文分割成16bits(4Bytes)的小块。
                                    3.将这些小块求和,得到一个16bits+的数。
                                    4.将16bit以上的位加回16bit里面。
                                    5.所得数求反码。
                   计算代码如下:
unsigned short CheckSum(unsigned short packet[], int size)
{
        unsigned long cksum = 0;
        while (size > 1)
        {
                cksum += *packet++;
                size -= sizeof(USHORT);
        }
        if (size)
        {
                cksum += *(UCHAR*)packet;
        }
        cksum = (cksum >> 16) + (cksum & 0xFFFF);
        cksum += (cksum >> 16);

        return (USHORT)(~cksum);
}
                   这个代码可以计算从输入的指针开始、到长度为size的数列的校验和。





(小部分代码摘录并修改自https://github.com/wangzhenshan3/SYN-Flood/blob/master/SYN-Flood.cpp,感谢原作者)

p.s:第一次写教程,就发在了这个大牛林立的论坛上,令我不胜惶恐。若有谬误之处,还请大神指出,本人将在第一时间修改。

anandyuan 发表于 2020-5-26 17:38

楼主厉害{:1_893:}

Hmily 发表于 2020-5-29 14:56


抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。

发表于 2020-5-29 18:19

呃,好像有点急功近利了。那我改天把IP、TCP的分段,以及伪首部,还有网络防火墙给说一说吧
页: [1]
查看完整版本: 申请ID:win32的无君