Skip to content

Commit

Permalink
fix: handle anonymous class self inlining (#604)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed May 16, 2019
1 parent 84b9f11 commit 9d5dda1
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 3 deletions.
4 changes: 4 additions & 0 deletions jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;

public class ClassGen {

Expand Down Expand Up @@ -261,6 +262,9 @@ private void addMethods(CodeWriter code) {
try {
addMethod(code, mth);
} catch (Exception e) {
if (mth.getParentClass().getTopParentClass().contains(AFlag.RESTART_CODEGEN)) {
throw new JadxRuntimeException("Method generation error", e);
}
code.newLine().add("/*");
code.newLine().addMultiLine(ErrorsCounter.methodError(mth, "Method generation error", e));
Utils.appendStackTrace(code, e);
Expand Down
14 changes: 13 additions & 1 deletion jadx-core/src/main/java/jadx/core/codegen/CodeGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;

public class CodeGen {

Expand All @@ -11,7 +12,18 @@ public static void generate(ClassNode cls) throws CodegenException {
cls.setCode(CodeWriter.EMPTY);
} else {
ClassGen clsGen = new ClassGen(cls, cls.root().getArgs());
cls.setCode(clsGen.makeClass());
CodeWriter code;
try {
code = clsGen.makeClass();
} catch (Exception e) {
if (cls.contains(AFlag.RESTART_CODEGEN)) {
cls.remove(AFlag.RESTART_CODEGEN);
code = clsGen.makeClass();
} else {
throw new JadxRuntimeException("Code generation error", e);
}
}
cls.setCode(code);
}
}

Expand Down
8 changes: 8 additions & 0 deletions jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,14 @@ private void makeConstructor(ConstructorInsn insn, CodeWriter code)
}

private void inlineAnonymousConstructor(CodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException {
if (this.mth.getParentClass() == cls) {
cls.remove(AFlag.ANONYMOUS_CLASS);
cls.remove(AFlag.DONT_GENERATE);
mth.getParentClass().getTopParentClass().add(AFlag.RESTART_CODEGEN);
throw new CodegenException("Anonymous inner class unlimited recursion detected."
+ " Convert class to inner: " + cls.getClassInfo().getFullName());
}

cls.add(AFlag.DONT_GENERATE);
ArgType parent;
if (cls.getInterfaces().size() == 1) {
Expand Down
3 changes: 3 additions & 0 deletions jadx-core/src/main/java/jadx/core/codegen/MethodGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ public void addInstructions(CodeWriter code) throws CodegenException {
classGen.insertDecompilationProblems(code, mth);
addInstructions(code);
} catch (Exception e) {
if (mth.getParentClass().getTopParentClass().contains(AFlag.RESTART_CODEGEN)) {
throw e;
}
mth.addError("Method code generation error", e);
classGen.insertDecompilationProblems(code, mth);
addInstructions(code);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum AFlag {
DONT_WRAP,
DONT_INLINE,
DONT_GENERATE, // process as usual, but don't output to generated code
RESTART_CODEGEN,
DONT_RENAME, // do not rename during deobfuscation
REMOVE, // can be completely removed
ADDED_TO_REGION,
Expand Down
3 changes: 3 additions & 0 deletions jadx-core/src/main/java/jadx/core/utils/files/InputFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ private static List<Path> loadFromJar(Path jar) throws DecodeException {
if (pathList.isEmpty()) {
throw new JadxException("Empty dx output");
}
if (LOG.isDebugEnabled()) {
LOG.debug("result dex files: {}", pathList);
}
return pathList;
} catch (Exception e) {
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
Expand Down
3 changes: 1 addition & 2 deletions jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public List<Path> convert(Path jar) throws JadxException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream errOut = new ByteArrayOutputStream()) {
DxContext context = new DxContext(out, errOut);
Path dir = FileUtils.createTempDir(jar.getFileName().toString());
Path dir = FileUtils.createTempDir("jar-to-dex-");
DxArgs args = new DxArgs(
context,
dir.toAbsolutePath().toString(),
Expand All @@ -58,7 +58,6 @@ public List<Path> convert(Path jar) throws JadxException {
child.toFile().deleteOnExit();
}
}
dir.toFile().deleteOnExit();
return list;
} catch (Exception e) {
throw new JadxException("dx exception: " + e.getMessage(), e);
Expand Down
4 changes: 4 additions & 0 deletions jadx-core/src/test/java/jadx/tests/api/SmaliTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ protected ClassNode getClassNodeFromSmaliFiles(String pkg, String testName, Stri
return getClassNodeFromFile(outDex, 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()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package jadx.tests.integration.inner;

import org.junit.jupiter.api.Test;

import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;

import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static jadx.tests.api.utils.JadxMatchers.countString;
import static org.hamcrest.MatcherAssert.assertThat;

public class TestIncorrectAnonymousClass extends SmaliTest {

// @formatter:off
/*
public static class TestCls {
public final class 1 {
public void invoke() {
new 1(); // cause infinite self inline
}
}
public void test() {
new 1();
}
}
*/
// @formatter:on

@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliFiles("TestCls");
String code = cls.getCode().toString();

assertThat(code, containsOne("public final class AnonymousClass1 {"));
assertThat(code, countString(2, "new AnonymousClass1();"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.class public final Linner/TestCls$1;
.super Ljava/lang/Object;


# direct methods
.method public constructor <init>()V
.registers 1

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

return-void
.end method


# virtual methods
.method public invoke()V
.registers 2

new-instance v0, Linner/TestCls$1;

invoke-direct {v0}, Linner/TestCls$1;-><init>()V

return-void
.end method
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.class public Linner/TestCls;
.super Ljava/lang/Object;

# direct methods
.method public constructor <init>()V
.registers 1

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

return-void
.end method

.method public test()V
.registers 2

new-instance v0, Linner/TestCls$1;

invoke-direct {v0}, Linner/TestCls$1;-><init>()V

return-void
.end method

0 comments on commit 9d5dda1

Please sign in to comment.