You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:32:02