伪造TCP/IP数据包, 刷票?!!
前言
最近,有一同学给我发来一投票的链接,当然希望我帮他投某某的票了o(︶︿︶)o
我立马投了票, 再投第二下那时
限制了IP了~
我却突然萌生一个想法, 如果我伪造TCP数据包, 伪造TCP头上的IP地址,不是可以想多少票,就多少票了~
PS:因为之前看过有关SYN攻击的描述,知道可以发出大量伪造的SYN数据包,使到服务器建立大量半连接,占用服务器的资源
先从简单的udp 数据包开始. 立马找了一段以前udp监听的代码,用它来负责测试.
首先,发送数据包的话, 我首先想到的是raw socket.
马上从网上拷了一段代码, 对我的测试代码发, 失败. 又调了老半天,还是失败.
网上的原因说到: 操作系统对raw socket的限制 - -!
至于能不能使用raw socket 发送数据包, 我就没有细细研究了.
我发现sendto一个数据包还要填地址和端口... 既然是建立在IP那一层, 为什么还要填个端口.
//===========================================================================//
接着, 我想到以前用过一下的库 --- Winpcap.
Winpcap能发数据链路层的数据!
使用Winpcap发送数据
//获取发送的句柄了 pcap_t* adhandle = NULL; char errbuf[PCAP_ERRBUF_SIZE] = { 0 }; pcap_if_t *alldevs = NULL; if(pcap_findalldevs(&alldevs, errbuf) == -1) return; if((adhandle = pcap_open(alldevs->name, 0x10000, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf)) == NULL) return; pcap_freealldevs(alldevs); //发送数据包 if(pcap_sendpacket(adhandle, (const u_char*)buf, totalLen) == -1) { }
接着就组装数据包了.
先看一下他们的校验和(来自网上的):
◆当发送IP包时,需要计算IP报头的校验和:
1、把校验和字段置为0;
2、对IP头部中的每16bit进行二进制求和;
3、如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;
4、将该16bit的值取反,存入校验和字段。
◆当接收IP包时,需要对报头进行确认,检查IP头是否有误,算法同上2、3步,然后判断取反的结果是否为0,是则正确,否则有错。
实现代码如下:
u_int16_t in_cksum (u_int16_t * p, int psize) { u_int32_t ret = 0; while (psize > 1) { ret += *p++; psize -= 2; } if (psize == 1) ret += *(u_int8_t*)p; ret = (ret >> 16) + (ret & 0xffff); ret += (ret >> 16); return ~ret; }
下面看一下IPv4数据包头(简要说明而已, 如果想相信了解每一个项是怎么用, 建议查一下书吧!!)
版本:4
报头长度: 首部长/4(没选项就是5了)
服务类型:没用,置0
总长度:首部长度+数据长度
标识,标志,片偏移: IP数据包分片相关,将标志赋0x2就是不分片了
生存时间:0xff(255)
协议:看运输层,TCP:6, UDP:17
头校验和: 就是使用上面的算法计算头部的校验和, 不包括数据部分
源地址,目的地址: ..
选项就忽略吧~
真正的UDP包就只有4个字段而已
源端口,目的端口: ..
长度: UDP头+数据的长度
校验和:这个校验和的算法,要加上伪头部和数据来算,如上图.其中伪头部中的长度=UDP总长度
数据部分: 不一定是16位的倍数, 上图只是计算检验和那时的情况
这里, 我成功把数据包发给了测试程序 :-) (代码等一下再贴)
下面是TCP的头.
源端口,目的端口: ..
序号: 你现在发的包的开始序号
确认号: 你收到对方发的包的序号个数
头部: 就是头部的长度, 要除以4了
保留: 置0
URG:使用紧急指针,不用,置0
ACK:使用确认号
PSH:尽快提交上层
RST:拒绝连接,重置连接
SYN:用于建立连接
FIN:用于建立连接
窗口大小:表示自己还能接收多少数据
检验和:如UDP的检验和的算法, 但是要把伪头部中协议置为6
紧急指针:不用,置为0
选项:不用, 它主要有MSS, SACK的功能
下面贴代码
#define HAVE_REMOTE #include <pcap.h> #pragma comment(lib, "wpcap.lib") #pragma comment(lib, "ws2_32.lib") u_int16_t crc_checksum(u_int16_t* p, int psize) { u_int32_t ret = 0; while (psize > 1) { ret += *p++; psize -= 2; } if (psize == 1) ret += *(u_int8_t*)p; ret = (ret >> 16) + (ret & 0xffff); ret += (ret >> 16); return ~ret; } struct ether_header{ u_int8_t ether_dhost[6]; u_int8_t ether_shost[6]; u_int16_t ether_type; //IP协议,为0x0800 }; struct ip_header { u_int8_t version_headerLen; u_int8_t servicetype; u_int16_t totalLen; u_int16_t identification; u_int16_t flags_fragOffset; u_int8_t ttl; u_int8_t protocol; u_int16_t checksum; u_int32_t saddr; u_int32_t daddr; }; struct udp_header { u_int16_t sport; u_int16_t dport; u_int16_t totalLen; u_int16_t checksum; }; struct tcp_header { u_int16_t sport; u_int16_t dport; u_int32_t seq; u_int32_t ack_seq; u_int16_t dataOffset_reserve_flags; u_int16_t window; u_int16_t checksum; u_int16_t urg_ptr; }; struct psd_header { u_int32_t saddr; u_int32_t daddr; u_int8_t zero; u_int8_t protocol; u_int16_t len; }; void* add_bytes_addr(void* st, int bytes) { return (void*)(((char*)st) + bytes); } pcap_t* adhandle = NULL; void send_ether_data_from_ip(const char* d, const int dlen) { int totalLen = sizeof(ether_header) + dlen; char* buf = new char[totalLen]; ether_header* etherhdr = (ether_header*)add_bytes_addr(buf, 0); char* data = (char*)add_bytes_addr(etherhdr, sizeof(ether_header)); memcpy(data, d, dlen); etherhdr->ether_shost[0] = 0x00; etherhdr->ether_shost[1] = 0xE0; etherhdr->ether_shost[2] = 0xB0; etherhdr->ether_shost[3] = 0xE7; etherhdr->ether_shost[4] = 0xA6; etherhdr->ether_shost[5] = 0xDD; etherhdr->ether_dhost[0] = 0xc8; etherhdr->ether_dhost[1] = 0x3a; etherhdr->ether_dhost[2] = 0x35; etherhdr->ether_dhost[3] = 0x2c; etherhdr->ether_dhost[4] = 0x07; etherhdr->ether_dhost[5] = 0x00; etherhdr->ether_type = htons(0x0800); if(pcap_sendpacket(adhandle, (const u_char*)buf, totalLen) == -1) { } delete[] buf; } void send_ip_data(u_int saddr, u_int daddr, u_char protocol, const char* d, int dlen) { int totalLen = sizeof(ip_header) + dlen; char* buf = new char[totalLen]; ip_header* iphdr = (ip_header*)add_bytes_addr(buf, 0); char* data = (char*)add_bytes_addr(iphdr, sizeof(ip_header)); memcpy(data, d, dlen); iphdr->version_headerLen = (4<<4) | 5; iphdr->servicetype = 0; iphdr->totalLen = htons(totalLen); iphdr->identification = htons(0); iphdr->flags_fragOffset = htons((2<<13) | 0); iphdr->ttl = 0xff; iphdr->protocol = protocol; iphdr->checksum = 0; iphdr->saddr = saddr; iphdr->daddr = daddr; iphdr->checksum = crc_checksum((u_int16_t*)iphdr, sizeof(ip_header)); send_ether_data_from_ip(buf, totalLen); delete[] buf; } u_int16_t calc_psd_checksum(u_int32_t saddr, u_int32_t daddr, u_int8_t protocol, const char* d, int dlen) { int totalLen = sizeof(psd_header) + dlen; char* buf = new char[totalLen]; psd_header* psdhdr = (psd_header*)add_bytes_addr(buf, 0); char* data = (char*)add_bytes_addr(psdhdr, sizeof(psd_header)); memcpy(data, d, dlen); psdhdr->saddr = saddr; psdhdr->daddr = daddr; psdhdr->zero = 0; psdhdr->protocol = protocol; psdhdr->len = htons(dlen); u_int16_t ret = crc_checksum((u_int16_t*)buf, totalLen); delete[] buf; return ret; } void send_udp_data(u_int32_t saddr, u_int16_t sport, u_int32_t daddr, u_int16_t dport, const char* d, int dlen) { int totalLen = sizeof(udp_header) + dlen; char* buf = new char[totalLen]; udp_header* udphdr = (udp_header*)add_bytes_addr(buf, 0); char* data = (char*)add_bytes_addr(udphdr, sizeof(udp_header)); memcpy(data, d, dlen); udphdr->sport = htons(sport); udphdr->dport = htons(dport); udphdr->totalLen = htons(totalLen); udphdr->checksum = 0; udphdr->checksum = calc_psd_checksum(saddr, daddr, IPPROTO_UDP, buf, totalLen); send_ip_data(saddr, daddr, IPPROTO_UDP, (const char*)udphdr, totalLen); delete[] buf; } inline u_int8_t tcp_flags(u_int8_t urg, u_int8_t ack, u_int8_t psh, u_int8_t rst, u_int8_t syn, u_int8_t fin) { return (urg << 5) | (ack << 4) | (psh << 3) | (rst << 2) | (syn << 1) | fin; } void send_tcp_data(u_int32_t saddr, u_int16_t sport, u_int32_t daddr, u_int16_t dport, u_int32_t seq, u_int32_t ack_seq, u_int8_t flags, const char* d, int dlen) { int totalLen = sizeof(tcp_header) + dlen; char* buf = new char[totalLen]; tcp_header* tcphdr = (tcp_header*)add_bytes_addr(buf, 0); char* data = (char*)add_bytes_addr(tcphdr, sizeof(tcp_header)); memcpy(data, d, dlen); tcphdr->sport = htons(sport); tcphdr->dport = htons(dport); tcphdr->seq = htonl(seq); tcphdr->ack_seq = htonl(ack_seq); tcphdr->dataOffset_reserve_flags = htons((u_short)(( (sizeof(tcp_header)/4) << 12) | (0<<6) | flags)); tcphdr->window = htons(8196); tcphdr->checksum = 0; tcphdr->urg_ptr = 0; tcphdr->checksum = calc_psd_checksum(saddr, daddr, IPPROTO_TCP, buf, totalLen); send_ip_data(saddr, daddr, IPPROTO_TCP, (const char*)tcphdr, totalLen); delete[] buf; } void main() { char errbuf[PCAP_ERRBUF_SIZE] = { 0 }; pcap_if_t *alldevs = NULL; if(pcap_findalldevs(&alldevs, errbuf) == -1) return; if((adhandle = pcap_open(alldevs->name, 0x10000, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf)) == NULL) return; pcap_freealldevs(alldevs); //udp //send_udp_data(inet_addr("192.168.0.100"), 45630, inet_addr("192.168.0.100"), 6000, "hello", 6); //tcp int saddr = inet_addr("192.168.0.100"); int sport = 52803; send_tcp_data(saddr, sport, inet_addr("202.104.205.75"), 80, 0, 0, tcp_flags(0, 0, 0, 0, 1, 0), "", 0 ); Sleep(20); send_tcp_data(saddr, sport, inet_addr("202.104.205.75"), 80, 1, 1, tcp_flags(0, 1, 0, 0, 0, 0), "", 0 ); return; }
//===========================================================================//
后记
虽然数据包组装了,也发送了, 还是出问题了.
因为当我发送一个数据包给服务器,服务器也给我发回来一个数据包.这时候系统收到数据包,发现这个端口占用..然后就发一个RST的TCP给服务器, 让服务器关闭这个连接 > <!!
Winpcap是没办法阻止系统收到/发送数据包的. 因而不能阻止RST.
下面是网上截下来TCP的说明(http://www.hackbase.com/tech/2011-04-26/63452.html):
大部分TCP/IP实现遵循以下原则:
1:当一个SYN或者FIN数据包到达一个关闭的端口,TCP丢弃数据包同时发送一个RST数据包。
2:当一个RST数据包到达一个监听端口,RST被丢弃。
3:当一个RST数据包到达一个关闭的端口,RST被丢弃。
4:当一个包含ACK的数据包到达一个监听端口时,数据包被丢弃,同时发送一个RST数据包。
5:当一个SYN位关闭的数据包到达一个监听端口时,数据包被丢弃。
6:当一个SYN数据包到达一个监听端口时,正常的三阶段握手继续,回答一个SYN|ACK数据包。
7:当一个FIN数据包到达一个监听端口时,数据包被丢弃。
如那篇文章所说,利用上述规则我们可以进行端口扫描(这方法总比遍历connect高效吧!),
但是伪造数据来刷票是不行了,除非自己能改操作系统...