文章目录

  • 一、 ActivityThread 中的 installProvider 方法 ( 创建 ContentProvider 内容提供者 )
  • 二、 installProvider 方法的第三分支分析
  • 三、 ContextImpl 中 createPackageContext 方法分析
  • 四、ContentProvider 中替换 Application 的总结

前置博客 : 【Android 安全】DEX 加密 ( Application 替换 | 分析 ContentProvider 组件中调用 getApplication() 获取的 Application )

一、 ActivityThread 中的 installProvider 方法 ( 创建 ContentProvider 内容提供者 )


在 installProvider 方法中 , 通过 反射创建 ContentProvider ;

// ★ 反射创建 ContentProvider
localProvider = (ContentProvider)cl.loadClass(info.name).newInstance();

在 创建 ContentProvider 之后 , 调用了 attachInfo 函数 , 注意此处与 Activity , Service , BrocastReceiver 不同 , 这三个组件创建后调用的是 attach 函数 ;

// XXX Need to create the correct context for this provider.
// ★ 创建 ContentProvider 之后 , 调用了 attachInfo 函数
// 注意此处与 Activity , Service , BrocastReceiver 不同 ,
// 这三个组件创建后调用的是 attach 函数
localProvider.attachInfo(c, info);

这里分析 attachInfo 中的 c 参数 , 也就是 Context 上下文的获取过程 :

声明空的 Context c 对象 ,

// ★ 该上下文对象很重要
Context c = null;

获取 ApplicationInfo 信息 ApplicationInfo ai , 即 AndroidManifest.xml 中配置的 application 节点信息

// 该 ApplicationInfo 是 AndroidManifest.xml 中配置的 application 节点信息
ApplicationInfo ai = info.applicationInfo;

进行如下三个分支的判定 :

  • 分支一 : if (context.getPackageName().equals(ai.packageName)) : 在应用中配置的代理 Application 包名与真实 Application 包名都是相等的 ;
  • 分之二 : else if (mInitialApplication != null && mInitialApplication.getPackageName().equals(ai.packageName)) : 与分支一类似 , 也是要求包名相等 ;
  • 分支三 : 上面两个分支没有命中 , 就执行第三个分支 ;
         // ★ 该上下文对象很重要 Context c = null;// 该 ApplicationInfo 是 AndroidManifest.xml 中配置的 application 节点信息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}}

上面的分支一 与 分支二 都是将 代理 Application 分支 , 因此必须要命中第三个分支 ;

如果将 代理 Application 的 getPackageName() 获取包名的方法返回空 , 此时肯定无法命中前两个分支 , 只能命中第三分支 ;

相关代码示例 :

public final class ActivityThread {/*** Installs the provider.** Providers that are local to the process or that come from the system server* may be installed permanently which is indicated by setting noReleaseNeeded to true.* Other remote providers are reference counted.  The initial reference count* for all reference counted providers is one.  Providers that are not reference* counted do not have a reference count (at all).** This method detects when a provider has already been installed.  When this happens,* it increments the reference count of the existing provider (if appropriate)* and returns the existing provider.  This can happen due to concurrent* attempts to acquire the same provider.*/private ContentProviderHolder installProvider(Context context,ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {// ★ 声明 ContentProvider ContentProvider localProvider = null;IContentProvider provider;if (holder == null || holder.provider == null) {if (DEBUG_PROVIDER || noisy) {Slog.d(TAG, "Loading provider " + info.authority + ": "+ info.name);}// 该上下文对象很重要 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}}if (c == null) {Slog.w(TAG, "Unable to get context for package " +ai.packageName +" while loading content provider " +info.name);return null;}try {final java.lang.ClassLoader cl = c.getClassLoader();// ★ 反射创建 ContentProvider localProvider = (ContentProvider)cl.loadClass(info.name).newInstance();provider = localProvider.getIContentProvider();if (provider == null) {Slog.e(TAG, "Failed to instantiate class " +info.name + " from sourceDir " +info.applicationInfo.sourceDir);return null;}if (DEBUG_PROVIDER) Slog.v(TAG, "Instantiating local provider " + info.name);// XXX Need to create the correct context for this provider.// ★ 创建 ContentProvider 之后 , 调用了 attachInfo 函数 // 注意此处与 Activity , Service , BrocastReceiver 不同 , // 这三个组件创建后调用的是 attach 函数localProvider.attachInfo(c, info);} catch (java.lang.Exception e) {return null;}} else {}return retHolder;}}

参考路径 : frameworks/base/core/java/android/app/ActivityThread.java

二、 installProvider 方法的第三分支分析


下面代码中的三个分支就是给 ContentProvider 组件设置 Application 上下文的代码 ;

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

在上面的分析中 , 如果要使得分支一 context.getPackageName().equals(ai.packageName) 与分支二 mInitialApplication.getPackageName().equals(ai.packageName) , 都无法命中 , 就需要 Application 的 getPackageName 方法获取的包名不等于在 AndroidManifest.xml 中的包名 ai.packageName , 这里重写 ProxyApplication 的 getPackageName 方法 , 使该方法返回值为 “” 字符串 , 这样就无法命中前两个分支 , 只能进入 else 分支 ;

三、 ContextImpl 中 createPackageContext 方法分析


继续分析 分支三 中的内容 , 如果 Application 的 getPackageName 方法返回 “” , 将导致分支一二都没有命中 , 进入分支三 ;

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)) {} else if (mInitialApplication != null &&mInitialApplication.getPackageName().equals(ai.packageName)) {} 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

分支三中调用了 , 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 方法创建上下文@Overridepublic Context createPackageContext(String packageName, int flags)throws NameNotFoundException {return createPackageContextAsUser(packageName, flags,mUser != null ? mUser : Process.myUserHandle());}@Overridepublic 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 ;

四、ContentProvider 中替换 Application 的总结


ContentProvider 中替换 Application 的总结 :

