序言
隨著app隱私政策的收緊,現在不經過用戶同意,就收集敏感資訊的行為一旦被檢測出來,很容易造成app下架,但是有些SDK的初始化是通過注冊ContentProvider實作自動呼叫其onCreate()方法,來實作無感初始化的,如果SDK在ContentProvider中獲取了敏感資訊,又沒有提供控制方法,我們就很被動,于是我花了點時間研究了怎么hook contentProvider的創建,讓其在用戶同意后再初始化,
方案1
宣告在清單檔案中的ContentProvider 會在應用啟動后就創建,具體是在 ActivityThread的handleBindApplication方法中,(以下截圖為Android 30的ActivityThread)

具體就在這一句

installContentProviders實作如下

最終是通過AppComponentFactory的instantiateProvider方法創建,


而AppComponentFactory是Android 28以后系統提供給我們的一個hook的工廠類,可以通過清單檔案指定,在這里面可以hook 所有組件的初始化,

這么指定

但是在Android 28以下,比如這個截圖是Android 25.沒有這類,ContentProvider直接通過反射獲得,無法通過該類來修改,

最終方案
為了兼容性,考慮如下方案,在呼叫installContentProviders前,如果這個data里面的providers為空豈不是不會走installContentProviders方法了嗎,

這個data 是一個AppBindData型別,通過handleBindApplication方法的引數傳入,會保存到ActivityThread的 mBoundApplication 欄位中,

于是就可以通過獲取這個mBoundApplication 欄位中的providers 來保存要初始化的provider,再講providers置為空即可,到了用戶同意以后,再去通過反射呼叫ActivityThread的installContentProviders方法即可,

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的隱私政策改造
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/300012.html
標籤:其他
上一篇:Android程式基礎開發步驟,以簡單的App開發為例
下一篇:敗了,蘋果徹底敗了
