Skip to content

Commit

Permalink
[Experimental] disassemble/assemble smali with ARSCLib
Browse files Browse the repository at this point in the history
  • Loading branch information
REAndroid committed May 11, 2024
1 parent 1d9f54f commit 41d861b
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 9 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,8 @@ task fatJar(type: Jar) {
with jar
}

task betaJar() {
version = "${version}-beta"
finalizedBy(fatJar)
}

3 changes: 3 additions & 0 deletions src/main/java/com/reandroid/apkeditor/APKEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public static String getJarName(){
}
return getName()+".jar";
}
public static boolean isExperimental() {
return APKEditor.getVersion().contains("beta");
}

public static final String PATH_properties = "/apkeditor.properties";

Expand Down
69 changes: 68 additions & 1 deletion src/main/java/com/reandroid/apkeditor/smali/SmaliCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@
import com.reandroid.apk.APKLogger;
import com.reandroid.apk.ApkModuleEncoder;
import com.reandroid.apk.DexEncoder;
import com.reandroid.apkeditor.APKEditor;
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.dex.sections.Marker;
import com.reandroid.dex.sections.SectionType;
import com.reandroid.dex.smali.SmaliReader;
import com.reandroid.utils.StringsUtil;
import com.reandroid.utils.io.FileIterator;
import com.reandroid.utils.io.IOUtil;
import org.jf.dexlib2.extra.DexMarker;
import org.jf.smali.Smali;
import org.jf.smali.SmaliOptions;
Expand Down Expand Up @@ -73,6 +80,12 @@ 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);
}
return buildJesusFreke(progress, classesDir, dexCacheFile);
}
private InputSource buildJesusFreke(String progress, File classesDir, File dexCacheFile) throws IOException {
logMessage(progress + "Smali: " + dexCacheFile.getName());
SmaliOptions smaliOptions = new SmaliOptions();
File dir = dexCacheFile.getParentFile();
Expand All @@ -88,14 +101,56 @@ private InputSource build(String progress, File classesDir, File dexCacheFile) t
smaliOptions.jobs = 1;
}
if (this.minSdkVersion != null) {
smaliOptions.apiLevel = this.minSdkVersion.intValue();
smaliOptions.apiLevel = this.minSdkVersion;
}
boolean success = Smali.assemble(smaliOptions, classesDir.getAbsolutePath());
if(!success){
throw new IOException("Failed to build smali, check the logs");
}
return new FileInputSource(dexCacheFile, dexCacheFile.getName());
}
private InputSource buildExperimental(String progress, File classesDir, File dexCacheFile) throws IOException {
logMessage(progress + "Smali: " + dexCacheFile.getName());
DexFile dexFile = DexFile.createDefault();
FileIterator fileIterator = new FileIterator(classesDir,
FileIterator.getExtensionFilter(".smali"));
while (fileIterator.hasNext()) {
File file = fileIterator.next();
try {
dexFile.fromSmali(SmaliReader.of(file));
}catch (Exception e) {
throw new IOException("Error at: " + file, e);
}
}
dexFile.refresh();
readMarkers(dexFile, classesDir);
int version = 0;
if (this.minSdkVersion != null) {
version = minSdkVersion;
}
version = apiToDexVersion(version);
dexFile.setVersion(version);
dexFile.clearEmptySections();
dexFile.sortSection(SectionType.getR8Order());
dexFile.refreshFull();
dexFile.write(dexCacheFile);
dexFile.close();
return new FileInputSource(dexCacheFile, dexCacheFile.getName());
}
private void readMarkers(DexFile dexFile, File classesDir) throws IOException {
File markersFile = new File(classesDir, DexMarker.FILE_NAME);
if(markersFile.isFile()){
logMessage("Reading markers ...");
String[] content = StringsUtil.split(IOUtil.readUtf8(markersFile), '\n');
for(String markerString : content) {
Marker marker = Marker.parse(markerString);
if(marker != null) {
dexFile.addMarker(marker);
}
}
}
}

private boolean isModified(File classesDir, File dexCacheFile){
if(noCache || !dexCacheFile.isFile()){
return true;
Expand Down Expand Up @@ -155,4 +210,16 @@ private void logMessage(String msg){
}
}

public static int apiToDexVersion(int api) {
if (api <= 23) {
return 35;
}
if (api <= 25) {
return 37;
}
if (api <= 27) {
return 38;
}
return 39;
}
}
72 changes: 64 additions & 8 deletions src/main/java/com/reandroid/apkeditor/smali/SmaliDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@
package com.reandroid.apkeditor.smali;

