Android平台ARP欺骗应用无法发送ARP数据包问题排查
Android ARP欺骗应用发送空数据包问题解决
问题描述
我正在开发基于Java和C的Android ARP欺骗应用,将C代码编译为.so库打包进APK。在Linux笔记本上测试该代码可正常发送ARP数据包,但在手机上运行时,日志显示“ARP reply sent successfully”,但Wireshark捕获到的是空数据包。
原代码
#include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <netinet/udp.h> #include <netinet/if_ether.h> #include <net/if_arp.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <net/if.h> #include <netpacket/packet.h> #include <sys/ioctl.h> struct my_arp_packet{ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ unsigned short h_proto; /* packet type ID field */ unsigned short int ar_hrd; /* Format of hardware address. */ unsigned short int ar_pro; /* Format of protocol address. */ unsigned char ar_hln; /* Length of hardware address. */ unsigned char ar_pln; /* Length of protocol address. */ unsigned short int ar_op; /* ARP opcode (command). */ unsigned char ar_sha[ETH_ALEN]; /* Sender hardware address. */ unsigned long ar_sip; /* Sender IP address. */ unsigned char ar_tha[ETH_ALEN]; /* Target hardware address. */ unsigned long ar_tip; /* Target IP address. */ } __attribute__((packed)); void listen_arp_requests(){ } void arp_reply_send(char *iface_name, unsigned long src_ip, unsigned char *src_mac, unsigned int dest_ip, unsigned char *dest_mac){ struct my_arp_packet arp; memcpy(arp.h_dest,dest_mac,ETH_ALEN); memcpy(arp.h_source,src_mac,ETH_ALEN); arp.h_proto=htons(ETH_P_ARP); arp.ar_hln = ETH_ALEN; arp.ar_pln = 4; arp.ar_hrd = htons(ARPHRD_ETHER); arp.ar_pro = htons(ETH_P_IP); arp.ar_op = htons(ARPOP_REPLY); memcpy(arp.ar_sha,src_mac,ETH_ALEN); memcpy(arp.ar_tha,dest_mac,ETH_ALEN); arp.ar_sip=src_ip; arp.ar_tip=dest_ip; int sock = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); if (sock == -1) { printf("Socket creation failed\n"); fflush(stdout); return; } struct sockaddr adr; if (strncpy(adr.sa_data, iface_name, sizeof(adr.sa_data) - 1) == NULL) { printf("Interface name copy failed\n"); fflush(stdout); close(sock); return; } strcpy(adr.sa_data, iface_name); adr.sa_family = AF_INET; if (sendto(sock, (void*)&arp, sizeof(struct my_arp_packet), 0, (struct sockaddr*)&adr, sizeof(struct sockaddr)) == -1) { printf("Failed to send ARP packet\n"); fflush(stdout); close(sock); return; } printf("ARP reply sent successfully\n"); fflush(stdout); close(sock); } void str_to_mac(unsigned char mac[ETH_ALEN],const char *str){ int i; char *token, *ptr; char *copy = strdup(str); token = strtok(copy, ":"); for (i = 0; i < ETH_ALEN; i++) { mac[i] = strtol(token, &ptr, 16); token = strtok(NULL, ":"); } free(copy); } int main(int argc, char **argv){ printf("START\n\n"); printf("Interface = %s\nsrc_ip = %s\nsrc_mac = %s\ndest_ip = %s\ndest_mac = %s\n", argv[1], argv[2], argv[3], argv[4], argv[5]); fflush(stdout); char *my_interface = argv[1]; // interface name unsigned long dest_ip=inet_addr(argv[4]); unsigned long src_ip=inet_addr(argv[2]); unsigned char dest_mac[ETH_ALEN]; unsigned char src_mac[ETH_ALEN]; str_to_mac(dest_mac, argv[5]); str_to_mac(src_mac, argv[3]); system("echo 1 > /proc/sys/net/ipv4/ip_forward"); system("iptables -A FORWARD -j ACCEPT"); system("iptables -D natctrl_FORWARD -j DROP"); system("iptables -A natctrl_FORWARD -j ACCEPT"); printf("\n"); fflush(stdout); while(1){ arp_reply_send(my_interface, src_ip, src_mac, dest_ip, dest_mac); sleep(1); } }
问题原因与解决方案
1. 废弃的Socket类型与错误的地址结构
SOCK_PACKET是Linux早期的原始套接字类型,已被废弃,Android系统不支持通过这种类型正确发送链路层数据包。同时,通用的sockaddr结构无法适配链路层的发送需求,需要使用sockaddr_ll(链路层专用地址结构)。
2. 接口索引获取错误
原代码直接将接口名写入sockaddr.sa_data,Android系统无法识别这种方式,必须通过ioctl的SIOCGIFINDEX命令获取接口的索引,再填充到sockaddr_ll中。
3. 结构体未初始化
原代码未初始化my_arp_packet结构体,内存中的垃圾数据可能导致数据包结构异常。
修改后的核心代码(arp_reply_send函数)
void arp_reply_send(char *iface_name, unsigned long src_ip, unsigned char *src_mac, unsigned int dest_ip, unsigned char *dest_mac){ struct my_arp_packet arp; memset(&arp, 0, sizeof(arp)); // 初始化结构体,清除垃圾数据 memcpy(arp.h_dest, dest_mac, ETH_ALEN); memcpy(arp.h_source, src_mac, ETH_ALEN); arp.h_proto = htons(ETH_P_ARP); arp.ar_hln = ETH_ALEN; arp.ar_pln = 4; arp.ar_hrd = htons(ARPHRD_ETHER); arp.ar_pro = htons(ETH_P_IP); arp.ar_op = htons(ARPOP_REPLY); memcpy(arp.ar_sha, src_mac, ETH_ALEN); memcpy(arp.ar_tha, dest_mac, ETH_ALEN); arp.ar_sip = src_ip; arp.ar_tip = dest_ip; // 使用SOCK_RAW替代废弃的SOCK_PACKET int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock == -1) { printf("Socket creation failed: %d\n", errno); fflush(stdout); return; } // 获取接口索引 struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, iface_name, IFNAMSIZ - 1); if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) { printf("Failed to get interface index: %d\n", errno); fflush(stdout); close(sock); return; } // 填充链路层地址结构 struct sockaddr_ll addr; memset(&addr, 0, sizeof(addr)); addr.sll_family = AF_PACKET; addr.sll_protocol = htons(ETH_P_ARP); addr.sll_ifindex = ifr.ifr_ifindex; addr.sll_halen = ETH_ALEN; memcpy(addr.sll_addr, dest_mac, ETH_ALEN); ssize_t sent_len = sendto(sock, &arp, sizeof(struct my_arp_packet), 0, (struct sockaddr*)&addr, sizeof(addr)); if (sent_len == -1) { printf("Failed to send ARP packet: %d\n", errno); fflush(stdout); } else if (sent_len != sizeof(struct my_arp_packet)) { printf("Sent partial packet: %zd/%zu bytes\n", sent_len, sizeof(struct my_arp_packet)); } printf("ARP reply sent successfully\n"); fflush(stdout); close(sock); }
额外注意事项
- Root权限:发送原始数据包、修改
/proc/sys/net/ipv4/ip_forward和iptables规则都需要应用获取root权限。 - SELinux限制:部分设备的SELinux会拦截原始数据包发送,可临时执行
setenforce 0关闭SELinux,或添加自定义SELinux规则。 - Android权限:在Manifest中添加
INTERNET、ACCESS_NETWORK_STATE、ACCESS_WIFI_STATE权限,Android 10+可能需要额外的VPN相关权限。
内容的提问来源于stack exchange,提问作者Black Ghost
相关产品推荐
相关产品推荐

