IP spoofing 在Windows上的实践

-目录-

什么是IP spoofing?

通常情况下,在网络上发包,程序员只需要考虑应用层数据和选择传输层协议,网络层及更底层的数据由操作系统填充。但是在某些情况下(善意的或恶意的),程序员想要手动改变在发送层的srcIP,这种手动改变IP头的srcIP的行为称为IP欺骗(IP spoofing)。

transmit_data.JPG

遇到的问题

现在需要测试某软件网络包解析性能,需要模拟发出具有不同源IP和负载数据的UDP包,不同负载数据很容易做到,但是改变源IP则不简单。为了高性能以及方便的使用,需要这个程序使用C++编写并可以原生跑在Windows系统上。

对于这种定制化需求,我们很容易想到使用socket编程,也就是使用raw socket。

在通常情况下,创建socket和发送数据使用Winsock2.h里的这些函数:

SOCKET sock = socket(AF_INET, type, protocol);
sendto(sock, buff, sizeof(buff),flag, addr, sizeof(addr));

其中type和protocol为对应关系如下:

type protocol
SOCK_STREAM IPPROTO_TCP
SOCK_DGRAM IPPROTO_UDP
SOCK_RAW IPPROTO_TCP/IPPROTO_UDP/IPPROTO_RAW

于是想到不指定socket类型和协议,即使用raw socket,这时type为SOCK_RAW对应的protocol为IPPROTO_RAW(当protocol为IPPROTO_RAW时,意味着系统将不自动添加IP头到数据段里),然后自己构造一个IP/UDP包放到buff里,再调用send函数发出。

实现的关键函数如下:

//创建raw socket
SOCKET sock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_RAW, NULL, 0, WSA_FLAG_OVERLAPPED);
//设置IP_HDRINCL,拒绝系统自动添加IP头
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)& flag, sizeof(flag));
//按照IP UDP填充buff,此处省略
//发送
sendto(sock, sendBuf, sizeof(ipHeader) + sizeof(tcpHeader));

虽然以上代码可以在非server版的Windows上运行,且不会报任何错误,但是在任务管理器的网络一栏发送速率表现为0,并且Wireshark抓取不到任何包,只有高的CPU占用率。如果发送成功,应该会显示有较大的流量通过。

原因微软为了安全原因进行了限制,具体见下图官网

原始套接字限制

表达的意思就是只有Windows Server可以使用raw发送自定义IP的包,普通版的Windows不能发自定义IP的包。可以看到Windows只有古早版本(Windows XP SP2之前)和server可以自由的使用raw socket。

那么怎样才能实现我们的需求呢,大概有以下几种方式:

  1. 修改系统内某个文件或注册表,破解掉限制
  2. 使用Cwinpcap库,使用winpcap需要从数据链路层开始构造数据包,而raw socket只需从网络层开始
  3. 使用Windows server版
  4. 使用其他语言和库,其他语言或许也有类似winpcap的库
  5. 在Linux上运行,Linux使用UNIX socket,具有完全的socket使用权限

这里先说结论:

序号 方法 可行性
方法1 修改Windows系统内某个文件或注册表,破解掉限制 未测试
方法2 使用Cwinpcap 可行
方法3 使用Windows server版 可行
方法4 使用其他语言和库使用其他语言和库 已测试Pyhton+scapy库Golang+net库均不可行
方法5 在Linux上运行 可行

实现方法

在Windows Server 2016上运行

这里使用虚拟机,把本机Windows 10之前可以跑但是抓不到包的程序直接拷贝到装了Windows Server 2016虚拟机上,运行后可以抓到包。

Winserver测试

小插曲

相同程序运行在没有装Visual Studio 2019版本及以上时Windows Server系统会报错:找不到vcruntime140.dll,这时需要把编译程序的机器上的“C:\Windows\System32”和“C:\Windows\SysWOW64“里的vcruntime140.dll复制到运行机器对应的目录里,然后在命令行里运行 regsvr32 vcruntime140.dll 即可解决。

在Linux上运行

Linux系统上具有完全的Socket控制权,可以通过raw socket直接发送数据包。

在Windows10上运行

在Windows上通过pcap.h实现。

什么是WinPcap?

Winpacp

具体操作流程就是下载winpcap库的dll和头文件,然后在项目里包含这两个文件,winpcap官网有完整的文档可以参考,此处不再赘述。通过构造以太网帧头、IP头、UDP头最后通过win pcap发送包,最后在本机Windows10系统上跑,结果如下:

Win10测试

小知识

大名鼎鼎的Wireshark也使用的是Winpcap库,在安装Wireshark时通常会一并安装Winpcap。

后记

为什么通过winpcap可以在Win10上自由的使用Socket?和系统的Winsock.h有何区别?

根据官网文档WinPcap: WinPcap internals

可以知道:实际上winpcap库实现了一个网卡驱动程序,相当于绕过了系统本身的驱动,直接去操控网卡的收发。

img

可以通过winpcap发送组播包/其他包吗?

文档中有这样一段话:

WinPcap receives and sends the packets independently from the host protocols, like TCP-IP. This means that it isn’t able to block, filter or manipulate the traffic generated by other programs on the same machine: it simply “sniffs” the packets that transit on the wire. Therefore, it does not provide the appropriate support for applications like traffic shapers, QoS schedulers and personal firewalls.

说明winpcap并不会适配任何协议的内容,换言之,它只是个无情的传输机器,不考虑发送的是什么数据流。对于TCP,不考虑拥塞、分包、ACK…,同样的也不管发送的目的IP。但是互联网的规则还是要遵守的,比如MAC地址,一般情况下是目的MAC地址写本地网关就好,不要变太多,不然会对交换机的性能有影响。

特别的,对于组播地址,目的MAC是根据一定的规则确定的,否则组播网络无法接收数据。

在Win10上通过winpcap发包的性能如何?

在本机Windows10、i5-8500、16GB RAM、1Gbps网卡上使用winpcap的pcap_sendpacket函数连续发送大小为700Bytes的UDP包,网卡发送速率为140-150Mbps。测试程序CPU占用10-20。同时运行7个测试程序可以跑满网卡传输速率(1Gbps)。

性能瓶颈在于CPU和网卡性能以及程序的发包数量。CPU和网卡性能越高,单位时间内发包数量越少,最大发送速率越大。总的来说就是减小写入网卡时的中断时间。优化方法是可以换更高性能的电脑,或者可以使用winpcap的发送队列函数pcap_sendqueue_transmit,一次性写入很多包然后合并发送。因为pcap_sendpacket使用简单且性能已经满足需求,发送队列功能未测试。

© 2019 - 2023 · YuYoung's Blog