diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 39ff60911..30629806b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.jetbrains.annotations.Nullable; @@ -194,6 +195,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN root().getConstValues().processConstFields(this, staticFields); } + /** + * Class signature format: + * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1 + */ private void parseClassSignature() { SignatureParser sp = SignatureParser.fromNode(this); if (sp == null) { @@ -203,7 +208,7 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN // parse class generic map generics = sp.consumeGenericTypeParameters(); // parse super class signature - superClass = sp.consumeType(); + superClass = validateSuperCls(sp.consumeType(), superClass); // parse interfaces signatures for (int i = 0; i < interfaces.size(); i++) { ArgType type = sp.consumeType(); @@ -218,6 +223,18 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN } } + private ArgType validateSuperCls(ArgType candidateType, ArgType currentType) { + if (!candidateType.isObject()) { + this.addComment("Incorrect class signature, super class is not object: " + SignatureParser.getSignature(this)); + return currentType; + } + if (Objects.equals(candidateType.getObject(), this.getClassInfo().getType().getObject())) { + this.addComment("Incorrect class signature, super class is equals to this class: " + SignatureParser.getSignature(this)); + return currentType; + } + return candidateType; + } + private void setFieldsTypesFromSignature() { for (FieldNode field : fields) { try { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java index 4b70dfd7c..f4adb4dab 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java @@ -33,14 +33,23 @@ public class SignatureParser { mark = 0; } - @SuppressWarnings("unchecked") + @Nullable public static SignatureParser fromNode(IAttributeNode node) { + String signature = getSignature(node); + if (signature == null) { + return null; + } + return new SignatureParser(signature); + } + + @SuppressWarnings("unchecked") + @Nullable + public static String getSignature(IAttributeNode node) { Annotation a = node.getAnnotation(Consts.DALVIK_SIGNATURE); if (a == null) { return null; } - String signature = mergeSignature((List) a.getDefaultValue()); - return new SignatureParser(signature); + return mergeSignature((List) a.getDefaultValue()); } private char next() { diff --git a/jadx-core/src/test/java/jadx/tests/integration/generics/TestClassSignature.java b/jadx-core/src/test/java/jadx/tests/integration/generics/TestClassSignature.java new file mode 100644 index 000000000..d9a51126c --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/generics/TestClassSignature.java @@ -0,0 +1,23 @@ +package jadx.tests.integration.generics; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestClassSignature extends SmaliTest { + // @formatter:off + /* + Incorrect class signature, super class is equals to this class: Lgenerics/TestClassSignature; + */ + // @formatter:on + + @Test + public void test() { + assertThat(getClassNodeFromSmali()) + .code() + .containsOne("Incorrect class signature") + .doesNotContain("StackOverflowError"); + } +} diff --git a/jadx-core/src/test/smali/generics/TestClassSignature.smali b/jadx-core/src/test/smali/generics/TestClassSignature.smali new file mode 100644 index 000000000..e95aa72ec --- /dev/null +++ b/jadx-core/src/test/smali/generics/TestClassSignature.smali @@ -0,0 +1,115 @@ +.class public abstract Lgenerics/TestClassSignature; +.super Ljava/lang/Object; +.source "SourceFile" + +# interfaces +.implements Ljava/util/Iterator; + + +# annotations +.annotation system Ldalvik/annotation/Signature; + value = { + "", + "Lgenerics/TestClassSignature<", + "TT;>;" + } +.end annotation + + +# instance fields +.field public f:Ljava/lang/Object; + .annotation system Ldalvik/annotation/Signature; + value = { + "TT;" + } + .end annotation +.end field + + +# direct methods +.method public constructor (Ljava/lang/Object;)V + .registers 2 + .annotation system Ldalvik/annotation/Signature; + value = { + "(TT;)V" + } + .end annotation + + invoke-direct {p0}, Ljava/lang/Object;->()V + iput-object p1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object; + return-void +.end method + + +# virtual methods +.method public abstract a(Ljava/lang/Object;)Ljava/lang/Object; + .annotation system Ldalvik/annotation/Signature; + value = { + "(TT;)TT;" + } + .end annotation +.end method + +.method public final hasNext()Z + .registers 2 + + .line 1 + iget-object v0, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object; + if-eqz v0, :cond_6 + const/4 v0, 0x1 + goto :goto_7 + + :cond_6 + const/4 v0, 0x0 + + :goto_7 + return v0 +.end method + +.method public final next()Ljava/lang/Object; + .registers 3 + .annotation system Ldalvik/annotation/Signature; + value = { + "()TT;" + } + .end annotation + + invoke-virtual {p0}, Lgenerics/TestClassSignature;->hasNext()Z + move-result v0 + if-eqz v0, :cond_1b + + :try_start_6 + iget-object v0, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object; + :try_end_8 + .catchall {:try_start_6 .. :try_end_8} :catchall_11 + + iget-object v1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object; + invoke-virtual {p0, v1}, Lgenerics/TestClassSignature;->a(Ljava/lang/Object;)Ljava/lang/Object; + move-result-object v1 + iput-object v1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object; + return-object v0 + + :catchall_11 + move-exception v0 + + iget-object v1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object; + invoke-virtual {p0, v1}, Lgenerics/TestClassSignature;->a(Ljava/lang/Object;)Ljava/lang/Object; + move-result-object v1 + iput-object v1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object; + throw v0 + + :cond_1b + new-instance v0, Ljava/util/NoSuchElementException; + invoke-direct {v0}, Ljava/util/NoSuchElementException;->()V + throw v0 +.end method + +.method public final remove()V + .registers 2 + + new-instance v0, Ljava/lang/UnsupportedOperationException; + invoke-direct {v0}, Ljava/lang/UnsupportedOperationException;->()V + throw v0 +.end method