use apkzlib manipulate apk
This commit is contained in:
parent
e3c998b36c
commit
2b0a830c55
|
|
@ -12,12 +12,11 @@ compileJava.options.encoding = "UTF-8"
|
|||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(':axmlprinter')
|
||||
//implementation project(':apksigner')
|
||||
implementation group: 'commons-io', name: 'commons-io', version: '2.8.0'
|
||||
implementation project(':share')
|
||||
implementation 'commons-io:commons-io:2.10.0'
|
||||
implementation 'com.android.tools.build:apkzlib:4.2.1'
|
||||
}
|
||||
|
||||
sourceSets.main.java.srcDirs += "$rootProject.projectDir/apksigner/src/main/java"
|
||||
sourceSets.main.java.srcDirs += "$rootProject.projectDir/apksigner/src/apksigner/java"
|
||||
|
||||
jar {
|
||||
|
|
|
|||
|
|
@ -3,26 +3,34 @@ package org.lsposed.patch;
|
|||
import static org.apache.commons.io.FileUtils.copyDirectory;
|
||||
import static org.apache.commons.io.FileUtils.copyFile;
|
||||
|
||||
import com.android.tools.build.apkzlib.zip.StoredEntry;
|
||||
import com.android.tools.build.apkzlib.zip.ZFile;
|
||||
import com.wind.meditor.core.FileProcesser;
|
||||
import com.wind.meditor.property.AttributeItem;
|
||||
import com.wind.meditor.property.ModificationProperty;
|
||||
import com.wind.meditor.utils.NodeValue;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.lsposed.lspatch.share.Constants;
|
||||
import org.lsposed.patch.base.BaseCommand;
|
||||
import org.lsposed.patch.task.BuildAndSignApkTask;
|
||||
import org.lsposed.patch.task.SaveApkSignatureTask;
|
||||
import org.lsposed.patch.task.SaveOriginalApplicationNameTask;
|
||||
import org.lsposed.patch.task.SoAndDexCopyTask;
|
||||
import org.lsposed.patch.util.ApkSignatureHelper;
|
||||
import org.lsposed.patch.util.ZipUtils;
|
||||
import org.lsposed.patch.util.ManifestParser;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LSPatch extends BaseCommand {
|
||||
|
|
@ -50,6 +58,15 @@ public class LSPatch extends BaseCommand {
|
|||
private int dexFileCount = 0;
|
||||
|
||||
private static final String UNZIP_APK_FILE_NAME = "apk-unzip-files";
|
||||
private static final String APPLICATION_NAME_ASSET_PATH = "assets/original_application_name.ini";
|
||||
private final static String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini";
|
||||
private static final String[] APK_LIB_PATH_ARRAY = {
|
||||
"lib/armeabi-v7a",
|
||||
"lib/armeabi",
|
||||
"lib/arm64-v8a",
|
||||
"lib/x86",
|
||||
"lib/x86_64"
|
||||
};
|
||||
|
||||
public static void main(String... args) {
|
||||
new LSPatch().doMain(args);
|
||||
|
|
@ -85,12 +102,18 @@ public class LSPatch extends BaseCommand {
|
|||
return;
|
||||
}
|
||||
|
||||
File finalApk = new File(String.format("%s-%s-unsigned.apk", getBaseName(srcApkFile.getAbsolutePath()),
|
||||
Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel));
|
||||
FileUtils.copyFile(srcApkFile, finalApk);
|
||||
|
||||
ZFile zFile = new ZFile(finalApk);
|
||||
|
||||
String currentDir = new File(".").getAbsolutePath();
|
||||
System.out.println("currentDir: " + currentDir);
|
||||
System.out.println("apkPath: " + apkPath);
|
||||
|
||||
if (outputPath == null || outputPath.length() == 0) {
|
||||
String sig = "sigbypasslv" + sigbypassLevel;
|
||||
String sig = Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel;
|
||||
outputPath = String.format("%s-%s-xposed-signed.apk", getBaseName(apkPath), sig);
|
||||
}
|
||||
|
||||
|
|
@ -108,8 +131,6 @@ public class LSPatch extends BaseCommand {
|
|||
outputApkFileParentPath = absPath.substring(0, index);
|
||||
}
|
||||
|
||||
System.out.println("output apk path: " + outputPath);
|
||||
|
||||
String apkFileName = getBaseName(srcApkFile);
|
||||
|
||||
String tempFilePath = outputApkFileParentPath + File.separator +
|
||||
|
|
@ -117,25 +138,26 @@ public class LSPatch extends BaseCommand {
|
|||
|
||||
unzipApkFilePath = tempFilePath + apkFileName + "-" + UNZIP_APK_FILE_NAME + File.separator;
|
||||
|
||||
System.out.println("outputApkFileParentPath: " + outputApkFileParentPath);
|
||||
System.out.println("unzipApkFilePath = " + unzipApkFilePath);
|
||||
|
||||
// save the apk original signature info, to support crach signature.
|
||||
new SaveApkSignatureTask(apkPath, unzipApkFilePath).run();
|
||||
String originalSignature = ApkSignatureHelper.getApkSignInfo(apkPath);
|
||||
if (originalSignature == null || originalSignature.isEmpty()) {
|
||||
throw new IllegalStateException("get original signature failed");
|
||||
}
|
||||
File osi = new File((unzipApkFilePath + SIGNATURE_INFO_ASSET_PATH).replace("/", File.separator));
|
||||
FileUtils.write(osi, originalSignature, Charset.defaultCharset());
|
||||
zFile.add(SIGNATURE_INFO_ASSET_PATH, new FileInputStream(osi));
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
ZipUtils.decompressZip(apkPath, unzipApkFilePath);
|
||||
|
||||
System.out.println("decompress apk cost time: " + (System.currentTimeMillis() - currentTime) + "ms");
|
||||
|
||||
// Get the dex count in the apk zip file
|
||||
dexFileCount = findDexFileCount(unzipApkFilePath);
|
||||
// get the dex count in the apk zip file
|
||||
dexFileCount = findDexFileCount(zFile);
|
||||
|
||||
System.out.println("dexFileCount: " + dexFileCount);
|
||||
|
||||
String manifestFilePath = unzipApkFilePath + "AndroidManifest.xml";
|
||||
|
||||
currentTime = System.currentTimeMillis();
|
||||
// copy out manifest file from zlib
|
||||
int copySize = IOUtils.copy(zFile.get("AndroidManifest.xml").open(), new FileOutputStream(unzipApkFilePath + "AndroidManifest.xml.bak"));
|
||||
if (copySize <= 0) {
|
||||
throw new IllegalStateException("wtf");
|
||||
}
|
||||
String manifestFilePath = unzipApkFilePath + "AndroidManifest.xml.bak";
|
||||
|
||||
// parse the app main application full name from the manifest file
|
||||
ManifestParser.Pair pair = ManifestParser.parseManifestFile(manifestFilePath);
|
||||
|
|
@ -147,31 +169,60 @@ public class LSPatch extends BaseCommand {
|
|||
System.out.println("original application name: " + applicationName);
|
||||
|
||||
// modify manifest
|
||||
File manifestFile = new File(manifestFilePath);
|
||||
String manifestFilePathNew = unzipApkFilePath + "AndroidManifest" + "-" + currentTimeStr() + ".xml";
|
||||
File manifestFileNew = new File(manifestFilePathNew);
|
||||
fuckIfFail(manifestFile.renameTo(manifestFileNew));
|
||||
|
||||
modifyManifestFile(manifestFilePathNew, manifestFilePath);
|
||||
|
||||
// new manifest may not exist
|
||||
if (manifestFile.exists() && manifestFile.length() > 0) {
|
||||
fuckIfFail(manifestFileNew.delete());
|
||||
}
|
||||
else {
|
||||
fuckIfFail(manifestFileNew.renameTo(manifestFile));
|
||||
}
|
||||
modifyManifestFile(manifestFilePath, new File(unzipApkFilePath, "AndroidManifest.xml").getPath());
|
||||
|
||||
// save original main application name to asset file even its empty
|
||||
new SaveOriginalApplicationNameTask(applicationName, unzipApkFilePath).run();
|
||||
File oan = new File((unzipApkFilePath + APPLICATION_NAME_ASSET_PATH).replace("/", File.separator));
|
||||
FileUtils.write(oan, applicationName, Charset.defaultCharset());
|
||||
zFile.add(APPLICATION_NAME_ASSET_PATH, new FileInputStream(oan));
|
||||
|
||||
// copy so and dex files into the unzipped apk
|
||||
new SoAndDexCopyTask(dexFileCount, unzipApkFilePath).run();
|
||||
Set<String> apkArchs = new HashSet<>();
|
||||
|
||||
System.out.println("search target apk library arch..");
|
||||
for (StoredEntry storedEntry : zFile.entries()) {
|
||||
for (String arch : APK_LIB_PATH_ARRAY) {
|
||||
if (storedEntry.getCentralDirectoryHeader().getName().startsWith(arch)) {
|
||||
apkArchs.add(arch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (apkArchs.isEmpty()) {
|
||||
apkArchs.add(APK_LIB_PATH_ARRAY[0]);
|
||||
}
|
||||
|
||||
for (String arch : apkArchs) {
|
||||
// lib/armeabi-v7a -> armeabi-v7a
|
||||
String justArch = arch.substring(arch.indexOf('/'));
|
||||
File sod = new File("list-so", justArch);
|
||||
File[] files = sod.listFiles();
|
||||
if (files == null) {
|
||||
System.out.println("Warning: Nothing so file has been copied in " + sod.getPath());
|
||||
continue;
|
||||
}
|
||||
for (File file : files) {
|
||||
zFile.add(arch + "/" + file.getName(), new FileInputStream(file));
|
||||
System.out.println("add " + file.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
// copy all dex files in list-dex
|
||||
File[] files = new File("list-dex").listFiles();
|
||||
if (files == null || files.length == 0) {
|
||||
System.out.println("Warning: Nothing dex file has been copied");
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
String copiedDexFileName = "classes" + (dexFileCount + 1) + ".dex";
|
||||
zFile.add(copiedDexFileName, new FileInputStream(file));
|
||||
dexFileCount++;
|
||||
}
|
||||
|
||||
// copy origin apk to assets
|
||||
// convenient to bypass some check like CRC
|
||||
if (sigbypassLevel >= Constants.SIGBYPASS_LV_PM) {
|
||||
copyFile(srcApkFile, new File(unzipApkFilePath, "assets/origin_apk.bin"));
|
||||
if (sigbypassLevel >= Constants.SIGBYPASS_LV_PM_OPENAT) {
|
||||
zFile.add("assets/origin_apk.bin", new FileInputStream(srcApkFile));
|
||||
}
|
||||
|
||||
File[] listAssets = new File("list-assets").listFiles();
|
||||
|
|
@ -179,16 +230,23 @@ public class LSPatch extends BaseCommand {
|
|||
System.out.println("Warning: No assets file copyied");
|
||||
}
|
||||
else {
|
||||
copyDirectory(new File("list-assets"), new File(unzipApkFilePath, "assets"));
|
||||
for (File f : listAssets) {
|
||||
if (f.isDirectory()) {
|
||||
throw new IllegalStateException("unsupport directory in assets");
|
||||
}
|
||||
zFile.add("assets/" + f.getName(), new FileInputStream(f));
|
||||
}
|
||||
}
|
||||
|
||||
// save lspatch config to asset..
|
||||
FileUtils.write(new File(unzipApkFilePath, "assets" + File.separator + Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel), "lspatch",
|
||||
Charset.defaultCharset());
|
||||
File sl = new File(unzipApkFilePath, "tmp");
|
||||
FileUtils.write(sl, "42", Charset.defaultCharset());
|
||||
zFile.add("assets/" + Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel, new FileInputStream(sl));
|
||||
|
||||
// compress all files into an apk and then sign it.
|
||||
new BuildAndSignApkTask(true, unzipApkFilePath, outputPath).run();
|
||||
zFile.update();
|
||||
zFile.close();
|
||||
|
||||
new BuildAndSignApkTask(true, unzipApkFilePath, finalApk.getAbsolutePath(), outputPath).run();
|
||||
System.out.println("Output APK: " + outputPath);
|
||||
}
|
||||
|
||||
|
|
@ -205,23 +263,12 @@ public class LSPatch extends BaseCommand {
|
|||
FileProcesser.processManifestFile(filePath, dstFilePath, property);
|
||||
}
|
||||
|
||||
private int findDexFileCount(String unzipApkFilePath) {
|
||||
File zipfileRoot = new File(unzipApkFilePath);
|
||||
if (!zipfileRoot.exists()) {
|
||||
return 0;
|
||||
private int findDexFileCount(ZFile zFile) {
|
||||
for (int i = 2; i < 30; i++) {
|
||||
if (zFile.get("classes" + i + ".dex") == null)
|
||||
return i - 1;
|
||||
}
|
||||
File[] childFiles = zipfileRoot.listFiles();
|
||||
if (childFiles == null || childFiles.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
for (File file : childFiles) {
|
||||
String fileName = file.getName();
|
||||
if (Pattern.matches("classes.*\\.dex", fileName)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
throw new IllegalStateException("wtf");
|
||||
}
|
||||
|
||||
// Use the current timestamp as the name of the build file
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package org.lsposed.patch.task;
|
|||
|
||||
import com.android.apksigner.ApkSignerTool;
|
||||
|
||||
import com.android.tools.build.apkzlib.zip.ZFile;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.lsposed.patch.LSPatch;
|
||||
import org.lsposed.patch.util.ZipUtils;
|
||||
|
|
@ -17,14 +19,14 @@ import java.util.ArrayList;
|
|||
*/
|
||||
public class BuildAndSignApkTask implements Runnable {
|
||||
|
||||
private boolean keepUnsignedApkFile;
|
||||
private final boolean keepUnsignedApkFile;
|
||||
private final String signedApkPath;
|
||||
private final String unzipApkFilePath;
|
||||
private final String unsignedApkPath;
|
||||
|
||||
private String signedApkPath;
|
||||
|
||||
private String unzipApkFilePath;
|
||||
|
||||
public BuildAndSignApkTask(boolean keepUnsignedApkFile, String unzipApkFilePath, String signedApkPath) {
|
||||
public BuildAndSignApkTask(boolean keepUnsignedApkFile, String unzipApkFilePath, String unsignedApkPath, String signedApkPath) {
|
||||
this.keepUnsignedApkFile = keepUnsignedApkFile;
|
||||
this.unsignedApkPath = unsignedApkPath;
|
||||
this.unzipApkFilePath = unzipApkFilePath;
|
||||
this.signedApkPath = signedApkPath;
|
||||
}
|
||||
|
|
@ -33,43 +35,30 @@ public class BuildAndSignApkTask implements Runnable {
|
|||
public void run() {
|
||||
|
||||
try {
|
||||
File unzipApkFile = new File(unzipApkFilePath);
|
||||
|
||||
String unsignedApkPath = unzipApkFile.getParent() + File.separator + "unsigned.apk";
|
||||
ZipUtils.compressToZip(unzipApkFilePath, unsignedApkPath);
|
||||
|
||||
String keyStoreFilePath = unzipApkFile.getParent() + File.separator + "keystore";
|
||||
|
||||
File keyStoreFile = new File(keyStoreFilePath);
|
||||
// assets/keystore分隔符不能使用File.separator,否则在windows上抛出IOException !!!
|
||||
File unzipApkPathFile = new File(unzipApkFilePath);
|
||||
File keyStoreFile = new File(unzipApkPathFile, "keystore");
|
||||
String keyStoreAssetPath;
|
||||
if (isAndroid()) {
|
||||
// BKS-V1 类型
|
||||
keyStoreAssetPath = "assets/android.keystore";
|
||||
}
|
||||
else {
|
||||
// BKS 类型
|
||||
keyStoreAssetPath = "assets/keystore";
|
||||
}
|
||||
|
||||
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(keyStoreAssetPath);
|
||||
FileOutputStream out = new FileOutputStream(keyStoreFilePath)) {
|
||||
FileOutputStream out = new FileOutputStream(keyStoreFile)) {
|
||||
IOUtils.copy(inputStream, out);
|
||||
}
|
||||
|
||||
boolean signResult = signApk(unsignedApkPath, keyStoreFilePath, signedApkPath);
|
||||
boolean signResult = signApk(unsignedApkPath, keyStoreFile.getAbsolutePath(), signedApkPath);
|
||||
|
||||
File unsignedApkFile = new File(unsignedApkPath);
|
||||
File signedApkFile = new File(signedApkPath);
|
||||
|
||||
// delete unsigned apk file
|
||||
if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) {
|
||||
LSPatch.fuckIfFail(unsignedApkFile.delete());
|
||||
}
|
||||
|
||||
// delete the keystore file
|
||||
if (keyStoreFile.exists()) {
|
||||
LSPatch.fuckIfFail(keyStoreFile.delete());
|
||||
}
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw new IllegalStateException("wtf", err);
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
package org.lsposed.patch.task;
|
||||
|
||||
import org.lsposed.patch.util.ApkSignatureHelper;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Created by Wind
|
||||
*/
|
||||
public class SaveApkSignatureTask implements Runnable {
|
||||
|
||||
private String apkPath;
|
||||
private String dstFilePath;
|
||||
|
||||
private final static String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini";
|
||||
|
||||
public SaveApkSignatureTask(String apkPath, String unzipApkFilePath) {
|
||||
this.apkPath = apkPath;
|
||||
this.dstFilePath = (unzipApkFilePath + SIGNATURE_INFO_ASSET_PATH).replace("/", File.separator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// First, get the original signature
|
||||
String originalSignature = ApkSignatureHelper.getApkSignInfo(apkPath);
|
||||
if (originalSignature == null || originalSignature.isEmpty()) {
|
||||
System.out.println("Get original signature failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Then, save the signature chars to the asset file
|
||||
File file = new File(dstFilePath);
|
||||
try {
|
||||
FileUtils.write(file, originalSignature, StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (Exception err) {
|
||||
// just crash now
|
||||
// todo: pass result to caller
|
||||
throw new IllegalStateException("wtf", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package org.lsposed.patch.task;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Created by xiawanli on 2019/4/6
|
||||
*/
|
||||
public class SaveOriginalApplicationNameTask implements Runnable {
|
||||
|
||||
private final String applcationName;
|
||||
private final String unzipApkFilePath;
|
||||
private String dstFilePath;
|
||||
|
||||
private final String APPLICATION_NAME_ASSET_PATH = "assets/original_application_name.ini";
|
||||
|
||||
public SaveOriginalApplicationNameTask(String applicationName, String unzipApkFilePath) {
|
||||
this.applcationName = applicationName;
|
||||
this.unzipApkFilePath = unzipApkFilePath;
|
||||
|
||||
this.dstFilePath = (unzipApkFilePath + APPLICATION_NAME_ASSET_PATH).replace("/", File.separator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FileUtils.write(new File(dstFilePath), applcationName, Charset.defaultCharset());
|
||||
}
|
||||
catch (Exception err) {
|
||||
// just crash
|
||||
// todo: pass result to caller
|
||||
throw new IllegalStateException("wtf", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
package org.lsposed.patch.task;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Wind
|
||||
*/
|
||||
public class SoAndDexCopyTask implements Runnable {
|
||||
|
||||
private final String[] APK_LIB_PATH_ARRAY = {
|
||||
"lib/armeabi-v7a/",
|
||||
"lib/armeabi/",
|
||||
"lib/arm64-v8a/",
|
||||
"lib/x86",
|
||||
"lib/x86_64"
|
||||
};
|
||||
|
||||
private int dexFileCount;
|
||||
private String unzipApkFilePath;
|
||||
|
||||
public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath) {
|
||||
this.dexFileCount = dexFileCount;
|
||||
this.unzipApkFilePath = unzipApkFilePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// 复制xposed兼容层的dex文件以及so文件到当前目录下
|
||||
copySoFile();
|
||||
copyDexFile(dexFileCount);
|
||||
|
||||
// 删除签名信息
|
||||
deleteMetaInfo();
|
||||
}
|
||||
|
||||
private void copySoFile() {
|
||||
List<String> existLibPathArray = new ArrayList<>();
|
||||
for (String libPath : APK_LIB_PATH_ARRAY) {
|
||||
String apkSoFullPath = fullLibPath(libPath);
|
||||
File apkSoFullPathFile = new File(apkSoFullPath);
|
||||
if (apkSoFullPathFile.exists()) {
|
||||
existLibPathArray.add(libPath);
|
||||
} else {
|
||||
System.out.println("Target app dont have " + libPath + ", skip");
|
||||
}
|
||||
}
|
||||
|
||||
if (existLibPathArray.isEmpty()) {
|
||||
System.out.println("Target app dont have any so in \"lib/\" dir, so create default \"armeabi-v7a\"");
|
||||
String libPath = APK_LIB_PATH_ARRAY[0];
|
||||
String apkSoFullPath = fullLibPath(libPath);
|
||||
File apkSoFullPathFile = new File(apkSoFullPath);
|
||||
if (!apkSoFullPathFile.mkdirs()) {
|
||||
throw new IllegalStateException("mkdir fail " + apkSoFullPathFile.getAbsolutePath());
|
||||
}
|
||||
existLibPathArray.add(libPath);
|
||||
}
|
||||
|
||||
for (String libPath : existLibPathArray) {
|
||||
if (libPath == null || libPath.isEmpty()) {
|
||||
throw new IllegalStateException("fail eabi path");
|
||||
}
|
||||
|
||||
String apkSoFullPath = fullLibPath(libPath);
|
||||
String eabi = libPath.substring(libPath.indexOf("/"));
|
||||
if (eabi.isEmpty()) {
|
||||
throw new IllegalStateException("fail find eabi in " + libPath);
|
||||
}
|
||||
|
||||
File[] files = new File("list-so", eabi).listFiles();
|
||||
if (files == null) {
|
||||
System.out.println("Warning: Nothing so file has been copied in " + libPath);
|
||||
continue;
|
||||
}
|
||||
for (File mySoFile : files) {
|
||||
File target = new File(apkSoFullPath, mySoFile.getName());
|
||||
try {
|
||||
FileUtils.copyFile(mySoFile, target);
|
||||
} catch (Exception err) {
|
||||
throw new IllegalStateException("wtf", err);
|
||||
}
|
||||
System.out.println("Copy " + mySoFile.getAbsolutePath() + " to " + target.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyDexFile(int dexFileCount) {
|
||||
try {
|
||||
// copy all dex files in list-dex
|
||||
File[] files = new File("list-dex").listFiles();
|
||||
if (files == null || files.length == 0) {
|
||||
System.out.println("Warning: Nothing dex file has been copied");
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
String copiedDexFileName = "classes" + (dexFileCount + 1) + ".dex";
|
||||
File target = new File(unzipApkFilePath, copiedDexFileName);
|
||||
FileUtils.copyFile(file, target);
|
||||
System.out.println("Copy " + file.getAbsolutePath() + " to " + target.getAbsolutePath());
|
||||
dexFileCount++;
|
||||
}
|
||||
} catch (Exception err) {
|
||||
throw new IllegalStateException("wtf", err);
|
||||
}
|
||||
}
|
||||
|
||||
private String fullLibPath(String libPath) {
|
||||
return unzipApkFilePath + libPath.replace("/", File.separator);
|
||||
}
|
||||
|
||||
private void deleteMetaInfo() {
|
||||
String metaInfoFilePath = "META-INF";
|
||||
File metaInfoFileRoot = new File(unzipApkFilePath + metaInfoFilePath);
|
||||
if (!metaInfoFileRoot.exists()) {
|
||||
return;
|
||||
}
|
||||
File[] childFileList = metaInfoFileRoot.listFiles();
|
||||
if (childFileList == null || childFileList.length == 0) {
|
||||
return;
|
||||
}
|
||||
for (File file : childFileList) {
|
||||
String fileName = file.getName().toUpperCase();
|
||||
if (fileName.endsWith(".MF") || fileName.endsWith(".RAS") || fileName.endsWith(".SF")) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue