自制网络工具集合(抓包,扫描主机,扫描端口,获取本机信息,dos攻击)

tech2022-08-25  118

Git链接

网络工具集

1.shark抓包工具设计思路数据包格式系统协议的类型原始套接字测试结果 2.主机扫描工具设计思路原始套接字自组ICMP报文校验和函数测试结果 3.端口扫描工具设计思路套接字测试结果 4.dos攻击设计思路原始套接字测试结果

1.shark抓包工具

篇幅有限,代码就不贴出来了,有兴趣去GitHub看看;

设计思路

根据socket原始套接字,我们从链路层捕获数据包,然后把数据包层层转换,转换成对应数据包格式的结构体,然后拿到头部信息,再偏移底层协议结构体的字节长度,再获取上一层协议的首部信息。

数据包格式

链路层: 以太网帧格式: "协议类型"字段为0800是IP数据包;0806是ARP包;8035是RARP包;

ARP数据包格式:

网络层 IP数据包 传输层 UDP: TCP:

系统协议的类型

每种协议对应的结构体都放在/usr/include/Linux/ 下; 或者/usr/include/netinet/这个下边也有,俩差不多;

示例:IP首部结构体,这个结构体的信息与上边图片里的IP数据包的信息一一对应。(到时候就用这个结构体指针将我们捕获的数据包强转,然后获得对应的头部信息)

tcp首部结构体:

其他协议结构体略。 示例: 获得tcp首部信息

void print_tcp(char *buf) { struct tcphdr *pt = (struct tcphdr*)buf; printf("TCP头部[ 源端口:%hu 目的端口: %hu 序号seq : %u ", ntohs(pt->source), ntohs(pt->dest), ntohl(pt->seq)); if ( pt->ack ) printf("确认号ack_seq: %u", ntohl(pt->ack_seq)); if ( pt->fin ) printf(" fin"); if ( pt->syn ) printf(" syn"); if ( pt->ack ) printf(" ack"); printf("]"); printf("\n\n"); }

原始套接字

这次我们需要将socket的参数设为

socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

参数一指定链路层,参数二原始套接字编程,参数三包含头部信息。

测试结果

抓取的结果如图:(只抓tcp包,其他类似)

2.主机扫描工具

设计思路

根据用户输入的字符串网段地址(假设c类网络地址),将其转换成IP地址,然后循环255次遍历这个网段的ip,每次给对应的ip地址发送ICMP的ping报文,当然这个包需要我们自己创建,给这个包的数据区填上发送的时间。然后再创建一个select,一边接收对方发回的消息,一边防止超时。当我们发送的ip给我们回复包的时候,我们先判断这个包的源ip跟我们发送的ip是否相等,相等的话再判断是否ICMP的类型值是8应答包,都相等则这个主机正在工作。并且打印出这个主机的一些信息,和这个包发送过来的时间差。如果发送三次ICMP请求包都超时,则我们认为这个主机没有在工作。

原始套接字

这次需要将socket参数设置为

socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)

参数一指定IPV4,参数二原始套接字编程,参数三只收发ICMP协议

自组ICMP报文

ICMP报文的类型值是0代表回显请求,8代表回显应答,我的这个程序只用到这俩。同时因为这个ICMP没有涉及到端口号,所以不可以使用write函数,需要用到recvfrom函数发送。

ICMP的报文格式: ICMP的结构体:

根据上边边这俩图片,我们可以设计一共自组ICMP包的函数;

void make_icmp_packet(struct icmp* picmp, int len, int n) { memset(picmp, 0x00, len); gettimeofday((struct timeval*)(picmp->icmp_data), NULL); picmp->icmp_type = ICMP_ECHO;//发送报文,0 picmp->icmp_code = 0; picmp->icmp_cksum = 0; picmp->icmp_id = getpid(); picmp->icmp_seq = n; picmp->icmp_cksum = checksum((u_short*)picmp, len); }

校验和函数

思想: 1.将校验和字段清零;2.对每16比特位进行二进制反码累加求和。

//校验和函数 u_short checksum(u_short *data, int len) { u_long sum = 0; for ( ; len > 1; len-=2 ) { // 以16位为单位进行加 sum += *data++; if ( sum & 0x80000000 ) sum = (sum&0xffff) + (sum>>16); } if ( len == 1 ) { // 最后剩下一个 u_short i = 0; *(u_char*)&i = *(u_char*)data; sum += i; } while ( sum >> 16 ) // 高16位如果有1,继续计算 sum = (sum&0xffff) + (sum>>16); return (sum == 0xffff) ? sum : ~sum; }

测试结果

开始扫描:

扫描出主机存在且在工作:

3.端口扫描工具

查看常用端口的名字: vim /etc/services

设计思路

做全连接扫描,即客户端发送connect,如果连接成功,则这个端口就是打开的,否则这个端口未打开。

套接字

这次只需要普通的TCP连接即可

socket(AF_INET, SOCK_STREAM, 0)

示例代码

for (i=start_port; i<=end_port; i++) { if ( tcp_connet(ip, i) == 1 ) { //connet返回1代表连接成功,即端口开放 struct servent *ps = getservbyport(htons(i), "tcp"); //把tcp协议里的i端口相关信息装进对应的结构体中,来获得端口信息 printf("%d, %s", i, (ps==NULL)?"unkown":ps->s_name); //如果是1023后的端口,不是常用端口,没有名字的话打印null } }

测试结果

4.dos攻击

设计思路

假设有一台 服务器,它正处于listen状态,此时它会有一个未完成三次握手的队列和已完成三次握手的队列,我们给它发一个请求建立连接的数据包,但是这个包的源ip我们用随机数生成一个,这样它就不会屏蔽我们的数据包,可以不停的给它发请求连接包,直到将它的未完成三次握手队列占满。这样,当真正需要建立连接服务的程序请求与服务器建立连接,那么它会因为未完成三次握手队列已经满了而陷入等待,从而影响服务器的正常工作。

原始套接字

socket(AF_INET, SOCK_RAW, IPPROTO_TCP)

参数一网络层,参数二原始套接字,参数三收发tcp协议

测试结果

netstat -anpt 我们启动一个自己写的服务器程序,开始监听; 然后再向那个服务器的ip地址和端口号发送请求连接报文; 可以看到以下情况

最新回复(0)