import com.reandroid.apk.APKLogger;
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;
import com.reandroid.dex.model.DexFile;
import com.reandroid.dex.smali.SmaliWriter;
import com.reandroid.dex.smali.SmaliWriterSetting;
import org.jf.baksmali.Baksmali;
import org.jf.baksmali.BaksmaliOptions;
import org.jf.dexlib2.Opcodes;
Expand All @@ -28,6 +34,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class SmaliDecompiler implements DexDecoder {
private final TableBlock tableBlock;
Expand All @@ -43,15 +50,44 @@ public SmaliDecompiler(TableBlock tableBlock){
this(tableBlock, new DecompileOptions());
}
@Override
public boolean decodeDex(DexFileInputSource inputSource, File mainDir) throws IOException {
public void decodeDex(DexFileInputSource inputSource, File mainDir) throws IOException {
logMessage("Baksmali: " + inputSource.getAlias());
disassembleDexFile(inputSource, mainDir);
File cache = new File(mainDir, SmaliUtil.CACHE_DIR);
cache = new File(cache, inputSource.getAlias());
inputSource.write(cache);
return true;
if(APKEditor.isExperimental()) {
disassembleDexFileExperimental(inputSource, mainDir);
}else {
disassembleJesusFreke(inputSource, mainDir);
}
writeDexCache(inputSource, mainDir);
}
@Override
public void decodeDex(ApkModule apkModule, File mainDirectory) throws IOException {
if(!canLoadFullDex(apkModule) || !APKEditor.isExperimental()) {
DexDecoder.super.decodeDex(apkModule, mainDirectory);
return;
}
logMessage("Loading full dex ...");
DexDirectory directory = DexDirectory.fromZip(apkModule.getZipEntryMap());
File smali = toSmaliRoot(mainDirectory);
SmaliWriterSetting setting = new SmaliWriterSetting();
setting.setResourceIdComment(tableBlock.pickOne());
setting.addClassComments(directory);
setting.addMethodComments(directory);
SmaliWriter smaliWriter = new SmaliWriter();
smaliWriter.setWriterSetting(setting);
logMessage("Baksmali ...");
directory.writeSmali(smaliWriter, smali);
directory.close();

List<DexFileInputSource> dexList = apkModule.listDexFiles();
for(DexFileInputSource inputSource : dexList) {
writeDexCache(inputSource, mainDirectory);
}
}
private void disassembleDexFile(DexFileInputSource inputSource, File mainDir) throws IOException {
private boolean canLoadFullDex(ApkModule apkModule) {
return apkModule.listDexFiles().size() < 5;
}

private void disassembleJesusFreke(DexFileInputSource inputSource, File mainDir) throws IOException {
File dir = toOutDir(inputSource, mainDir);
BaksmaliOptions options = new BaksmaliOptions();
options.localsDirective = true;
Expand All @@ -63,19 +99,39 @@ private void disassembleDexFile(DexFileInputSource inputSource, File mainDir) th
DexBackedDexFile dexFile = getInputDexFile(inputSource, options);
Baksmali.disassembleDexFile(dexFile, dir, 1, options);
}
private void disassembleDexFileExperimental(DexFileInputSource inputSource, File mainDir) throws IOException {
DexFile dexFile = DexFile.read(inputSource.openStream());
dexFile.setSimpleName(inputSource.getAlias());
SmaliWriterSetting setting = new SmaliWriterSetting();
setting.setResourceIdComment(tableBlock.pickOne());
setting.addClassComments(dexFile);
setting.addMethodComments(dexFile);
SmaliWriter smaliWriter = new SmaliWriter();
smaliWriter.setWriterSetting(setting);
dexFile.writeSmali(smaliWriter, toSmaliRoot(mainDir));
dexFile.close();
}
private void writeDexCache(DexFileInputSource inputSource, File mainDir) throws IOException {
File cache = new File(mainDir, SmaliUtil.CACHE_DIR);
cache = new File(cache, inputSource.getAlias());
inputSource.write(cache);
}
private File toOutDir(DexFileInputSource inputSource, File mainDir){
String name = "classes";
int num = inputSource.getDexNumber();
if(num != 0){
name = name + num;
}
File dir = new File(mainDir, "smali");
File dir = toSmaliRoot(mainDir);
dir = new File(dir, name);
if(!dir.exists()){
dir.mkdirs();
}
return dir;
}
private File toSmaliRoot(File mainDir){
return new File(mainDir, DexDecoder.SMALI_DIRECTORY_NAME);
}

private DexBackedDexFile getInputDexFile(DexFileInputSource inputSource, BaksmaliOptions options) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Expand Down

0 comments on commit 41d861b

Please sign in to comment.