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

简介: 【Android 安全】DEX 加密 ( Application 替换 | 兼容 ContentProvider 操作 | 源码资源 )(一)

文章目录

一、 命中 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(){
  }  
}



目录
相关文章
|
4天前
|
Java Android开发
Android12 双击power键启动相机源码解析
Android12 双击power键启动相机源码解析
13 0
|
1天前
|
Android开发
在android源码中编译ADW_Launcher
在android源码中编译ADW_Launcher
10 2
|
2天前
|
开发工具 Android开发 git
Windows下载android2.2完整源码(转)
Windows下载android2.2完整源码(转)
14 3
|
2天前
|
Java 开发工具 Android开发
如何在Eclipse中查看Android源码或者第三方组件包源码(转)
如何在Eclipse中查看Android源码或者第三方组件包源码(转)
10 4
|
4天前
|
Android开发
Android 高通平台集成无源码apk示例
Android 高通平台集成无源码apk示例
12 0
|
Java 数据安全/隐私保护
Java实现最电话号码的简单加密源码
Java实现最电话号码的简单加密源码
20 0
|
3月前
|
存储 安全 算法
【接口加密】Java中的接口加密实践
【接口加密】Java中的接口加密实践
|
3月前
|
算法 安全 Java
Java 实现 RSA 非对称加密算法-加解密和签名验签
Java 实现 RSA 非对称加密算法-加解密和签名验签
100 0
|
Java 数据安全/隐私保护
java实现加密电话号码,有具体的加密流程注释
java实现加密电话号码,有具体的加密流程注释
31 0
|
4月前
|
存储 算法 安全
3分钟快速了解使用Java进行对称加密
对称加密算法有很多种,其中较为知名的有AES(高级加密标准)、DES(数据加密标准)以及3DES(三重数据加密算法)等。这些算法在不同的场景下有着广泛的应用,为保障数据安全提供了有力支持。
63 0