Add new function: support xposed modules being packed into the apk

This commit is contained in:
Windy 2019-04-24 02:05:34 +08:00
parent 6818dfed05
commit e0dc1a644c
4 changed files with 113 additions and 29 deletions

Binary file not shown.

View File

@ -39,6 +39,10 @@ public class MainCommand extends BaseCommand {
description = "disable craching the apk's signature.") description = "disable craching the apk's signature.")
private boolean disableCrackSignature = false; private boolean disableCrackSignature = false;
@Opt(opt = "xm", longOpt = "xposed-modules", description = "the xposed mpdule files to be packaged into the apk, " +
"multi files should be seperated by :(mac) or ;(win) ")
private String xposedModules;
// 原来apk中dex文件的数量 // 原来apk中dex文件的数量
private int dexFileCount = 0; private int dexFileCount = 0;
@ -151,7 +155,7 @@ public class MainCommand extends BaseCommand {
dexFileCount)); dexFileCount));
// 2. copy xposed so and dex files into the unzipped apk // 2. copy xposed so and dex files into the unzipped apk
mXpatchTasks.add(new SoAndDexCopyTask(dexFileCount, unzipApkFilePath)); mXpatchTasks.add(new SoAndDexCopyTask(dexFileCount, unzipApkFilePath, getXposedModules(xposedModules)));
// 3. compress all files into an apk and then sign it. // 3. compress all files into an apk and then sign it.
mXpatchTasks.add(new BuildAndSignApkTask(keepBuildFiles, unzipApkFilePath, output)); mXpatchTasks.add(new BuildAndSignApkTask(keepBuildFiles, unzipApkFilePath, output));
@ -197,4 +201,11 @@ public class MainCommand extends BaseCommand {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");//设置日期格式 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");//设置日期格式
return df.format(new Date()); return df.format(new Date());
} }
private String[] getXposedModules(String modules) {
if (modules == null || modules.isEmpty()) {
return null;
}
return modules.split(File.pathSeparator);
}
} }

View File

