• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

Android 控制 ContentProvider的创建

互联网 diligentman 2小时前 2次浏览

序言

随着app隐私政策的收紧,现在不经过用户同意,就收集敏感信息的行为一旦被检测出来。很容易造成app下架。但是有些SDK的初始化是通过注册ContentProvider实现自动调用其onCreate()方法,来实现无感初始化的。如果SDK在ContentProvider中获取了敏感信息,又没有提供控制方法。我们就很被动。于是我花了点时间研究了怎么hook contentProvider的创建。让其在用户同意后再初始化。

方案1

声明在清单文件中的ContentProvider 会在应用启动后就创建。具体是在 ActivityThread的handleBindApplication方法中。(以下截图为Android 30的ActivityThread)
Android 控制 ContentProvider的创建
具体就在这一句
Android 控制 ContentProvider的创建

installContentProviders实现如下
Android 控制 ContentProvider的创建
最终是通过AppComponentFactory的instantiateProvider方法创建。
Android 控制 ContentProvider的创建
Android 控制 ContentProvider的创建

而AppComponentFactory是Android 28以后系统提供给我们的一个hook的工厂类。可以通过清单文件指定,在这里面可以hook 所有组件的初始化。
Android 控制 ContentProvider的创建
这么指定
Android 控制 ContentProvider的创建

但是在Android 28以下,比如这个截图是Android 25.没有这类,ContentProvider直接通过反射获得。无法通过该类来修改。
Android 控制 ContentProvider的创建

最终方案

为了兼容性,考虑如下方案。在调用installContentProviders前,如果这个data里面的providers为空岂不是不会走installContentProviders方法了吗。
Android 控制 ContentProvider的创建
这个data 是一个AppBindData类型,通过handleBindApplication方法的参数传入。会保存到ActivityThread的 mBoundApplication 字段中。
Android 控制 ContentProvider的创建

于是就可以通过获取这个mBoundApplication 字段中的providers 来保存要初始化的provider。再讲providers置为空即可。到了用户同意以后,再去通过反射调用ActivityThread的installContentProviders方法即可。
Android 控制 ContentProvider的创建

hook时机

这个时机只有Application的attachBaseContext方法中。该方法会比installContentProviders提前执行。

最后的代码App中

public class MyApp extends Application {

    static MyApp app;

	/**
	*用户同意
	*/
    public static void agree(Action action) {
        HookUtil.initProvider(app);
        action.doAction();
    }

    public interface Action {
        void doAction();
    }


    @Override
    protected void attachBaseContext(Context base) {
        app = this;
        try {
            HookUtil.attachContext();
        } catch (Exception e) {
            e.printStackTrace();
        }
        super.attachBaseContext(base);
    }
}

HookUtil

package com.zgh.testcontentprovider;

import android.content.Context;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;


/**
 * Created by zhuguohui
 * Date: 2021/9/13
 * Time: 11:23
 * Desc:
 */
public class HookUtil {

    private static Object providers;

    private static Method installContentProvidersMethod;
    private static Object currentActivityThread;

	/*
	*用户同意后调用
	*/
    public static void initProvider(Context context){
        try {
            installContentProvidersMethod.invoke(currentActivityThread,context,providers);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void attachContext() throws Exception {

        // 先获取到当前的ActivityThread对象
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        //currentActivityThread是一个static函数所以可以直接invoke,不需要带实例参数
        currentActivityThread = currentActivityThreadMethod.invoke(null);

        hookInstallContentProvider(activityThreadClass);

    }

    private static void hookInstallContentProvider(Class activityThreadClass) throws Exception{
        Field appDataField = activityThreadClass.getDeclaredField("mBoundApplication");
        appDataField.setAccessible(true);
        Object appData= appDataField.get(currentActivityThread);
        Field providersField= appData.getClass().getDeclaredField("providers");
        providersField.setAccessible(true);
        providers = providersField.get(appData);
        //清空provider,避免有些sdk通过provider来初始化
        providersField.set(appData,null);

        installContentProvidersMethod = activityThreadClass.getDeclaredMethod("installContentProviders", Context.class, List.class);
        installContentProvidersMethod.setAccessible(true);
    }
}

搭配

搭配我之前写的工具,可以更完美的实现用户同意之前不初始化任何SDK的目标
通过拦截 Activity的创建 实现APP的隐私政策改造


程序员灯塔
转载请注明原文链接:Android 控制 ContentProvider的创建
喜欢 (0)