Skip to content

Commit

Permalink
feat(gui): show smali (#197) (PR #635)
Browse files Browse the repository at this point in the history
  • Loading branch information
asashour authored and skylot committed Apr 29, 2019
1 parent 745c52e commit 031582d
Show file tree
Hide file tree
Showing 14 changed files with 177 additions and 47 deletions.
2 changes: 1 addition & 1 deletion jadx-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies {
}
compile 'com.google.guava:guava:27.1-jre'

testCompile 'org.smali:baksmali:2.2.7'
compile 'org.smali:baksmali:2.2.7'

testCompile 'org.apache.commons:commons-lang3:3.8.1'

Expand Down
45 changes: 45 additions & 0 deletions jadx-core/src/main/java/jadx/api/JadxDecompiler.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
package jadx.api;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.Baksmali;
import org.jf.baksmali.BaksmaliOptions;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.Opcodes;
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.util.IndentingWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -26,6 +44,8 @@
import jadx.core.dex.visitors.SaveCode;
import jadx.core.export.ExportGradleProject;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.DexFile;
import jadx.core.utils.files.FileUtils;
import jadx.core.utils.files.InputFile;
import jadx.core.xmlgen.BinaryXMLParser;
import jadx.core.xmlgen.ResourcesSaver;
Expand Down Expand Up @@ -290,6 +310,31 @@ void processClass(ClassNode cls) {
ProcessClass.process(cls, passes, true);
}

void generateSmali(ClassNode cls) {
Path path = cls.dex().getDexFile().getPath();
String className = cls.getAlias().makeRawFullName();
className = 'L' + className.replace('.', '/') + ';';
try (InputStream in = Files.newInputStream(path)) {
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(path.toFile(), Opcodes.getDefault());
boolean decompiled = false;
for (DexBackedClassDef classDef : dexFile.getClasses()) {
if (classDef.getType().equals(className)) {
ClassDefinition classDefinition = new ClassDefinition(new BaksmaliOptions(), classDef);
StringWriter sw = new StringWriter();
classDefinition.writeTo(new IndentingWriter(sw));
cls.setSmali(sw.toString());
decompiled = true;
break;
}
}
if (!decompiled) {
LOG.error("Failed to find smali class {}", className);
}
} catch (IOException e) {
LOG.error("Error generating smali", e);
}
}

RootNode getRoot() {
return root;
}
Expand Down
10 changes: 10 additions & 0 deletions jadx-core/src/main/java/jadx/api/JavaClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ public synchronized void decompile() {
}
}

public synchronized String getSmali() {
if (decompiler == null) {
return null;
}
if (cls.getSmali() == null) {
decompiler.generateSmali(cls);
}
return cls.getSmali();
}

public synchronized void unload() {
cls.unload();
}
Expand Down
21 changes: 16 additions & 5 deletions jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package jadx.core.dex.nodes;

import static jadx.core.dex.nodes.ProcessState.UNLOADED;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.android.dex.ClassData;
import com.android.dex.ClassData.Field;
import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef;
import com.android.dex.Dex;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jadx.core.Consts;
import jadx.core.codegen.CodeWriter;
Expand All @@ -35,8 +38,6 @@
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;

import static jadx.core.dex.nodes.ProcessState.UNLOADED;

public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);

