From 6cfa79e4567745ca2b449d623b455e4210812a5c Mon Sep 17 00:00:00 2001 From: IgorEisberg Date: Sun, 4 Jul 2021 02:14:56 +0300 Subject: [PATCH 1/2] Determine minSdkVersion with baksmali for JARs + don't compress webp We take advantage of the fact that baksmali can determine the minimum API version needed for a given collection of opcodes and extract it, then save to mMinSdkVersion, which is only used by smali via buildSourcesSmali. This change, in addition to my previous change, allows to decompile and recompile JARs without worrying about specifying an API version via -api/--api-level. As an extra, added webp file extension to NO_COMPRESS_PATTERN, according to AAPT2 standards, and since it's factually never compresses as it's an already compressed format. https://cs.android.com/android/platform/superproject/+/master:frameworks/base/tools/aapt2/cmd/Link.cpp;l=2328 --- .../src/main/java/brut/androlib/Androlib.java | 20 ++++++++++++------- .../main/java/brut/androlib/ApkDecoder.java | 11 ++++++++++ .../java/brut/androlib/src/SmaliDecoder.java | 8 +++++--- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index 151f9786..bc8780c6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -50,16 +50,15 @@ public class Androlib { public ApkOptions apkOptions; private int mMinSdkVersion = 0; + public Androlib() { + this(new ApkOptions()); + } + public Androlib(ApkOptions apkOptions) { this.apkOptions = apkOptions; mAndRes.apkOptions = apkOptions; } - public Androlib() { - this.apkOptions = new ApkOptions(); - mAndRes.apkOptions = this.apkOptions; - } - public ResTable getResTable(ExtFile apkFile) throws AndrolibException { return mAndRes.getResTable(apkFile, true); @@ -70,6 +69,10 @@ public class Androlib { return mAndRes.getResTable(apkFile, loadMainPkg); } + public int getMinSdkVersion() { + return mMinSdkVersion; + } + public void decodeSourcesRaw(ExtFile apkFile, File outDir, String filename) throws AndrolibException { try { @@ -92,7 +95,10 @@ public class Androlib { OS.rmdir(smaliDir); smaliDir.mkdirs(); LOGGER.info("Baksmaling " + filename + "..."); - SmaliDecoder.decode(apkFile, smaliDir, filename, bakDeb, apiLevel); + apiLevel = SmaliDecoder.decode(apkFile, smaliDir, filename, bakDeb, apiLevel); + if (mMinSdkVersion == 0 || mMinSdkVersion > apiLevel) { + mMinSdkVersion = apiLevel; + } } catch (BrutException ex) { throw new AndrolibException(ex); } @@ -809,5 +815,5 @@ public class Androlib { "lib", "libs", "assets", "META-INF", "kotlin" }; private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("(" + "jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" + - "m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|mkv)$"); + "m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|webp|mkv)$"); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java index 9accf1bb..826293a6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java @@ -332,6 +332,8 @@ public class ApkDecoder { putVersionInfo(meta); putSharedLibraryInfo(meta); putSparseResourcesInfo(meta); + } else { + putMinSdkInfo(meta); } putUnknownInfo(meta); putFileCompressionInfo(meta); @@ -386,6 +388,15 @@ public class ApkDecoder { } } + private void putMinSdkInfo(MetaInfo meta) { + int minSdkVersion = mAndrolib.getMinSdkVersion(); + if (minSdkVersion > 0) { + Map sdkInfo = new LinkedHashMap<>(); + sdkInfo.put("minSdkVersion", Integer.toString(minSdkVersion)); + meta.sdkInfo = sdkInfo; + } + } + private void putPackageInfo(MetaInfo meta) throws AndrolibException { String renamed = mResTable.getPackageRenamed(); String original = mResTable.getPackageOriginal(); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java index 46bbcd7f..cc9b5fe4 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java @@ -31,9 +31,9 @@ import java.io.IOException; public class SmaliDecoder { - public static void decode(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) + public static int decode(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) throws AndrolibException { - new SmaliDecoder(apkFile, outDir, dexName, bakDeb, apiLevel).decode(); + return new SmaliDecoder(apkFile, outDir, dexName, bakDeb, apiLevel).decode(); } private SmaliDecoder(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) { @@ -44,7 +44,7 @@ public class SmaliDecoder { mApiLevel = apiLevel; } - private void decode() throws AndrolibException { + private int decode() throws AndrolibException { try { final BaksmaliOptions options = new BaksmaliOptions(); @@ -96,6 +96,8 @@ public class SmaliDecoder { } Baksmali.disassembleDexFile(dexFile, mOutDir, jobs, options); + + return dexFile.getOpcodes().api; } catch (IOException ex) { throw new AndrolibException(ex); } From fad2f99541d1de33588ee9c40801297eda1d8179 Mon Sep 17 00:00:00 2001 From: IgorEisberg Date: Sun, 4 Jul 2021 14:12:38 +0300 Subject: [PATCH 2/2] style: return DexFile interface from SmaliDecoder --- .../src/main/java/brut/androlib/Androlib.java | 14 ++++++++------ .../main/java/brut/androlib/src/SmaliDecoder.java | 10 ++++++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index bc8780c6..b646a2f6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -32,6 +32,10 @@ import brut.androlib.src.SmaliDecoder; import brut.common.BrutException; import brut.directory.*; import brut.util.*; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.jf.dexlib2.iface.DexFile; + import java.io.*; import java.util.*; import java.util.logging.Logger; @@ -41,9 +45,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; - public class Androlib { private final AndrolibResources mAndRes = new AndrolibResources(); protected final ResUnknownFiles mResUnknownFiles = new ResUnknownFiles(); @@ -95,9 +96,10 @@ public class Androlib { OS.rmdir(smaliDir); smaliDir.mkdirs(); LOGGER.info("Baksmaling " + filename + "..."); - apiLevel = SmaliDecoder.decode(apkFile, smaliDir, filename, bakDeb, apiLevel); - if (mMinSdkVersion == 0 || mMinSdkVersion > apiLevel) { - mMinSdkVersion = apiLevel; + DexFile dexFile = SmaliDecoder.decode(apkFile, smaliDir, filename, bakDeb, apiLevel); + int minSdkVersion = dexFile.getOpcodes().api; + if (mMinSdkVersion == 0 || mMinSdkVersion > minSdkVersion) { + mMinSdkVersion = minSdkVersion; } } catch (BrutException ex) { throw new AndrolibException(ex); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java index cc9b5fe4..226ae3da 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java @@ -24,6 +24,7 @@ import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.dexbacked.DexBackedDexFile; import org.jf.dexlib2.dexbacked.DexBackedOdexFile; import org.jf.dexlib2.analysis.InlineMethodResolver; +import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.iface.MultiDexContainer; import java.io.File; @@ -31,7 +32,7 @@ import java.io.IOException; public class SmaliDecoder { - public static int decode(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) + public static DexFile decode(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) throws AndrolibException { return new SmaliDecoder(apkFile, outDir, dexName, bakDeb, apiLevel).decode(); } @@ -44,7 +45,7 @@ public class SmaliDecoder { mApiLevel = apiLevel; } - private int decode() throws AndrolibException { + private DexFile decode() throws AndrolibException { try { final BaksmaliOptions options = new BaksmaliOptions(); @@ -67,7 +68,8 @@ public class SmaliDecoder { } // create the container - MultiDexContainer container = DexFileFactory.loadDexContainer(mApkFile, mApiLevel > 0 ? Opcodes.forApi(mApiLevel) : null); + MultiDexContainer container = + DexFileFactory.loadDexContainer(mApkFile, mApiLevel > 0 ? Opcodes.forApi(mApiLevel) : null); MultiDexContainer.DexEntry dexEntry; DexBackedDexFile dexFile; @@ -97,7 +99,7 @@ public class SmaliDecoder { Baksmali.disassembleDexFile(dexFile, mOutDir, jobs, options); - return dexFile.getOpcodes().api; + return dexFile; } catch (IOException ex) { throw new AndrolibException(ex); }