@ -11,6 +11,8 @@ import java.util.HashMap;
public class SoAndDexCopyTask implements Runnable { public class SoAndDexCopyTask implements Runnable {
private static final String SO_FILE_NAME = "libxpatch_wl.so"; private static final String SO_FILE_NAME = "libxpatch_wl.so";
private static final String XPOSED_MODULE_FILE_NAME_PREFIX = "libxpatch_xp_module_";
private static final String SO_FILE_SUFFIX = ".so";
private final String[] APK_LIB_PATH_ARRAY = { private final String[] APK_LIB_PATH_ARRAY = {
"lib/armeabi-v7a/", "lib/armeabi-v7a/",
@ -20,18 +22,20 @@ public class SoAndDexCopyTask implements Runnable {
private final HashMap<String, String> SO_FILE_PATH_MAP = new HashMap<String, String>() { private final HashMap<String, String> SO_FILE_PATH_MAP = new HashMap<String, String>() {
{ {
put(APK_LIB_PATH_ARRAY[0], "assets/" + APK_LIB_PATH_ARRAY[0] + SO_FILE_NAME); put(APK_LIB_PATH_ARRAY[0], "assets/lib/armeabi-v7a/" + SO_FILE_NAME);
put(APK_LIB_PATH_ARRAY[1], "assets/" + APK_LIB_PATH_ARRAY[0] + SO_FILE_NAME); put(APK_LIB_PATH_ARRAY[1], "assets/lib/armeabi-v7a/" + SO_FILE_NAME);
put(APK_LIB_PATH_ARRAY[2], "assets/" + APK_LIB_PATH_ARRAY[2] + SO_FILE_NAME); put(APK_LIB_PATH_ARRAY[2], "assets/lib/arm64-v8a/" + SO_FILE_NAME);
} }
}; };
private int dexFileCount; private int dexFileCount;
private String unzipApkFilePath; private String unzipApkFilePath;
private String[] xposedModuleArray;
public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath) { public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath, String[] xposedModuleArray) {
this.dexFileCount = dexFileCount; this.dexFileCount = dexFileCount;
this.unzipApkFilePath = unzipApkFilePath; this.unzipApkFilePath = unzipApkFilePath;
this.xposedModuleArray = xposedModuleArray;
} }
@Override @Override
@ -45,24 +49,27 @@ public class SoAndDexCopyTask implements Runnable {
} }
private void copySoFile() { private void copySoFile() {
// Try to find so file path in the apk, then copy so into it String apkSoLibPath = findTargetLibPath();
boolean copySuccess = false; String apkSoFullPath = fullLibPath(apkSoLibPath);
for (String libPath : APK_LIB_PATH_ARRAY) {
boolean copied = copyLibFile(unzipApkFilePath + libPath.replace("/", File.separator),
SO_FILE_PATH_MAP.get(libPath), false);
if (copied) {
copySuccess = true;
}
}
// Iif apk do not contain so file path, then create lib/armeabi-v7a, and copy libwhale.so into it copyLibFile(apkSoFullPath, SO_FILE_PATH_MAP.get(apkSoLibPath));
if (!copySuccess) {
String path = APK_LIB_PATH_ARRAY[0]; // copy xposed modules into the lib path
copySuccess = copyLibFile(unzipApkFilePath + path.replace("/", File.separator), if (xposedModuleArray != null && xposedModuleArray.length > 0) {
SO_FILE_PATH_MAP.get(path), true); int index = 0;
} for (String modulePath : xposedModuleArray) {
if (!copySuccess) { modulePath = modulePath.trim();
throw new IllegalArgumentException(" copy so file failed "); if (modulePath == null || modulePath.length() == 0) {
continue;
}
File moduleFile = new File(modulePath);
if (!moduleFile.exists()) {
continue;
}
String outputModuleFile = XPOSED_MODULE_FILE_NAME_PREFIX + index + SO_FILE_SUFFIX;
FileUtils.copyFile(moduleFile, new File(apkSoFullPath, outputModuleFile));
index++;
}
} }
} }
@ -73,18 +80,49 @@ public class SoAndDexCopyTask implements Runnable {
FileUtils.copyFileFromJar("assets/classes.dex", unzipApkFilePath + copiedDexFileName); FileUtils.copyFileFromJar("assets/classes.dex", unzipApkFilePath + copiedDexFileName);
} }
private boolean copyLibFile(String libFilePath, String srcSoPath, boolean forceCopy) { private String fullLibPath(String libPath) {
return unzipApkFilePath + libPath.replace("/", File.separator);
}
private void copyLibFile(String libFilePath, String srcSoPath) {
File apkSoParentFile = new File(libFilePath); File apkSoParentFile = new File(libFilePath);
if (forceCopy && !apkSoParentFile.exists()) { if (!apkSoParentFile.exists()) {
apkSoParentFile.mkdirs(); apkSoParentFile.mkdirs();
} }
File[] childs = apkSoParentFile.listFiles(); // get the file name first
if (apkSoParentFile.exists() && (forceCopy || (childs != null && childs.length > 0))) { int lastIndex = srcSoPath.lastIndexOf('/');
FileUtils.copyFileFromJar(srcSoPath, new File(apkSoParentFile, SO_FILE_NAME).getAbsolutePath()); int length = srcSoPath.length();
return true; String soFileName = srcSoPath.substring(lastIndex, length);
// do copy
FileUtils.copyFileFromJar(srcSoPath, new File(apkSoParentFile, soFileName).getAbsolutePath());
}
// Try to find the lib path where the so file should put.
// If there is many lib path, try to find the path which has the most so files
private String findTargetLibPath() {
int maxChildFileCount = 0;
int maxChildFileIndex = 0;
int index = 0;
for (String libPath : APK_LIB_PATH_ARRAY) {
String fullPath = fullLibPath(libPath);
File file = new File(fullPath);
if (file.exists()) {
String[] childList = file.list();
int childCount = 0;
if (childList != null) {
childCount = childList.length;
}
if (childCount > maxChildFileCount) {
maxChildFileCount = childCount;
maxChildFileIndex = index;
}
}
index++;
} }
return false;
return APK_LIB_PATH_ARRAY[maxChildFileIndex];
} }
private void deleteMetaInfo() { private void deleteMetaInfo() {

View File

@ -11,6 +11,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.zip.CRC32; import java.util.zip.CRC32;
@ -113,6 +115,39 @@ public class FileUtils {
} }
} }
public static void copyFile(String sourcePath, String targetPath) {
copyFile(new File(sourcePath), new File(targetPath));
}
public static void copyFile(File source, File target) {
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = new FileInputStream(source);
outputStream = new FileOutputStream(target);
FileChannel iChannel = inputStream.getChannel();
FileChannel oChannel = outputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
buffer.clear();
int r = iChannel.read(buffer);
if (r == -1) {
break;
}
buffer.limit(buffer.position());
buffer.position(0);
oChannel.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close(inputStream);
close(outputStream);
}
}
public static void deleteDir(File file) { public static void deleteDir(File file) {
if (file.isDirectory()) { if (file.isDirectory()) {
File[] files = file.listFiles(); File[] files = file.listFiles();