android检测arp攻击

  前段时间突然和别人讨论到arp检测这块的实现,心血来潮,将腾讯的wifi管家给反编译了一下,看了它如何实现arp检测,下面是分析的结果。     


 下面是wifi管家检测arp攻击的类,进入的时候会发送一个message,然后执行handleMessage函数   

public void handleMessage(Message arg8) {
            int v6 = 10001;
            boolean v0 = true;
            super.handleMessage(arg8);
            switch(arg8.what) {
                case 10001: {
                    goto label_7;
                }
                case 10002: {
                    goto label_42;
                }
            }

            return;
        label_7:
            if((bth.a(this.eES)) && (bth.b(this.eES).refresh()) && (bap.a(bth.b(this.eES)))) {
                bth.c(this.eES);
                if(bth.d(this.eES) == 3) {
                    bth.e(this.eES);
                    bth.a(this.eES, 0);
                }

                bth.b(this.eES).Fu();
                try {
                    v0 = bth.b(this.eES, 10001);
                }
                catch(Exception v1) {
                }
            }

            if(bth.f(this.eES) == null) {
                return;
            }

            if(!v0) {
                return;
            }

            this.sendEmptyMessageDelayed(v6, bth.g(this.eES));
            return;
        label_42:
            bth.a(this.eES, false);
            bth.c(this.eES, 0);
            int v2 = 0;
            boolean v1_1 = true;
            while(v2 != 2) {
                try {
                    if((bth.b(this.eES).refresh()) && (bap.a(bth.b(this.eES)))) {
                        if(v2 == 0) {
                            bth.h(this.eES);
                        }
                        else {
                            bth.e(this.eES);
                        }

                        bth.b(this.eES).Fu();
                        v1_1 = bth.b(this.eES, 10002);
                        if(bth.i(this.eES) != 0) {
                            break;
                        }

                        SystemClock.sleep(700);
                    }
                }
                catch(Throwable v2_1) {
                    break;
                }

                ++v2;
            }

            if(bth.f(this.eES) != null && (v1_1)) {
                this.sendEmptyMessageDelayed(v6, bth.g(this.eES));
            }

            bth.a(this.eES, true);
        }

刚开始执行的时候,runnable的run函数调用handler发送10002,所以label_42是检测的入口函数,bth.h是调用this.Fo函数,首先看一下this.Fo函数:

private void Fo() {
        int v1 = this.eGX.Ft();
        if(v1 != 0) {
            int v0;
            for(v0 = 0; v0 != 256; ++v0) {
                String v2 = bao.kR(v1);
                if(!v2.equals(this.eGX.eHl) && !v2.equals(this.eGX.eHj)) {
                    ban.io(v2);
                }

                v1 = bao.kS(v1);
            }
        }
}
public int Ft() {
        int v0;
        DhcpInfo v1 = this.eHC.getDhcpInfo();
        if(v1 == null) {
            v0 = 0;
        }
        else {
            v0 = 16777215;
            if(v1.netmask != 0) {
                v0 = v1.netmask;
            }

            v0 &= v1.gateway;
        }

        return v0;
    }
public static String kR(int arg2) {
        return (arg2 & 255) + "." + (arg2 >> 8 & 255) + "." + (arg2 >> 16 & 255) + "." + (arg2 >> 24 & 255);
}
static {
        ban.eHh = new byte[]{-126, 40, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 32, 67, 75, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 0, 33, 0, 1};
}
public static void io(String arg6) {
        try {
            DatagramSocket v0_1 = new DatagramSocket();
            DatagramPacket v1 = new DatagramPacket(ban.eHh, ban.eHh.length, InetAddress.getByName(arg6), 137);
            v0_1.setSoTimeout(100);
            v0_1.send(v1);
            v0_1.close();
        }
        catch(Exception v0) {
        }
}
 public static int kS(int arg2) {
        int v0 = (arg2 >> 24 & 255) + 1;
        return v0 <= 255 ? v0 << 24 | 16777215 & arg2 : -1;
}

