伪造TCP/IP数据包, 刷票?!!

前言

最近,有一同学给我发来一投票的链接,当然希望我帮他投某某的票了o(︶︿︶)o

我立马投了票, 再投第二下那时

image

限制了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数据包头(简要说明而已, 如果想相信了解每一个项是怎么用, 建议查一下书吧!!)

6_74

版本:4

报头长度: 首部长/4(没选项就是5了)

服务类型:没用,置0

总长度:首部长度+数据长度

标识,标志,片偏移: IP数据包分片相关,将标志赋0x2就是不分片了

生存时间:0xff(255)

协议:看运输层,TCP:6, UDP:17

头校验和: 就是使用上面的算法计算头部的校验和, 不包括数据部分

源地址,目的地址: ..

选项就忽略吧~

 

6_60

 

真正的UDP包就只有4个字段而已

源端口,目的端口: ..

长度: UDP头+数据的长度

校验和:这个校验和的算法,要加上伪头部和数据来算,如上图.其中伪头部中的长度=UDP总长度

数据部分: 不一定是16位的倍数, 上图只是计算检验和那时的情况

 

这里, 我成功把数据包发给了测试程序 :-)   (代码等一下再贴)

下面是TCP的头.

 

 

 

6_63

 

源端口,目的端口: ..

序号: 你现在发的包的开始序号

确认号: 你收到对方发的包的序号个数

头部: 就是头部的长度, 要除以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高效吧!),

但是伪造数据来刷票是不行了,除非自己能改操作系统...

posted @ 2011-10-29 23:48  yanjielong  阅读(11754)  评论(4编辑  收藏  举报