文章目录
一、 命中 ActivityThread 中 installProvider 方法的分支三
1、 原理分析
2、 代码实现
二、 在 ContextImpl 的 createPackageContext 方法执行前进行 Application 替换
1、 原理分析
2、 代码实现
三、 完整代码示例
四、日志分析
五、源码资源
前两篇分析 ContentProvider 中的 Application 的博客 :
【Android 安全】DEX 加密 ( Application 替换 | 分析 ContentProvider 组件中调用 getApplication() 获取的 Application )
【Android 安全】DEX 加密 ( Application 替换 | 分析 ContentProvider 组件中调用 getApplication() 获取的 Application 二 )
ContentProvider 中替换 Application 的总结 :
① 分支选择 : 首先要命中 ActivityThread 中 installProvider 方法的分支三 ;
② Application 替换 : 然后要在 ContextImpl 的 createPackageContext 方法执行前进行一次 Application 替换 ;
一、 命中 ActivityThread 中 installProvider 方法的分支三
1、 原理分析
ActivityThread 中的 installProvider 方法中的三个分支如下 , 在上面的分析中 , 如果要使得分支一 context.getPackageName().equals(ai.packageName) 与分支二 mInitialApplication.getPackageName().equals(ai.packageName) , 都无法命中 , 就需要 Application 的 getPackageName 方法获取的包名不等于在 AndroidManifest.xml 中的包名 ai.packageName , 这里重写 ProxyApplication 的 getPackageName 方法 , 使该方法返回值为 “” 字符串 , 这样就无法命中前两个分支 , 只能进入 else 分支 ;
public final class ActivityThread { private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { // 该上下文对象很重要 Context c = null; ApplicationInfo ai = info.applicationInfo; // 该 context 是 ProxyApplication , 代理 Application if (context.getPackageName().equals(ai.packageName)) { // 在应用中配置的代理 Application 包名与真实 Application 包名都是相等的 // 该分支是命中的 c = context; } else if (mInitialApplication != null && mInitialApplication.getPackageName().equals(ai.packageName)) { // 该分支中 mInitialApplication 就是 Context context 参数 , 肯定不为空 // 该分支无法命中 c = mInitialApplication; } else { // 上述两个分支都无法命中 , 才进入该分支 // 需要将代理 Application 的包名 与 真实应用的包名设置成不同的 // 此时上面两个分支都无法命中 try { c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } catch (PackageManager.NameNotFoundException e) { // Ignore } } return retHolder; } }
参考路径 : frameworks/base/core/java/android/app/ActivityThread.java
2、 代码实现
代码示例 : 暂时省略其余代码 ;
public class ProxyApplication extends Application { @Override public String getPackageName() { if(TextUtils.isEmpty(app_name)){ // 如果 AndroidManifest.xml 中配置的 Application 全类名为空 // 那么 不做任何操作 }else{ // 如果 AndroidManifest.xml 中配置的 Application 全类名不为空 // 为了使 ActivityThread 的 installProvider 方法 // 无法命中如下两个分支 // 分支一 : context.getPackageName().equals(ai.packageName) // 分支二 : mInitialApplication.getPackageName().equals(ai.packageName) // 设置该方法返回值为空 , 上述两个分支就无法命中 return ""; } return super.getPackageName(); } }
二、 在 ContextImpl 的 createPackageContext 方法执行前进行 Application 替换
1、 原理分析
分支三中调用了 , context 的 createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE) 方法 , 该方法在 ContextImpl 中定义 ;
在 ContextImpl 中的 createPackageContext 方法 , 调用了 createPackageContextAsUser 方法 , 调用了如下代码 , 创建 Context 上下文 ,
ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user, flags, null);
上述代码中创建 ContextImpl 时 , 使用的 mMainThread , pi , 都没有替换过 Application , 因此分支三创建的 ContentProvider 对应的 Application 也是代理 Application , 替换前的 Application 对象 ;
class ContextImpl extends Context { // 在该方法中调用了 createPackageContextAsUser 方法创建上下文 @Override public Context createPackageContext(String packageName, int flags) throws NameNotFoundException { return createPackageContextAsUser(packageName, flags, mUser != null ? mUser : Process.myUserHandle()); } @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws NameNotFoundException { if (packageName.equals("system") || packageName.equals("android")) { // The system resources are loaded in every application, so we can safely copy // the context without reloading Resources. return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user, flags, null); } // 注意该 LoadedApk 对象 LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { // 创建新的 ContextImpl // 此时还没有替换 Application ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user, flags, null); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; c.setResources(createResources(mActivityToken, pi, null, displayId, null, getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; } } // Should be a better exception. throw new PackageManager.NameNotFoundException( "Application package " + packageName + " not found"); } }
源码路径 : frameworks/base/core/java/android/app/ContextImpl.java
ContextImpl 中的 public Context createPackageContext(String packageName, int flags) 方法是公开方法 , 重写该方法 , 在重写的 createPackageContext 方法中 , 先进行一次 Application 替换 , 然后继续执行 super.createPackageContext 方法的后续操作 , 这样创建的 ContentProvider 中的上下文就是用户自定义的 MyApplication , 不再是 ProxyApplication ;
只有在创建 ContentProvider 时才调用到该 createPackageContext 方法 , 如果没有调用到该方法 , 说明该应用中没有配置 ContentProvider ;
2、 代码实现
代码实现 : 在 代理 Application 的中重写 public Context createPackageContext(String packageName, int flags) 方法 , 先替换 Application , 然后再继续向后执行 ;
这里建议 Application 替换操作 , 只执行一次 , 使用 Application delegate 是否为空 , 作为替换操作是否执行的标志 ;
public class ProxyApplication extends Application { @Override public void onCreate() { super.onCreate(); // 如果之前没有替换过 , 执行 Application 替换操作 // 说明没有调用到 createPackageContext 方法 // 该 createPackageContext 方法只有在创建 ContentProvider 时才调用到 // 如果没有调用到 , 说明 AndroidManifest.xml 中没有配置 ContentProvider // 此时需要在此处进行 Application 替换 if (delegate == null){ applicationExchange(); } } @Override public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException { if(TextUtils.isEmpty(app_name)){ // 如果 AndroidManifest.xml 中配置的 Application 全类名为空 // 说明没有进行 dex 加密操作 , 返回父类方法执行即可 return super.createPackageContext(packageName, flags); }else{ // 只有在创建 ContentProvider 时才调用到该 createPackageContext 方法 , // 如果没有调用到该方法 , 说明该应用中没有配置 ContentProvider ; // 该方法不一定会调用到 // 先进行 Application 替换 applicationExchange(); // Application 替换完成之后 , 再继续向下执行创建 ContentProvider return delegate; } } /** * 调用 applicationExchange 替换 Application * 该成员就是替换后的 Application */ private Application delegate; /** * Application 替换主方法 */ private void applicationExchange(){ } }