检查 “同意用户隐私” 之前的隐私代码调用(epic > DexposedBridge)

        最近在小米开放平台上线时,驳回报告中说在用户同意之前获取了设备ID。

         一般来说,合规政策推出这么久了,又上线了这么多次,正常业务流程是不会再出现这种问题,就是怕一些第三方库在ContentProvider做初始化,然后调用了TelephonyManager的getDeviceId或getImei方法,这可能会导致未经用户同意就获取设备ID的情况。解决办法首先就是要找到调用这2个方法的代码位置,然后再避免在用户同意之前获取设备ID。

        要找出这样的代码,或者要实现一些类似判断app是否调用了某些方法的情况,靠肉眼和逻辑分析可能会很难受,还需要借助工具来帮我们判断。

        现在我要说的工具就是epic库中的DexposedBridge类,这个是它的中文说明文档:epic/README_cn.md at master · tiann/epic · GitHub 。这个库类在本文论述中的作用就是检测app中是否调用了某些方法。

        下面我们直接上使用步骤。

一、库的引入

1、在工程级的build.gradle中添加:

allprojects { repositories { ... maven { url 'https://jitpack.io' } } }

2、在主模块的build.gradle中添加:

implementation 'com.github.tiann:epic:0.11.2'

implementation 'com.github.tiann:epic:0.11.2@aar'

3、确保主模块的build.gradle中配置的CPU架构仅仅是:

abiFilters  "arm64-v8a"

        为什么只是它?因为上面那个链接里面有说:

4、确保你运行的设备的是 Android 5.0 ~ 11,原因见上图。

二、准备要监听的方法配置表

        在主模块的assets目录新建一个叫privacy.json的文件,文件内容如下:

{
    "methods": [
        {
            "className": "android.telephony.TelephonyManager",
            "method": "getDeviceId"
        },
        {
            "className": "android.telephony.TelephonyManager",
            "method": "getImei"
        }
    ]
}

        这个代码的意思很明显了,就是要监听className这个类当中的method方法。在本例中,就是要检查下app是否调用了TelephonyManager的getDeviceId和getImei方法。

三、实现对方法的监听(专业点叫Hook)

import android.content.Context;
import android.util.Log;

import com.lancoo.homework.util.JsonUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;

public class MyHook {
    public static void initPrivacy(Context context) {
        try {
            InputStream is = context.getAssets().open("privacy.json");
            Privacy privacy = JsonUtil.parseJson(getJson(is), Privacy.class);
            if (privacy == null) {
                Log.e("My", "没发现privacy.json,或者privacy.json里面没正确的东西");
                return;
            }
            ArrayList<PrivacyMethod> methods = privacy.methods;
            for (PrivacyMethod method : methods) {
                hookMethod(method.className, method.method);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void hookMethod(String className, String method) {
        Class<?> clazz;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            Log.e("My", "没发现privacy.json里面的className: " + className);
            return;
        }
        DexposedBridge.findAndHookMethod(clazz, method, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                Log.e("My", "beforeHookedMethod: this:" + param.thisObject, new RuntimeException("stack"));
            }

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
            }
        });
    }

    private static String getJson(InputStream is) {
        StringBuilder sb = new StringBuilder();
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String s;
            while ((s = reader.readLine()) != null) {
                sb.append(s);
                sb.append("\n");
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    public static class Privacy {
        private ArrayList<PrivacyMethod> methods;
    }

    public static class PrivacyMethod {
        String className;
        String method;
    }
}

四、监听调用

        在你的Application的onCreate方法的super.onCreate()下一行代码打上MyHook.initPrivacy(this)。

class App : MultiDexApplication() { 
   override fun onCreate() {
        super.onCreate()
        MyHook.initPrivacy(this)
        // ...
    }
}

        这样你就可以监听到所有调用被监听方法的地方了。

五、查看监听反馈

        我们先看MyHook中的下面这句代码:

Log.e("My", "beforeHookedMethod: this:" + param.thisObject, new RuntimeException("stack"));

        这句代码的意思就是,在app的业务代码中,如果有地方调用 TelephonyManager.getDeviceId 或getImei,都会被epic的beforeHookedMethod拦截到,我们只需要在beforeHookedMethod打印出堆栈,即可看到是谁调用的。打印堆栈也很简单,我们就让他抛出一个异常即可在LogCat中看到。

六、@#¥%……&*

        说个秘密:这个思路我是从App 隐私合规代码排查思路!获得的,只不过我把坑都列出来了,这样一来,小伙子们只要原原本本按照我这个步骤就可以搞定了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值