大家可以到我git托管平台clone一份测测看,本人程序小白,有问题欢迎指出,共同进步。https://github.com/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};
这是个百度www.baidu.com的DNS查询报文请求。
类似这样的数据是有通用结构的。
11 12 为标识字段
01 00 为标志字段,该字段设置了TC表示该报文是可截断的
00 01 查询报文数量为1
00 00 00 00 00 00 表示回答,授权和额外信息都为0
03 77 77 77 05 62 61 69 64 75 03 63 6F 6D 00 表示查询的名字为www.baidu.com 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
#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PCKT_LEN 100
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;
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 , 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 www.baidu.com看下是否一样。