利用tcp发送syn,我们可以从网络层进行下发,其实就是组装tcp /ip包发送出去.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//#include <asm/types.h>
#include <linux/ip.h>
//#include <linux/tcp_new.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <sys/time.h>
#define getrandom(min, max) ((rand() % (int)(((max)+1) - (min))) + (min))
void send_tcp(int sockfd, struct sockaddr_in *addr);
unsigned short checksum(unsigned short *buffer, int size);
unsigned short random_port(unsigned short minport, unsigned short maxport);
void random_ip(char *str);
//nmap:sudo nmap -sP 192.168.1.* 检测内网ip
//nmap:sudo nmap -sS 192.168.1.2 -D 192.168.1.3 用后面的假ip欺诈前面的内网ip,并获取开放的端口号.D:decoys诱饵
//查看公网ip:curl ident.me
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in addr;
//int dport;
int on = 1;
if (argc != 3)
{
printf("usage: <command_name> <target_ip> <port>\n");
exit(-1);
}
bzero(&addr, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
//addr.sin_addr.s_addr=inet_aton(argv[1]);
inet_pton(AF_INET, argv[1], &addr.sin_addr);
/*if(inet_aton(argv[1],&addr.sin_addr)==0){
* host=gethostbyname
* }*/
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);//SOCK_RAW原始套接字,root才可以创建
if (sockfd<0)
{
printf("Socket error!\n");
exit(-1);
}
setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));//IP_HDRINCL自己编写ip头
struct timeval tv,tm;
gettimeofday(&tv,NULL);
while (1)
{
static int i = 0;
++i;
//if(i=5) break;
send_tcp(sockfd, &addr);
sleep(1024);
//gettimeofday(&tm,NULL);
//if(tv.tv_sec + 10 <= tm.tv_sec) break;
}
printf("exit.\n");
return 0;
}
void send_tcp(int sockfd, struct sockaddr_in *addr)
{
char buff[sizeof(struct iphdr) + sizeof(struct tcphdr)];
memset(buff,0x0,sizeof(buff));
struct iphdr *ip_header = (struct iphdr *)buff;
struct tcphdr *tcp_header = (struct tcphdr *)&buff[sizeof(struct iphdr)];
unsigned short source_port = random_port(1024, 5000);
char ip_str[50];
struct in_addr ip;
//random_ip(ip_str);
//if (inet_aton(ip_str, &ip) == 0)
//{
// printf("inet_aton error!\n");
// exit(1);
//}
ip.s_addr = inet_addr("192.168.0.69");
//网络层IP
//ip_header=(struct iphdr*)buff;
//31 0
//|----|----|------|--|-------------------|----------
//|ver |ihl | -tos | -| tot_len |
//|----|----|------|--|-------------------|
//| id | frag_off -|
//|---------|---------|-------------------|
//| ttl |protocol | check | 20 Bytes
//|---------|---------|-------------------|
//| saddr |
//|---------------------------------------|
//| daddr |
//|---------------------------------------|----------
//| |
//| options | 40 Bytes
//| |
//|---------------------------------------|
// 图:IPv4头部
//大对大小对小 小端对齐 little endian
//高位在低地址,低位在高地址,big endian,也是网络字节序:32bit每次传输8bit共4次
//先传0~7bit 再8~15 16~23 24~31
//4bit版本号;IPv4
ip_header->version = 4;
//4bit首部长度;以32bit为单位 普通ip数据报无选择项,大小是5。5*32/8=20Bytes.
ip_header->ihl = sizeof(struct iphdr)/4;//0x45
//8bit服务类型字段;包含三部分:3bit的优先权子字段(现已忽略),4bitTOS字段,1bit未用(但必须置零).其中4bitTOS字段分别代表最小时延,最大吞吐量,最高可靠性,最小费用(4bit中只能设置其中1bit).如果4bit均为0,则为一般服务.
ip_header->tos = 0;//0x00
//16bit总长度字段;以bytes为单位 再利用首部长度,可知道数据报中数据的起始位置和长度.总长度最长65535.
//一些数据链路(如以太网)会填充一些数据以达到最小长度.尽管以太网最小帧长为46bytes,但ip数据可能更短,此时ip层不知道46bytes中有多少是ip数据报的内容,因此总长度字段是必要内容.
ip_header->tot_len = htons(sizeof(buff));//0x00 0x2e
//16bit标识字段;唯一地标识主机发送的数据报.每发送一次报文,通常会id+1
ip_header->id = htons(random());//0xbe 0x55
//16bit分段偏移Fragment
//低13位:
//高3位:第1位保留,必须0.
// 第2位是"更多分片"(More Fragment)标志.除了最后一片,其他组成数据报的片都应将该位置1.
// 第3位是"不分片"(Don't Fragment)标志,
ip_header->frag_off = 0;//0x00 0x00
//8bit生存时间段;每经过一个路由-1,每一跳(秒)-1,在路由器排队时间较长时计数器多倍递减.到0时数据报被丢弃并发送ICMP报文通知源主机.值通常为32或64.计数单位为s,因此最大生存期为255s.
//此特性避免数据报长时间逗留在网络中.
ip_header->ttl = 0x40;//0x1e
//8bit协议字段;识别是哪个协议向IP传送数据,或将数据报交给哪个进程.UDP,TCP等.
ip_header->protocol = IPPROTO_TCP;//0x11
//16bit首部检验和;用32bit计算;计算公式:ip_header全部统计,包括check;接受时如果check不结果不合法,则ip丢掉该数据报,不生成差错报文,由上层(TCP UDP等)去发现丢失的数据报.
//发送:将ip首部以16bit为单位统计:0x4500+0x002e+0xbe55+0x0000+0x1e11+0x0000+0xdeb7+0x7ee3+0xc0a8+0x127a=0x35250; 11 0101 0010 0101 0000
// check += (check >> 32) 0101 0010 0101 0011//如果仍有17bit高位,重复
// 取反(反码) 1010 1101 1010 1100 得0xadac
//接受:将ip首部以16bit为单位统计:0x4500+0x002e+0xbe55+0x0000+0x1e11+0xadac+0xdeb7+0x7ee3+0xc0a8+0x127a=3fffc; 11 1111 1111 1111 1100
// check += (check >> 32) 1111 1111 1111 1111//如果仍有17bit高位,重复
// 取反(反码) 0000 0000 0000 0000 结果为0,说明ip首部合法
ip_header->check = 0;//0x00 0x00 -->0xad 0xac
//32bit源ip地址,这里随机生成.在内网环境下,非法ip将被路由抛弃.
ip_header->saddr = ip.s_addr;//0xde 0xb7 0x7e 0xe3 (222.183.126.227)
//32bit目的ip地址
ip_header->daddr = addr->sin_addr.s_addr;//0xc0 0xa8 0x12 0x7a (192.168.18.122)
//传输层TCP
//tcp_header=(struct tcphdr*)(buff+sizeof(struct iphdr));
// |----------------|----------------|-------------
// | source | dest |
// |----------------|----------------|
// | seq |
// |---------------------------------|
// | ack_seq | 20 Bytes
// |----|----|------|----------------|
// |doff|res1| | window |
// |----|----|------|----------------|
// | check | urg_ptr |
// |----------------|----------------|-------------
// | options | 4 Bytes(有选项就4bytes否则0bytes)
// |---------------------------------|
// 图:TCP头部
//struct tcphdr {
// __be16 source;
// __be16 dest;
// __be32 seq;
// __be32 ack_seq;
//#if defined(__LITTLE_ENDIAN_BITFIELD)
// __u16 res1:4,
// doff:4,
// fin:1,
// syn:1,
// rst:1,
// psh:1,
// ack:1,
// urg:1,
// ece:1,
// cwr:1;
//#elif defined(__BIG_ENDIAN_BITFIELD)
// __u16 doff:4,
// res1:4,
// cwr:1,
// ece:1,
// urg:1,
// ack:1,
// psh:1,
// rst:1,
// syn:1,
// fin:1;
//#else
//#error "Adjust your <asm/byteorder.h> defines"
//#endif
// __be16 window;
// __sum16 check;
// __be16 urg_ptr;
//};
//TCP选项:有就4bytes,无就0bytes.
//typedef struct _TCP_OPTIONS{
// char m_ckind;
// char m_cLength;
// char m_cContext[32];
// }__attribute__((packed))TCP_OPTIONS, *PTCP_OPTIONS;
//16bit源端口号
tcp_header->source = htons(source_port);
//16bit目标端口号
tcp_header->dest = addr->sin_port;
//32bit序列号;整个报文中,起始序列号为随机数(for safety),后续的序列号=已接收bit数+起始序列号.
tcp_header->seq =rand();
//32bit 期望对方下次发送的seq,表示ack_seq-1及之前的数据都正确收到.在seq达到最大时,便从0开始.
tcp_header->ack_seq = 0;
//4bitTCP头部长度;指明了TCP头部包含多少个32bit的字.因为options长度是可变的,tcp头部长度也就跟着变化.最长16*32/8=60bytes.
tcp_header->doff = sizeof(struct tcphdr) / 4;
//6bit保留位
tcp_header->res1 = 0;
//1bit紧急指针标志位;置为1表示ugr_ptr生效.
tcp_header->urg = 0;
//1bit确认信息;置为1表示该报文的ack_seq有效.如果为0,ack_seq将被接收方忽略.
tcp_header->ack = 0;
//1bit push;置为1表示该数据应当立即将数据交给应用程序,而不是缓存到缓冲区.
tcp_header->psh = 0;
//1bit重置连接;置为1表示接收端有问题,也可用来拒绝连接请求.接受者应当处理.
tcp_header->rst = 0;
//1bit建立连接;此时ack_seq不应生效,因此应该让ack=0.
tcp_header->syn = 1;
//1bit释放连接;表示发送方数据传输结束.
tcp_header->fin = 0;
//16bit滑动窗口;表示从该报文的seq算起,还能接受多少bytes数据.当window=0时,表示已经收到ack_seq-1个bit的数据,现在还不能接收新报文.可以接收新报文时,再重复发一次该报文,但window != 0.即这是TCP的流量控制,最大65535.
//窗口指发送方的接收窗口。它告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。之所以有此限制是因为接收方的数据缓存空间是有限的。它指出现在允许对方发送数据量。由于接收缓存不断变化,因此窗口值也不断变化
tcp_header->window = htons(65535);
//16bit检验和;ip检验和只包括ip头部,而TCP/UDP检验和包括伪头部,头部和数据.
tcp_header->check = 0;
//16bit紧急指针;记录了该数据在整个数据报中的位置.
//紧急数据的seq号:该报文的seq+紧急指针urg_ptr-1.因为不包括syn(不知道为什么),因此-1.
//紧急数据又称带外数据(out-of-band data),但描述不准确,因为它仍然随着普通数据发送,并无特殊待遇.只是让接受端知道,这是紧急数据.
//紧急数据会被覆盖:如果发送了一个字节紧急数据如下,在紧急数据被发送出去前,又发送一个紧急数据'B',那么'B'将覆盖'A'发送出去.
//write(fd,'A',1,URG);发送紧急数据A,使紧急指针指向'A'.
tcp_header->urg_ptr = 0;
//TCP伪头部:不应该将tcp/udp校验和归纳于任何层,因为ip实际上tcp不该从ip知道的.96bit的伪头部.能够防止出现路由选择错误.
//-----------------------------------------
//| 32bit Source IP address |
//-----------------------------------------
//| 32bit Destination IP addr |
//-----------------------------------------
//| 0 | 8bit Proto| 16bit header length|
//-----------------------------------------
// 图:tcp/udp伪头部
//send_tcp_segment(&ip_header,&tcp_header,"",0);
/*struct
{
unsigned long saddr;//32bit源ip地址
unsigned long daddr;//32bit目的ip地址
char mbz;//8bit填充对齐,必须置0
char ptcl;//8bitprotocol协议号.(IPPROTO_TCP=6,IPPROTO_UDP=17)
unsigned short tcpl;//16bitTCP/UDP头长度(不包含数据部分)
}psd_header;*/
#pragma pack(1)
struct pseudo_header //needed for checksum calculation
{
//伪头部 不发送只校验
unsigned int source_address;//32bit源ip地址
unsigned int dest_address;//32bit目的ip地址
unsigned char placeholder;//8bit填充对齐,必须置0
unsigned char protocol;//8bitprotocol协议号.(IPPROTO_TCP=6,IPPROTO_UDP=17)
unsigned short tcp_length;//16bitTCP/UDP头长度(不包含数据部分)
//头部 随ip发送
struct tcphdr tcp;
};
#pragma pack()
struct pseudo_header psd_header;
psd_header.source_address = ip_header->saddr;
psd_header.dest_address = ip_header->daddr;
psd_header.placeholder = 0;
psd_header.protocol = IPPROTO_TCP;
psd_header.tcp_length = htons(sizeof(struct tcphdr));
memcpy(&psd_header.tcp,tcp_header,sizeof(struct tcphdr));
//包括了tcp伪头部 tcp头部 tcp数据.
tcp_header->check = checksum((unsigned short*)&psd_header, sizeof(psd_header));
//只包括ip头部.
ip_header->check = checksum((unsigned short*)buff, (sizeof(int)/sizeof(unsigned short)) * ip_header->ihl);
ssize_t sentlen = sendto(sockfd, buff, tcp_header->doff * 4 + ip_header->ihl * 4 /*sizeof(buff)*/, 0,
(struct sockaddr *)addr, sizeof(struct sockaddr_in));
printf("sentlen:%d,bufflen=%d,tcp/iplen=%d,iplen=%d\n",sentlen,sizeof(buff),tcp_header->doff*4+ip_header->ihl*4,ip_header->ihl*4);
if(sentlen == -1) perror("sendto");
}
unsigned short checksum(unsigned short *buffer, int size)
{
unsigned long cksum = 0;
//size>1时可以直接以16bit为单位进行累加保存.
while (size >1)
{
cksum += *buffer++;
size -= sizeof(unsigned short);
}
//最后一个size==1说明有数据剩余小于16bit,累加.
if (size) cksum += *(unsigned char*)buffer; //..buffer..size..2......
//将多出16bit的高位加到低位.
cksum = (cksum >> 16) + (cksum & 0xffff);
//可能仍然有高位存在,再次加到低位.
cksum += (cksum >> 16);
//取反得到校验和.
return (unsigned short)(~cksum);
}
unsigned short random_port(unsigned short minport, unsigned short maxport) {
/*struct time stime;
*unsigned seed;
*gettime(&stime);
*seed=stime.ti_hund*stime.ti_min*stime.ti_hour;
* srand(seed);*/
srand((unsigned)time(NULL));
return(getrandom(minport, maxport));
}
void random_ip(char *str) {
int a, b, c, d, i = 0;
static long j = 0;
srand((unsigned)time(NULL) + (i++) + (j++));
a = getrandom(0, 255);
srand((unsigned)time(NULL) + (i++) + (j++));
b = getrandom(0, 255);
srand((unsigned)time(NULL) + (i++) + (j++));
c = getrandom(0, 255);
srand((unsigned)time(NULL) + (i++) + (j++));
d = getrandom(0, 255);
sprintf(str, "%d.%d.%d.%d", a, b, c, d);
printf("%s\n", str);
}