大家可以到我git托管平台clone一份测测看,本人程序小白,有问题欢迎指出,共同进步。/guojunfengcode/udp_raw_socket.git
1.简单的讲一下IP欺骗。
目的是往A发包,一般本应在包里面填充自己的ip地址,在这把ip地址设为B的地址,让A误以为是B地址发的请求包,所以A响应response的时候是往B发包。这就是ip欺骗,主动让A跟B相互联系。
2.那域名解析呢?
只要向网关或都域名服务器的53端口发送一个DNS查询报文,就可以收到服务器响应的报文,解析这个报文就可以得到域名对应的IP地址。在这就需要构建一个DNS查询报文了。
unsigned char DNS[] = {0x11, 0x12, 0x01, 0x00, 0x00, 0x01, 0x00 ,0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x05, 0x62, 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6F, 0x6D, 0x00,0x00, 0x01, 0x00, 0x01};
这是个百度的DNS查询报文请求。
类似这样的数据是有通用结构的。
11 12 为标识字段01 00 为标志字段,该字段设置了TC表示该报文是可截断的00 01 查询报文数量为100 00 00 00 00 00 表示回答,授权和额外信息都为003 77 77 77 05 62 61 69 64 75 03 63 6F 6D 00 表示查询的名字为 03是本文结束标志 05是请求标志 通用[03 三级域名 05 二级域名 03 顶级域名 00]00 01 为类型,1代表查询IP地址、2代表名字服务器、5代表规范名称、12代表指针记录00 01 为类型,1表示Internet数据
需要注意的是:
标识字段可以自己填充理想值,这个是transaction id,域名服务器会返回一致。
再把域名填充好就可以了,其余数据不用变。
3.利用RawSocket实现ip欺骗效果
socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
使用UDP协议创建原始套接字
现在就要自己实现ip协议头跟udp协议头了,对应结构体可在/usr/include/linux目录下的ip.h和udp.h查看。
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error “Please fix <asm/byteorder.h>”
#endif
__u8 tos;
__be16 -tot_len;
__be16 -id;
__be16 -frag_off;
__u8 ttl;
__u8 protocol;
__be16 -check;
__be32 -saddr;
__be32 -daddr;
};
struct udphdr {
__be16 source;
__be16 dest;
__be16 len;
__sum16 check;
};
注意udp头部check校验和的值需要udp伪头部的。
这个伪首部指,源地址、目的地址、UDP数据长度、协议类型(0x11),协议类型就一个字节,但需要补一个字节的0x0,构成12个字节。
typedef struct
{
u_int32_t src;
u_int32_t des;
u_int8_t zero;
u_int8_t pro;
u_int16_t len;
}UDP_PSEUDO_HEADER;
校验和是依赖12字节的UDP伪首部+8字节的udp首部+数据计算的。
校验和的计算方法,取16的值相加,溢出16位的值就丢弃溢位值,把这溢位值加到后面,重复动作,得到最后的值,取反就是检验和了
4.直接上代码吧
#include <unistd.h>#include <stdio.h>#include <sys/socket.h>#include <netinet/ip.h>#include <netinet/udp.h>#include<memory.h>#include<stdlib.h>#include <linux/if_ether.h>#include <linux/if_packet.h> #include<arpa/inet.h>#include<netinet/if_ether.h>#define PCKT_LEN 100typedef struct{u_int32_t src;u_int32_t des;u_int8_t zero;u_int8_t pro;u_int16_t len;}UDP_PSEUDO_HEADER;unsigned short checksum(unsigned short *buf, int nwords){unsigned long sum;for (sum = 0; nwords > 0; nwords--){sum += *buf++;}sum = (sum >> 16) + (sum & 0xffff);sum += (sum >> 16);return (unsigned short)(~sum);}int main(int argc, char *argv[]){int fd;char buffer[PCKT_LEN] ;unsigned char DNS[] = {0x11, 0x12, 0x01, 0x00, 0x00, 0x01, 0x00 ,0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x05, 0x62, 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6F, 0x6D, 0x00,0x00, 0x01, 0x00, 0x01};struct iphdr *ip = (struct iphdr *) buffer;struct udphdr *udp = (struct udphdr *) (buffer + sizeof(struct iphdr));struct sockaddr_in sin, din;int one = 1;const int *val = &one;memset(buffer, 0, PCKT_LEN);if (argc != 5) {printf("- Usage %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);exit(-1);}fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);if (fd < 0) {perror("socket() error");exit(-1);}printf("socket() - Using SOCK_RAW socket and UDP protocol is OK.\n");if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int))) {perror("setsockopt() error");exit(-1);}printf("setsockopt() is OK.\n");sin.sin_family = AF_INET;din.sin_family = AF_INET;sin.sin_port = htons(atoi(argv[2]));din.sin_port = htons(atoi(argv[4]));sin.sin_addr.s_addr = inet_addr(argv[1]);din.sin_addr.s_addr = inet_addr(argv[3]);ip->ihl = 5;ip->version = 4;ip->tos = 0;ip->tot_len = ((sizeof(struct iphdr) + sizeof(struct udphdr)+sizeof(DNS)));ip->ttl = 64;ip->protocol = 17;ip->check = 0;ip->saddr = inet_addr(argv[1]);ip->daddr = inet_addr(argv[3]);udp->source = htons(atoi(argv[2]));udp->dest = htons(atoi(argv[4]));udp->len = htons(sizeof(struct udphdr)+sizeof(DNS));char for_udp_check[sizeof(UDP_PSEUDO_HEADER) + sizeof(struct udphdr)+sizeof(DNS)+1] = {0};char * udpchecksum = for_udp_check;memset(udpchecksum, 0, sizeof(UDP_PSEUDO_HEADER) + sizeof(struct udphdr) + sizeof(DNS) + 1);UDP_PSEUDO_HEADER * udp_psd_Header = (UDP_PSEUDO_HEADER *)udpchecksum;udp_psd_Header->src = inet_addr(argv[1]);udp_psd_Header->des = inet_addr(argv[3]);udp_psd_Header->zero = 0;udp_psd_Header->pro = 17;udp_psd_Header->len = htons(sizeof(struct udphdr)+sizeof(DNS));memcpy(udpchecksum + sizeof(UDP_PSEUDO_HEADER), udp, sizeof(struct udphdr));memcpy(udpchecksum + sizeof(UDP_PSEUDO_HEADER) + sizeof(struct udphdr), DNS, sizeof(DNS));udp->check = checksum((unsigned short *)udpchecksum,(sizeof(struct udphdr)+sizeof(UDP_PSEUDO_HEADER)+sizeof(DNS)+1)/2);printf("Source IP: %s port: %u, Target IP: %s port: %u. Ip length: %d\n\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]), ip->tot_len);int count;memcpy(buffer + sizeof(struct iphdr) + sizeof(struct udphdr), DNS, sizeof(DNS));for (count = 1; count <= 10; count++){if (sendto(fd, buffer, ip->tot_len, 0, (struct sockaddr *)&din, sizeof(din)) < 0) {perror("sendto() error");exit(-1);} else {printf("Count #%u - sendto() is OK.\n", count);sleep(2);}}close(fd);return 0;}
5.测试结果
查看宿主机的ip是192.168.1.8
在centos上发了一个源地址192.168.1.8发往域名服务器114.114.114.114的53端口。
在宿主机捉包看看有没响应成功。。。
可以看到是成功响应的。响应的域名解析地址是14.215.177.38和14.215.177.39,这俩个都是百度在广东省内电信的域名地址。
也可以ping 看下是否一样。