From c27f2badf7a5beee58148a238840c687c82efa24 Mon Sep 17 00:00:00 2001 From: Skylot Date: Mon, 18 Jul 2022 18:50:48 +0100 Subject: [PATCH] fix(gui): resolve payload offset for switch insns in debug smali code (#1575) --- .../test/java/jadx/tests/api/SmaliTest.java | 15 ++-- .../jadx/gui/device/debugger/smali/Smali.java | 26 ++++-- .../device/debugger/smali/DbgSmaliTest.java | 26 ++++++ jadx-gui/src/test/smali/switch.smali | 81 +++++++++++++++++++ 4 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 jadx-gui/src/test/java/jadx/gui/device/debugger/smali/DbgSmaliTest.java create mode 100644 jadx-gui/src/test/smali/switch.smali diff --git a/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java b/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java index 29cc8fc1..533dfd55 100644 --- a/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java @@ -19,10 +19,15 @@ import static org.hamcrest.Matchers.notNullValue; public abstract class SmaliTest extends IntegrationTest { - private static final String SMALI_TESTS_PROJECT = "jadx-core"; private static final String SMALI_TESTS_DIR = "src/test/smali"; private static final String SMALI_TESTS_EXT = ".smali"; + private String currentProject = "jadx-core"; + + public void setCurrentProject(String currentProject) { + this.currentProject = currentProject; + } + @BeforeEach public void init() { Assumptions.assumeFalse(USE_JAVA_INPUT, "skip smali test for java input tests"); @@ -89,24 +94,24 @@ public abstract class SmaliTest extends IntegrationTest { .collect(Collectors.toList()); } - private static File getSmaliFile(String baseName) { + private File getSmaliFile(String baseName) { File smaliFile = new File(SMALI_TESTS_DIR, baseName + SMALI_TESTS_EXT); if (smaliFile.exists()) { return smaliFile; } - File pathFromRoot = new File(SMALI_TESTS_PROJECT, smaliFile.getPath()); + File pathFromRoot = new File(currentProject, smaliFile.getPath()); if (pathFromRoot.exists()) { return pathFromRoot; } throw new AssertionError("Smali file not found: " + smaliFile.getPath()); } - private static File getSmaliDir(String baseName) { + private File getSmaliDir(String baseName) { File smaliDir = new File(SMALI_TESTS_DIR, baseName); if (smaliDir.exists()) { return smaliDir; } - File pathFromRoot = new File(SMALI_TESTS_PROJECT, smaliDir.getPath()); + File pathFromRoot = new File(currentProject, smaliDir.getPath()); if (pathFromRoot.exists()) { return pathFromRoot; } diff --git a/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/Smali.java b/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/Smali.java index 438dcd4d..8ee84511 100644 --- a/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/Smali.java +++ b/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/Smali.java @@ -46,6 +46,7 @@ import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.exceptions.JadxRuntimeException; import static jadx.api.plugins.input.data.AccessFlagsScope.FIELD; import static jadx.api.plugins.input.data.AccessFlagsScope.METHOD; @@ -60,7 +61,9 @@ import static jadx.api.plugins.input.insns.Opcode.INVOKE_CUSTOM; import static jadx.api.plugins.input.insns.Opcode.INVOKE_CUSTOM_RANGE; import static jadx.api.plugins.input.insns.Opcode.INVOKE_POLYMORPHIC; import static jadx.api.plugins.input.insns.Opcode.INVOKE_POLYMORPHIC_RANGE; +import static jadx.api.plugins.input.insns.Opcode.PACKED_SWITCH; import static jadx.api.plugins.input.insns.Opcode.PACKED_SWITCH_PAYLOAD; +import static jadx.api.plugins.input.insns.Opcode.SPARSE_SWITCH; import static jadx.api.plugins.input.insns.Opcode.SPARSE_SWITCH_PAYLOAD; public class Smali { @@ -288,6 +291,14 @@ public class Smali { if (codeReader.getDebugInfo() != null) { formatDbgInfo(codeReader.getDebugInfo(), line); } + // first pass to fill payload offsets for switch instructions + codeReader.visitInstructions(insn -> { + Opcode opcode = insn.getOpcode(); + if (opcode == PACKED_SWITCH || opcode == SPARSE_SWITCH) { + insn.decode(); + line.addPayloadOffset(insn.getOffset(), insn.getTarget()); + } + }); codeReader.visitInstructions(insn -> { InsnNode node = decodeInsn(insn, line); nodes.put((long) insn.getOffset(), node); @@ -404,7 +415,6 @@ public class Smali { line.addTip(target, String.format(FMT_S_SWITCH_TAG, target), ""); line.getLineWriter().append(", ").append(String.format(FMT_S_SWITCH, target)); } - line.addPayloadOffset(insn.getOffset(), target); return true; } } @@ -733,7 +743,7 @@ public class Smali { ISwitchPayload payload = (ISwitchPayload) insn.getPayload(); if (payload != null) { - fmtSwitchPayload(insn, FMT_P_SWITCH_CASE, FMT_P_SWITCH_CASE_TAG, line, payload, insn.getOffset()); + fmtSwitchPayload(insn, FMT_P_SWITCH_CASE, FMT_P_SWITCH_CASE_TAG, line, payload); } return true; } @@ -743,7 +753,7 @@ public class Smali { ISwitchPayload payload = (ISwitchPayload) insn.getPayload(); if (payload != null) { - fmtSwitchPayload(insn, FMT_S_SWITCH_CASE, FMT_S_SWITCH_CASE_TAG, line, payload, insn.getOffset()); + fmtSwitchPayload(insn, FMT_S_SWITCH_CASE, FMT_S_SWITCH_CASE_TAG, line, payload); } return true; } @@ -755,17 +765,19 @@ public class Smali { return false; } - private void fmtSwitchPayload(InsnData insn, String fmtTarget, String fmtTag, LineInfo line, - ISwitchPayload payload, int curOffset) { + private void fmtSwitchPayload(InsnData insn, String fmtTarget, String fmtTag, LineInfo line, ISwitchPayload payload) { int lineStart = getInsnColStart(); lineStart += CODE_OFFSET_COLUMN_WIDTH + 1 + 1; // plus 1s for space and the ':' String basicIndent = new String(new byte[lineStart]).replace("\0", " "); String indent = SmaliWriter.INDENT_STR + basicIndent; int[] keys = payload.getKeys(); int[] targets = payload.getTargets(); - int opcodeOffset = line.payloadOffsetMap.get(curOffset); + Integer switchOffset = line.payloadOffsetMap.get(insn.getOffset()); + if (switchOffset == null) { + throw new JadxRuntimeException("Unknown switch insn for payload at " + insn.getOffset()); + } for (int i = 0; i < keys.length; i++) { - int target = opcodeOffset + targets[i]; + int target = switchOffset + targets[i]; line.addInsnLine(insn.getOffset(), String.format("%scase %d: -> " + fmtTarget, indent, keys[i], target)); line.addTip(target, diff --git a/jadx-gui/src/test/java/jadx/gui/device/debugger/smali/DbgSmaliTest.java b/jadx-gui/src/test/java/jadx/gui/device/debugger/smali/DbgSmaliTest.java new file mode 100644 index 00000000..124d2167 --- /dev/null +++ b/jadx-gui/src/test/java/jadx/gui/device/debugger/smali/DbgSmaliTest.java @@ -0,0 +1,26 @@ +package jadx.gui.device.debugger.smali; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.SmaliTest; + +class DbgSmaliTest extends SmaliTest { + private static final Logger LOG = LoggerFactory.getLogger(DbgSmaliTest.class); + + @BeforeEach + public void initProject() { + setCurrentProject("jadx-gui"); + } + + @Test + void test() { + disableCompilation(); + ClassNode cls = getClassNodeFromSmali("switch", "SwitchTest"); + Smali disasm = Smali.disassemble(cls); + LOG.debug("{}", disasm.getCode()); + } +} diff --git a/jadx-gui/src/test/smali/switch.smali b/jadx-gui/src/test/smali/switch.smali new file mode 100644 index 00000000..263079ff --- /dev/null +++ b/jadx-gui/src/test/smali/switch.smali @@ -0,0 +1,81 @@ +.class public final LSwitchTest; +.super Ljava/lang/Object; + +.field public static final synthetic a:LSwitchTest; +.field public static final synthetic b:LSwitchTest; + +.field private final synthetic c:I + +.method public final test(Ljava/lang/Runnable;)Ljava/lang/Thread; + .registers 4 + const v0, 0xa + const v1, 0xa + add-int v0, v0, v1 + rem-int v0, v0, v1 + if-gtz v0, :cond_f + goto/32 :goto_d2 + + :cond_f + :goto_f + goto/32 :goto_bf + :goto_18 + const-string v1, "A" + goto/32 :goto_68 + :goto_26 + const-string v1, "B" + goto/32 :goto_7c + :goto_33 + return-object v0 + + :pswitch_38 + goto/32 :goto_4e + :goto_41 + new-instance v0, Labo; + goto/32 :goto_84 + :goto_4e + new-instance v0, Lwf; + goto/32 :goto_ab + :goto_5b + new-instance v0, Ljava/lang/Thread; + goto/32 :goto_18 + :goto_68 + invoke-direct {v0, p1, v1}, Ljava/lang/Thread;->(Ljava/lang/Runnable;Ljava/lang/String;)V + goto/32 :goto_71 + :goto_71 + return-object v0 + :pswitch_75 + goto/32 :goto_b3 + :goto_7c + invoke-direct {v0, p1, v1}, Ljava/lang/Thread;->(Ljava/lang/Runnable;Ljava/lang/String;)V + goto/32 :goto_33 + :goto_84 + invoke-direct {v0, p1}, Labo;->(Ljava/lang/Runnable;)V + goto/32 :goto_90 + :goto_90 + return-object v0 + + :pswitch_data_96 + .packed-switch 0x0 + :pswitch_a3 + :pswitch_38 + :pswitch_75 + .end packed-switch + + :goto_a0 + return-object v0 + :pswitch_a3 + goto/32 :goto_41 + :goto_ab + invoke-direct {v0, p1}, Lwf;->(Ljava/lang/Runnable;)V + goto/32 :goto_a0 + :goto_b3 + new-instance v0, Ljava/lang/Thread; + goto/32 :goto_26 + :goto_bf + iget v0, p0, LSwitchTest;->c:I + + packed-switch v0, :pswitch_data_96 + goto/32 :goto_5b + :goto_d2 + goto/32 :goto_f +.end method -- GitLab