Skip to content

Commit

Permalink
fix: bring back smali files support (#961)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Aug 5, 2020
1 parent bfd60b7 commit 558a867
Show file tree
Hide file tree
Showing 28 changed files with 334 additions and 75 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ node_modules/
jadx-output/
*-tmp/
**/tmp/
*.jobf

*.class
*.dump
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'org.sonarqube' version '3.0'
id 'com.github.ben-manes.versions' version '0.28.0'
id 'com.github.ben-manes.versions' version '0.29.0'
id "com.diffplug.gradle.spotless" version "4.5.1"
}

Expand Down
1 change: 1 addition & 0 deletions jadx-cli/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dependencies {
implementation(project(':jadx-core'))

runtimeOnly(project(':jadx-plugins:jadx-dex-input'))
runtimeOnly(project(':jadx-plugins:jadx-smali-input'))
runtimeOnly(project(':jadx-plugins:jadx-java-convert'))

implementation 'com.beust:jcommander:1.78'
Expand Down
15 changes: 10 additions & 5 deletions jadx-cli/src/main/java/jadx/cli/JadxCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ public class JadxCLI {
public static void main(String[] args) {
int result = 0;
try {
JadxCLIArgs jadxArgs = new JadxCLIArgs();
if (jadxArgs.processArgs(args)) {
result = processAndSave(jadxArgs.toJadxArgs());
}
result = execute(args);
} catch (JadxArgsValidateException e) {
LOG.error("Incorrect arguments: {}", e.getMessage());
result = 1;
Expand All @@ -31,7 +28,15 @@ public static void main(String[] args) {
}
}

static int processAndSave(JadxArgs jadxArgs) {
public static int execute(String[] args) {
JadxCLIArgs jadxArgs = new JadxCLIArgs();
if (jadxArgs.processArgs(args)) {
return processAndSave(jadxArgs.toJadxArgs());
}
return 0;
}

private static int processAndSave(JadxArgs jadxArgs) {
jadxArgs.setCodeCache(new NoOpCodeCache());
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
jadx.load();
Expand Down
68 changes: 68 additions & 0 deletions jadx-cli/src/test/java/jadx/cli/TestInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package jadx.cli;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jadx.core.utils.files.FileUtils;

import static org.assertj.core.api.Assertions.assertThat;

public class TestInput {
private static final Logger LOG = LoggerFactory.getLogger(TestInput.class);

@Test
public void testDexInput() throws Exception {
decompile("dex", "samples/hello.dex");
}

@Test
public void testSmaliInput() throws Exception {
decompile("smali", "samples/HelloWorld.smali");
}

private void decompile(String tmpDirName, String inputSample) throws URISyntaxException, IOException {
StringBuilder args = new StringBuilder();
Path tempDir = FileUtils.createTempDir(tmpDirName);
args.append("-v ");
args.append("-d ").append(tempDir.toAbsolutePath()).append(' ');

URL resource = getClass().getClassLoader().getResource(inputSample);
assertThat(resource).isNotNull();
String sampleFile = resource.toURI().getRawPath();
args.append(sampleFile);

int result = JadxCLI.execute(args.toString().split(" "));
assertThat(result).isEqualTo(0);
List<Path> resultJavaFiles = collectJavaFilesInDir(tempDir);
assertThat(resultJavaFiles).isNotEmpty();
}

private static List<Path> collectJavaFilesInDir(Path dir) throws IOException {
PathMatcher matcher = dir.getFileSystem().getPathMatcher("glob:**.java");
try (Stream<Path> pathStream = Files.walk(dir)) {
return pathStream
.filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS))
.peek(f -> LOG.debug("File in result dir: {}", f))
.filter(matcher::matches)
.collect(Collectors.toList());
}
}

@AfterAll
public static void cleanup() {
FileUtils.clearTempRootDir();
}
}
26 changes: 26 additions & 0 deletions jadx-cli/src/test/resources/samples/HelloWorld.smali
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.class LHelloWorld;
.super Ljava/lang/Object;
.source "HelloWorld.java"

.method constructor <init>()V
.registers 1

.line 1
invoke-direct {p0}, Ljava/lang/Object;-><init>()V

return-void
.end method

.method public static main([Ljava/lang/String;)V
.registers 2

.line 3
sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream;

const-string v0, "Hello, World"

invoke-virtual {p0, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

.line 4
return-void
.end method
Binary file added jadx-cli/src/test/resources/samples/hello.dex
Binary file not shown.
1 change: 1 addition & 0 deletions jadx-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {
testImplementation 'org.apache.commons:commons-lang3:3.10'

testRuntimeOnly(project(':jadx-plugins:jadx-dex-input'))
testRuntimeOnly(project(':jadx-plugins:jadx-smali-input'))
testRuntimeOnly(project(':jadx-plugins:jadx-java-convert'))
}

Expand Down
11 changes: 4 additions & 7 deletions jadx-core/src/main/java/jadx/api/JadxArgsValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,11 @@ private static void checkInputFiles(JadxArgs args) {
if (inputFiles.isEmpty()) {
throw new JadxArgsValidateException("Please specify input file");
}
if (inputFiles.size() > 1) {
for (File inputFile : inputFiles) {
String fileName = inputFile.getName();
if (fileName.startsWith("--")) {
throw new JadxArgsValidateException("Unknown argument: " + fileName);
}
for (File inputFile : inputFiles) {
String fileName = inputFile.getName();
if (fileName.startsWith("--")) {
throw new JadxArgsValidateException("Unknown argument: " + fileName);
}
throw new JadxArgsValidateException("Only one input file supported");
}
for (File file : inputFiles) {
checkFile(file);
Expand Down
5 changes: 4 additions & 1 deletion jadx-core/src/main/java/jadx/api/JadxDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ private void loadInputFiles() {
loadedInputs.clear();
List<Path> inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath);
for (JadxInputPlugin inputPlugin : pluginManager.getInputPlugins()) {
loadedInputs.add(inputPlugin.loadFiles(inputPaths));
ILoadResult loadResult = inputPlugin.loadFiles(inputPaths);
if (loadResult != null && !loadResult.isEmpty()) {
loadedInputs.add(loadResult);
}
}
}

Expand Down
20 changes: 12 additions & 8 deletions jadx-core/src/main/java/jadx/api/ResourcesLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import jadx.core.utils.Utils;
import jadx.core.utils.android.Res9patchStreamDecoder;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.FileUtils;
import jadx.core.utils.files.ZipSecurity;
import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.ResTableParser;
Expand Down Expand Up @@ -127,16 +128,19 @@ private void loadFile(List<ResourceFile> list, File file) {
if (file == null) {
return;
}
try (ZipFile zip = new ZipFile(file)) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (ZipSecurity.isValidZipEntry(entry)) {
addEntry(list, file, entry);
if (FileUtils.isZipFile(file)) {
try (ZipFile zip = new ZipFile(file)) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (ZipSecurity.isValidZipEntry(entry)) {
addEntry(list, file, entry);
}
}
} catch (Exception e) {
LOG.warn("Failed to open zip file: {}", file.getAbsolutePath());
}
} catch (Exception e) {
LOG.debug("Not a zip file: {}", file.getAbsolutePath());
} else {
addResourceFile(list, file);
}
}
Expand Down
2 changes: 1 addition & 1 deletion jadx-core/src/main/java/jadx/core/clsp/ClsSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public void loadFromClstFile() throws IOException, DecodeException {
if (LOG.isDebugEnabled()) {
long time = System.currentTimeMillis() - startTime;
int methodsCount = Stream.of(classes).mapToInt(clspClass -> clspClass.getMethodsMap().size()).sum();
LOG.debug("Load class set in {}ms, classes: {}, methods: {}", time, classes.length, methodsCount);
LOG.debug("Clst file loaded in {}ms, classes: {}, methods: {}", time, classes.length, methodsCount);
}
}

Expand Down
1 change: 1 addition & 0 deletions jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public void loadClasses(List<ILoadResult> loadedInputs) {
// sort classes by name, expect top classes before inner
classes.sort(Comparator.comparing(ClassNode::getFullName));
initInnerClasses();
LOG.debug("Classes loaded: {}", classes.size());
}

private void addDummyClass(IClassData classData, Exception exc) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ private static String bytesToHex(byte[] bytes) {
return new String(hexChars);
}

private static boolean isZipFile(File file) {
public static boolean isZipFile(File file) {
try (InputStream is = new FileInputStream(file)) {
byte[] headers = new byte[4];
int read = is.read(headers, 0, 4);
Expand Down
11 changes: 5 additions & 6 deletions jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public abstract class IntegrationTest extends TestUtils {
DebugChecks.checksEnabled = true;
}

private JadxDecompiler jadxDecompiler;
protected JadxDecompiler jadxDecompiler;

@BeforeEach
public void init() {
Expand Down Expand Up @@ -142,16 +142,16 @@ public String getTestPkg() {
public ClassNode getClassNode(Class<?> clazz) {
try {
File jar = getJarForClass(clazz);
return getClassNodeFromFile(jar, clazz.getName());
return getClassNodeFromFiles(Collections.singletonList(jar), clazz.getName());
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
return null;
}

public ClassNode getClassNodeFromFile(File file, String clsName) {
jadxDecompiler = loadFiles(Collections.singletonList(file));
public ClassNode getClassNodeFromFiles(List<File> files, String clsName) {
jadxDecompiler = loadFiles(files);
RootNode root = JadxInternalAccess.getRoot(jadxDecompiler);

ClassNode cls = root.resolveClass(clsName);
Expand All @@ -173,9 +173,8 @@ public ClassNode searchCls(List<ClassNode> list, String fullClsName) {
}

protected JadxDecompiler loadFiles(List<File> inputFiles) {
JadxDecompiler d;
args.setInputFiles(inputFiles);
d = new JadxDecompiler(args);
JadxDecompiler d = new JadxDecompiler(args);
try {
d.load();
} catch (Exception e) {
Expand Down
33 changes: 5 additions & 28 deletions jadx-core/src/test/java/jadx/tests/api/SmaliTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
import java.util.stream.Stream;

import org.jetbrains.annotations.Nullable;
import org.jf.smali.Smali;
import org.jf.smali.SmaliOptions;

import jadx.api.JadxDecompiler;
import jadx.api.JadxInternalAccess;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode;
Expand All @@ -26,9 +23,7 @@ public abstract class SmaliTest extends IntegrationTest {

protected ClassNode getClassNodeFromSmali(String file, String clsName) {
File smaliFile = getSmaliFile(file);
File outDex = createTempFile(".dex");
compileSmali(outDex, Collections.singletonList(smaliFile));
return getClassNodeFromFile(outDex, clsName);
return getClassNodeFromFiles(Collections.singletonList(smaliFile), clsName);
}

/**
Expand All @@ -51,23 +46,18 @@ protected ClassNode getClassNodeFromSmaliWithPkg(String pkg, String clsName) {
}

protected ClassNode getClassNodeFromSmaliFiles(String pkg, String testName, String clsName) {
File outDex = createTempFile(".dex");
compileSmali(outDex, collectSmaliFiles(pkg, testName));
return getClassNodeFromFile(outDex, pkg + '.' + clsName);
return getClassNodeFromFiles(collectSmaliFiles(pkg, testName), pkg + '.' + clsName);
}

protected ClassNode getClassNodeFromSmaliFiles(String clsName) {
return searchCls(loadFromSmaliFiles(), getTestPkg() + '.' + clsName);
}

protected List<ClassNode> loadFromSmaliFiles() {
File outDex = createTempFile(".dex");
compileSmali(outDex, collectSmaliFiles(getTestPkg(), getTestName()));

JadxDecompiler d = loadFiles(Collections.singletonList(outDex));
RootNode root = JadxInternalAccess.getRoot(d);
jadxDecompiler = loadFiles(collectSmaliFiles(getTestPkg(), getTestName()));
RootNode root = JadxInternalAccess.getRoot(jadxDecompiler);
List<ClassNode> classes = root.getClasses(false);
decompileAndCheck(d, classes);
decompileAndCheck(jadxDecompiler, classes);
return classes;
}

Expand Down Expand Up @@ -97,17 +87,4 @@ private static File getSmaliFile(String baseName) {
}
throw new AssertionError("Smali file not found: " + smaliFile.getPath());
}

private static boolean compileSmali(File output, List<File> inputFiles) {
try {
SmaliOptions options = new SmaliOptions();
options.outputDexFile = output.getAbsolutePath();
options.verboseErrors = true;
List<String> inputFileNames = inputFiles.stream().map(File::getAbsolutePath).collect(Collectors.toList());
Smali.assemble(options, inputFileNames);
} catch (Exception e) {
throw new AssertionError("Smali assemble error", e);
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public class DexFileLoader {

public static List<DexReader> collectDexFiles(List<Path> pathsList) {
return pathsList.stream()
.map((Path path) -> loadDexFromPath(path, 0))
.map(path -> loadDexFromPath(path, 0))
.filter(list -> !list.isEmpty())
.flatMap(Collection::stream)
.peek(dr -> LOG.debug("Loading dex: {}", dr))
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package jadx.plugins.input.dex;

import java.io.Closeable;
import java.nio.file.Path;
import java.util.List;

import jadx.api.plugins.JadxPluginInfo;
import jadx.api.plugins.input.JadxInputPlugin;
import jadx.api.plugins.input.data.ILoadResult;
import jadx.api.plugins.input.data.impl.EmptyLoadResult;

public class DexInputPlugin implements JadxInputPlugin {

Expand All @@ -16,6 +18,14 @@ public JadxPluginInfo getPluginInfo() {

@Override
public ILoadResult loadFiles(List<Path> input) {
return new DexLoadResult(DexFileLoader.collectDexFiles(input));
return loadDexFiles(input, null);
}

public static ILoadResult loadDexFiles(List<Path> inputFiles, Closeable closeable) {
List<DexReader> dexReaders = DexFileLoader.collectDexFiles(inputFiles);
if (dexReaders.isEmpty()) {
return EmptyLoadResult.INSTANCE;
}
return new DexLoadResult(dexReaders, closeable);
}
}
Loading

0 comments on commit 558a867

Please sign in to comment.