fix(gui): resolve payload offset for switch insns in debug smali code (#1575)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;-><init>(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;-><init>(Ljava/lang/Runnable;Ljava/lang/String;)V
|
||||
goto/32 :goto_33
|
||||
:goto_84
|
||||
invoke-direct {v0, p1}, Labo;-><init>(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;-><init>(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
|
||||
Reference in New Issue
Block a user