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',