文章目录
- 一、multiple-dex-core 依赖库作用
- 二、配置目录元数据
- 三、multiple-dex-core 代理 Application
- 四、获取 apk 文件并准备相关目录
- 五、相关代码
参考博客 :
- 【Android 安全】DEX 加密 ( 常用 Android 反编译工具 | apktool | dex2jar | enjarify | jd-gui | jadx )
在 【Android 安全】DEX 加密 ( 支持多 DEX 的 Android 工程结构 ) 博客中介绍了 DEX 加密工程的基本结构 ,
app 是主应用 , 其 Module 类型是 “Phone & Tablet Module” ,
multiple-dex-core 是 Android 依赖库 , 其作用是解密并加载多 DEX 文件 , 其 Module 类型是 “Android Library” ,
multiple-dex-tools 是 Java 依赖库 , 其类型是 “Java or Kotlin Library” , 其作用是用于生成主 DEX ( 主 DEX 的作用就是用于解密与加载多 DEX ) , 并且还要为修改后的 APK 进行签名 ;
本博客中开始讲解 multiple-dex-core 依赖库开发 ;
一、multiple-dex-core 依赖库作用
在该依赖库中 , 最主要的是开发一个 代理 Application , 其主要作用有两个 :
- 作用一 : 解密并加载多个 DEX 文件 ;
- 作用二 : 将真实的 Application 替换成应用的主 Application ;
二、配置目录元数据
此处配置的元数据是开发者配置的 , 解压 dex 文件的 目的文件夹路径 ;
该路径在后面开发时会用到 ;
在 app 的 AndroidManifest.xml 的 application 标签中配置如下元数据 ,
代码语言:javascript复制 <!-- app_name 值是该应用的 Application 的真实全类名 -->
<meta-data android:name="app_name" android:value="kim.hsl.multipledex.ProxyApplication"/>
<!-- DEX 解密之后的目录名称 -->
<meta-data android:name="app_version" android:value="1.0"/>
配置后完整的配置文件内容如下 :
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="kim.hsl.dex">
<application
android:name="kim.hsl.multipledex.ProxyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!-- app_name 值是该应用的 Application 的真实全类名
真实 Application : kim.hsl.dex.MyApplication
代理 Application : kim.hsl.multipledex.ProxyApplication -->
<meta-data android:name="app_name" android:value="kim.hsl.dex.MyApplication"/>
<!-- DEX 解密之后的目录名称版本号 , 完整目录名称为 :
kim.hsl.dex.MyApplication_1.0 -->
<meta-data android:name="app_version" android:value="1.0"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
三、multiple-dex-core 代理 Application
在 multiple-dex-core 中 , 主要是开发其 代理 Application ,
在 Application 在 ActivityThread 中被创建之后 , 第一个调用的方法是 attachBaseContext 函数 ,
该函数是 Application 中最先执行的函数 ;
因此在 代理 Application 中 , 主要代码逻辑都是在 attachBaseContext 函数中开发的 ;
代码语言:javascript复制public class ProxyApplication extends Application {
/**
* 在 Application 在 ActivityThread 中被创建之后,
* 第一个调用的方法是 attachBaseContext 函数.
* 该函数是 Application 中最先执行的函数.
*/
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
}
四、获取 apk 文件并准备相关目录
解密与加载多 dex 文件 , 首先要 解密 dex 文件 , 然后 加载解密后的 dex 文件 ;
加密的 dex 文件都在 apk 文件中 , 首先将 apk 文件解压 , 放到一个指定的目录中 ;
获取 apk 文件 : 获取当前的 APK 文件, 下面的 getApplicationInfo().sourceDir 就是本应用 APK 安装文件的全路径 ;
代码语言:javascript复制 // 获取当前的 APK 文件, 下面的 getApplicationInfo().sourceDir 就是本应用 APK 安装文件的全路径
File apkFile = new File(getApplicationInfo().sourceDir);
获取在 app Module 下的 AndroidManifest.xml 中配置的 元数据 ,
- ① 应用真实的 Application 全类名
- ② 解密后的 dex 文件存放目录
首先获取应用信息 ApplicationInfo ,
然后获取应用信息中的元数据 , 该元数据是 Bundle 类型的 ,
最后从 Bundle 元数据中获取 app_name 和 app_version 对应的值 , 如果有 , 取出并存入成员变量中 ;
代码语言:javascript复制 // 获取在 app Module 下的 AndroidManifest.xml 中配置的元数据,
// 应用真实的 Application 全类名
// 解密后的 dex 文件存放目录
ApplicationInfo applicationInfo = null;
applicationInfo = getPackageManager().getApplicationInfo(
getPackageName(),
PackageManager.GET_META_DATA
);
Bundle metaData = applicationInfo.metaData;
if (metaData != null) {
// 检查是否存在 app_name 元数据
if (metaData.containsKey("app_name")) {
app_name = metaData.getString("app_name").toString();
}
// 检查是否存在 app_version 元数据
if (metaData.containsKey("app_version")) {
app_version = metaData.getString("app_version").toString();
}
}
创建相关的目录 :
创建用户的私有目录 : 将 apk 文件解压到该目录中 privateDir 路径是 : /data/data/kim.hsl.multipledex/kim.hsl.multipledex.ProxyApplication_1.0
其中 /data/data/kim.hsl.multipledex/ 是返回的格式目录 ,
kim.hsl.multipledex.ProxyApplication_1.0 是从元数据中获取的目录 ;
代码语言:javascript复制 // 创建用户的私有目录 , 将 apk 文件解压到该目录中
File privateDir = getDir(app_name "_" app_version, MODE_PRIVATE);
创建解压后的 apk 文件目录 : 上述目录下创建 app 目录 , 创建该目录的目的是 存放 解压后的 apk 文件的 ;
代码语言:javascript复制 // 在上述目录下创建 app 目录
// 创建该目录的目的是存放解压后的 apk 文件的
File appDir = new File(privateDir, "app");
创建 dex 文件目录 : app 中存放的是解压后的所有的 apk 文件 , app 下创建 dexDir 目录 , 将所有的 dex 目录移动到该 deDir 目录中 , dexDir 目录存放应用的所有 dex 文件 , 这些 dex 文件都需要进行解密 ;
代码语言:javascript复制 // app 中存放的是解压后的所有的 apk 文件
// app 下创建 dexDir 目录 , 将所有的 dex 目录移动到该 dexDir 目录中
// dexDir 目录存放应用的所有 dex 文件
// 这些 dex 文件都需要进行解密
File dexDir = new File(appDir, "dexDir");
代码语言:javascript复制 // 创建用户的私有目录 , 将 apk 文件解压到该目录中
File privateDir = getDir(app_name "_" app_version, MODE_PRIVATE);
Log.i(TAG, "attachBaseContext 创建用户的私有目录 : " privateDir.getAbsolutePath());
// 在上述目录下创建 app 目录
// 创建该目录的目的是存放解压后的 apk 文件的
File appDir = new File(privateDir, "app");
// app 中存放的是解压后的所有的 apk 文件
// app 下创建 dexDir 目录 , 将所有的 dex 目录移动到该 dexDir 目录中
// dexDir 目录存放应用的所有 dex 文件
// 这些 dex 文件都需要进行解密
File dexDir = new File(appDir, "dexDir");
五、相关代码
完整相关代码 :
代码语言:javascript复制public class ProxyApplication extends Application {
public static final String TAG = "ProxyApplication";
/**
* 应用真实的 Application 全类名
*/
String app_name;
/**
* DEX 解密之后的目录名称
*/
String app_version;
/**
* 在 Application 在 ActivityThread 中被创建之后,
* 第一个调用的方法是 attachBaseContext 函数.
* 该函数是 Application 中最先执行的函数.
*/
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
Log.i(TAG, "attachBaseContext");
/*
在该 Application 中主要进行两个操作 :
1 . 解密并加载多个 DEX 文件
2 . 将真实的 Application 替换成应用的主 Application
*/
/*
I . 解密与加载多 DEX 文件
先进行解密, 然后再加载解密之后的 DEX 文件
1. 先获取当前的 APK 文件
2. 然后解压该 APK 文件
*/
// 获取当前的 APK 文件, 下面的 getApplicationInfo().sourceDir 就是本应用 APK 安装文件的全路径
File apkFile = new File(getApplicationInfo().sourceDir);
// 获取在 app Module 下的 AndroidManifest.xml 中配置的元数据,
// 应用真实的 Application 全类名
// 解密后的 dex 文件存放目录
ApplicationInfo applicationInfo = null;
applicationInfo = getPackageManager().getApplicationInfo(
getPackageName(),
PackageManager.GET_META_DATA
);
Bundle metaData = applicationInfo.metaData;
if (metaData != null) {
// 检查是否存在 app_name 元数据
if (metaData.containsKey("app_name")) {
app_name = metaData.getString("app_name").toString();
}
// 检查是否存在 app_version 元数据
if (metaData.containsKey("app_version")) {
app_version = metaData.getString("app_version").toString();
}
}
// 创建用户的私有目录 , 将 apk 文件解压到该目录中
File privateDir = getDir(app_name "_" app_version, MODE_PRIVATE);
Log.i(TAG, "attachBaseContext 创建用户的私有目录 : " privateDir.getAbsolutePath());
// 在上述目录下创建 app 目录
// 创建该目录的目的是存放解压后的 apk 文件的
File appDir = new File(privateDir, "app");
// app 中存放的是解压后的所有的 apk 文件
// app 下创建 dexDir 目录 , 将所有的 dex 目录移动到该 dexDir 目录中
// dexDir 目录存放应用的所有 dex 文件
// 这些 dex 文件都需要进行解密
File dexDir = new File(appDir, "dexDir");
}
}