可以看到这个函数的作用是针对这个局域网的子网下除了当前连接wifi的网关以外的所有ip发送udp请求,以此来让arp文件里记录局域网子网下所有的ip和mac地址,bth.e也是发送udp,不过它只向网关ip发送

 
arp文件初始化之后,紧接着调用Fu()函数,如下:
public ArrayList Fu() {
        BufferedReader v0_2;
        BufferedReader v1 = null;
        this.eHy.clear();
        try {
            v0_2 = new BufferedReader(new FileReader("/proc/net/arp"));
        }
        catch(Throwable v0) {
            goto label_50;
        }
        catch(Exception v0_1) {
            v0_2 = v1;
            goto label_40;
        }
 
        try {
            v0_2.readLine();
            while(true) {
                String v1_3 = v0_2.readLine();
                if(v1_3 == null) {
                    break;
                }
 
                String[] v1_4 = v1_3.split("[ ]+");
                if(v1_4.length < 6) {
                    continue;
                }
 
                String v2 = v1_4[0];
                v1_3 = v1_4[3];
                if(v2.equals(this.eHj)) {
                    this.eHk = v1_3;
                }
 
                if(v1_3.equals("00:00:00:00:00:00")) {
                    continue;
                }
 
                this.eHy.add(new String[]{v2, v1_3, ""});
            }
        }
        catch(Exception v1_1) {
            goto label_40;
        }
        catch(Throwable v1_2) {
            goto label_58;
        }
 
        if(v0_2 == null) {
            goto label_42;
        }
 
        try {
            v0_2.close();
        }
        catch(IOException v0_3) {
        }
        ...
这段代码对this.eGX.eHy变量重新赋值,这个变量就是用来存放arp文件内容的,下面会用到这个变量,它的内容其实就是”/proc/net/arp”文件的内容
 

变量初始化完之后,这时候arp内容就可以进行分析了,然后执行bth.b函数,它调用了dY函数,如下:

扫描二维码关注公众号,回复: 1830552 查看本文章
private boolean dY(int arg11) {
        Object v1_1;
        Object v2;
        boolean v8 = false;
        if(!TextUtils.equals(this.eHc, this.eGX.BSSID)) {
            this.Fn();
        }
        else {
            int v7 = 0;
            boolean v1 = true;
            while(v7 != this.eGX.eHy.size()) {
                CharSequence v4 = this.eGX.eHy.get(v7)[0];
                Object v3 = this.eGX.eHy.get(v7)[1];
                if(!TextUtils.equals(v4, this.eGX.eHj)) {
                    this.eGZ.put(v3, v4);
                }
                else {
                    Object v0 = this.eGY.get(v4);
                    if(TextUtils.isEmpty(((CharSequence)v0))) {
                        this.eGY.put(v4, v3);
                    }
                    else {
                        if(v3 != null && (TextUtils.equals(((CharSequence)v0), ((CharSequence)v3)))) {
                            goto label_29;
                        }

                        if(TextUtils.equals(((CharSequence)v3), this.eGX.eHk)) {
                            v2 = this.eGY.get(v4);
                        }
                        else {
                            String v2_1 = ((String)v3);
                        }

                        v0 = this.eGZ.get(v3);
                        if(v0 == null || (((String)v0).equals(v4))) {
                            this.nx();
                            String v1_2 = this.ah(((String)v2), ((String)v4));
                        }
                        else {
                            v1_1 = v0;
                        }

                        this.kP(arg11);
                        if(this.eHb != null) {
                            this.eHb.a(((String)v1_1), ((String)v2), this.im(((String)v1_1)), arg11, this.eGU, this.c(this.eGX.eHj, this.eGX.eHk, ((String)v1_1), ((String)v2)));
                        }

                        this.reset();
                        v1 = false;
                    }
                }

            label_29:
                ++v7;
            }

            v8 = v1;
        }

        return v8;
    }

上面的v7是this.eGX.eHy的元素下标,目的是为了遍历this.eGX.eHy,所以这边是对arp文件内容进行分析的函数,首先对this.eGX.eHy进行解释,这个变量是一个ArrayList,上面提到的Fu()函数初始化了这个变量,它将arp文件里的每行记录变成this.eGX.eHy的一条记录,下面是执行查看arp的结果,可以看到每行的数据包含六个元素,第一个是Ip,第四个是mac address
 
  
然后再解释一下this.eGX.BSSID和this.eGX.eHj怎么来的,下面是它的初始化函数
public boolean refresh() {
        int v7 = 34;
        boolean v0 = false;
        try {
            WifiInfo v2 = this.eHC.getConnectionInfo();
            DhcpInfo v3 = this.eHC.getDhcpInfo();
            if(!this.eHC.isWifiEnabled()) {
                return v0;
            }
 
            if(!this.Fs()) {
                return v0;
            }
 
            if(v2 != null && v3 != null && v2.getSupplicantState() == SupplicantState.COMPLETED) {
                this.SSID = v2.getSSID();
                int v4 = v2.getIpAddress();
                if(this.SSID.charAt(0) == v7 && this.SSID.charAt(this.SSID.length() - 1) == v7) {
                    this.SSID = this.SSID.substring(1, this.SSID.length() - 1);
                }
 
                if(!this.Fs()) {
                    return v0;
                }
 
                if(TextUtils.isEmpty(this.SSID)) {
                    return v0;
                }
 
                if("<unknown ssid>".equalsIgnoreCase(this.SSID)) {
                    return v0;
                }
 
                if(v4 == 0) {
                    return v0;
                }
 
                this.BSSID = v2.getBSSID();
                     this.eHj = bao.kR(v3.gateway);
              ...
通过上面可以看到this.eGX.BSSID其实就是当前连接的wifi的bssid,this.eGX.eHj其实就是网关ip
 

最后来看一下怎样检测arp攻击,this.eGZ是一个HashMap,key是mac地址,value是ip,下面是找出现在的arp文件里网关Ip对应的mac是否和之前的不一样的arp记录

if(!TextUtils.equals(v4, this.eGX.eHj)) {
                    this.eGZ.put(v3, v4);
                }
                else {
                    Object v0 = this.eGY.get(v4);
                    if(TextUtils.isEmpty(((CharSequence)v0))) {
                        this.eGY.put(v4, v3);
                    }

this.eGY也是一个HashMap,key是ip,value是mac,下面的代码是找出网关ip对应的多个mac地址情况,如果发现网关ip对应的mac和之前的不一样,那么说明遇到arp攻击,就需要发送攻击者相关信息给服务器,注意的是this.eGY在开始检测前会先将arp里面的网关ip和mac地址放进去,然后刷新arp,所以才会出现同一个网关ip对应多个Mac情况

else {
                        if(v3 != null && (TextUtils.equals(((CharSequence)v0), ((CharSequence)v3)))) {
                            goto label_29;
                        }

                        if(TextUtils.equals(((CharSequence)v3), this.eGX.eHk)) {
                            v2 = this.eGY.get(v4);
                        }
                        else {
                            String v2_1 = ((String)v3);
                        }

                        v0 = this.eGZ.get(v3);
                        if(v0 == null || (((String)v0).equals(v4))) {
                            this.nx();
                            String v1_2 = this.ah(((String)v2), ((String)v4));
                        }
                        else {
                            v1_1 = v0;
                        }

                        this.kP(arg11);
                        if(this.eHb != null) {
                            this.eHb.a(((String)v1_1), ((String)v2), this.im(((String)v1_1)), arg11, this.eGU, this.c(this.eGX.eHj, this.eGX.eHk, ((String)v1_1), ((String)v2)));
                        }

                        this.reset();
                        v1 = false;
                    }
private String ah(String arg6, String arg7) {
        int v1;
        for(v1 = 0; v1 != this.eGX.eHy.size(); ++v1) {
            String v3 = this.eGX.eHy.get(v1)[0];
            if((this.eGX.eHy.get(v1)[1].equals(arg6)) && !v3.equals(arg7)) {
                String v0 = v3;
                return v0;
            }
        }

        return "0.0.0.0";
    }

现在真相大白了,主要检测方法:

1. arp攻击就是发送arp广播让局域网内的所有设备将网关ip对应的mac地址改成攻击者的mac地址,这样的话被攻击者的网关ip是对的,但是mac地址变了,因此只要找出来当前网络的网关mac地址对应的所有Ip,里面肯定包括了真正的网关ip和攻击者的网关ip,另外根据上面的代码还会出现同一个网关ip对应多个Mac的情况,这种事因为检测开始前先将arp里面的网关ip和mac地址放进去,然后检测开始时会刷新arp文件,如果受到攻击,此刻的网关ip对应的mac可能会和之前的不一样
2. 扫描局域网内子网下的所有ip,然后获取arp文件内容,生成一个三维数组,第一个元素是ip,第二个是mac地址,最后一个不用管
3. 将上面获取到的三维数组进行遍历,找出网关ip对应是否多个mac地址

4. 如果发现网关ip对应至少两个mac地址,那么不等于网关Ip的那个mac地址就是攻击者的mac地址,然后再找出这个mac对应的不等于网关ip的那个ip就是攻击者的真实ip

5. 可能大家也会遇到一个疑惑,上面的检测是基于刷新后的arp里面的网关对应的mac是正确的基础上的,那如何保证网关mac此刻是正确的呢,抓包看了一下,wfi管家并没有加快wifi网关mac的更新,测了一下,我的手机上是30秒左右更新一次网关mac,所以初步怀疑只有在刚好更新mac的这段时间内wifi管家才能检测出问题。



猜你喜欢

转载自blog.csdn.net/u012292247/article/details/77119318