Android hook WifiManager控制Wi-Fi

2,203 阅读3分钟

最近业务接入了xxxSDK进来,他需要了2个比较特殊的权限

    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

因为这2个权限可以控制Wi-Fi的开关和关闭 代码如下

    WifiManager wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
     wifi.setWifiEnabled(false);

出去xxx考虑,我肯定不想给xx人发现我们带了xxxSDK会执行这个操作,但是为了提高xxx,只能允许他这样做,但是为了xxx,我必须要控制下他这个行为

因为Wi-Fi打开了,是没办法判断数据是否打开,所以需要通过setWifiEnabled把Wi-Fi关闭了,才能读取当前Wi-Fi情况把,或者考虑是什么情况下,具体我就假装不知道,但是我手机上没有sim卡,他还执行关闭Wi-Fi这操作,是非常影响我测试的,

所以找了下资料,我需要对他进行hook判断,限制他的行为,就有如下代码:


/**
 * Copyright (C), 2018-2020
 * Author: ziqimo
 * Date: 2020/7/18 8:00 AM
 * Description:
 * History:
 * <author> <time> <version> <desc>
 * 作者姓名 修改时间 版本号 描述
 */
public class WifiHook {


    private static Class iWifiManager;

    private static Field serviceField;

    private volatile static boolean isHook;

    public static void hook(Context context) {
        try {
            if (isHook) {
                return;
            }
            iWifiManager = Class.forName("android.net.wifi.IWifiManager");
            serviceField = WifiManager.class.getDeclaredField("mService");
            serviceField.setAccessible(true);

            WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
            Object realIwm = serviceField.get(wifi);
            serviceField.set(wifi, Proxy.newProxyInstance(iWifiManager.getClassLoader(),
                    new Class[]{iWifiManager},
                    new IWMInvocationHandler(context, realIwm)));
            LogUtils.e("AAA", "hook success");
            isHook = true;
            wifi.setWifiEnabled(false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static class IWMInvocationHandler implements InvocationHandler {

        private Object real;

        private Context context;


        public IWMInvocationHandler(Context context, Object real) {
            this.context = context;
            this.real = real;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            LogUtils.e("AAA", "method invoke " + method.getName() + ",args:" + Arrays.toString(args));
            if ("setWifiEnabled".equals(method.getName())) {
                try {
                    String networkOperator = SystemUtils.getNetworkOperator(context);
                    String networkOperatorName = SystemUtils.getNetworkOperatorName(context);
                    String simCountryIso = SystemUtils.getSimCountryIso(context);
                    String simOperator = SystemUtils.getSimOperator(context);
                    String simOperatorName = SystemUtils.getSimOperatorName(context);
                    String networkType = SystemUtils.getNetworkType(context);


                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return method.invoke(real, args);
        }
    }
}

在执行setWifiEnabled,我可以判断下sim的状态,是否有sim卡,若没sim的卡(7.0)如下的log行为:

 hook success
 method invoke setWifiEnabled,args:[false]
 
getNetworkOperator:
getNetworkOperatorName:
getSimCountryIso:
getSimOperator:
getSimOperatorName:
getNetworkType:0

在有sim卡(8.0)的状态下:

 E/AAA: hook success
 E/AAA: method invoke setWifiEnabled,args:[com.xxx, false]
 I/xxxxxxxx: CCC----getNetworkOperator:46001
 I/xxxxxxxx: CCC----getNetworkOperatorName:CHN-UNICOM
 I/xxxxxxxx: CCC----getSimCountryIso:cn
 I/xxxxxxxx: CCC----getSimOperator:46006
 I/xxxxxxxx: CCC----getSimOperatorName:
 I/xxxxxxxx: CCC----getNetworkType:13

试了2个不同版本发现入参有区别的,所以我们需要去适配不同版本的手机

打开androidxref.com

查了下看,在8.0以上增加了当前包名的这个入参,在低版本以下没有这个问题,所以我们可以安心的去hook,再判断有sim卡的情况下,才允许给xxxSDK进去hook,我们通过

StackTraceElement stack[] = Thread.currentThread().getStackTrace();  

获取调用栈信息,根据xxxSDK的包名进去拦截,实际情况在说把

最终判断

            if ("setWifiEnabled".equals(method.getName())) {
                try {
                    boolean arg = true;
                    String networkOperator = SystemUtils.getNetworkOperator(context);
                    String simCountryIso = SystemUtils.getSimCountryIso(context);
                    String simOperator = SystemUtils.getSimOperator(context);
                    if (StringUtils.isEmpty(simCountryIso) ||
                            StringUtils.isEmpty(networkOperator) ||
                            StringUtils.isEmpty(simOperator)) {
                        arg = true;
                    } else {
                        //如果这3个地方有值才允许,不管true或者false
                        arg = (boolean) args[args.length - 1];
                    }
                    args[args.length - 1] = arg;
                    return method.invoke(real, args);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

最后:

判断手机是否有sim的代码,其实没必要危险权限来判断的,我使用的代码如下:

public static String getSimCountryIso(Context context) {
        String sn = "";
        try {
            sn = getTelephonyManager(context).getSimCountryIso();
        } catch (Exception e) {
            LogUtils.e("getSimCountryIso", e);
        }
        LogUtils.ii("getSimCountryIso:" + sn);
        return sn;
    }

    public static String getNetworkOperatorName(Context context) {
        String sn = "";
        try {
            sn = getTelephonyManager(context).getNetworkOperatorName();
        } catch (Exception e) {
            LogUtils.e("getNetworkOperatorName", e);
        }
        LogUtils.ii("getNetworkOperatorName:" + sn);
        return sn;
    }

    /**
     * https://blog.csdn.net/android_ls/article/details/8672442
     * @param context
     * @return
     */
    public static String getNetworkOperator(Context context) {
        String sn = "";
        try {
            sn = getTelephonyManager(context).getNetworkOperator();
        } catch (Exception e) {
            LogUtils.e("getNetworkOperator", e);
        }
        LogUtils.ii("getNetworkOperator:" + sn);
        return sn;
    }

    public static String getSimOperator(Context context) {
        String sn = "";
        try {
            sn = getTelephonyManager(context).getSimOperator();
        } catch (Exception e) {
            LogUtils.e("getSimOperator", e);
        }
        LogUtils.ii("getSimOperator:" + sn);
        return sn;
    }

    public static String getSimOperatorName(Context context) {
        String sn = "";
        try {
            sn = getTelephonyManager(context).getSimOperatorName();
        } catch (Exception e) {
            LogUtils.e("getSimOperatorName", e);
        }
        LogUtils.ii("getSimOperatorName:" + sn);
        return sn;
    }

    public static String getNetworkType(Context context) {
        int sn = 0;
        try {
            sn = getTelephonyManager(context).getNetworkType();
        } catch (Exception e) {
            LogUtils.e("getNetworkType", e);
        }
        LogUtils.ii("getNetworkType:" + sn);
        return sn + "";
    }

参考的博客: blog.csdn.net/lotty_wh/ar…