Expand All @@ -53,6 +54,8 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {

// store decompiled code
private CodeWriter code;
// store smali
private String smali;
// store parent for inner classes or 'this' otherwise
private ClassNode parentClass;

Expand Down Expand Up @@ -482,6 +485,14 @@ public CodeWriter getCode() {
return code;
}

public void setSmali(String smali) {
this.smali = smali;
}

public String getSmali() {
return smali;
}

public ProcessState getState() {
return state;
}
Expand Down
10 changes: 9 additions & 1 deletion jadx-core/src/main/java/jadx/core/utils/files/DexFile.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package jadx.core.utils.files;

import java.nio.file.Path;

import com.android.dex.Dex;

public class DexFile {
private final InputFile inputFile;
private final String name;
private final Dex dexBuf;
private final Path path;

public DexFile(InputFile inputFile, String name, Dex dexBuf) {
public DexFile(InputFile inputFile, String name, Dex dexBuf, Path path) {
this.inputFile = inputFile;
this.name = name;
this.dexBuf = dexBuf;
this.path = path;
}

public String getName() {
Expand All @@ -21,6 +25,10 @@ public Dex getDexBuf() {
return dexBuf;
}

public Path getPath() {
return path;
}

public InputFile getInputFile() {
return inputFile;
}
Expand Down
54 changes: 28 additions & 26 deletions jadx-core/src/main/java/jadx/core/utils/files/InputFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,20 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep
String fileName = file.getName();

if (fileName.endsWith(".dex")) {
addDexFile(new Dex(file));
addDexFile(fileName, new Dex(file), file.toPath());
return;
}
if (fileName.endsWith(".smali")) {
Path output = FileUtils.createTempFile(".dex");
SmaliOptions options = new SmaliOptions();
options.outputDexFile = output.toAbsolutePath().toString();
Smali.assemble(options, file.getAbsolutePath());
addDexFile(new Dex(output.toFile()));
addDexFile("", new Dex(output.toFile()), output);
return;
}
if (fileName.endsWith(".class")) {
for (Dex dex : loadFromClassFile(file)) {
addDexFile(dex);
for (Path path : loadFromClassFile(file)) {
addDexFile(path);
}
return;
}
Expand All @@ -81,8 +81,8 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep
return;
}
if (fileName.endsWith(".jar")) {
for (Dex dex : loadFromJar(file.toPath())) {
addDexFile(dex);
for (Path path : loadFromJar(file.toPath())) {
addDexFile(path);
}
return;
}
Expand All @@ -98,12 +98,16 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep
LOG.warn("No dex files found in {}", file);
}

private void addDexFile(Dex dexBuf) {
addDexFile("", dexBuf);
private void addDexFile(Path path) throws IOException {
addDexFile("", path);
}

private void addDexFile(String fileName, Dex dexBuf) {
dexFiles.add(new DexFile(this, fileName, dexBuf));
private void addDexFile(String fileName, Path path) throws IOException {
addDexFile(fileName, new Dex(Files.readAllBytes(path)), path);
}

private void addDexFile(String fileName, Dex dexBuf, Path path) {
dexFiles.add(new DexFile(this, fileName, dexBuf, path));
}

private boolean loadFromZip(String ext) throws IOException, DecodeException {
Expand All @@ -125,9 +129,9 @@ private boolean loadFromZip(String ext) throws IOException, DecodeException {
|| entryName.endsWith(instantRunDexSuffix)) {
switch (ext) {
case ".dex":
Dex dexBuf = makeDexBuf(entryName, inputStream);
if (dexBuf != null) {
addDexFile(entryName, dexBuf);
Path path = makeDexBuf(entryName, inputStream);
if (path != null) {
addDexFile(entryName, path);
index++;
}
break;
Expand All @@ -136,8 +140,8 @@ private boolean loadFromZip(String ext) throws IOException, DecodeException {
index++;
Path jarFile = FileUtils.createTempFile(entryName);
Files.copy(inputStream, jarFile, StandardCopyOption.REPLACE_EXISTING);
for (Dex dex : loadFromJar(jarFile)) {
addDexFile(entryName, dex);
for (Path p : loadFromJar(jarFile)) {
addDexFile(entryName, p);
}
break;

Expand All @@ -164,28 +168,26 @@ private boolean loadFromZip(String ext) throws IOException, DecodeException {
}

@Nullable
private Dex makeDexBuf(String entryName, InputStream inputStream) {
private Path makeDexBuf(String entryName, InputStream inputStream) {
try {
return new Dex(inputStream);
Path path = FileUtils.createTempFile(".dex");
Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
return path;
} catch (Exception e) {
LOG.error("Failed to load file: {}, error: {}", entryName, e.getMessage(), e);
return null;
}
}

private static List<Dex> loadFromJar(Path jar) throws DecodeException {
private static List<Path> loadFromJar(Path jar) throws DecodeException {
JavaToDex j2d = new JavaToDex();
try {
LOG.info("converting to dex: {} ...", jar.getFileName());
List<byte[]> byteList = j2d.convert(jar);
if (byteList.isEmpty()) {
List<Path> pathList = j2d.convert(jar);
if (pathList.isEmpty()) {
throw new JadxException("Empty dx output");
}
List<Dex> dexList = new ArrayList<>(byteList.size());
for (byte[] b : byteList) {
dexList.add(new Dex(b));
}
return dexList;
return pathList;
} catch (Exception e) {
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
} finally {
Expand All @@ -195,7 +197,7 @@ private static List<Dex> loadFromJar(Path jar) throws DecodeException {
}
}

private static List<Dex> loadFromClassFile(File file) throws IOException, DecodeException {
private static List<Path> loadFromClassFile(File file) throws IOException, DecodeException {
Path outFile = FileUtils.createTempFile(".jar");
try (JarOutputStream jo = new JarOutputStream(Files.newOutputStream(outFile))) {
String clsName = AsmUtils.getNameFromClassFile(file);
Expand Down
10 changes: 5 additions & 5 deletions jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public DxArgs(DxContext context, String dexDir, String[] input) {

private String dxErrors;

public List<byte[]> convert(Path jar) throws JadxException {
public List<Path> convert(Path jar) throws JadxException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream errOut = new ByteArrayOutputStream()) {
DxContext context = new DxContext(out, errOut);
Expand All @@ -51,14 +51,14 @@ public List<byte[]> convert(Path jar) throws JadxException {
if (result != 0) {
throw new JadxException("Java to dex conversion error, code: " + result);
}
List<byte[]> list = new ArrayList<>();
List<Path> list = new ArrayList<>();
try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
for (Path child : ds) {
list.add(Files.readAllBytes(child));
Files.delete(child);
list.add(child);
child.toFile().deleteOnExit();
}
}
Files.delete(dir);
dir.toFile().deleteOnExit();
return list;
} catch (Exception e) {
throw new JadxException("dx exception: " + e.getMessage(), e);
Expand Down
5 changes: 5 additions & 0 deletions jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public String getContent() {
return cls.getCode();
}

@Override
public String getSmali() {
return cls.getSmali();
}

@Override
public String getSyntaxName() {
return SyntaxConstants.SYNTAX_STYLE_JAVA;
Expand Down
4 changes: 4 additions & 0 deletions jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public String getContent() {
return null;
}

public String getSmali() {
return null;
}

public String getSyntaxName() {
return SyntaxConstants.SYNTAX_STYLE_NONE;
}
Expand Down
Loading

0 comments on commit 031582d

Please sign in to comment.