diff --git a/xpatch/src/main/assets/classes.dex b/xpatch/src/main/assets/classes.dex index 2e81e5b..1807eaf 100755 Binary files a/xpatch/src/main/assets/classes.dex and b/xpatch/src/main/assets/classes.dex differ diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java b/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java index 370d77f..435c940 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java @@ -3,6 +3,7 @@ package com.storm.wind.xpatch; import com.storm.wind.xpatch.base.BaseCommand; import com.storm.wind.xpatch.task.ApkModifyTask; import com.storm.wind.xpatch.task.BuildAndSignApkTask; +import com.storm.wind.xpatch.task.SaveApkSignatureTask; import com.storm.wind.xpatch.task.SoAndDexCopyTask; import com.storm.wind.xpatch.util.FileUtils; import com.storm.wind.xpatch.util.ManifestParser; @@ -34,6 +35,10 @@ public class MainCommand extends BaseCommand { @Opt(opt = "l", longOpt = "log", hasArg = false, description = "show all the debug logs") private boolean showAllLogs = false; + @Opt(opt = "c", longOpt = "crach", hasArg = false, + description = "disable craching the apk's signature.") + private boolean disableCrackSignature = false; + // 原来apk中dex文件的数量 private int dexFileCount = 0; @@ -93,12 +98,14 @@ public class MainCommand extends BaseCommand { return; } - System.out.println(" !!!!! output apk path --> " + output); + System.out.println(" !!!!! output apk path --> " + output + + " disableCrackSignature --> " + disableCrackSignature); String apkFileName = getBaseName(srcApkFile); // 中间文件临时存储的位置 - String tempFilePath = srcApkFileParentPath + File.separator + currentTimeStr() + "-tmp" + File.separator; + String tempFilePath = srcApkFileParentPath + File.separator + + currentTimeStr() + "-tmp" + File.separator; // apk文件解压的目录 unzipApkFilePath = tempFilePath + apkFileName + "-" + UNZIP_APK_FILE_NAME + File.separator; @@ -108,6 +115,11 @@ public class MainCommand extends BaseCommand { "\n unzipApkFilePath = " + unzipApkFilePath); } + if (!disableCrackSignature) { + // save the apk original signature info, to support crach signature. + new SaveApkSignatureTask(apkPath, unzipApkFilePath).run(); + } + // 先解压apk到指定目录下 FileUtils.decompressZip(apkPath, unzipApkFilePath); diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveApkSignatureTask.java b/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveApkSignatureTask.java new file mode 100644 index 0000000..4c4db8a --- /dev/null +++ b/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveApkSignatureTask.java @@ -0,0 +1,41 @@ +package com.storm.wind.xpatch.task; + +import com.storm.wind.xpatch.util.ApkSignatureHelper; +import com.storm.wind.xpatch.util.FileUtils; + +import java.io.File; + +/** + * Created by Wind + */ +public class SaveApkSignatureTask implements Runnable { + + private String apkPath; + private String dstFilePath; + + private final static String SIGNATURE_INFO_ASSET_PATH = "assets/xpatch_asset/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); + File fileParent = file.getParentFile(); + if (!fileParent.exists()) { + fileParent.mkdirs(); + } + + FileUtils.writeFile(dstFilePath, originalSignature); + } +} diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/util/ApkSignatureHelper.java b/xpatch/src/main/java/com/storm/wind/xpatch/util/ApkSignatureHelper.java new file mode 100644 index 0000000..b98020f --- /dev/null +++ b/xpatch/src/main/java/com/storm/wind/xpatch/util/ApkSignatureHelper.java @@ -0,0 +1,82 @@ +package com.storm.wind.xpatch.util; + +import java.io.InputStream; +import java.security.cert.Certificate; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * Created by Wind + */ +public class ApkSignatureHelper { + + private static char[] toChars(byte[] mSignature) { + byte[] sig = mSignature; + final int N = sig.length; + final int N2 = N * 2; + char[] text = new char[N2]; + for (int j = 0; j < N; j++) { + byte v = sig[j]; + int d = (v >> 4) & 0xf; + text[j * 2] = (char) (d >= 10 ? ('a' + d - 10) : ('0' + d)); + d = v & 0xf; + text[j * 2 + 1] = (char) (d >= 10 ? ('a' + d - 10) : ('0' + d)); + } + return text; + } + + private static Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer) { + try { + InputStream is = jarFile.getInputStream(je); + while (is.read(readBuffer, 0, readBuffer.length) != -1) { + } + is.close(); + return (Certificate[]) (je != null ? je.getCertificates() : null); + } catch (Exception e) { + } + return null; + } + + public static String getApkSignInfo(String apkFilePath) { + byte[] readBuffer = new byte[8192]; + Certificate[] certs = null; + try { + JarFile jarFile = new JarFile(apkFilePath); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry je = (JarEntry) entries.nextElement(); + if (je.isDirectory()) { + continue; + } + if (je.getName().startsWith("META-INF/")) { + continue; + } + Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer); + if (certs == null) { + certs = localCerts; + } else { + for (int i = 0; i < certs.length; i++) { + boolean found = false; + for (int j = 0; j < localCerts.length; j++) { + if (certs[i] != null && certs[i].equals(localCerts[j])) { + found = true; + break; + } + } + if (!found || certs.length != localCerts.length) { + jarFile.close(); + return null; + } + } + } + } + jarFile.close(); + System.out.println(" getApkSignInfo result --> " + certs[0]); + return new String(toChars(certs[0].getEncoded())); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java b/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java index 09db06a..50dc9f5 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java @@ -2,6 +2,7 @@ package com.storm.wind.xpatch.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.BufferedWriter; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; @@ -9,6 +10,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.zip.CRC32; @@ -205,6 +207,35 @@ public class FileUtils { } } + public static void writeFile(String filePath, String content) { + if (filePath == null || filePath.isEmpty()) { + return; + } + if (content == null || content.isEmpty()) { + return; + } + + File dstFile = new File(filePath); + + if (!dstFile.getParentFile().exists()) { + dstFile.getParentFile().mkdirs(); + } + + FileOutputStream outputStream = null; + BufferedWriter writer = null; + try { + outputStream = new FileOutputStream(dstFile); + writer = new BufferedWriter(new OutputStreamWriter(outputStream)); + writer.write(content); + writer.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + close(outputStream); + close(writer); + } + } + private static void close(Closeable closeable) { try { if (closeable != null) {