diff --git a/README.md b/README.md index ad68af2..bd25233 100644 --- a/README.md +++ b/README.md @@ -14,5 +14,5 @@ ``` ## 单步调试 -将命令行参数放到``container-builder-repkg/src/main/java/ratelentry/Main.java``,然后单步调试main函数的执行流程即可 +将命令行参数放到``container-builder-repkg/src/main/java/com/virjar/ratel/builder/ratelentry/Main.java``,然后单步调试main函数的执行流程即可 diff --git a/base-lib-allcommon-lib/base-lib-allcommon-sources.jar b/base-lib-allcommon-lib/base-lib-allcommon-sources.jar index 3930658..f98f0db 100644 Binary files a/base-lib-allcommon-lib/base-lib-allcommon-sources.jar and b/base-lib-allcommon-lib/base-lib-allcommon-sources.jar differ diff --git a/base-lib-allcommon-lib/base-lib-allcommon.jar b/base-lib-allcommon-lib/base-lib-allcommon.jar index 16d97cb..8522e1e 100644 Binary files a/base-lib-allcommon-lib/base-lib-allcommon.jar and b/base-lib-allcommon-lib/base-lib-allcommon.jar differ diff --git a/base-lib-allcommon/src/main/java/com/virjar/ratel/allcommon/ClassNames.java b/base-lib-allcommon/src/main/java/com/virjar/ratel/allcommon/ClassNames.java new file mode 100644 index 0000000..3aa6a52 --- /dev/null +++ b/base-lib-allcommon/src/main/java/com/virjar/ratel/allcommon/ClassNames.java @@ -0,0 +1,35 @@ +package com.virjar.ratel.allcommon; + +/** + * 多个模块部件相互关联,通过多个部件无法在同一个classloader直接引用和检查所有的class
+ * 所以className在这里管理,如果未来ratel存在对抗,需要修改className,那么需要同步修改这张表 + */ +public enum ClassNames { + BUILDER_HELPER_MAIN("com.virjar.ratel.builder.helper.Main"), + BUILDER_MAIN("com.virjar.ratel.builder.ratelentry.Main"), + INJECT_REBUILD_BOOTSTRAP("com.virjar.ratel.inject.template.rebuild.BootStrap"), + INJECT_REBUILD_BOOTSTRAP_CINT("com.virjar.ratel.inject.template.rebuild.BootStrapWithStaticInit"), + + + INJECT_TOOL_SMALI_LOG("com.virjar.ratel.inject.template.RatelSmaliLog"), + INJECT_TOOL_EVENT_NOTIFIER("com.virjar.ratel.inject.template.EventNotifier"), + + + ; + + ClassNames(String className) { + this.className = className; + } + + private final String className; + + public void check(Class clazz) { + if (!clazz.getName().equals(className)) { + throw new IllegalStateException("class name define error-> expected: " + className + " actually: " + clazz.getName()); + } + } + + public String getClassName() { + return className; + } +} diff --git a/base-lib-allcommon/src/main/java/com/virjar/ratel/allcommon/NewConstants.java b/base-lib-allcommon/src/main/java/com/virjar/ratel/allcommon/NewConstants.java index c356ad0..0e0f686 100644 --- a/base-lib-allcommon/src/main/java/com/virjar/ratel/allcommon/NewConstants.java +++ b/base-lib-allcommon/src/main/java/com/virjar/ratel/allcommon/NewConstants.java @@ -4,33 +4,62 @@ * 新版本的常量定义,之前的太乱了 */ public interface NewConstants { + /** * builder的资源文件名称定义 */ - interface BUILDER_RESOURCE_LAYOUT { - String LAYOUT_BASE = "build-asset"; - String BUILDER_HELPER_NAME = LAYOUT_BASE + "/builder-helper.jar"; + enum BUILDER_RESOURCE_LAYOUT { + LAYOUT_BASE("build-asset", true, true), + BUILDER_HELPER_NAME(LAYOUT_BASE.NAME + "/builder-helper.jar.bin", true), + // BUILDER_HELPER_NAME2(LAYOUT_BASE.NAME + "/builder-helper.jar.bin", true), - String RUNTIME_APK_FILE = LAYOUT_BASE + "/runtime.apk"; - String RUNTIME_JAR_FILE = LAYOUT_BASE + "/runtime.jar"; + RUNTIME_APK_FILE(LAYOUT_BASE.NAME + "/runtime.apk", true), + RUNTIME_JAR_FILE(LAYOUT_BASE.NAME + "/runtime.jar", false), - String XPOSED_BRIDGE_APK_FILE = LAYOUT_BASE + "/xpBridge.apk"; - String XPOSED_BRIDGE_JAR_FILE = LAYOUT_BASE + "/xpBridge.jar"; + XPOSED_BRIDGE_APK_FILE(LAYOUT_BASE.NAME + "/xpBridge.apk", true), + XPOSED_BRIDGE_JAR_FILE(LAYOUT_BASE.NAME + "/xpBridge.jar", false), /** * 入口代码定义,包括一些smali模版 */ - String TEMPLATE_APK_FILE = LAYOUT_BASE + "/template.apk"; + TEMPLATE_APK_FILE(LAYOUT_BASE.NAME + "/template.apk", true), /** * appendDex模式下,直接使用dex文件 */ - String TEMPLATE_DEX_FILE = LAYOUT_BASE + "/template.dex"; + TEMPLATE_DEX_FILE(LAYOUT_BASE.NAME + "/template.dex", false), /** * template需要解成smali */ - String TEMPLATE_SMALI_ZIP_FILE = LAYOUT_BASE + "/template-smali.zip.bin"; + TEMPLATE_SMALI_ZIP_FILE(LAYOUT_BASE.NAME + "/template-smali.zip.bin", false), + ; + + private final String NAME; + private final boolean raw; + private final boolean dir; + + BUILDER_RESOURCE_LAYOUT(String NAME, boolean raw, boolean dir) { + this.NAME = NAME; + this.raw = raw; + this.dir = dir; + } + + BUILDER_RESOURCE_LAYOUT(String NAME, boolean raw) { + this(NAME, raw, false); + } + + public String getNAME() { + return NAME; + } + + public boolean isRaw() { + return raw; + } + + public boolean isDir() { + return dir; + } } diff --git a/container-builder-helper/build.gradle b/container-builder-helper/build.gradle index fdee90e..0066e18 100644 --- a/container-builder-helper/build.gradle +++ b/container-builder-helper/build.gradle @@ -1,3 +1,5 @@ +import com.virjar.ratel.allcommon.ClassNames + buildscript { repositories { maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } @@ -26,6 +28,7 @@ dependencies { api 'org.smali:util:2.2.7' api 'org.smali:baksmali:2.2.7' api 'org.smali:smali:2.2.7' + api(name: 'base-lib-allcommon', ext: 'jar') } @@ -39,7 +42,7 @@ shadowJar { classifier = null version = 1.0 manifest { - attributes 'Main-Class': 'com.virjar.ratel.builder.helper.Main' + attributes 'Main-Class': ClassNames.BUILDER_HELPER_MAIN.className } } assemble.dependsOn(shadowJar) \ No newline at end of file diff --git a/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/Main.java b/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/Main.java index 31a880a..31cb6a0 100644 --- a/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/Main.java +++ b/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/Main.java @@ -1,5 +1,6 @@ package com.virjar.ratel.builder.helper; +import com.virjar.ratel.allcommon.ClassNames; import com.virjar.ratel.builder.helper.apk2jar.APK2Jar; import com.virjar.ratel.builder.helper.bro.OptimizeBuilderResource; @@ -14,6 +15,7 @@ public class Main { * 本模块干的都是脏活儿累活儿,他不需要考虑资源大小问题,因为他最终不会出现在发布版本的构建工具中 */ public static void main(String[] args) throws Exception { + ClassNames.BUILDER_HELPER_MAIN.check(Main.class); if (args.length == 0) { showHelpMessage(); return; diff --git a/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/apk2jar/AndroidJarUtil.java b/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/apk2jar/AndroidJarUtil.java index 3959ac0..8421f31 100644 --- a/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/apk2jar/AndroidJarUtil.java +++ b/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/apk2jar/AndroidJarUtil.java @@ -54,6 +54,12 @@ public static void transformApkToAndroidJar(File apkFile, File outJarFile, Packa //清除无意义的class,因为我们使用了Android的打包工具链,存在一些android本身的class,这些class对于我们来说没有意义 zipOutputStream.write(cleanClasses(zipFile.getInputStream(zipEntry), packageTrie)); } + } else if (zipEntry.getName().startsWith("lib/") + // || zipEntry.getName().startsWith("assets/") + ) { + zipOutputStream.putNextEntry(new ZipEntry(zipEntry.getName())); + // lib资源需要迁移过去,因为我们构建的时候需要lib资源 + zipOutputStream.write(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); } } } diff --git a/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/bro/OptimizeBuilderResource.java b/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/bro/OptimizeBuilderResource.java index 8466d23..4c9c146 100644 --- a/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/bro/OptimizeBuilderResource.java +++ b/container-builder-helper/src/main/java/com/virjar/ratel/builder/helper/bro/OptimizeBuilderResource.java @@ -1,19 +1,41 @@ package com.virjar.ratel.builder.helper.bro; +import com.google.common.io.Files; +import com.virjar.ratel.allcommon.NewConstants; +import com.virjar.ratel.builder.helper.apk2jar.APK2Jar; + import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile; +import org.apache.tools.zip.ZipOutputStream; +import org.jf.baksmali.Baksmali; +import org.jf.baksmali.BaksmaliOptions; +import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.writer.io.MemoryDataStore; +import org.jf.dexlib2.writer.pool.DexPool; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; public class OptimizeBuilderResource { public static void main(String[] args) throws Exception { Options options = new Options(); - options.addOption(new Option("", "input", true, "input apk file")); - options.addOption(new Option("", "output", true, "output jar file")); + options.addOption(new Option("i", "input", true, "input apk file")); + options.addOption(new Option("o", "output", true, "output jar file")); options.addOption(new Option("h", "help", false, "show help message")); DefaultParser parser = new DefaultParser(); @@ -26,10 +48,223 @@ public static void main(String[] args) throws Exception { } File builderJarInputFile = new File(cmd.getOptionValue("input")); + String output = cmd.getOptionValue("output"); + Map optimizeData = null; + ByteArrayOutputStream byteArrayOutputStream; try (ZipFile zipFile = new ZipFile(builderJarInputFile)) { + optimizeData = handleJarInput(zipFile); + + if (output.endsWith("/")) { + writeDataToDir(output, optimizeData); + return; + } + if (!output.endsWith(".jar")) { + throw new IllegalArgumentException("the output file must be jar file"); + } + byteArrayOutputStream = new ByteArrayOutputStream(); + try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { + writeDataToNewJar(zipOutputStream, zipFile, optimizeData); + } + } + FileUtils.writeByteArrayToFile(new File(output), byteArrayOutputStream.toByteArray()); + } + + private static void writeDataToNewJar(ZipOutputStream zos, ZipFile zipFile, Map optimizeData) throws IOException { + HashMap data = new HashMap<>(); + for (NewConstants.BUILDER_RESOURCE_LAYOUT layout : optimizeData.keySet()) { + data.put(layout.getNAME(), optimizeData.get(layout)); + } + Enumeration entries = zipFile.getEntries(); + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + if (NewConstants.BUILDER_RESOURCE_LAYOUT.BUILDER_HELPER_NAME2.getNAME().equals(zipEntry.getName())) { + // 对于jar包本身,在构建工具完成优化之后,则不在需要helper了,此时需要把helper干掉 + // 但是在开发环境下,由于需要支持AndroidStudio的单步调试,这个时候需要把helper包含在其中,使用helper在调试的时候进行binder resource handling + continue; + } + zos.putNextEntry(new ZipEntry(zipEntry)); + + + byte[] bytes = data.remove(zipEntry.getName()); + if (bytes != null) { + IOUtils.write(bytes, zos); + } else { + IOUtils.copy(zipFile.getInputStream(zipEntry), zos); + } + } + for (String key : data.keySet()) { + zos.putNextEntry(new ZipEntry(key)); + IOUtils.write(data.get(key), zos); + } + } + + private static void writeDataToDir(String output, Map optimizeData) throws IOException { + //释放到文件夹下,这个时候应该是调试模式下, + File dir = new File(output); + for (NewConstants.BUILDER_RESOURCE_LAYOUT resource : optimizeData.keySet()) { + final File file = new File(dir, resource.getNAME()); + FileUtils.forceMkdirParent(file); + FileUtils.writeByteArrayToFile(file, optimizeData.get(resource)); + } + } + + private static Map handleJarInput(ZipFile zipFile) throws Exception { + Map optimizeData = new HashMap<>(); + + // runtime 核心文件 + handleResource(zipFile, + NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_APK_FILE, + NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_JAR_FILE, + new Apk2jarOptimizer("apk_to_jar.keep.runtime"), optimizeData); + + // xposed兼容层 + handleResource(zipFile, + NewConstants.BUILDER_RESOURCE_LAYOUT.XPOSED_BRIDGE_APK_FILE, + NewConstants.BUILDER_RESOURCE_LAYOUT.XPOSED_BRIDGE_JAR_FILE, + new Apk2jarOptimizer("apk_to_jar.keep.xposedBridge"), optimizeData); + + + // 注入使用的模版文件 + handleResource(zipFile, + NewConstants.BUILDER_RESOURCE_LAYOUT.TEMPLATE_APK_FILE, + NewConstants.BUILDER_RESOURCE_LAYOUT.TEMPLATE_DEX_FILE, + new TemplateApkOptimizer(), optimizeData); + + // 模版文件解压为smali + unpackTemplateSmali(optimizeData); + return optimizeData; + + } + + private static void unpackTemplateSmali(Map optimizeData) throws IOException { + final byte[] bytes = optimizeData.get(NewConstants.BUILDER_RESOURCE_LAYOUT.TEMPLATE_DEX_FILE); + DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.getDefault(), bytes); + final BaksmaliOptions options = new BaksmaliOptions(); + + options.localsDirective = true; + options.sequentialLabels = true; + options.accessorComments = false; + + + File tempDir = Files.createTempDir(); + // 小文件,其实一个线程就够了 + Baksmali.disassembleDexFile(dexFile, tempDir, Runtime.getRuntime().availableProcessors(), options); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { + addToZip(zipOutputStream, tempDir, tempDir); + } + FileUtils.deleteDirectory(tempDir); + optimizeData.put(NewConstants.BUILDER_RESOURCE_LAYOUT.TEMPLATE_SMALI_ZIP_FILE, byteArrayOutputStream.toByteArray()); + } + + private static void addToZip(ZipOutputStream zos, File root, File nowFile) throws IOException { + String name = nowFile.getAbsolutePath().substring(root.getAbsolutePath().length()); + if (name.startsWith("/")) { + name = name.substring(1); + } + if (nowFile.isFile()) { + zos.putNextEntry(new ZipEntry(name)); + IOUtils.copy(new FileInputStream(nowFile), zos); + return; + } + if (!nowFile.isDirectory()) { + return; + } + final File[] files = nowFile.listFiles(); + if (files == null) { + return; + } + if (!name.isEmpty()) { + zos.putNextEntry(new ZipEntry(name + "/")); + } + for (File file : files) { + addToZip(zos, root, file); + } + } + + private static class TemplateApkOptimizer implements Transformer { + @Override + public byte[] transform(byte[] input) throws Exception { + // 对于模版apk,首先需要进行一次class优化,删除无效的class + byte[] optimizedJarData = new Apk2jarOptimizer("apk_to_jar.keep.injectTemplate").transform(input); + File outJar = File.createTempFile("ratel-template-tmp", ".jar"); + FileUtils.writeByteArrayToFile(outJar, optimizedJarData); + // 把所有的dex合并到同一个dex文件中,由于这是模版文件,这里完全不用考虑dex膨胀溢出问题 + DexPool dexPool = new DexPool(Opcodes.getDefault()); + try (ZipFile zipFile = new ZipFile(outJar)) { + Pattern classesPattern = Pattern.compile("classes(\\d*)\\.dex"); + Enumeration entries = zipFile.getEntries(); + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + if (!classesPattern.matcher(zipEntry.getName()).matches()) { + continue; + } + DexBackedDexFile dexBackedDexFile = new DexBackedDexFile(Opcodes.getDefault(), IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); + for (ClassDef classDef : dexBackedDexFile.getClasses()) { + dexPool.internClass(classDef); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + MemoryDataStore memoryDataStore = new MemoryDataStore(); + dexPool.writeTo(memoryDataStore); + memoryDataStore.close(); + return memoryDataStore.getData(); } + } + + private static class Apk2jarOptimizer implements Transformer { + private final String scene; + + public Apk2jarOptimizer(String scene) { + this.scene = scene; + } + + @Override + public byte[] transform(byte[] input) throws Exception { + File apkFile = File.createTempFile("ratel-apk2jar-tmp", ".apk"); + File outJar = File.createTempFile("ratel-apk2jar-tmp", ".jar"); + FileUtils.writeByteArrayToFile(apkFile, input); + APK2Jar.main(new String[]{ + "--input", + apkFile.getAbsolutePath(), + "--output", + outJar.getAbsolutePath(), + "--keepScene", + scene + }); + ZipFile zipFile = null; + try { + zipFile = new ZipFile(outJar); + } finally { + IOUtils.closeQuietly(zipFile); + } + return FileUtils.readFileToByteArray(outJar); + } + } + private static void handleResource(ZipFile zipFile, NewConstants.BUILDER_RESOURCE_LAYOUT rowKey, NewConstants.BUILDER_RESOURCE_LAYOUT optimizedKey, Transformer transformer, Map optimizeData) throws Exception { + ZipEntry rowResource = zipFile.getEntry(rowKey.getNAME()); + if (rowResource != null) { + byte[] bytes = IOUtils.toByteArray(zipFile.getInputStream(rowResource)); + optimizeData.put(optimizedKey, transformer.transform(bytes)); + return; + } + ZipEntry optimizeResource = zipFile.getEntry(optimizedKey.getNAME()); + if (optimizeResource != null) { + optimizeData.put(optimizedKey, IOUtils.toByteArray(zipFile.getInputStream(optimizeResource))); + return; + } + throw new IOException("can not find resource from jar file with key: (" + rowKey + "," + optimizedKey + ")"); + + } + + private interface Transformer { + byte[] transform(byte[] input) throws Exception; } } diff --git a/container-builder-helper/src/main/resources/config.properties b/container-builder-helper/src/main/resources/config.properties index 41aca43..7a3935d 100644 --- a/container-builder-helper/src/main/resources/config.properties +++ b/container-builder-helper/src/main/resources/config.properties @@ -3,7 +3,7 @@ apk_to_jar.keep.runtime=com.virjar,external.org.apache,external.okio,mirror,de.s # xposedBridge用于兼容xposed,只保留xposed官方的class apk_to_jar.keep.xposedBridge=de.robv.android.xposed,android.app.AndroidAppHelper # 注入和引导相关代码,以及提供SmaliLog等模版的代码,包名必须在com.virjar下 -apk_to_jar.keep.injectTemplate=com.virjar +apk_to_jar.keep.injectTemplate=com.virjar.ratel.inject,com.virjar.ratel.allcommon diff --git a/container-builder-repkg/BuilderRepkgBase.gradle b/container-builder-repkg/BuilderRepkgBase.gradle index cead1ad..9f7584b 100644 --- a/container-builder-repkg/BuilderRepkgBase.gradle +++ b/container-builder-repkg/BuilderRepkgBase.gradle @@ -1,4 +1,3 @@ - buildscript { repositories { repositories { @@ -8,7 +7,7 @@ buildscript { } } dependencies { - classpath (name: 'base-lib-allcommon', ext: 'jar') + classpath(name: 'base-lib-allcommon', ext: 'jar') } } import com.virjar.ratel.allcommon.Constants @@ -49,7 +48,7 @@ dependencies { compile group: 'net.dongliu', name: 'apk-parser', version: '2.6.10' compile project(':base-lib-apksigner') compile project(':base-lib-pxb-axml') - compile (name: 'base-lib-allcommon', ext: 'jar') + compile(name: 'base-lib-allcommon', ext: 'jar') } sourceCompatibility = 1.8 @@ -144,7 +143,7 @@ task injectXposedBridgeAPK(type: Copy) { from '../base-lib-xposed-bridge/build/outputs/apk/release/base-lib-xposed-bridge-release.apk' into 'src/main/resources/' rename { - Constants.xposedApiBridgeAPKFileName + NewConstants.BUILDER_RESOURCE_LAYOUT.XPOSED_BRIDGE_APK_FILE.NAME } } @@ -164,17 +163,32 @@ task injectRDPJar(type: Copy) { injectRDPJar.dependsOn(':container-builder-rdp:assemble') processResources.dependsOn(injectRDPJar) +// 构建工具的优化器 task injectBuilderHelperJar(type: Copy) { from '../container-builder-helper/build/libs/BuilderHelper-1.0.jar' into 'src/main/resources/' rename { - NewConstants.BUILDER_RESOURCE_LAYOUT.BUILDER_HELPER_NAME + NewConstants.BUILDER_RESOURCE_LAYOUT.BUILDER_HELPER_NAME2.NAME } } injectBuilderHelperJar.dependsOn(':container-builder-helper:assemble') processResources.dependsOn(injectBuilderHelperJar) +// 注入代码模版工程 +task injectTemplateApk(type: Copy) { + from '../container-inject-template/build/outputs/apk/release/container-inject-template-release.apk' + into 'src/main/resources/' + rename { + NewConstants.BUILDER_RESOURCE_LAYOUT.TEMPLATE_APK_FILE.NAME + } +} + +injectTemplateApk.dependsOn(':container-inject-template:assembleRelease') +processResources.dependsOn(injectTemplateApk) + + + task storeBuildConstantsProperties() { Properties engineProperties = new Properties() diff --git a/container-builder-repkg/build-debug.gradle b/container-builder-repkg/build-debug.gradle index 837fc73..28f8caf 100644 --- a/container-builder-repkg/build-debug.gradle +++ b/container-builder-repkg/build-debug.gradle @@ -1,4 +1,5 @@ import com.virjar.ratel.allcommon.Constants +import com.virjar.ratel.allcommon.NewConstants buildscript { repositories { @@ -19,7 +20,7 @@ task injectRuntimeAPK(type: Copy) { from '../container-runtime-repkg/build/outputs/apk/debug/container-runtime-repkg-debug.apk' into 'src/main/resources/' rename { - Constants.RATEL_ENGINE_APK + NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_APK_FILE.NAME } } injectRuntimeAPK.dependsOn(':container-runtime-repkg:assembleDebug') diff --git a/container-builder-repkg/build-release.gradle b/container-builder-repkg/build-release.gradle index 341797c..2bddec6 100644 --- a/container-builder-repkg/build-release.gradle +++ b/container-builder-repkg/build-release.gradle @@ -1,4 +1,5 @@ import com.virjar.ratel.allcommon.Constants +import com.virjar.ratel.allcommon.NewConstants buildscript { repositories { @@ -22,7 +23,7 @@ task injectRuntimeAPK(type: Copy) { from '../container-runtime-repkg/build/outputs/apk/release/container-runtime-repkg-release.apk' into 'src/main/resources/' rename { - Constants.RATEL_ENGINE_APK + NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_APK_FILE.NAME } } injectRuntimeAPK.dependsOn(':container-runtime-repkg:assembleRelease') diff --git a/container-builder-repkg/build.gradle b/container-builder-repkg/build.gradle index 837fc73..cbfa73c 100644 --- a/container-builder-repkg/build.gradle +++ b/container-builder-repkg/build.gradle @@ -1,3 +1,4 @@ +import com.virjar.ratel.allcommon.NewConstants import com.virjar.ratel.allcommon.Constants buildscript { @@ -19,7 +20,7 @@ task injectRuntimeAPK(type: Copy) { from '../container-runtime-repkg/build/outputs/apk/debug/container-runtime-repkg-debug.apk' into 'src/main/resources/' rename { - Constants.RATEL_ENGINE_APK + NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_APK_FILE.NAME } } injectRuntimeAPK.dependsOn(':container-runtime-repkg:assembleDebug') diff --git a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/BootstrapCodeInjector.java b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/BootstrapCodeInjector.java index 9ef1246..42c3373 100644 --- a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/BootstrapCodeInjector.java +++ b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/BootstrapCodeInjector.java @@ -3,6 +3,7 @@ import com.google.common.base.Charsets; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.virjar.ratel.allcommon.ClassNames; import com.virjar.ratel.allcommon.Constants; import org.apache.commons.io.FileUtils; @@ -92,15 +93,17 @@ public static File injectBootstrapCode(File dexImage, File runtimeSmaliDir = makeSureRuntimeSmaliDir(workDir); - FileUtils.copyFile( - new File(bootstrapAPKDecodeDir, "smali/com/virjar/container_runtimer_repkg_bootstrap/BootStrap.smali"), - new File(runtimeSmaliDir, "com/virjar/container_runtimer_repkg_bootstrap/BootStrap.smali") + String bootStrapSmaliFilePath = Util.classNameToSmaliPath(ClassNames.INJECT_REBUILD_BOOTSTRAP.getClassName()); + + FileUtils.copyFile(new File(bootstrapAPKDecodeDir, bootStrapSmaliFilePath), + new File(runtimeSmaliDir, bootStrapSmaliFilePath) ); if (injectLogComponent) { System.out.println("inject ratel SmaliLog component!!"); + String logSmaliFilePath = Util.classNameToSmaliPath(ClassNames.INJECT_TOOL_SMALI_LOG.getClassName()); FileUtils.copyFile( - new File(bootstrapAPKDecodeDir, "smali/com/virjar/container_runtimer_repkg_bootstrap/RatelSmaliLog.smali"), - new File(runtimeSmaliDir, "com/virjar/container_runtimer_repkg_bootstrap/RatelSmaliLog.smali") + new File(bootstrapAPKDecodeDir, logSmaliFilePath), + new File(runtimeSmaliDir, logSmaliFilePath) ); } @@ -291,9 +294,11 @@ private static boolean doStaticLink(String applicationClassName, File applicatio File bootstrapAPKDecodeDir, File runtimeSmaliDir, File dexImage, boolean decodeAllSmali, boolean force ) throws IOException { + String bootStrapCintSmaliFilePath = Util.classNameToSmaliPath(ClassNames.INJECT_REBUILD_BOOTSTRAP_CINT.getClassName()); + String bootStrapCintNativeName = ClassNames.INJECT_REBUILD_BOOTSTRAP_CINT.getClassName().replaceAll("\\.", "/"); FileUtils.copyFile( - new File(bootstrapAPKDecodeDir, "smali/com/virjar/container_runtimer_repkg_bootstrap/BootStrapWithStaticInit.smali"), - new File(runtimeSmaliDir, "com/virjar/container_runtimer_repkg_bootstrap/BootStrapWithStaticInit.smali") + new File(bootstrapAPKDecodeDir, bootStrapCintSmaliFilePath), + new File(runtimeSmaliDir, bootStrapCintSmaliFilePath) ); if (decodeAllSmali) { @@ -339,7 +344,7 @@ private static boolean doStaticLink(String applicationClassName, File applicatio newCodeLines.add(" .line 0"); } newCodeLines.add(".locals 0"); - newCodeLines.add(" invoke-static {}, Lcom/virjar/container_runtimer_repkg_bootstrap/BootStrapWithStaticInit;->startup()V"); + newCodeLines.add(" invoke-static {}, L" + bootStrapCintNativeName + ";->startup()V"); newCodeLines.add(" "); newCodeLines.add(" return-void"); newCodeLines.add(".end method"); @@ -357,7 +362,7 @@ private static boolean doStaticLink(String applicationClassName, File applicatio if (hasLineDeclared) { newCodeLines.add(" .line 0"); } - newCodeLines.add(" invoke-static {}, Lcom/virjar/container_runtimer_repkg_bootstrap/BootStrapWithStaticInit;->startup()V"); + newCodeLines.add(" invoke-static {}, L" + bootStrapCintNativeName + ";->startup()V"); newCodeLines.add(" "); //copy after @@ -489,6 +494,7 @@ private static void handleAttachBaseContext(String applicationClassName, DexBack } else { baksmali(applicationClassName, DexFileFactory.loadDexFile(dexImage, Opcodes.getDefault()), runtimeSmaliDir); } + String bootStrapNativeName = ClassNames.INJECT_REBUILD_BOOTSTRAP.getClassName().replaceAll("\\.", "/"); boolean findAttacheBaseContextMethod = false; for (DexBackedMethod dexBackedMethod : dexClass.getMethods()) { @@ -556,14 +562,14 @@ private static void handleAttachBaseContext(String applicationClassName, DexBack if (hasLineDeclared) { newCodeLines.add(" .line 2"); } - newCodeLines.add(" invoke-static {v8, v2}, Lcom/virjar/container_runtimer_repkg_bootstrap/BootStrap;->startUp(Landroid/content/Context;Landroid/content/Context;)V"); + newCodeLines.add(" invoke-static {v8, v2}, L" + bootStrapNativeName + ";->startUp(Landroid/content/Context;Landroid/content/Context;)V"); newCodeLines.add(" "); } else { if (hasLineDeclared) { newCodeLines.add(" .line 0"); } - newCodeLines.add(" invoke-static {p1, p0}, Lcom/virjar/container_runtimer_repkg_bootstrap/BootStrap;->startUp(Landroid/content/Context;Landroid/content/Context;)V"); + newCodeLines.add(" invoke-static {p1, p0}, L" + bootStrapNativeName + ";->startUp(Landroid/content/Context;Landroid/content/Context;)V"); newCodeLines.add(" "); } @@ -586,7 +592,7 @@ private static void handleAttachBaseContext(String applicationClassName, DexBack " .param p1, \"base\" # Landroid/content/Context;\n" + "\n" + " .line 12\n" + - " invoke-static {p1,p0}, Lcom/virjar/container_runtimer_repkg_bootstrap/BootStrap;->startUp(Landroid/content/Context;Landroid/content/Context;)V\n" + + " invoke-static {p1,p0}, L" + bootStrapNativeName + ";->startUp(Landroid/content/Context;Landroid/content/Context;)V\n" + "\n" + " .line 13\n" + " invoke-super {p0, p1}, " + superclass + "->attachBaseContext(Landroid/content/Context;)V\n" + diff --git a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/Util.java b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/Util.java index 3666e29..caf6fe6 100644 --- a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/Util.java +++ b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/Util.java @@ -2,6 +2,7 @@ import com.google.common.base.Defaults; import com.google.common.base.Splitter; +import com.virjar.ratel.allcommon.ClassNames; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -48,6 +49,12 @@ private static String primitiveTypeLabel(char typeChar) { } } + public static String classNameToSmaliPath(String className) { + String nativeName = className.replaceAll("\\.", "/"); + return nativeName + ".smali"; + } + + /** * Converts a type descriptor to human-readable "dotted" form. For * example, "Ljava/lang/String;" becomes "java.lang.String", and diff --git a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/mode/RatelPackageBuilderRepackage.java b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/mode/RatelPackageBuilderRepackage.java index 1933a37..2fdcef8 100644 --- a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/mode/RatelPackageBuilderRepackage.java +++ b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/mode/RatelPackageBuilderRepackage.java @@ -2,6 +2,7 @@ import com.virjar.ratel.allcommon.Constants; +import com.virjar.ratel.allcommon.NewConstants; import com.virjar.ratel.builder.BootstrapCodeInjector; import com.virjar.ratel.builder.BuildParamMeta; import com.virjar.ratel.builder.DexMergeFailedException; @@ -13,6 +14,7 @@ import com.virjar.ratel.builder.manifesthandler.AXmlEditorCmdHandler; import com.virjar.ratel.builder.manifesthandler.EnableDebug; import com.virjar.ratel.builder.manifesthandler.RequestLegacyExternalStorage; +import com.virjar.ratel.builder.ratelentry.BindingResourceManager; import net.dongliu.apk.parser.utils.Pair; @@ -40,18 +42,15 @@ import java.util.Properties; import java.util.regex.Matcher; -import brut.androlib.AndrolibException; -import brut.androlib.ApkDecoder; -import brut.directory.DirectoryException; public class RatelPackageBuilderRepackage { public static void handleTask(File workDir, Param param, BuildParamMeta buildParamMeta, Properties ratelBuildProperties, ZipOutputStream zos, CommandLine cmd - ) throws IOException, AndrolibException, DirectoryException { + ) throws IOException { File bootstrapDecodeDir = new File(workDir, "ratel_bootstrap_apk"); - decodeBootstrapAPK(new File(workDir, Constants.bootstrapAPKPath), bootstrapDecodeDir); + decodeBootstrapAPK(bootstrapDecodeDir); System.out.println("work dir: " + workDir.getCanonicalPath()); @@ -198,7 +197,7 @@ public static void handleTask(File workDir, Param param, BuildParamMeta buildPar //close originAPKZip.close(); - Util.copyAssets(zos, new File(workDir, Constants.RATEL_ENGINE_JAR), Constants.RATEL_ENGINE_JAR); + Util.copyAssets(zos, BindingResourceManager.get(NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_JAR_FILE), Constants.RATEL_ENGINE_JAR); } @@ -220,21 +219,25 @@ private static File createOrGetDex(String dexIndex, File originApk, File workDir } - private static void decodeBootstrapAPK(File bootstrapAPK, File outDir) throws AndrolibException, IOException, DirectoryException { - System.out.println("decode oringin apk:" + bootstrapAPK.getAbsolutePath() + "..."); - ApkDecoder decoder = new ApkDecoder(); - decoder.setApkFile(bootstrapAPK); - - decoder.setOutDir(outDir); - //不对源码进行解码 - decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE); - decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_SMALI); - // decoder.setKeepBrokenResources(true); - decoder.setForceDelete(true); - decoder.setForceDecodeManifest(ApkDecoder.FORCE_DECODE_MANIFEST_NONE); - decoder.setDecodeAssets(ApkDecoder.DECODE_ASSETS_NONE); - decoder.decode(); - decoder.close(); + private static void decodeBootstrapAPK(File outDir) throws IOException { + File templateZip = BindingResourceManager.get(NewConstants.BUILDER_RESOURCE_LAYOUT.TEMPLATE_SMALI_ZIP_FILE); + try (ZipFile zipFile = new ZipFile(templateZip)) { + Enumeration entries = zipFile.getEntries(); + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + if (zipEntry.isDirectory()) { + FileUtils.forceMkdir(new File(outDir, zipEntry.getName())); + continue; + } + File file = new File(outDir, zipEntry.getName()); + FileUtils.forceMkdirParent(file); + try (InputStream inputStream = zipFile.getInputStream(zipEntry)) { + FileUtils.writeByteArrayToFile(file, IOUtils.toByteArray(inputStream)); + } + } + } + + } diff --git a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/ratelentry/BindingResourceManager.java b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/ratelentry/BindingResourceManager.java new file mode 100644 index 0000000..17f77cd --- /dev/null +++ b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/ratelentry/BindingResourceManager.java @@ -0,0 +1,103 @@ +package com.virjar.ratel.builder.ratelentry; + +import com.virjar.ratel.allcommon.ClassNames; +import com.virjar.ratel.allcommon.NewConstants; +import com.virjar.ratel.allcommon.ReflectUtil; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.tools.zip.ZipEntry; +import org.apache.tools.zip.ZipOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * 处理打包到rebuilder中的附属资源逻辑,包括资源数据索引,资源优化,解压等 + */ +public class BindingResourceManager { + + private static File workDir; + + public static File get(NewConstants.BUILDER_RESOURCE_LAYOUT layout) { + return new File(workDir, layout.getNAME()); + } + + public static void extract(File workDir) throws IOException { + BindingResourceManager.workDir = workDir; + // todo 这部分数据可以直接缓存,理论上每一个引擎版本都可以有一份缓存文件,extract流程只需要处理一次 + URL runtimeUrl = BindingResourceManager.class.getClassLoader().getResource(NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_JAR_FILE.getNAME()); + if (runtimeUrl == null) { + // 请注意,这个分支只有AndroidStudio调试的时候才会走 + // 发布版本和dex + doOptimizeExtract(workDir); + return; + } + directExtract(workDir); + } + + private static void directExtract(File baseDir) throws IOException { + ClassLoader classLoader = BindingResourceManager.class.getClassLoader(); + for (NewConstants.BUILDER_RESOURCE_LAYOUT layout : NewConstants.BUILDER_RESOURCE_LAYOUT.values()) { + if (!layout.isDir()) { + InputStream inputStream = classLoader.getResourceAsStream(layout.getNAME()); + if (inputStream == null) { + continue; + } + File file = new File(baseDir, layout.getNAME()); + FileUtils.forceMkdirParent(file); + try (FileOutputStream fileOutputStream = new FileOutputStream(file)) { + IOUtils.copy(inputStream, fileOutputStream); + } + } + } + } + + + private static void doOptimizeExtract(File workDir) throws IOException { + ClassLoader classLoader = BindingResourceManager.class.getClassLoader(); + // 伪造一个jar包 + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { + for (NewConstants.BUILDER_RESOURCE_LAYOUT layout : NewConstants.BUILDER_RESOURCE_LAYOUT.values()) { + if (layout.isRaw() && !layout.isDir()) { + zipOutputStream.putNextEntry(new ZipEntry(layout.getNAME())); + InputStream inputStream = classLoader.getResourceAsStream(layout.getNAME()); + if (inputStream == null) { + throw new IOException("can not find resource: " + layout.getNAME()); + } + IOUtils.copy(inputStream, zipOutputStream); + } + } + } + File tempFile = File.createTempFile("fake-binding-resource-builder", ".jar"); + FileUtils.writeByteArrayToFile(tempFile, byteArrayOutputStream.toByteArray()); + + File helperJar = new File(workDir, NewConstants.BUILDER_RESOURCE_LAYOUT.BUILDER_HELPER_NAME2.getNAME()); + FileUtils.forceMkdirParent(helperJar); + + try (final FileOutputStream fileOutputStream = new FileOutputStream(helperJar)) { + IOUtils.copy(classLoader.getResourceAsStream(NewConstants.BUILDER_RESOURCE_LAYOUT.BUILDER_HELPER_NAME2.getNAME()), + fileOutputStream); + } + + URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{helperJar.toURL()}); + + Class helperMainClass = ReflectUtil.findClass(ClassNames.BUILDER_HELPER_MAIN.getClassName(), urlClassLoader); + + ReflectUtil.callStaticMethod(helperMainClass, "main", + (Object) new String[]{ + "OPTIMIZE_BUILDER_RESOURCE", + "-i", + tempFile.getAbsolutePath(), + "-o", + workDir.getAbsolutePath() + "/" + }); + FileUtils.deleteQuietly(tempFile); + } +} diff --git a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/ratelentry/Main.java b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/ratelentry/Main.java index c07776c..0e03e9a 100644 --- a/container-builder-repkg/src/main/java/com/virjar/ratel/builder/ratelentry/Main.java +++ b/container-builder-repkg/src/main/java/com/virjar/ratel/builder/ratelentry/Main.java @@ -10,7 +10,9 @@ import com.google.common.base.Splitter; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.virjar.ratel.allcommon.ClassNames; import com.virjar.ratel.allcommon.Constants; +import com.virjar.ratel.allcommon.NewConstants; import com.virjar.ratel.allcommon.StandardEncryptor; import com.virjar.ratel.builder.AndroidJarUtil; import com.virjar.ratel.builder.BootstrapCodeInjector; @@ -93,6 +95,7 @@ public class Main { public static void main(String[] args) throws Exception { + ClassNames.BUILDER_MAIN.check(Main.class); int xApkIndex = -1; for (int i = 0; i < args.length; i++) { if (args[i].endsWith(".xapk")) { @@ -256,54 +259,8 @@ private static File ratelMain(String[] args, com.virjar.ratel.builder.ratelentry System.out.println("clean working directory:" + workDir.getAbsolutePath()); - File driverAPKFile = new File(workDir, Constants.RATEL_ENGINE_APK); - System.out.println("release ratel container apk ,into :" + driverAPKFile.getAbsolutePath()); - copyAndClose(Main.class.getClassLoader().getResourceAsStream(Constants.RATEL_ENGINE_APK), new FileOutputStream(driverAPKFile)); - - File ratelRuntimeJarFile = new File(workDir, Constants.RATEL_ENGINE_JAR); - System.out.println("transform ratel runtime apk to jar format:" + ratelRuntimeJarFile.getAbsolutePath()); - AndroidJarUtil.transformApkToAndroidJar(driverAPKFile, ratelRuntimeJarFile, new PackageTrie() - //混淆模式下,需要保留的class - .addToTree("com.virjar") - .addToTree("external.org.apache") - .addToTree("external.okio") - //正常模式下,需要保留的class - .addToTree("mirror") - .addToTree("de.schlichtherle.util") - .addToTree("net.kk.plus.preference") - .addToTree("external.com.android.dx") - .addToTree("external.com.android.dex") - .addToTree("external.com.android.multidex") - ); - - File xposedBridgeAPKFile = new File(workDir, Constants.xposedApiBridgeAPKFileName); - System.out.println("release xposed bridge apk info:" + xposedBridgeAPKFile.getAbsolutePath()); - copyAndClose(Main.class.getClassLoader().getResourceAsStream(Constants.xposedApiBridgeAPKFileName), new FileOutputStream(xposedBridgeAPKFile)); - File xposedBridgeJarFile = new File(workDir, Constants.xposedApiBridgeJarFileName); - System.out.println("transform xposedBridge apk to jar format:" + xposedBridgeJarFile.getAbsolutePath()); - AndroidJarUtil.transformApkToAndroidJar(xposedBridgeAPKFile, xposedBridgeJarFile, new PackageTrie().addToTree("de.robv.android.xposed").addToTree("android.app.AndroidAppHelper")); - - File bootstrapAPKFile = new File(workDir, Constants.bootstrapAPKPath); - System.out.println("release ratel bootstrap apk ,into :" + bootstrapAPKFile.getAbsolutePath()); - copyAndClose(Main.class.getClassLoader().getResourceAsStream(Constants.bootstrapAPKPath), new FileOutputStream(bootstrapAPKFile)); - - - File multiDexBootstrapAPKFile = new File(workDir, Constants.ratelMultiDexBootstrapApkPath); - System.out.println("release multi apk bootstrap apk into: " + multiDexBootstrapAPKFile.getAbsolutePath()); - copyAndClose(Main.class.getClassLoader().getResourceAsStream(Constants.ratelMultiDexBootstrapApkPath), new FileOutputStream(multiDexBootstrapAPKFile)); - File multiDexBootstrapJarFile = new File(workDir, Constants.ratelMultiDexBootstrapJarPath); - System.out.println("transform multi apk bootstrap apk to jar format:" + multiDexBootstrapJarFile.getAbsolutePath()); - AndroidJarUtil.transformApkToAndroidJar(multiDexBootstrapAPKFile, multiDexBootstrapJarFile, new PackageTrie().addToTree("com.virjar")); - - - File zeldaBootstrapAPKFile = new File(workDir, Constants.ratelZeldaBootstrapApkPath); - System.out.println("release zelda apk bootstrap apk into: " + zeldaBootstrapAPKFile.getAbsolutePath()); - copyAndClose(Main.class.getClassLoader().getResourceAsStream(Constants.ratelZeldaBootstrapApkPath), new FileOutputStream(zeldaBootstrapAPKFile)); - File zeldaBootstrapJarFile = new File(workDir, Constants.ratelZeldaBootstrapJarPath); - System.out.println("transform zelda apk bootstrap apk to jar format:" + zeldaBootstrapJarFile.getAbsolutePath()); - //只能保留 com.virjar - AndroidJarUtil.transformApkToAndroidJar(zeldaBootstrapAPKFile, zeldaBootstrapJarFile, new PackageTrie().addToTree("com.virjar")); + BindingResourceManager.extract(workDir); File signatureKeyFile = new File(workDir, Constants.ratelDefaultApkSignatureKey); System.out.println("release ratel default apk signature key into : " + signatureKeyFile.getAbsolutePath()); @@ -391,7 +348,7 @@ private static File ratelMain(String[] args, com.virjar.ratel.builder.ratelentry Util.copyAssets(zos, param.xposedApk, Constants.xposedBridgeApkFileName); } - Util.copyAssets(zos, xposedBridgeJarFile, Constants.xposedApiBridgeJarFileName); + Util.copyAssets(zos, BindingResourceManager.get(NewConstants.BUILDER_RESOURCE_LAYOUT.XPOSED_BRIDGE_JAR_FILE), Constants.xposedApiBridgeJarFileName); //请注意,shell模式下,不需要copy driver的资源 ratelBuildProperties.setProperty(Constants.hasEmbedXposedModuleKey, String.valueOf(param.xposedApk != null)); @@ -407,7 +364,7 @@ private static File ratelMain(String[] args, com.virjar.ratel.builder.ratelentry if (param.xposedApk != null) { Util.copyLibrary(zos, supportArch, param.xposedApk); } - Util.copyLibrary(zos, supportArch, new File(workDir, Constants.RATEL_ENGINE_APK)); + Util.copyLibrary(zos, supportArch, BindingResourceManager.get(NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_JAR_FILE)); // } else { // // xapk模式下,so不能植入到主包,而是置入到分包 // if (param.xposedApk != null) { @@ -418,7 +375,7 @@ private static File ratelMain(String[] args, com.virjar.ratel.builder.ratelentry // } //so文件需要插入到asset目录,因为 execve 注入会依赖so,且依赖多个版本 - insertRatelNativeLib(zos, new File(workDir, Constants.RATEL_ENGINE_APK)); + insertRatelNativeLib(zos, BindingResourceManager.get(NewConstants.BUILDER_RESOURCE_LAYOUT.RUNTIME_JAR_FILE)); //add dexmaker opt file if exist File dexMakerOptFile = DexMakerOpt.genDexMakerOptFile(workDir()); @@ -884,7 +841,7 @@ public static void signatureApk(File outApk, File keyStoreFile, BuildParamMeta b System.out.println(outApk.getAbsolutePath() + " has been Signed"); } - private static boolean useV1Sign(BuildParamMeta buildParamMeta){ + private static boolean useV1Sign(BuildParamMeta buildParamMeta) { // targetSdkVersion为安卓11的必须使用v2签名 // 安卓7之前使用v1签名 return buildParamMeta != null && NumberUtils.toInt(buildParamMeta.apkMeta.getMinSdkVersion(), 24) <= 23 && NumberUtils.toInt(buildParamMeta.apkMeta.getTargetSdkVersion()) < 30; diff --git a/container-inject-template/.gitignore b/container-inject-template/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/container-inject-template/.gitignore @@ -0,0 +1 @@ +/build diff --git a/container-inject-template/build.gradle b/container-inject-template/build.gradle new file mode 100644 index 0000000..00114ff --- /dev/null +++ b/container-inject-template/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + + + + defaultConfig { + applicationId "com.virjar.ratel.inject.template" + minSdkVersion 19 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + + } + + signingConfigs { + release { + storeFile rootProject.file('script/hermes_key') + storePassword "hermes" + keyAlias "hermes" + keyPassword "hermes" + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'com.android.support:appcompat-v7:28.0.0' + compile (name: 'base-lib-allcommon', ext: 'jar') +} diff --git a/container-inject-template/proguard-rules.pro b/container-inject-template/proguard-rules.pro new file mode 100644 index 0000000..3c48ecd --- /dev/null +++ b/container-inject-template/proguard-rules.pro @@ -0,0 +1,23 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +-keep class com.virjar.ratel.inject.** {*;} \ No newline at end of file diff --git a/container-inject-template/src/main/AndroidManifest.xml b/container-inject-template/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1ba4c19 --- /dev/null +++ b/container-inject-template/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + diff --git a/container-inject-template/src/main/java/com/virjar/ratel/inject/template/EventNotifier.java b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/EventNotifier.java new file mode 100644 index 0000000..8edb20e --- /dev/null +++ b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/EventNotifier.java @@ -0,0 +1,21 @@ +package com.virjar.ratel.inject.template; + +import android.util.Log; + +public class EventNotifier { + + private static final String TAG = "RATEL_EVENT"; + + public static void send(String str1, String str2) { + Log.i(TAG, "str1: " + str1 + " str2:" + str2); + } + + public static void send(String str1, String str2, String str3) { + Log.i(TAG, "str1: " + str1 + " str2:" + str2 + " str3:" + str3); + } + + public static void send(String str1, String str2, String str3, String str4) { + Log.i(TAG, "str1: " + str1 + " str2:" + str2 + " str3:" + str3 + " str4:" + str4); + } + +} diff --git a/container-inject-template/src/main/java/com/virjar/ratel/inject/template/RatelSmaliLog.java b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/RatelSmaliLog.java new file mode 100644 index 0000000..f32ef7d --- /dev/null +++ b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/RatelSmaliLog.java @@ -0,0 +1,27 @@ +package com.virjar.ratel.inject.template; + +import android.util.Log; + +public class RatelSmaliLog { + private static String TAG = "RATEL_SAMLI"; + + public static void resetTag(String tag) { + TAG = tag; + } + + public static void log(String message) { + Log.i(TAG, message, new Throwable()); + } + + public static void printStack() { + Log.i(TAG, "stackTrace", new Throwable()); + } + + public static void logObject(Object o) { + if (o == null) { + return; + } + Log.i(TAG, "logObject class: " + o.getClass() + " objectToString: " + o); + Log.i(TAG, "stackTrace", new Throwable()); + } +} diff --git a/container-inject-template/src/main/java/com/virjar/ratel/inject/template/append/RatelMultiDexApplication.java b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/append/RatelMultiDexApplication.java new file mode 100644 index 0000000..92a2b92 --- /dev/null +++ b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/append/RatelMultiDexApplication.java @@ -0,0 +1,248 @@ +package com.virjar.ratel.inject.template.append; + + +import android.app.Application; +import android.content.ComponentCallbacks; +import android.content.Context; +import android.content.res.Configuration; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; + +import dalvik.system.DexClassLoader; + +public class RatelMultiDexApplication extends Application { + private static Class ratelRuntimeClass = null; + + @Override + protected void attachBaseContext(Context base) { + try { + if (ratelRuntimeClass == null) { + ratelRuntimeClass = createRatelRuntimeClass(base); + } + ratelRuntimeClass.getMethod("applicationAttachWithMultiDexMode", Context.class).invoke(null, base); + // RatelCore.getInstance().callBeforeAttach(base); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + super.attachBaseContext(base); + } + + @Override + public void onCreate() { + super.onCreate(); + // RatelCore.getInstance().callBeforeApplicationOnCreate(); + try { + ratelRuntimeClass.getMethod("applicationOnCreateWithMultiMode").invoke(null); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void onTerminate() { + super.onTerminate(); + try { + ratelRuntimeClass.getMethod("callOnTerminate").invoke(null); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + try { + ratelRuntimeClass.getMethod("callOnConfigurationChanged", Configuration.class).invoke(null, newConfig); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + try { + ratelRuntimeClass.getMethod("callOnLowMemory").invoke(null); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + try { + ratelRuntimeClass.getMethod("callOnTrimMemory", int.class).invoke(null, level); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void registerComponentCallbacks(ComponentCallbacks callback) { + super.registerComponentCallbacks(callback); + try { + ratelRuntimeClass.getMethod("callRegisterComponentCallbacks", ComponentCallbacks.class).invoke(null, callback); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void unregisterComponentCallbacks(ComponentCallbacks callback) { + super.unregisterComponentCallbacks(callback); + try { + ratelRuntimeClass.getMethod("callUnregisterComponentCallbacks", ComponentCallbacks.class).invoke(null, callback); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) { + super.registerActivityLifecycleCallbacks(callback); + try { + ratelRuntimeClass.getMethod("callRegisterActivityLifecycleCallbacks", ActivityLifecycleCallbacks.class).invoke(null, callback); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) { + super.unregisterActivityLifecycleCallbacks(callback); + try { + ratelRuntimeClass.getMethod("callUnregisterActivityLifecycleCallbacks", ActivityLifecycleCallbacks.class).invoke(null, callback); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void registerOnProvideAssistDataListener(OnProvideAssistDataListener callback) { + super.registerOnProvideAssistDataListener(callback); + try { + ratelRuntimeClass.getMethod("callRegisterOnProvideAssistDataListener", OnProvideAssistDataListener.class).invoke(null, callback); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + @Override + public void unregisterOnProvideAssistDataListener(OnProvideAssistDataListener callback) { + super.unregisterOnProvideAssistDataListener(callback); + try { + ratelRuntimeClass.getMethod("callUnregisterOnProvideAssistDataListener", OnProvideAssistDataListener.class).invoke(null, callback); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + private static Class createRatelRuntimeClass(Context context) { + File ratelRuntimeDir = context.getDir("ratel_runtime", Context.MODE_PRIVATE); + try { + boolean codeUpdate = false; + InputStream stream = context.getAssets().open("ratel_serialNo.txt"); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + int n; + int EOF = -1; + byte[] buffer = new byte[1024]; + while (EOF != (n = stream.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + stream.close(); + byteArrayOutputStream.close(); + String serialNo = byteArrayOutputStream.toString("utf8"); + //序列号文件,需要放置在work 目录下,后置决定是否覆盖或者删除 + File serialNoFile = new File(context.getDir("ratel_resource", Context.MODE_PRIVATE), "ratel_serialNo.txt"); + if (!serialNoFile.exists()) { + codeUpdate = true; + } else { + FileInputStream fileInputStream = new FileInputStream(serialNoFile); + byteArrayOutputStream = new ByteArrayOutputStream(); + EOF = -1; + buffer = new byte[1024]; + while (EOF != (n = fileInputStream.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + stream.close(); + byteArrayOutputStream.close(); + String oldSerialNo = byteArrayOutputStream.toString("utf8"); + if (!serialNo.equals(oldSerialNo)) { + codeUpdate = true; + } + } + + File dest = new File(ratelRuntimeDir, "ratel_container-driver.jar"); + if (!dest.exists() || !dest.isFile()) { + codeUpdate = true; + } + + File optimizedDirectory = new File(ratelRuntimeDir, "runtime_code_dex"); + + if (codeUpdate) { + File parentFile = dest.getParentFile(); + if (!parentFile.exists()) { + if (!parentFile.mkdirs()) { + throw new IllegalStateException("can not create dir: " + parentFile); + } + } + InputStream input = context.getAssets().open("ratel_container-driver.jar"); + FileOutputStream fileOutputStream = new FileOutputStream(dest); + + EOF = -1; + buffer = new byte[1024 * 4]; + while (EOF != (n = input.read(buffer))) { + fileOutputStream.write(buffer, 0, n); + } + input.close(); + fileOutputStream.close(); + deleteDir(optimizedDirectory); + } + + if (!optimizedDirectory.exists()) { + if (!optimizedDirectory.mkdirs()) { + throw new RuntimeException("can not create dir: " + optimizedDirectory); + } + } + + DexClassLoader dexClassLoader = new DexClassLoader(dest.getCanonicalPath(), optimizedDirectory.getCanonicalPath(), context.getApplicationInfo().nativeLibraryDir, ClassLoader.getSystemClassLoader().getParent()); + + return dexClassLoader.loadClass("com.virjar.ratel.runtime.RatelRuntime"); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + + public static boolean deleteDir(File dir) { + if (!dir.exists()) { + return true; + } + if (dir.isDirectory()) { + String[] children = dir.list(); + for (String file : children) { + boolean success = deleteDir(new File(dir, file)); + if (!success) { + return false; + } + } + } + return dir.delete(); + } +} diff --git a/container-inject-template/src/main/java/com/virjar/ratel/inject/template/kratos/KratosBootstrap.java b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/kratos/KratosBootstrap.java new file mode 100644 index 0000000..6848618 --- /dev/null +++ b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/kratos/KratosBootstrap.java @@ -0,0 +1,233 @@ +package com.virjar.ratel.inject.template.kratos; + + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; + +import dalvik.system.DexClassLoader; + +public class KratosBootstrap { + private static boolean startup = false; + private static final String RATEL_PACKAGE_NAME = "com.virjar.ratel.manager"; + private static final String RATEL_RUNTIME_CLASS_NAME = "com.virjar.ratel.runtime.RatelRuntime"; + + private static final String DREAMLAND_RUNTIME_PACKAGE_NAME = "com.dreamland.runtime"; + private static final String DREAMLAND_RUNTIME_CLASS = "com.dreamland.runtime.DreamlandRuntime"; + + /** + * ratel manager VersionCode >= 13 才支持Kratos引擎 + */ + private static final long SUPPORT_KRATOS_RM_VERSION = 13; + + public static void bootstrap(Context context) { + if (startup) { + return; + } + PackageManager packageManager = context.getPackageManager(); + if (packageManager == null) { + //for system_server ,there no pm ready when application attach first + return; + } + + if (startupForRatelFramework(packageManager, context)) { + startup = true; + return; + } + + if (startupForDreamlandFramework(packageManager, context)) { + startup = true; + } + } + + private static boolean startupForDreamlandFramework(PackageManager packageManager, Context context) { + Context dreamlandContext; + try { + dreamlandContext = context.createPackageContext(DREAMLAND_RUNTIME_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + try { + dreamlandContext.getClassLoader().loadClass(DREAMLAND_RUNTIME_CLASS) + .getDeclaredMethod("callApplicationAttach", Context.class) + .invoke(null, context); + return true; + } catch (Throwable throwable) { + Log.e("DreamLand", "DreamLand kratos engine startup failed!!", throwable); + throw new RuntimeException(throwable); + } + } + + private static boolean startupForRatelFramework(PackageManager packageManager, Context context) { + if (RATEL_PACKAGE_NAME.equals(context.getPackageName())) { + // rm 不允许被注入 + return false; + } + PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfo(RATEL_PACKAGE_NAME, PackageManager.GET_META_DATA); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (packageInfo.getLongVersionCode() < SUPPORT_KRATOS_RM_VERSION) { + return false; + } + } else { + if (packageInfo.versionCode < SUPPORT_KRATOS_RM_VERSION) { + return false; + } + } + + Context kratosContex; + try { + kratosContex = context.createPackageContext(packageInfo.packageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + + File ratelRuntimeDir = context.getDir("ratel_runtime", Context.MODE_PRIVATE); + try { + boolean codeUpdate = false; + InputStream stream = kratosContex.getAssets().open("ratel_serialNo.txt"); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + int n; + int EOF = -1; + byte[] buffer = new byte[1024]; + while (EOF != (n = stream.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + stream.close(); + byteArrayOutputStream.close(); + String serialNo = byteArrayOutputStream.toString("utf8"); + //序列号文件,需要放置在work 目录下,后置决定是否覆盖或者删除 + File serialNoFile = new File(context.getDir("ratel_resource", Context.MODE_PRIVATE), "ratel_serialNo.txt"); + if (!serialNoFile.exists()) { + codeUpdate = true; + } else { + FileInputStream fileInputStream = new FileInputStream(serialNoFile); + byteArrayOutputStream = new ByteArrayOutputStream(); + EOF = -1; + buffer = new byte[1024]; + while (EOF != (n = fileInputStream.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + stream.close(); + byteArrayOutputStream.close(); + String oldSerialNo = byteArrayOutputStream.toString("utf8"); + if (!serialNo.equals(oldSerialNo)) { + codeUpdate = true; + } + } + + File dest = new File(ratelRuntimeDir, "ratel_container-driver.jar"); + if (!dest.exists() || !dest.isFile()) { + codeUpdate = true; + } + + File optimizedDirectory = new File(ratelRuntimeDir, "runtime_code_dex"); + + if (codeUpdate) { + File parentFile = dest.getParentFile(); + if (!parentFile.exists()) { + if (!parentFile.mkdirs()) { + throw new IllegalStateException("can not create dir: " + parentFile); + } + } + InputStream input = kratosContex.getAssets().open("ratel_container-driver.jar"); + FileOutputStream fileOutputStream = new FileOutputStream(dest); + + EOF = -1; + buffer = new byte[1024 * 4]; + while (EOF != (n = input.read(buffer))) { + fileOutputStream.write(buffer, 0, n); + } + input.close(); + fileOutputStream.close(); + deleteDir(optimizedDirectory); + } + + if (!optimizedDirectory.exists()) { + if (!optimizedDirectory.mkdirs()) { + throw new RuntimeException("can not create dir: " + optimizedDirectory); + } + } + + String nativeLibDir = getNativeLibDir(packageInfo.applicationInfo, kratosContex.getClassLoader()); + DexClassLoader dexClassLoader = new DexClassLoader(dest.getCanonicalPath(), optimizedDirectory.getCanonicalPath(), nativeLibDir, ClassLoader.getSystemClassLoader().getParent()); + dexClassLoader + .loadClass("com.virjar.ratel.runtime.RatelRuntime") + .getMethod("applicationAttachWithKratosEngine", Context.class, Context.class) + .invoke(null, context, kratosContex); + return true; + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + + private static String getNativeLibDir(ApplicationInfo applicationInfo, ClassLoader baseClassloader) { + + Set nativeLibs = new HashSet<>(); + nativeLibs.add(applicationInfo.nativeLibraryDir); + + String classLoaderStr = baseClassloader.toString(); + int i = classLoaderStr.indexOf("nativeLibraryDirectories="); + if (i > 0) { + classLoaderStr = classLoaderStr.substring(i); + int start = classLoaderStr.indexOf("["); + int end = classLoaderStr.indexOf("]"); + classLoaderStr = classLoaderStr.substring(start + 1, end); + for (String libItem : classLoaderStr.split(",")) { + String trim = libItem.trim(); + if (trim.startsWith("/system/")) { + continue; + } + nativeLibs.add(trim); + } + //主要为了处理splitApk的问题。 + } else { + Log.w("RAEL", "can not get nativeLibraryDirectories from classLoader: " + baseClassloader); + } + StringBuilder buf = new StringBuilder(nativeLibs.size() * 16); + + boolean isFirst = true; + for (String str : nativeLibs) { + if (!isFirst) { + buf.append(":"); + } + isFirst = false; + buf.append(str); + } + return buf.toString(); + } + + + private static boolean deleteDir(File dir) { + if (!dir.exists()) { + return true; + } + if (dir.isDirectory()) { + String[] children = dir.list(); + for (String file : children) { + boolean success = deleteDir(new File(dir, file)); + if (!success) { + return false; + } + } + } + Log.i("RATEL", "remove file:" + dir.getAbsolutePath()); + return dir.delete(); + } +} diff --git a/container-inject-template/src/main/java/com/virjar/ratel/inject/template/rebuild/BootStrap.java b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/rebuild/BootStrap.java new file mode 100644 index 0000000..e647977 --- /dev/null +++ b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/rebuild/BootStrap.java @@ -0,0 +1,161 @@ +package com.virjar.ratel.inject.template.rebuild; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; + +import dalvik.system.DexClassLoader; + +public class BootStrap { + private static boolean isStartUp = false; + + public static void startUp(Context context, Context applicationContext) { + startUp(context, applicationContext, true); + } + + @SuppressWarnings("unused") + public static void startUp(Context context, Context applicationContext, boolean bypassHiddenAPIEnforcementPolicy) { + if (isStartUp) { + return; + } + isStartUp = true; + File ratelRuntimeDir = context.getDir("ratel_runtime", Context.MODE_PRIVATE); + try { + boolean codeUpdate = false; + InputStream stream = context.getAssets().open("ratel_serialNo.txt"); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + int n; + int EOF = -1; + byte[] buffer = new byte[1024]; + while (EOF != (n = stream.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + stream.close(); + byteArrayOutputStream.close(); + String serialNo = byteArrayOutputStream.toString("utf8"); + //序列号文件,需要放置在work 目录下,后置决定是否覆盖或者删除 + File serialNoFile = new File(context.getDir("ratel_resource", Context.MODE_PRIVATE), "ratel_serialNo.txt"); + if (!serialNoFile.exists()) { + codeUpdate = true; + } else { + FileInputStream fileInputStream = new FileInputStream(serialNoFile); + byteArrayOutputStream = new ByteArrayOutputStream(); + EOF = -1; + buffer = new byte[1024]; + while (EOF != (n = fileInputStream.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + stream.close(); + byteArrayOutputStream.close(); + String oldSerialNo = byteArrayOutputStream.toString("utf8"); + if (!serialNo.equals(oldSerialNo)) { + codeUpdate = true; + } + } + + File dest = new File(ratelRuntimeDir, "ratel_container-driver.jar"); + if (!dest.exists() || !dest.isFile()) { + codeUpdate = true; + } + + File optimizedDirectory = new File(ratelRuntimeDir, "runtime_code_dex"); + + if (codeUpdate) { + File parentFile = dest.getParentFile(); + if (!parentFile.exists()) { + if (!parentFile.mkdirs()) { + throw new IllegalStateException("can not create dir: " + parentFile); + } + } + InputStream input = context.getAssets().open("ratel_container-driver.jar"); + FileOutputStream fileOutputStream = new FileOutputStream(dest); + + EOF = -1; + buffer = new byte[1024 * 4]; + while (EOF != (n = input.read(buffer))) { + fileOutputStream.write(buffer, 0, n); + } + input.close(); + fileOutputStream.close(); + deleteDir(optimizedDirectory); + } + + if (!optimizedDirectory.exists()) { + if (!optimizedDirectory.mkdirs()) { + throw new RuntimeException("can not create dir: " + optimizedDirectory); + } + } + + String nativeLibDir = getNativeLibDir(context.getApplicationInfo()); + DexClassLoader dexClassLoader = new DexClassLoader(dest.getCanonicalPath(), optimizedDirectory.getCanonicalPath(), nativeLibDir, ClassLoader.getSystemClassLoader().getParent()); + dexClassLoader + .loadClass("com.virjar.ratel.runtime.RatelRuntime") + .getMethod("startUp", Context.class, Context.class, boolean.class) + .invoke(null, applicationContext, context, bypassHiddenAPIEnforcementPolicy); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + + private static String getNativeLibDir(ApplicationInfo applicationInfo) { + + Set nativeLibs = new HashSet<>(); + nativeLibs.add(applicationInfo.nativeLibraryDir); + + String classLoaderStr = BootStrap.class.getClassLoader().toString(); + int i = classLoaderStr.indexOf("nativeLibraryDirectories="); + if (i > 0) { + classLoaderStr = classLoaderStr.substring(i); + int start = classLoaderStr.indexOf("["); + int end = classLoaderStr.indexOf("]"); + classLoaderStr = classLoaderStr.substring(start + 1, end); + for (String libItem : classLoaderStr.split(",")) { + String trim = libItem.trim(); + if (trim.startsWith("/system/")) { + continue; + } + nativeLibs.add(trim); + } + //主要为了处理splitApk的问题。 + } else { + Log.w("RAEL", "can not get nativeLibraryDirectories from classLoader: " + BootStrap.class.getClassLoader()); + } + StringBuilder buf = new StringBuilder(nativeLibs.size() * 16); + + boolean isFirst = true; + for (String str : nativeLibs) { + if (!isFirst) { + buf.append(":"); + } + isFirst = false; + buf.append(str); + } + return buf.toString(); + } + + + private static boolean deleteDir(File dir) { + if (!dir.exists()) { + return true; + } + if (dir.isDirectory()) { + String[] children = dir.list(); + for (String file : children) { + boolean success = deleteDir(new File(dir, file)); + if (!success) { + return false; + } + } + } + Log.i("RATEL", "remove file:" + dir.getAbsolutePath()); + return dir.delete(); + } +} diff --git a/container-inject-template/src/main/java/com/virjar/ratel/inject/template/rebuild/BootStrapWithStaticInit.java b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/rebuild/BootStrapWithStaticInit.java new file mode 100644 index 0000000..7314c15 --- /dev/null +++ b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/rebuild/BootStrapWithStaticInit.java @@ -0,0 +1,121 @@ +package com.virjar.ratel.inject.template.rebuild; + +import android.content.Context; +import android.os.Build; +import android.util.Log; + +import java.lang.reflect.Method; + +public class BootStrapWithStaticInit { + public static void startup() { + try { + if (isPie()) { + passApiCheck(); + } + java.lang.Class activityThreadClass = java.lang.Class.forName("android.app.ActivityThread"); + java.lang.reflect.Method declaredMethod = activityThreadClass.getDeclaredMethod("currentActivityThread", new java.lang.Class[0]); + declaredMethod.setAccessible(true); + java.lang.Object activityThread = declaredMethod.invoke(null, new java.lang.Object[0]); + java.lang.reflect.Field declaredField = activityThreadClass.getDeclaredField("mBoundApplication"); + declaredField.setAccessible(true); + java.lang.Object loadApk = declaredField.get(activityThread); + java.lang.reflect.Field declaredField2 = loadApk.getClass().getDeclaredField("info"); + declaredField2.setAccessible(true); + loadApk = declaredField2.get(loadApk); + + java.lang.reflect.Method declaredMethod2 = java.lang.Class.forName("android.app.ContextImpl").getDeclaredMethod("createAppContext", new java.lang.Class[]{activityThreadClass, loadApk.getClass()}); + declaredMethod2.setAccessible(true); + Context context = (android.content.Context) declaredMethod2.invoke(null, new java.lang.Object[]{activityThread, loadApk}); + BootStrap.startUp(context, context, false); + + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + + private static Method addWhiteListMethod; + + private static Object vmRuntime; + + static { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + try { + //高版本才需要执行这个 + Method getMethodMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class); + Method forNameMethod = Class.class.getDeclaredMethod("forName", String.class); + Class vmRuntimeClass = (Class) forNameMethod.invoke(null, "dalvik.system.VMRuntime"); + addWhiteListMethod = (Method) getMethodMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class}); + Method getVMRuntimeMethod = (Method) getMethodMethod.invoke(vmRuntimeClass, "getRuntime", null); + vmRuntime = getVMRuntimeMethod.invoke(null); + } catch (Exception e) { + Log.e("ReflectionUtils", "error get methods", e); + } + } + } + + private static void passApiCheck() { + try { + if (isQ() && isEMUI()) { + addReflectionWhiteList("Landroid/", + "Lcom/android/", + "Ljava/lang/", + "Ldalvik/system/", + "Llibcore/io/", + "Lsun/misc/", + "Lhuawei/" + ); + } else { + addReflectionWhiteList("Landroid/", + "Lcom/android/", + "Ljava/lang/", + "Ldalvik/system/", + "Llibcore/io/", + "Lsun/misc/" + ); + } + } catch (Throwable throwable) { + Log.w("RATEL", "pass Hidden API enforcement policy failed", throwable); + } + } + + public static boolean isEMUI() { + if (Build.DISPLAY.toUpperCase().startsWith("EMUI")) { + return true; + } + try { + String emuiVersion = (String) Class.forName("android.os.SystemProperties").getMethod("get", String.class).invoke(null, "ro.build.version.emui"); + if (emuiVersion == null || !emuiVersion.contains("EmotionUI")) { + return false; + } + } catch (Throwable throwable) { + //ignore + } + return true; + } + + + public static boolean isQ() { + return Build.VERSION.SDK_INT > 28 || (Build.VERSION.SDK_INT == 28 && getPreviewSDKInt() > 0); + } + + private static boolean isPie() { + return Build.VERSION.SDK_INT > 27 || (Build.VERSION.SDK_INT == 27 && getPreviewSDKInt() > 0); + } + + private static int getPreviewSDKInt() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + try { + return Build.VERSION.PREVIEW_SDK_INT; + } catch (Throwable e) { + // ignore + } + } + return 0; + } + + + //methidSigs like Lcom/swift/sandhook/utils/ReflectionUtils;->vmRuntime:java/lang/Object; (from hidden policy list) + private static void addReflectionWhiteList(String... memberSigs) throws Throwable { + addWhiteListMethod.invoke(vmRuntime, new Object[]{memberSigs}); + } +} diff --git a/container-inject-template/src/main/java/com/virjar/ratel/inject/template/zelda/RatelZeldaApplication.java b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/zelda/RatelZeldaApplication.java new file mode 100644 index 0000000..042231d --- /dev/null +++ b/container-inject-template/src/main/java/com/virjar/ratel/inject/template/zelda/RatelZeldaApplication.java @@ -0,0 +1,137 @@ +package com.virjar.ratel.inject.template.zelda; + +import android.annotation.SuppressLint; +import android.app.Application; +import android.content.Context; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; + +import dalvik.system.DexClassLoader; + +@SuppressLint("Registered") +public class RatelZeldaApplication extends Application { + private static Class ratelRuntimeClass = null; + + @Override + protected void attachBaseContext(Context base) { + try { + if (ratelRuntimeClass == null) { + ratelRuntimeClass = createRatelRuntimeClass(base); + } + ratelRuntimeClass.getMethod("applicationAttachWithZeldaEngine", Context.class).invoke(null, base); + // RatelCore.getInstance().callBeforeAttach(base); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + super.attachBaseContext(base); + } + + @Override + public void onCreate() { + super.onCreate(); + // RatelCore.getInstance().callBeforeApplicationOnCreate(); + try { + ratelRuntimeClass.getMethod("applicationOnCreateWithMultiMode").invoke(null); + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw new RuntimeException(throwable); + } + } + + private static Class createRatelRuntimeClass(Context context) { + File ratelRuntimeDir = context.getDir("ratel_runtime", Context.MODE_PRIVATE); + try { + boolean codeUpdate = false; + InputStream stream = context.getAssets().open("ratel_serialNo.txt"); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + int n; + int EOF = -1; + byte[] buffer = new byte[1024]; + while (EOF != (n = stream.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + stream.close(); + byteArrayOutputStream.close(); + String serialNo = byteArrayOutputStream.toString("utf8"); + //序列号文件,需要放置在work 目录下,后置决定是否覆盖或者删除 + File serialNoFile = new File(context.getDir("ratel_resource", Context.MODE_PRIVATE), "ratel_serialNo.txt"); + if (!serialNoFile.exists()) { + codeUpdate = true; + } else { + FileInputStream fileInputStream = new FileInputStream(serialNoFile); + byteArrayOutputStream = new ByteArrayOutputStream(); + EOF = -1; + buffer = new byte[1024]; + while (EOF != (n = fileInputStream.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + stream.close(); + byteArrayOutputStream.close(); + String oldSerialNo = byteArrayOutputStream.toString("utf8"); + if (!serialNo.equals(oldSerialNo)) { + codeUpdate = true; + } + } + + File dest = new File(ratelRuntimeDir, "ratel_container-driver.jar"); + if (!dest.exists() || !dest.isFile()) { + codeUpdate = true; + } + + File optimizedDirectory = new File(ratelRuntimeDir, "runtime_code_dex"); + + if (codeUpdate) { + File parentFile = dest.getParentFile(); + if (!parentFile.exists()) { + if (!parentFile.mkdirs()) { + throw new IllegalStateException("can not create dir: " + parentFile); + } + } + InputStream input = context.getAssets().open("ratel_container-driver.jar"); + FileOutputStream fileOutputStream = new FileOutputStream(dest); + + EOF = -1; + buffer = new byte[1024 * 4]; + while (EOF != (n = input.read(buffer))) { + fileOutputStream.write(buffer, 0, n); + } + input.close(); + fileOutputStream.close(); + deleteDir(optimizedDirectory); + } + + if (!optimizedDirectory.exists()) { + if (!optimizedDirectory.mkdirs()) { + throw new RuntimeException("can not create dir: " + optimizedDirectory); + } + } + + DexClassLoader dexClassLoader = new DexClassLoader(dest.getCanonicalPath(), optimizedDirectory.getCanonicalPath(), context.getApplicationInfo().nativeLibraryDir, ClassLoader.getSystemClassLoader().getParent()); + + return dexClassLoader.loadClass("com.virjar.ratel.runtime.RatelRuntime"); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + + public static boolean deleteDir(File dir) { + if (!dir.exists()) { + return true; + } + if (dir.isDirectory()) { + String[] children = dir.list(); + for (String file : children) { + boolean success = deleteDir(new File(dir, file)); + if (!success) { + return false; + } + } + } + return dir.delete(); + } +} diff --git a/container-inject-template/src/main/res/drawable-v24/ic_launcher_foreground.xml b/container-inject-template/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..1f6bb29 --- /dev/null +++ b/container-inject-template/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/container-inject-template/src/main/res/drawable/ic_launcher_background.xml b/container-inject-template/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/container-inject-template/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/container-inject-template/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/container-inject-template/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/container-inject-template/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/container-inject-template/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/container-inject-template/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/container-inject-template/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/container-inject-template/src/main/res/mipmap-hdpi/ic_launcher.png b/container-inject-template/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..898f3ed Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/container-inject-template/src/main/res/mipmap-hdpi/ic_launcher_round.png b/container-inject-template/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..dffca36 Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/container-inject-template/src/main/res/mipmap-mdpi/ic_launcher.png b/container-inject-template/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..64ba76f Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/container-inject-template/src/main/res/mipmap-mdpi/ic_launcher_round.png b/container-inject-template/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..dae5e08 Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/container-inject-template/src/main/res/mipmap-xhdpi/ic_launcher.png b/container-inject-template/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..e5ed465 Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/container-inject-template/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/container-inject-template/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..14ed0af Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/container-inject-template/src/main/res/mipmap-xxhdpi/ic_launcher.png b/container-inject-template/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b0907ca Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/container-inject-template/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/container-inject-template/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..d8ae031 Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/container-inject-template/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/container-inject-template/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..2c18de9 Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/container-inject-template/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/container-inject-template/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..beed3cd Binary files /dev/null and b/container-inject-template/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/container-inject-template/src/main/res/values/colors.xml b/container-inject-template/src/main/res/values/colors.xml new file mode 100644 index 0000000..69b2233 --- /dev/null +++ b/container-inject-template/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #008577 + #00574B + #D81B60 + diff --git a/container-inject-template/src/main/res/values/strings.xml b/container-inject-template/src/main/res/values/strings.xml new file mode 100644 index 0000000..283f406 --- /dev/null +++ b/container-inject-template/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + container_runtimer_repkg_bootstrap + diff --git a/container-inject-template/src/main/res/values/styles.xml b/container-inject-template/src/main/res/values/styles.xml new file mode 100644 index 0000000..5885930 --- /dev/null +++ b/container-inject-template/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/crack-demoapp/src/main/java/com/virjar/ratel/demoapp/crack/DemoAppHooker.java b/crack-demoapp/src/main/java/com/virjar/ratel/demoapp/crack/DemoAppHooker.java index 0af9552..bc22756 100644 --- a/crack-demoapp/src/main/java/com/virjar/ratel/demoapp/crack/DemoAppHooker.java +++ b/crack-demoapp/src/main/java/com/virjar/ratel/demoapp/crack/DemoAppHooker.java @@ -117,8 +117,8 @@ private static void switchUserBySdcardFile() { } } - private static void switchTo18782165597() { - RatelToolKit.virtualEnv.switchEnv("18782165597"); + private static void switchTo18788885597() { + RatelToolKit.virtualEnv.switchEnv("18788885597"); } @@ -235,7 +235,7 @@ public boolean handleActivity(Activity activity, ViewImage root) { Log.i(tag, "无法定位登陆WebView..."); return false; } - WebViewHelper.JsCallFuture jsCallFuture = new WebViewHelper(webView).typeByXpath("//input[@data-componentname='phoneNumber']", "18782165597"); + WebViewHelper.JsCallFuture jsCallFuture = new WebViewHelper(webView).typeByXpath("//input[@data-componentname='phoneNumber']", "18788885597"); jsCallFuture.success() diff --git a/ratelRsaKey.md b/ratelRsaKey.md deleted file mode 100644 index c62eb27..0000000 --- a/ratelRsaKey.md +++ /dev/null @@ -1,79 +0,0 @@ -# ratel项目授权加密算法 - -# 1基础算法 - -## 1.1 base64 -ratel扩展base64算法,扩展后仍然属于一种编码算法而非加密算法(其实融合了密码功能,但是考虑本身也是对称算法,密码依然需要写入到客户端。所以将密码写私死到算法中)。 -具体包括如下扩展: - -### 1.1.1 码表 -修改了base64标准码表,这是很容易实现的对抗。攻击难度不大,但是实现简单。 -### 1.1.2 取值方式 -标准base64算法,采用三字节分组,结果为三字节到四字节的转换。``3 * 8 -> 4 * 6``,对应关系从高位到低位顺序分割。 -ratel扩展算法,使用了随机分割方式,在24比特中,按照随机码表,抽取对应的比特位。组成另外的 4 * 6组合。抽取码表随机生成,并且码表会附加到base64算法输出内容中。 - -### 1.1.3 异或混淆 -码表对应关系,及其容易通过概率统计方式攻击成功。为了避免相同输入,产生相同的输出,需要对输出结果执行震荡操作。最简单的实现方式就是异或。 -首先由于伪随机算法特征,对于指定随机种子,按照特定的随机数获取顺序,可以产生特定的随机数序列。可以保证输出的随机数序列不可逆+具有震荡效果。 -我们使用特定随机数种子,产生无限长的随机数序列。并且使用该序列和base64输出执行异或操作。 - - -## 1.2 rsa -rsa算法做二次定制难度较大,需要有比较高的数学功底。所以我没有对rsa做自定义,但是rsa算法可以实现非对称加密。依赖这个特征,没有人可以实现通用注册机(客户端只会存在rsa公钥,不会存在私钥)。 -没有人在没有私钥的情况下,获取到ratel授权证书签发权限,就算最终破译我们的加密算法也无法做到。 - -在ras公钥私钥描述中,我使用ratelBase64算法dump字节流(正常情况下,应该使用标准的bases64实现dump)。 -ras公私钥定义如下 - -### 1.2.1 private key -``` -wBHNol9A#+ZmOaGnPYFudvxbc8a367c8bXJgXbAuquFXaPKIllTl1tRTnu6cQfbDLtA92G9tZBAvrAxWzh9m4R2OSk9ysIvDUuGNAPWfBSmqUpmuoiOdvHUmGPEnntYB6#TcUpVPsbGgQRk1OOaMtWCAlNg1JZLEJz5WMBmZAR8hwC+rI2aCsWxxa3k1yEwxiMSqsb5UgZNDXz1USwtK6C73qWPo0oQNNigbs5eModQqvRgCR1rO4jUbBGGjISpMyh4Ayrwk3g0tjm4zSrnCt3+SWOS00huE4J7va4ZxJfDpWNlnj#Mh7didiHwC5qNg7fwSVTa+0ERUK3BNAwMwsC6YHapvnOuWVbrI#GqDeeN4iMdPeY5bn3I+Ob#yfbLaGjbytrUqGNcQHXFkT2jzfzfiIXZ3JmW2lqbpwyiB91SlN5gj2ZmxHXfFcwOpEyMjNWTVUuLM2FsKmEM3O6VmqRXmMgkFJHQb58WzpYNAon9pnM5gsJGlmbd#4blRTRUMJGOwEkvCSM30kEpNX94nKJVfiL7ZrbnILJzbnoPYfejsx86tmdCs92zK11+2ZDXG1lcJigIBv0U8knmdpb4XwCXusbvEzK#Cut#tphla2gcrBtmhaS59rOec0or18DSLgMpGZfFIH5UsUhSI4PmDDjTme3XG8zyMIEZUucCGjnGQBGm21bz76OL60#A0HGAUzcY628Ij#IpTdc6bClJxHZ0wTvu4HfIyDu0SJfrCNyyY9MMplvrXHnepvyeESgjhzPJ90vDhyPnv1559M82nWjRYUnhk7z4b4TLXrqZzmrpdJWJtXTV2NuVufwLQ415nx9jL8LTAjnGKYvIIKfh0om2eSmG6x+RR2Ydji6gveJlE4aB= -``` - -### 1.2.2 public key -``` -wZYxOmvlud#b9NaPnBHFAoG+fc530e06couQVdTZOaaq+bhrof1qtSLwz3IR0alykOlNN#lFO1GRe9E#sZoPzUvCdamtAdHoVhbt#YONYdJWyvPsW#q8Ly1GO6WzRTJWYnvAxiHrNaU9vzycEejWhyfaWEHdsLh908r0QLXnqT6seG3fSRmHx6eMImKX2KDtgTcHxuL5P8uzvJD4r1i#FFq8wpx1BJ8xvCHXLaWinIANQoZnm0Jca4zD -``` - -## 1.3 DES - -base64算法本身来说,起算法逻辑还是比较简单,不少人可以在不看算法描述的情况下,独立完成base64算法实现。我们对base64的改造,应该也不会增加太大强度(其实我觉得难度已经让大部分高手无法攻破了)。 -所以,仍然需要一个复杂的加密算法,进行二次改造。在标准算法基础上,做少许改动,让他不符合标准之后,攻击者在对算法逆向过程中,纯粹依靠对二进制的熟悉程度来说是无法做到攻击成功的。需要攻击成功必须对本算法的标准, -做到非常了解(知道改动点和标准的差异分支)。也就是说,二进制逆向能力+标准加密算法能力都需要非常精通才行。 - -des修改点如下: - -1. 处理轮数,由16轮,修改为17轮。然后在subkey的移位表中,需要append低17轮的移位配置。 -2. 采用cbc模式加密,并设定初始向量(Initial Vector)为:5597 -3. 调整E置换表(扩展置换表),起左右冗余关系顺序调整 -4. subkey 产生过程,增加异或操作: cd = cd ^ 3543654654623L; -5. sBox,第二组,第三组修改顺序 -6. feistel函数输出,增加异或操作: return P(dst) ^ 3436547; - -# 2算法描述 - -## 2.1 加密逻辑 -首先执行des加密,加过结果使用ras私钥再次加密,rsa加密的输出,使用自定义base64处理,得到最终结果 - -## 2.2 解密逻辑,使用自定义base64处理,结果使用ras公钥解密,rsa解密结果使用des解密,得到明文 - - -# 3 授权证书内容定义 -TODO - - -# 4 证书命令 - -## 4.1 生成证书 -``` - ratel-keygen -a virjar -t AuthorizeTypeDebug -x "just for ratel debug key" -p "com.xxx.xxx" -s "23435,547657,95645" - - UlOLyd5iWWJopE0XlrjUk+daaNAKvf5+YMsl7UTZPOmZQHINMwQogqK2VUBpH79PpkY+HFEwfLTr7LVnwQXgZf0v+IfY3f7vBR3rfBgQUQufJLBDAVXFzjJ9HNmRQV1Nbv4EBWmz1CSd67zJ32vlLa74nr2CmZuVeYrl/wNUlAdXy96fP3SUZQ8qIIC7sVtJyyoKlgH+fUGhxyGbbG3ObO5I2pI52x+ugqaAu2i5Z8zjkrtYE4KWxb6XvkH33Ke6uL8NqdE3dGUTIxBZSxW03GT6NgYZ5gvhM00X3JnSEF7DZIMfcE8qXo2JrWu628OYMHIRQOc41XN1aAQw7B6dsBpKCtrAFLEdwQ1z4WsjRqG5GaKTLvQWNBwAjKFz0vvxRZEIpsaEvET1rPhfjVS39MLihK6SCq9fIiJcoIHMtDkMZ0kWUomYK0Dkf1ePXJlmpOC1puRIacGgXF617yXfJY6dWFJs4DtlMm+vu4oCNmsxwbEP+d3Pg9q2mufIeF4zHuSLj9SnGGqa/F/CoQo+rrZkWtUAFJOpt0nAzSvxvbZnVDnIDnz3wVHpRAxKq7h8CSaZi7qwuMjE24Cr/aKhUY4yqR/Cc7C0VzIJSmWSXQm7P6EUHbUb1HYQtCnPyCS/mSkVRUeIWINSFaXfEBBl3e9eFGF901a1QNVMzfk3EFgotgyNfNmxn7smtVIrjbVoNDyg97qFtZlbPO66I2+/w7fGy6X1nIHmmCwuJnX6IBrz951lXSUFcGxLpaLtSCMPUErmydPtFNcUv6C6OPIBCbDttgr2mdQaMyfxiMuAfVZcEAl0lmwzk3aPCtapINBXDi5n7jL1BEbh0v+jtttzwiOtWCIp5v5+6KeYBCOg4s2Z0oZVxhmf0bcBA8lRiUk2HR4BY6cYVrvuAy6OCjDLtXdJQPYSQxJ+HCRVh/2oXfIitmUWesrmC7w4Tnhj94s3FkptGnJ2d62riCaRlr2h4LMiosAfpmh+1Gx/mrA5nndsEMNVFST1XHOdPIffoPlR - -``` - -## 4.2 打印证书 -``` -ratel-keygen -i UlOLyd5iWWJopE0XlrjUk+daaNAKvf5+YMsl7UTZPOmZQHINMwQogqK2VUBpH79PpkY+HFEwfLTr7LVnwQXgZf0v+IfY3f7vBR3rfBgQUQufJLBDAVXFzjJ9HNmRQV1Nbv4EBWmz1CSd67zJ32vlLa74nr2CmZuVeYrl/wNUlAdXy96fP3SUZQ8qIIC7sVtJyyoKlgH+fUGhxyGbbG3ObO5I2pI52x+ugqaAu2i5Z8zjkrtYE4KWxb6XvkH33Ke6uL8NqdE3dGUTIxBZSxW03GT6NgYZ5gvhM00X3JnSEF7DZIMfcE8qXo2JrWu628OYMHIRQOc41XN1aAQw7B6dsBpKCtrAFLEdwQ1z4WsjRqG5GaKTLvQWNBwAjKFz0vvxRZEIpsaEvET1rPhfjVS39MLihK6SCq9fIiJcoIHMtDkMZ0kWUomYK0Dkf1ePXJlmpOC1puRIacGgXF617yXfJY6dWFJs4DtlMm+vu4oCNmsxwbEP+d3Pg9q2mufIeF4zHuSLj9SnGGqa/F/CoQo+rrZkWtUAFJOpt0nAzSvxvbZnVDnIDnz3wVHpRAxKq7h8CSaZi7qwuMjE24Cr/aKhUY4yqR/Cc7C0VzIJSmWSXQm7P6EUHbUb1HYQtCnPyCS/mSkVRUeIWINSFaXfEBBl3e9eFGF901a1QNVMzfk3EFgotgyNfNmxn7smtVIrjbVoNDyg97qFtZlbPO66I2+/w7fGy6X1nIHmmCwuJnX6IBrz951lXSUFcGxLpaLtSCMPUErmydPtFNcUv6C6OPIBCbDttgr2mdQaMyfxiMuAfVZcEAl0lmwzk3aPCtapINBXDi5n7jL1BEbh0v+jtttzwiOtWCIp5v5+6KeYBCOg4s2Z0oZVxhmf0bcBA8lRiUk2HR4BY6cYVrvuAy6OCjDLtXdJQPYSQxJ+HCRVh/2oXfIitmUWesrmC7w4Tnhj94s3FkptGnJ2d62riCaRlr2h4LMiosAfpmh+1Gx/mrA5nndsEMNVFST1XHOdPIffoPlR - -``` \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 68442cd..9ff5aec 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ include ':base-lib-allcommon', ':base-lib-pxb-axml', ':ratelmanager', ':container-runtime-repkg', + ':container-inject-template', ':container-multi-dex-bootstrap', ':container-kratos-bootstrap', ':container-zelda-bootstrap',