  • ① 分支选择 : 首先要命中 ActivityThread 中 installProvider 方法的分支三 ;
  • ② Application 替换 : 然后要在 ContextImpl 的 createPackageContext 方法执行前进行一次 Application 替换 ;

【Android 安全】DEX 加密 ( Application 替换 | 分析 ContentProvider 组件中调用 getApplication() 获取的 Application 二 )相关推荐

  1. 【Android 安全】DEX 加密 ( Application 替换 | 分析 ContentProvider 组件中调用 getApplication() 获取的 Application )

    文章目录 一. ContentProvider 创建过程分析 二. ActivityThread 中的 H 处理 BIND_APPLICATION 消息 三. ActivityThread 中的 ha ...

  2. 【Android 安全】DEX 加密 ( Application 替换 | 分析 Service 组件中调用 getApplication() 获取的 Application 是否替换成功 )

    文章目录 一. Service 中的 getApplication() 方法分析 二. ActivityThread 中的 H 处理 CREATE_SERVICE 消息 三. ActivityThre ...

  3. 【Android 安全】DEX 加密 ( Application 替换 | 分析 BroadcastReceiver 组件中调用 getApplication() 获取的 Application )

    文章目录 一. Service 中的 getApplication() 方法分析 二. ActivityThread 中的 H 处理 RECEIVER 消息 三. ActivityThread 中的 ...

  4. 【Android 安全】DEX 加密 ( Application 替换 | 分析 Activity 组件中获取的 Application | ActivityThread | LoadedApk )

    文章目录 一. Activity 中的 getApplication() 方法分析 二. ActivityThread 中的 H 处理 消息及 handleLaunchActivity 方法操作 三. ...

  5. 【Android 安全】DEX 加密 ( Application 替换 | 兼容 ContentProvider 操作 | 源码资源 )

    文章目录 一. 命中 ActivityThread 中 installProvider 方法的分支三 1. 原理分析 2. 代码实现 二. 在 ContextImpl 的 createPackageC ...

  6. Delphi XE2中调用DLL窗体传递Application句柄

    传统调用DLL窗体,为了达到DLL窗体与主程序融为一体的效果,通常会把主程序的Application传递到DLL工程中,类似如下方法: procedure SynAPP(App: THandle); ...

  7. Android中调用ANE获取设备ID

    相应工具或SDK http:// 1.Flex Builder 4.6 2.Android SDK 3.JDK 一.创建Android扩展 运行FLEX BUILDER 选择文件新建->其它-& ...

  8. 【Android 应用开发】 Application 使用分析

    博客地址 : http://blog.csdn.net/shulianghan/article/details/40737419 代码下载 : Android 应用 Application 经典用法; ...

  9. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

最新文章

  1. 一个包从服务器到达客户端
  2. Leetcode 682. 棒球比赛 解题思路及C++实现
  3. ASP.NET Core API 版本控制
  4. mysql人事管理系统源代码_人事管理系统(源代码.doc
  5. 每日源码分析 - Lodash(remove.js)
  6. iOS11 获取手机已安装应用列表
  7. 移形换影 - 短视频色彩特效背后的故事
  8. fiddler4安装及使用手机抓包
  9. 用 Python 自动定时发微博
  10. 纯js实现数据导出excel表格
  11. Java POJO 自动生成器
  12. Eclipse中导入项目前有红叉提示但是项目内容不报错解决办法
  13. A股的日内交易如何进行?
  14. 教资篇(1)—高中信息技术科目
  15. HJL-E6/A DC220V数字式【电流继电器】
  16. uva live 4043 km
  17. 计算机的硬盘维修,计算机硬盘的维修方法和技巧
  18. 发现一个有趣的漫画网站
  19. 三极管工作原理--我见过最通俗讲法
  20. OSChina 周三乱弹 —— 生活终于对我这只小猫咪动手啦

热门文章

  1. js weibo api
  2. Android adb opendir failed ,permission denied
  3. 【005】◀▶ C#学习笔记(四)(集合)
  4. 在ubuntu10.4安装snort ACID
  5. Ubuntu里解压tar.xz格式
  6. Xming+putty操作篇
  7. Esfog_UnityShader教程_漫反射DiffuseReflection
  8. ZOJ Problem Set - 1048 Financial Management
  9. 即时通讯软件设计(一)
  10. Ubuntu下查看命令的源码