序言
隨著app隱私政策的收緊,現(xiàn)在不經(jīng)過用戶同意,就收集敏感信息的行為一旦被檢測出來。很容易造成app下架。但是有些SDK的初始化是通過注冊ContentProvider實現(xiàn)自動調(diào)用其onCreate()方法,來實現(xiàn)無感初始化的。如果SDK在ContentProvider中獲取了敏感信息,又沒有提供控制方法。我們就很被動。于是我花了點時間研究了怎么hook contentProvider的創(chuàng)建。讓其在用戶同意后再初始化。
方案1
聲明在清單文件中的ContentProvider 會在應用啟動后就創(chuàng)建。具體是在 ActivityThread的handleBindApplication方法中。<font color='red'>(以下截圖為Android 30的ActivityThread)</font>
具體就在這一句
installContentProviders實現(xiàn)如下
最終是通過AppComponentFactory的instantiateProvider方法創(chuàng)建。
而AppComponentFactory是Android 28以后系統(tǒng)提供給我們的一個hook的工廠類。可以通過清單文件指定,在這里面可以hook 所有組件的初始化。
這么指定
但是在Android 28以下,比如這個截圖是Android 25.沒有這類,ContentProvider直接通過反射獲得。無法通過該類來修改。
最終方案
為了兼容性,考慮如下方案。在調(diào)用installContentProviders前,如果這個data里面的providers為空豈不是不會走installContentProviders方法了嗎。
這個data 是一個AppBindData類型,通過handleBindApplication方法的參數(shù)傳入。會保存到ActivityThread的 mBoundApplication 字段中。
于是就可以通過獲取這個mBoundApplication 字段中的providers 來保存要初始化的provider。再講providers置為空即可。到了用戶同意以后,再去通過反射調(diào)用ActivityThread的installContentProviders方法即可。

hook時機
這個時機只有Application的attachBaseContext方法中。該方法會比installContentProviders提前執(zhí)行。
最后的代碼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;
/*
*用戶同意后調(diào)用
*/
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函數(shù)所以可以直接invoke,不需要帶實例參數(shù)
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);
}
}
搭配
搭配我之前寫的工具,可以更完美的實現(xiàn)用戶同意之前不初始化任何SDK的目標
通過攔截 Activity的創(chuàng)建 實現(xiàn)APP的隱私政策改造