From 23696d397101ccc9f1c7cfce6401a57672a2eacf Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Fri, 20 Sep 2024 21:34:21 +0100 Subject: [PATCH] fix: use type from `new-instance` if differ from constructor call (#2285) --- .../instructions/mods/ConstructorInsn.java | 6 +- .../core/dex/visitors/ConstructorVisitor.java | 27 ++++++- .../integration/others/TestConstructor2.java | 20 +++++ .../smali/others/TestConstructor2/A.smali | 5 ++ .../TestConstructor2/TestConstructor2.smali | 79 +++++++++++++++++++ 5 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/others/TestConstructor2.java create mode 100644 jadx-core/src/test/smali/others/TestConstructor2/A.smali create mode 100644 jadx-core/src/test/smali/others/TestConstructor2/TestConstructor2.smali diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java index dcb29ab49..ead8a4bf1 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java @@ -25,8 +25,12 @@ public final class ConstructorInsn extends BaseInvokeNode { } public ConstructorInsn(MethodNode mth, InvokeNode invoke) { + this(mth, invoke, invoke.getCallMth()); + } + + public ConstructorInsn(MethodNode mth, InvokeNode invoke, MethodInfo callMth) { super(InsnType.CONSTRUCTOR, invoke.getArgsCount() - 1); - this.callMth = invoke.getCallMth(); + this.callMth = callMth; this.callType = getCallType(mth, callMth.getDeclClass(), invoke.getArg(0)); int argsCount = invoke.getArgsCount(); for (int i = 1; i < argsCount; i++) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java index a520d5b59..22d8ef591 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java @@ -6,9 +6,13 @@ import org.jetbrains.annotations.Nullable; import jadx.core.codegen.TypeGen; import jadx.core.dex.attributes.AFlag; +import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.PhiInsn; +import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.RegisterArg; @@ -59,10 +63,17 @@ public class ConstructorVisitor extends AbstractVisitor { private static boolean processInvoke(MethodNode mth, BlockNode block, int indexInBlock, InsnRemover remover) { InvokeNode inv = (InvokeNode) block.getInstructions().get(indexInBlock); - if (!inv.getCallMth().isConstructor()) { + MethodInfo callMth = inv.getCallMth(); + if (!callMth.isConstructor()) { return false; } - ConstructorInsn co = new ConstructorInsn(mth, inv); + ArgType instType = searchInstanceType(inv); + if (instType != null && !instType.equals(callMth.getDeclClass().getType())) { + ClassInfo instCls = ClassInfo.fromType(mth.root(), instType); + callMth = MethodInfo.fromDetails(mth.root(), instCls, callMth.getName(), + callMth.getArgumentsTypes(), callMth.getReturnType()); + } + ConstructorInsn co = new ConstructorInsn(mth, inv, callMth); if (canRemoveConstructor(mth, co)) { remover.addAndUnbind(inv); return false; @@ -101,6 +112,18 @@ public class ConstructorVisitor extends AbstractVisitor { return true; } + private static @Nullable ArgType searchInstanceType(InvokeNode inv) { + InsnArg instanceArg = inv.getInstanceArg(); + if (instanceArg == null || !instanceArg.isRegister()) { + return null; + } + InsnNode assignInsn = ((RegisterArg) instanceArg).getSVar().getAssignInsn(); + if (assignInsn == null || assignInsn.getType() != InsnType.NEW_INSTANCE) { + return null; + } + return ((IndexInsnNode) assignInsn).getIndexAsType(); + } + private static RegisterArg insertPhiInsn(MethodNode mth, BlockNode curBlock, RegisterArg instArg, ConstructorInsn otherCtr) { BlockNode otherBlock = BlockUtils.getBlockByInsn(mth, otherCtr); diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructor2.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructor2.java new file mode 100644 index 000000000..d9a4090b8 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructor2.java @@ -0,0 +1,20 @@ +package jadx.tests.integration.others; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +/** + * Constructor called on object instance is from Object not instance type + */ +public class TestConstructor2 extends SmaliTest { + + @Test + public void test() { + assertThat(getClassNodeFromSmaliFiles()) + .code() + .containsOne("A a = new A();"); + } +} diff --git a/jadx-core/src/test/smali/others/TestConstructor2/A.smali b/jadx-core/src/test/smali/others/TestConstructor2/A.smali new file mode 100644 index 000000000..552adb17a --- /dev/null +++ b/jadx-core/src/test/smali/others/TestConstructor2/A.smali @@ -0,0 +1,5 @@ +.class public final Lothers/TestConstructor2$A; +.super Ljava/lang/Object; + +.field public a:I +.field public b:Ljava/util/ArrayDeque; diff --git a/jadx-core/src/test/smali/others/TestConstructor2/TestConstructor2.smali b/jadx-core/src/test/smali/others/TestConstructor2/TestConstructor2.smali new file mode 100644 index 000000000..27d7f21da --- /dev/null +++ b/jadx-core/src/test/smali/others/TestConstructor2/TestConstructor2.smali @@ -0,0 +1,79 @@ +.class public Lothers/TestConstructor2; +.super Ljava/lang/Object; + +.field public a:Ljava/util/HashMap; + +.method public final a(III)V + .registers 6 + + .line 1 + .line 2 + new-instance v0, Lothers/TestConstructor2$A; + + .line 3 + .line 4 + .line 5 + invoke-direct {v0}, Ljava/lang/Object;->()V + + .line 6 + .line 7 + if-gt p2, p3, :cond_1c + + .line 8 + .line 9 + new-instance p2, Ljava/util/ArrayDeque; + + .line 10 + .line 11 + iget v1, v0, Lothers/TestConstructor2$A;->a:I + + .line 12 + .line 13 + .line 14 + invoke-direct {p2, v1}, Ljava/util/ArrayDeque;->(I)V + + .line 15 + .line 16 + iput-object p2, v0, Lothers/TestConstructor2$A;->b:Ljava/util/ArrayDeque; + + .line 17 + .line 18 + iput p3, v0, Lothers/TestConstructor2$A;->a:I + + .line 19 + .line 20 + iget-object p2, p0, Lothers/TestConstructor2;->a:Ljava/util/HashMap; + + .line 21 + .line 22 + .line 23 + invoke-static {p1}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; + + .line 24 + move-result-object p1 + + .line 25 + .line 26 + .line 27 + invoke-virtual {p2, p1, v0}, Ljava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; + + .line 28 + return-void + + .line 29 + .line 30 + :cond_1c + new-instance p1, Ljava/lang/IllegalArgumentException; + + .line 31 + .line 32 + const-string/jumbo p2, "error" + + .line 33 + .line 34 + .line 35 + invoke-direct {p1, p2}, Ljava/lang/IllegalArgumentException;->(Ljava/lang/String;)V + + .line 36 + throw p1 +.end method