linux或者Android使用内核ioctl抓包
linux或者Android使用内核抓包c语言程序
本文连接:https://www.cnblogs.com/muphy/p/14928858.html
参考程序:http://www.nsfocus.net/index.php?act=magazine&do=view&mid=1797
设置包过滤参考:https://www.kernel.org/doc/Documentation/networking/filter.txt
完整程序 ioctl.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <linux/in.h> #include <linux/if_ether.h> #include <net/if.h> #include <linux/filter.h> #include <sys/ioctl.h> #include <arpa/inet.h> int main(int argc, char **argv) { int sock, n; char buffer[2048]; unsigned char *iphead, *ethhead; struct ifreq ethreq; //$tcpdump -i eth0 tcp src port 502 -dd # 设置过滤 可以通过此命令获取c程序片段,复制到下面 struct sock_filter BPF_code[] = { {0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x000086dd}, {0x30, 0, 0, 0x00000014}, {0x15, 0, 15, 0x00000006}, {0x28, 0, 0, 0x00000036}, {0x15, 12, 0, 0x000001f6}, {0x28, 0, 0, 0x00000038}, {0x15, 10, 11, 0x000001f6}, {0x15, 0, 10, 0x00000800}, {0x30, 0, 0, 0x00000017}, {0x15, 0, 8, 0x00000006}, {0x28, 0, 0, 0x00000014}, {0x45, 6, 0, 0x00001fff}, {0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x0000000e}, {0x15, 2, 0, 0x000001f6}, {0x48, 0, 0, 0x00000010}, {0x15, 0, 1, 0x000001f6}, {0x6, 0, 0, 0x00040000}, {0x6, 0, 0, 0x00000000}}; struct sock_fprog Filter; Filter.len = sizeof(BPF_code) / sizeof(BPF_code[0]); Filter.filter = BPF_code; if ((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0) { perror("socket"); exit(1); } /* Set the network card in promiscuos mode */ strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, ðreq) == -1) { perror("ioctl"); close(sock); exit(1); } ethreq.ifr_flags |= IFF_PROMISC; if (ioctl(sock, SIOCSIFFLAGS, ðreq) == -1) { perror("ioctl"); close(sock); exit(1); } /* Attach the filter to the socket */ if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)) < 0) { perror("setsockopt"); close(sock); exit(1); } while (1) { printf("----------\n"); n = recvfrom(sock, buffer, 2048, 0, NULL, NULL); printf("%d bytes read\n", n); /* Check to see if the packet contains at least * complete Ethernet (14), IP (20) and TCP/UDP * (8) headers. */ if (n < 42) { perror("recvfrom():"); printf("Incomplete packet (errno is %d)\n", errno); close(sock); exit(0); } ethhead = buffer; printf("Ethernet MAC: [%02X:%02X:%02X:%02X:%02X:%02X]", ethhead[0], ethhead[1], ethhead[2], ethhead[3], ethhead[4], ethhead[5]); printf("->[%02X:%02X:%02X:%02X:%02X:%02X]", ethhead[6], ethhead[7], ethhead[8], ethhead[9], ethhead[10], ethhead[11]); printf(" type[%04x]\n", (ntohs(ethhead[12] | ethhead[13] << 8))); iphead = buffer + 14; /* Skip Ethernet header */ if (*iphead == 0x45) { /* Double check for IPv4 * and no options present */ printf("Layer-4 protocol %d\n", iphead[9]); printf("Version: %d HeaderLen: %d[%d] ", (*iphead >> 4), (*iphead & 0x0f), (*iphead & 0x0f) * 4); printf("TotalLen %d\n", (iphead[2] << 8 | iphead[3])); printf("IP [%d.%d.%d.%d:%d]", iphead[12], iphead[13], iphead[14], iphead[15], (iphead[20] << 8 | iphead[21])); printf("->[%d.%d.%d.%d:%d]\n", iphead[16], iphead[17], iphead[18], iphead[19], (iphead[22] << 8 | iphead[23])); for (int i = 0; i < n; ++i) { printf(" %02x", buffer[i] & 0xff); if ((i + 1) % 16 == 0) printf("\n"); } printf("\n"); } } }
抓包并转发到指定服务器工具
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <linux/in.h> #include <linux/if_ether.h> #include <net/if.h> #include <linux/filter.h> #include <sys/ioctl.h> #include <arpa/inet.h> #include <pthread.h> /* 强调:当客户端连接服务器时,服务器会产生一个新的文件描述符(套接字)与客户端交互,这个新的套接字不是服务器端的监听套接字 套接字是全双工的,在一个网络管道中的两端,每端都可以进行读写操作。 */ typedef struct _recvmodel { int sockfd; struct sockaddr_in *addr; } RecvModel; RecvModel model; int trun_port; char turn_ip[20]; char dev[20]; int filter_port = 502; int DATA_START_INDEX = 54; //send message void send_msg(const u_char *msg, int len) { printf("send start...\n"); if (send(model.sockfd, msg, len, 0) == -1) { printf("send failed ! error message %s\n", strerror(errno)); } printf("send end!\n"); } //recv message void *recv_thread() { int flag = 0; char buf[1024] = {0}; while (1) { flag = recv(model.sockfd, buf, sizeof(buf), 0); if (flag == 0) { printf("对方已经关闭连接!\n"); return NULL; } else if (flag == -1) { printf("recv failed ! error message : %s\n", strerror(errno)); return NULL; } printf("%s:%s", inet_ntoa(model.addr->sin_addr), buf); memset(buf, 0, sizeof(buf)); } return NULL; } void *listen_thread(void *arg) { int sock; char buffer[2048]; unsigned char *iphead, *ethhead, *data; struct ifreq ethreq; //$tcpdump -i eth0 -dd # 设置过滤 struct sock_filter BPF_code[] = {{0x28, 0, 0, 0x0000000c}, {0x15, 0, 5, 0x000086dd}, {0x30, 0, 0, 0x00000014}, {0x15, 6, 0, 0x00000006}, {0x15, 0, 6, 0x0000002c}, {0x30, 0, 0, 0x00000036}, {0x15, 3, 4, 0x00000006}, {0x15, 0, 3, 0x00000800}, {0x30, 0, 0, 0x00000017}, {0x15, 0, 1, 0x00000006}, {0x6, 0, 0, 0x00040000}, {0x6, 0, 0, 0x00000000}}; struct sock_fprog Filter; Filter.len = sizeof(BPF_code) / sizeof(BPF_code[0]); Filter.filter = BPF_code; if ((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) { perror("socket"); exit(1); } /* Set the network card in promiscuos mode */ strncpy(ethreq.ifr_name, dev, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, ðreq) == -1) { perror("ioctl"); close(sock); exit(1); } ethreq.ifr_flags |= IFF_PROMISC; if (ioctl(sock, SIOCSIFFLAGS, ðreq) == -1) { perror("ioctl"); close(sock); exit(1); } /* Attach the filter to the socket */ if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)) < 0) { perror("setsockopt"); close(sock); exit(1); } while (1) { printf("--------------------------------------------------------\n"); int n = recvfrom(sock, buffer, 2048, 0, NULL, NULL); int src_port, dest_port; printf("%d bytes read\n", n); /* Check to see if the packet contains at least * complete Ethernet (14), IP (20) and TCP/UDP * (8) headers. */ if (n < 42) { perror("recvfrom():"); printf("Incomplete packet (errno is %d)\n", errno); continue; } ethhead = buffer; iphead = buffer + 14; /* Skip Ethernet header */ printf("Ethernet MAC: [%02X:%02X:%02X:%02X:%02X:%02X]", ethhead[0], ethhead[1], ethhead[2], ethhead[3], ethhead[4], ethhead[5]); printf("->[%02X:%02X:%02X:%02X:%02X:%02X]", ethhead[6], ethhead[7], ethhead[8], ethhead[9], ethhead[10], ethhead[11]); printf(" type[%04x] iphead[%d]\n", (ntohs(ethhead[12] | ethhead[13] << 8)), ethhead[14]); if (*iphead != 0x45) { for (int i = 0; i < n; ++i) { printf(" %02x", ethhead[i] & 0xff); if ((i + 1) % 16 == 0) printf("\n"); } printf("\n"); printf("不是tcp协议,跳过抓发!\n"); continue; } src_port = iphead[20] << 8 | iphead[21]; dest_port = iphead[22] << 8 | iphead[23]; if (trun_port == src_port || trun_port == dest_port) { continue; } printf("Layer-4 protocol %d\n", iphead[9]); printf("Version: %d HeaderLen: %d[%d] ", (*iphead >> 4), (*iphead & 0x0f), (*iphead & 0x0f) * 4); printf("TotalLen %d\n", (iphead[2] << 8 | iphead[3])); printf("IP [%d.%d.%d.%d:%d]", iphead[12], iphead[13], iphead[14], iphead[15], src_port); printf("->[%d.%d.%d.%d:%d]\n", iphead[16], iphead[17], iphead[18], iphead[19], dest_port); if (iphead[9] != 6) { continue; } // printf("header-[54]:\n"); // for (int i = 0; i < 54; ++i) // { // printf(" %02x", buffer[i] & 0xff); // if ((i + 1) % 27 == 0) // printf("\n"); // } data = buffer + DATA_START_INDEX; int l = n - DATA_START_INDEX; printf("data-[%d]:\n", l); for (int i = 0; i < l; ++i) { printf(" %02x", data[i] & 0xff); if ((i + 1) % 27 == 0) printf("\n"); } printf("\n"); if (l < 1) { continue; } if (filter_port != 0 && filter_port != src_port && filter_port != dest_port) { continue; } //send_msg(data, l); send_msg(buffer, n); } } int main(int argc, char **argv) { //打开socket int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { printf("open socket failed! error message:%s\n", strerror(errno)); return -1; } //定义IP地址结构 struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); //设置TCP/IP连接 addr.sin_family = AF_INET; //设置端口号 printf("设置转发端口号:"); scanf("%d", &trun_port); addr.sin_port = htons(trun_port); //设置允许连接地址 do { printf("设置转发IP:"); scanf("%s", turn_ip); } while (turn_ip[0] == '\0'); printf("转发地址:ip:%s, port: %d\n", turn_ip, trun_port); addr.sin_addr.s_addr = inet_addr(turn_ip); printf("设置TCP抓包过滤端口(只是不转发,0表示所有端口都转发):"); scanf("%d", &filter_port); printf("TCP抓包过滤端口:%d\n", filter_port); //设置允许连接地址 do { printf("设置监听网卡(如:eth0):"); scanf("%s", dev); } while (dev[0] == '\0'); printf("监听网卡:%s\n", dev); //connect server int numx = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); if (numx == -1) { printf("connect server failed ! error message :%s\n", strerror(errno)); goto END; } model.sockfd = sockfd; model.addr = &addr; printf("connect server ok ! \n"); pthread_t thr1, thr2; if (pthread_create(&thr1, NULL, listen_thread, NULL) != 0) { printf("create thread failed ! \n"); goto END; } printf("create listen_thread thread ok ! \n"); if (pthread_create(&thr2, NULL, recv_thread, NULL) != 0) { printf("create thread failed ! \n"); goto END; } printf("create recv_thread thread ok ! \n"); pthread_join(thr1, NULL); pthread_join(thr2, NULL); END: close(sockfd); return 0; }
测试
如果在Android编译,需要安卓gcc,这个apk下载后使用压缩软件winrar打开,将gcc.zip复制出来解压到/data(似乎这里读写权限没有问题)目录下(我的目录是/data/ruphy)并设置环境变量:
export PATH=$PATH:/data/ruphy/gcc/bin:/data/ruphy/gcc/arm-linux-androideabi/bin:/data/ruphy/gcc/libexec
雷电模拟器开启终端,使用gcc编译ioctl.c为ioctl.o可执行文件并执行启动抓包

在Linux使用nc开启一个tcp服务,也可以不需要,因为模拟器可以上网,随便产生tcp连接即可
然后在模拟器使用telnet连接服务并通信


TCP/IP数据包格式
参考:https://blog.csdn.net/jrunw/article/details/56835404

黄色的是数据链路层的头部,一共14字节
绿色的部分是IP头部,一般是20字节
紫色部分是TCP头部,一般是20字节
最内部的是数据包内容
浙公网安备 33010602011771号