package com.example.unshell;
import
android.app.
*
;
/
/
导入 android.app 包中的所有类
import
android.content.
*
;
/
/
导入 android.content 包中的所有类
import
android.os.Build;
import
android.util.
*
;
/
/
导入 android.util 包中的所有类
import
java.io.
*
;
/
/
导入 java.io 包中的所有类
import
java.lang.ref.WeakReference;
import
java.lang.reflect.Field;
import
java.lang.reflect.Method;
import
java.nio.
*
;
/
/
导入 java.nio 包中的所有类
import
java.util.
*
;
import
java.util.
zip
.
*
;
/
/
导入 java.util.
zip
包中的所有类
import
dalvik.system.DexClassLoader;
public
class
ProxyApplication extends Application {
private String srcApkPath;
private String optDirPath;
private String libDirPath;
private ZipFile getApkZip() throws IOException {
Log.i(
"demo"
, this.getApplicationInfo().sourceDir);
ZipFile apkZipFile
=
new ZipFile(this.getApplicationInfo().sourceDir);
return
apkZipFile;
}
private byte[] readDexFileFromApk() throws IOException {
/
*
从本体apk中获取dex文件
*
/
ZipFile apkZip
=
this.getApkZip();
ZipEntry zipEntry
=
apkZip.getEntry(
"classes.dex"
);
InputStream inputStream
=
apkZip.getInputStream(zipEntry);
byte[]
buffer
=
new byte[
1024
];
ByteArrayOutputStream baos
=
new ByteArrayOutputStream();
int
length;
while
((length
=
inputStream.read(
buffer
)) >
0
) {
baos.write(
buffer
,
0
, length);
}
return
baos.toByteArray();
}
private byte[] reverseProcessApkData(byte[] data) {
/
/
解密函数
for
(
int
i
=
0
; i < data.length; i
+
+
) {
data[i]
=
data[i];
}
return
data;
}
private byte[] splitSrcApkFromDex(byte[] dexFileData) {
/
*
从dex文件中分离源apk文件
*
/
int
length
=
dexFileData.length;
ByteBuffer bb
=
ByteBuffer.wrap(Arrays.copyOfRange(dexFileData, length
-
8
, length));
bb.order(java.nio.ByteOrder.LITTLE_ENDIAN);
/
/
设置为小端模式
long
processedSrcApkDataSize
=
bb.getLong();
/
/
读取这
8
个字节作为
long
类型的值
byte[] processedSrcApkData
=
Arrays.copyOfRange(dexFileData, (
int
) (length
-
8
-
processedSrcApkDataSize), length
-
8
);
byte[] srcApkData
=
reverseProcessApkData(processedSrcApkData);
return
srcApkData;
}
public static void replaceClassLoader1(Context context, DexClassLoader dexClassLoader) {
ClassLoader pathClassLoader
=
ProxyApplication.
class
.getClassLoader();
try
{
/
/
1.
通过currentActivityThread方法获取ActivityThread实例
Class ActivityThread
=
pathClassLoader.loadClass(
"android.app.ActivityThread"
);
Method currentActivityThread
=
ActivityThread.getDeclaredMethod(
"currentActivityThread"
);
Object
activityThreadObj
=
currentActivityThread.invoke(null);
/
/
2.
拿到mPackagesObj
Field mPackagesField
=
ActivityThread.getDeclaredField(
"mPackages"
);
mPackagesField.setAccessible(true);
ArrayMap mPackagesObj
=
(ArrayMap) mPackagesField.get(activityThreadObj);
/
/
3.
拿到LoadedApk
String packageName
=
context.getPackageName();
WeakReference wr
=
(WeakReference) mPackagesObj.get(packageName);
Object
LoadApkObj
=
wr.get();
/
/
4.
拿到mClassLoader
Class LoadedApkClass
=
pathClassLoader.loadClass(
"android.app.LoadedApk"
);
Field mClassLoaderField
=
LoadedApkClass.getDeclaredField(
"mClassLoader"
);
mClassLoaderField.setAccessible(true);
Object
mClassLoader
=
mClassLoaderField.get(LoadApkObj);
Log.i(
"mClassLoader"
, mClassLoader.toString());
/
/
5.
将系统组件ClassLoader给替换
mClassLoaderField.
set
(LoadApkObj, dexClassLoader);
} catch (Exception e) {
Log.i(
"demo"
,
"error:"
+
Log.getStackTraceString(e));
e.printStackTrace();
}
}
private void extractSoFiles(
File
srcApkFile,
File
libDir) throws IOException {
/
/
获取当前设备的架构
String abi
=
Build.CPU_ABI;
/
/
或者使用 Build.SUPPORTED_ABIS 来获取更全面的信息
/
/
提取 APK 中的 lib 文件夹并复制到 libDir 目录中
ZipFile apkZip
=
new ZipFile(srcApkFile);
ZipEntry libDirEntry
=
null;
Enumeration extends ZipEntry> entries
=
apkZip.entries();
while
(entries.hasMoreElements()) {
ZipEntry entry
=
entries.nextElement();
/
/
找到 lib 文件夹并提取里面的 .so 文件
if
(entry.getName().startsWith(
"lib/"
) && entry.getName().endsWith(
".so"
)) {
/
/
检查 .so 文件是否属于当前设备架构
if
(entry.getName().contains(
"lib/"
+
abi
+
"/"
)) {
File
destSoFile
=
new
File
(libDir, entry.getName().substring(entry.getName().lastIndexOf(
"/"
)
+
1
));
/
/
创建目标文件夹
if
(!destSoFile.getParentFile().exists()) {
destSoFile.getParentFile().mkdirs();
}
/
/
复制 .so 文件
try
(InputStream inputStream
=
apkZip.getInputStream(entry);
FileOutputStream fos
=
new FileOutputStream(destSoFile)) {
byte[]
buffer
=
new byte[
1024
];
int
length;
while
((length
=
inputStream.read(
buffer
)) >
0
) {
fos.write(
buffer
,
0
, length);
}
}
}
}
}
}
@Override
protected void attachBaseContext(Context base) {
Log.i(
"demo"
,
"attachBaseContext"
);
super
.attachBaseContext(base);
try
{
byte[] dexFileData
=
this.readDexFileFromApk();
byte[] srcApkData
=
this.splitSrcApkFromDex(dexFileData);
/
/
创建储存apk的文件夹,写入src.apk
File
apkDir
=
base.getDir(
"apk_out"
, MODE_PRIVATE);
srcApkPath
=
apkDir.getAbsolutePath()
+
"/src.apk"
;
File
srcApkFile
=
new
File
(srcApkPath);
srcApkFile.setWritable(true);
try
(FileOutputStream fos
=
new FileOutputStream(srcApkFile)) {
Log.i(
"demo"
, String.
format
(
"%d"
, srcApkData.length));
fos.write(srcApkData);
}
srcApkFile.setReadOnly();
/
/
受安卓安全策略影响,dex必须为只读
Log.i(
"demo"
,
"Write src.apk into "
+
srcApkPath);
/
/
新建加载器
File
optDir
=
base.getDir(
"opt_dex"
, MODE_PRIVATE);
File
libDir
=
base.getDir(
"lib_dex"
, MODE_PRIVATE);
optDirPath
=
optDir.getAbsolutePath();
libDirPath
=
libDir.getAbsolutePath();
/
/
提取 .so 文件到 lib_dex 目录
extractSoFiles(srcApkFile, libDir);
ClassLoader pathClassLoader
=
ProxyApplication.
class
.getClassLoader();
DexClassLoader dexClassLoader
=
new DexClassLoader(srcApkPath, optDirPath, libDirPath, pathClassLoader);
Log.i(
"demo"
,
"Successfully initiate DexClassLoader."
);
/
/
修正加载器
replaceClassLoader1(base, dexClassLoader);
Log.i(
"demo"
,
"ClassLoader replaced."
);
} catch (Exception e) {
Log.i(
"demo"
,
"error:"
+
Log.getStackTraceString(e));
e.printStackTrace();
}
}
}