From 6145aad3981bfa34914d654c334243be594ba36a Mon Sep 17 00:00:00 2001 From: REAndroid Date: Tue, 21 Jan 2025 20:38:19 +0100 Subject: [PATCH] Use internal dex library --- .../java/com/reandroid/apkeditor/Options.java | 3 ++ .../apkeditor/compile/BuildOptions.java | 9 +++++ .../apkeditor/decompile/DecompileOptions.java | 16 +++++++++ .../apkeditor/smali/SmaliCompiler.java | 21 +++++------- .../apkeditor/smali/SmaliDecompiler.java | 34 +++++-------------- src/main/resources/strings/strings.properties | 5 +++ 6 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/reandroid/apkeditor/Options.java b/src/main/java/com/reandroid/apkeditor/Options.java index 6eadd4d0..a9721df4 100644 --- a/src/main/java/com/reandroid/apkeditor/Options.java +++ b/src/main/java/com/reandroid/apkeditor/Options.java @@ -160,4 +160,7 @@ public static String getHelp(Class optionClass){ public static final String TYPE_RAW = "raw"; public static final String TYPE_XML = "xml"; public static final String TYPE_TEXT = "text"; + + public static final String DEX_LIB_INTERNAL = "internal"; + public static final String DEX_LIB_JF = "jf"; } diff --git a/src/main/java/com/reandroid/apkeditor/compile/BuildOptions.java b/src/main/java/com/reandroid/apkeditor/compile/BuildOptions.java index 7f06fc37..48a0bb75 100644 --- a/src/main/java/com/reandroid/apkeditor/compile/BuildOptions.java +++ b/src/main/java/com/reandroid/apkeditor/compile/BuildOptions.java @@ -57,6 +57,15 @@ public class BuildOptions extends OptionsWithFramework { @OptionArg(name = "-no-cache", description = "build_no_cache", flag = true) public boolean noCache; + @ChoiceArg(name = "-dex-lib", + values = { + DEX_LIB_INTERNAL, + DEX_LIB_JF + }, + description = "dex_lib" + ) + public String dexLib = DEX_LIB_JF; + @OptionArg(name = "-sig", description = "signatures_path") public File signaturesDirectory; diff --git a/src/main/java/com/reandroid/apkeditor/decompile/DecompileOptions.java b/src/main/java/com/reandroid/apkeditor/decompile/DecompileOptions.java index d9cc76b8..49a98df0 100644 --- a/src/main/java/com/reandroid/apkeditor/decompile/DecompileOptions.java +++ b/src/main/java/com/reandroid/apkeditor/decompile/DecompileOptions.java @@ -33,6 +33,10 @@ "decode_example_3", "decode_example_4", "decode_example_5" + }, + notes = { + "decode_note_1", + "decode_note_2" }) public class DecompileOptions extends OptionsWithFramework { @@ -68,6 +72,18 @@ public class DecompileOptions extends OptionsWithFramework { @OptionArg(name = "-dex-markers", flag = true, description = "dump_dex_markers") public boolean dexMarkers; + @OptionArg(name = "-load-dex", description = "decode_load_dex") + public int loadDex = 3; + + @ChoiceArg(name = "-dex-lib", + values = { + DEX_LIB_INTERNAL, + DEX_LIB_JF + }, + description = "dex_lib" + ) + public String dexLib = DEX_LIB_INTERNAL; + @OptionArg(name = "-sig", description = "signatures_path") public File signaturesDirectory; diff --git a/src/main/java/com/reandroid/apkeditor/smali/SmaliCompiler.java b/src/main/java/com/reandroid/apkeditor/smali/SmaliCompiler.java index c993e51d..dfd9f824 100644 --- a/src/main/java/com/reandroid/apkeditor/smali/SmaliCompiler.java +++ b/src/main/java/com/reandroid/apkeditor/smali/SmaliCompiler.java @@ -18,13 +18,13 @@ import com.reandroid.apk.APKLogger; import com.reandroid.apk.ApkModuleEncoder; import com.reandroid.apk.DexEncoder; -import com.reandroid.apkeditor.APKEditor; import com.reandroid.apkeditor.compile.BuildOptions; import com.reandroid.archive.FileInputSource; import com.reandroid.archive.InputSource; import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; import com.reandroid.dex.model.DexFile; import com.reandroid.utils.StringsUtil; +import com.reandroid.utils.io.FileUtil; import org.jf.dexlib2.extra.DexMarker; import org.jf.smali.Smali; import org.jf.smali.SmaliOptions; @@ -79,18 +79,15 @@ private InputSource build(String progress, File classesDir) throws IOException { } } private InputSource build(String progress, File classesDir, File dexCacheFile) throws IOException { - if(APKEditor.isExperimental()) { - return buildExperimental(progress, classesDir, dexCacheFile); + if(BuildOptions.DEX_LIB_INTERNAL.equals(buildOptions.dexLib)) { + return buildWithInternalLib(progress, classesDir, dexCacheFile); } - return buildJesusFreke(progress, classesDir, dexCacheFile); + return buildWithJesusFreke(progress, classesDir, dexCacheFile); } - private InputSource buildJesusFreke(String progress, File classesDir, File dexCacheFile) throws IOException { - logMessage(progress + "Smali: " + dexCacheFile.getName()); + private InputSource buildWithJesusFreke(String progress, File classesDir, File dexCacheFile) throws IOException { + logMessage(progress + "Smali: " + dexCacheFile.getName()); SmaliOptions smaliOptions = new SmaliOptions(); - File dir = dexCacheFile.getParentFile(); - if(dir != null && !dir.exists()){ - dir.mkdirs(); - } + FileUtil.ensureParentDirectory(dexCacheFile); smaliOptions.outputDexFile = dexCacheFile.getAbsolutePath(); File marker = new File(classesDir, DexMarker.FILE_NAME); if(marker.isFile()){ @@ -108,8 +105,8 @@ private InputSource buildJesusFreke(String progress, File classesDir, File dexCa } return new FileInputSource(dexCacheFile, dexCacheFile.getName()); } - private InputSource buildExperimental(String progress, File classesDir, File dexCacheFile) throws IOException { - logMessage(progress + "Smali: " + dexCacheFile.getName()); + private InputSource buildWithInternalLib(String progress, File classesDir, File dexCacheFile) throws IOException { + logMessage(progress + "Smali: " + dexCacheFile.getName()); DexFile dexFile = DexFile.createDefault(); int version = 0; if (this.minSdkVersion != null) { diff --git a/src/main/java/com/reandroid/apkeditor/smali/SmaliDecompiler.java b/src/main/java/com/reandroid/apkeditor/smali/SmaliDecompiler.java index ea7a6e96..5771d619 100644 --- a/src/main/java/com/reandroid/apkeditor/smali/SmaliDecompiler.java +++ b/src/main/java/com/reandroid/apkeditor/smali/SmaliDecompiler.java @@ -19,7 +19,6 @@ import com.reandroid.apk.ApkModule; import com.reandroid.apk.DexDecoder; import com.reandroid.apk.DexFileInputSource; -import com.reandroid.apkeditor.APKEditor; import com.reandroid.apkeditor.decompile.DecompileOptions; import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.dex.model.DexDirectory; @@ -59,26 +58,26 @@ public SmaliDecompiler(TableBlock tableBlock){ @Override public void decodeDex(DexFileInputSource inputSource, File mainDir) throws IOException { logMessage("Baksmali: " + inputSource.getAlias()); - if(APKEditor.isExperimental()) { - disassembleDexFileExperimental(inputSource, mainDir); + if(DecompileOptions.DEX_LIB_INTERNAL.equals(decompileOptions.dexLib)) { + disassembleWithInternalDexLib(inputSource, mainDir); }else { - disassembleJesusFreke(inputSource, mainDir); + disassembleWithJesusFrekeLib(inputSource, mainDir); } writeDexCache(inputSource, mainDir); } @Override public void decodeDex(ApkModule apkModule, File mainDirectory) throws IOException { - if(!APKEditor.isExperimental()) { + if(!DecompileOptions.DEX_LIB_INTERNAL.equals(decompileOptions.dexLib)) { DexDecoder.super.decodeDex(apkModule, mainDirectory); return; } DexDirectory directory = (DexDirectory) apkModule.getTag(DexDirectory.class); - if(directory == null) { - if(!canLoadFullDex(apkModule)) { + if (directory == null) { + if (apkModule.listDexFiles().size() > decompileOptions.loadDex) { DexDecoder.super.decodeDex(apkModule, mainDirectory); return; } - logMessage("Loading full dex ..."); + logMessage("Loading full dex files: " + apkModule.listDexFiles().size()); Predicate> filter; if (decompileOptions.noDexDebug) { filter = sectionType -> sectionType != SectionType.DEBUG_INFO; @@ -88,7 +87,6 @@ public void decodeDex(ApkModule apkModule, File mainDirectory) throws IOExceptio directory = DexDirectory.fromZip(apkModule.getZipEntryMap(), filter); } - logMessage("Dumping smali ..."); File smali = toSmaliRoot(mainDirectory); SmaliWriterSetting setting = new SmaliWriterSetting(); if (tableBlock != null) { @@ -108,22 +106,8 @@ public void decodeDex(ApkModule apkModule, File mainDirectory) throws IOExceptio writeDexCache(inputSource, mainDirectory); } } - private boolean canLoadFullDex(ApkModule apkModule) { - int CLASSES_LIMIT = 4; - if (decompileOptions.noDexDebug) { - CLASSES_LIMIT += 3; - } - int size = apkModule.listDexFiles().size(); - logMessage("Total dex files: " + size); - if(size > CLASSES_LIMIT) { - logMessage("Huge classes your memory might not handle it, decoding separately without advanced features." + - " You can disable this restrictions by increasing \"CLASSES_LIMIT\" variable here on source code"); - return false; - } - return true; - } - private void disassembleJesusFreke(DexFileInputSource inputSource, File mainDir) throws IOException { + private void disassembleWithJesusFrekeLib(DexFileInputSource inputSource, File mainDir) throws IOException { File dir = toOutDir(inputSource, mainDir); BaksmaliOptions options = new BaksmaliOptions(); options.localsDirective = true; @@ -135,7 +119,7 @@ private void disassembleJesusFreke(DexFileInputSource inputSource, File mainDir) DexBackedDexFile dexFile = getInputDexFile(inputSource, options); Baksmali.disassembleDexFile(dexFile, dir, 1, options); } - private void disassembleDexFileExperimental(DexFileInputSource inputSource, File mainDir) throws IOException { + private void disassembleWithInternalDexLib(DexFileInputSource inputSource, File mainDir) throws IOException { Predicate> filter; if (decompileOptions.noDexDebug) { filter = sectionType -> sectionType != SectionType.DEBUG_INFO; diff --git a/src/main/resources/strings/strings.properties b/src/main/resources/strings/strings.properties index 0db6932b..ed4599de 100644 --- a/src/main/resources/strings/strings.properties +++ b/src/main/resources/strings/strings.properties @@ -40,10 +40,14 @@ decode_example_2=[Specify output]\njava -jar APKEditor.jar d -i path/input.apk - decode_example_3=[Specify decode type]\njava -jar APKEditor.jar d -t xml -i path/input.apk decode_example_4=[Specify framework file(s)]\njava -jar APKEditor.jar d -i path/input.apk -framework framework-res.apk -framework platforms/android-32/android.jar decode_example_5=[Decode apk signature block]\njava -jar APKEditor.jar d -t sig -i path/input.apk -sig path/signatures_dir +decode_load_dex=Number of dex files to load at a time.\nIf the apk dex files count greater than this value, then the decoder loads one dex at a time.\n *Applies only when -dex-lib set to internal.\n *Default = 3\n *See below. +decode_note_1=[internal] Dex builder\:\n* Fully supports dex files up to 042.\n* Highest dex file compression.\n* Builds with similar dex-section order as r8/dx.\n* Convenient dex markers editing, see file smali/classes/dex-file.json \n* Additional helpful smali comments: e.g class/method hierarchy.\n* Supports whitespaces on class simple name as introduced on dex 041+ +decode_note_2=[-load-dex] To print correct class/method hierarchy, it is necessary to load all dex files at once. This may result high memory consumption and could fail with "OutOfMemoryError" thus you are required to limit the number of dex files to load at a time. You can overcome this problem with -Xmx memory arg e.g java -Xmx8g -jar APKEditor.jar ... decode_types=Decode types\: decode_usage=d [Options, flags] dump_dex_markers=Dumps dex markers (applies only when smali mode). duplicate_option_exception=Duplicate option '%s' +dex_lib=Dex library to use\:\n 1) internal : Use internal library, supports dex versions up to 042.\n 2) jf : Use library by JesusFreke/smali, supports dex versions 035 and below.\n *Default = jf\n **WARN: The default value will be replaced by "internal" on coming versions.\n *See below. empty_command_args_exception=Empty command, run with -h to get help empty_command_option_exception=Empty options, run with -h to get help force_delete=Force delete output path. @@ -121,6 +125,7 @@ title_example=Examples\: title_flags=Flags\: title_options=Options\: title_other_options=Other options\: +title_notes=Notes\: title_usage=Usage\: unknown_command_exception=Unknown command\: '%s' unknown_option_exception=Unknown option\: '%s'