From 1ce3fc972a31e06f82e4928006abe98cb9e46d3d Mon Sep 17 00:00:00 2001 From: Skylot Date: Sun, 22 Aug 2021 16:53:54 +0100 Subject: [PATCH] fix: improve disassemble view for java-input --- .../jadx/gui/device/debugger/smali/Smali.java | 61 +++++++++++++------ .../plugins/input/dex/insns/DexInsnData.java | 5 ++ .../input/dex/insns/DexInsnMnemonics.java | 35 +++++++---- jadx-plugins/jadx-java-input/build.gradle | 4 ++ .../input/java/data/code/JavaCodeReader.java | 7 ++- .../input/java/data/code/JavaInsnData.java | 15 ++++- .../plugins/input/java/utils/DisasmUtils.java | 19 +++++- .../api/plugins/input/data/impl/CallSite.java | 5 ++ .../api/plugins/input/insns/InsnData.java | 2 + 9 files changed, 115 insertions(+), 38 deletions(-) rename jadx-gui/src/main/java/jadx/gui/device/debugger/smali/MNEMONIC.java => jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnMnemonics.java (79%) 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 23649ffd..438dcd4d 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 @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; @@ -72,6 +73,8 @@ public class Smali { private final boolean printFileOffset = true; private final boolean printBytecode = true; + private boolean isJavaBytecode; + private Smali() { } @@ -79,6 +82,7 @@ public class Smali { cls = cls.getTopParentClass(); SmaliWriter code = new SmaliWriter(cls); Smali smali = new Smali(); + smali.isJavaBytecode = cls.getInputFileName().endsWith(".class"); // TODO: add flag to api smali.writeClass(code, cls); smali.codeInfo = code.finish(); return smali; @@ -334,33 +338,41 @@ public class Smali { } private void formatInsn(InsnData insn, InsnNode node, LineInfo line) { - line.getLineWriter().delete(0, line.getLineWriter().length()); + StringBuilder lw = line.getLineWriter(); + lw.delete(0, lw.length()); fmtCols(insn, line); if (fmtPayloadInsn(insn, line)) { return; } - line.getLineWriter() - .append(String.format(FMT_INSN_COL, MNEMONIC.MNEMONICS[getOpenCodeByte(insn)])) - .append(" "); + lw.append(formatInsnName(insn)).append(" "); fmtRegs(insn, node.getType(), line); if (!tryFormatTargetIns(insn, node.getType(), line)) { if (hasLiteral(insn)) { - line.getLineWriter().append(", ").append(literal(insn)); + lw.append(", ").append(literal(insn)); } else if (node.getType() == InsnType.INVOKE) { - line.getLineWriter().append(", ").append(method(insn)); + lw.append(", ").append(method(insn)); } else if (insn.getIndexType() == InsnIndexType.FIELD_REF) { - line.getLineWriter().append(", ").append(field(insn)); + lw.append(", ").append(field(insn)); } else if (insn.getIndexType() == InsnIndexType.STRING_REF) { - line.getLineWriter().append(", ").append(str(insn)); + lw.append(", ").append(str(insn)); } else if (insn.getIndexType() == InsnIndexType.TYPE_REF) { - line.getLineWriter().append(", ").append(type(insn)); + lw.append(", ").append(type(insn)); } else if (insn.getOpcode() == CONST_METHOD_HANDLE) { - line.getLineWriter().append(", ").append(methodHandle(insn)); + lw.append(", ").append(methodHandle(insn)); } else if (insn.getOpcode() == CONST_METHOD_TYPE) { - line.getLineWriter().append(", ").append(proto(insn, insn.getIndex())); + lw.append(", ").append(proto(insn, insn.getIndex())); } } - line.addInsnLine(insn.getOffset(), line.getLineWriter().toString()); + line.addInsnLine(insn.getOffset(), lw.toString()); + } + + private String formatInsnName(InsnData insn) { + if (isJavaBytecode) { + // add api opcode, because registers not used + return String.format("%-" + INSN_COL_WIDTH + "s | %-15s", + insn.getOpcodeMnemonic(), insn.getOpcode().name().toLowerCase(Locale.ROOT).replace('_', '-')); + } + return String.format(FMT_INSN_COL, insn.getOpcodeMnemonic()); } private boolean tryFormatTargetIns(InsnData insn, InsnType insnType, LineInfo line) { @@ -647,23 +659,30 @@ public class Smali { private void fmtRegs(InsnData insn, InsnType insnType, LineInfo line) { boolean appendBrace = insnType == InsnType.INVOKE || isRegList(insn); + StringBuilder lw = line.getLineWriter(); + if (insnType == InsnType.INVOKE) { + int resultReg = insn.getResultReg(); + if (resultReg != -1) { + lw.append(line.getRegName(resultReg)).append(" <= "); + } + } if (appendBrace) { - line.getLineWriter().append("{"); + lw.append("{"); } if (isRangeRegIns(insn)) { - line.getLineWriter().append(line.getRegName(insn.getReg(0))) + lw.append(line.getRegName(insn.getReg(0))) .append(" .. ") .append(line.getRegName(insn.getReg(insn.getRegsCount() - 1))); } else if (insn.getRegsCount() > 0) { for (int i = 0; i < insn.getRegsCount(); i++) { if (i > 0) { - line.getLineWriter().append(", "); + lw.append(", "); } - line.getLineWriter().append(line.getRegName(insn.getReg(i))); + lw.append(line.getRegName(insn.getReg(i))); } } if (appendBrace) { - line.getLineWriter().append("}"); + lw.append("}"); } } @@ -692,9 +711,11 @@ public class Smali { private void formatByteCode(StringBuilder smali, byte[] bytes) { int maxLen = Math.min(bytes.length, 4 * 2); // limit to 4 units StringBuilder inHex = new StringBuilder(); - for (int i = 0; i < maxLen - 1; i += 2) { - int temp = ((bytes[i] & 0xff) << 8) | (bytes[i + 1] & 0xff); - inHex.append(String.format("%04x ", temp)); + for (int i = 0; i < maxLen; i++) { + inHex.append(String.format("%02x", bytes[i])); + if (i % 2 == 1) { + inHex.append(' '); + } } smali.append(String.format(FMT_BYTECODE_COL, inHex)); if (maxLen < bytes.length) { diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java index 87bdb1d9..b813aaad 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java @@ -66,6 +66,11 @@ public class DexInsnData implements InsnData { return info.getApiOpcode(); } + @Override + public String getOpcodeMnemonic() { + return DexInsnMnemonics.get(opcodeUnit); + } + @Override public byte[] getByteCode() { return externalReader.getByteCode(insnStart, length * 2); // a unit is 2 bytes diff --git a/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/MNEMONIC.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnMnemonics.java similarity index 79% rename from jadx-gui/src/main/java/jadx/gui/device/debugger/smali/MNEMONIC.java rename to jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnMnemonics.java index c58a01b5..f53c8df4 100644 --- a/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/MNEMONIC.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnMnemonics.java @@ -1,21 +1,28 @@ -package jadx.gui.device.debugger.smali; +package jadx.plugins.input.dex.insns; -public class MNEMONIC { - public static final String[] MNEMONICS = new String[] { - "nop", "move", "move/from16", "move/16", "move-wide", +public class DexInsnMnemonics { + + public static String get(int opcode) { + return MNEMONICS[opcode & 0xFF]; + } + + private static final String[] MNEMONICS = new String[] { + "nop", + "move", "move/from16", "move/16", "move-wide", "move-wide/from16", "move-wide/16", "move-object", "move-object/from16", "move-object/16", "move-result", "move-result-wide", "move-result-object", "move-exception", "return-void", - "return", "return-wide", "return-object", "const/4", "const/16", - "const", "const/high16", "const-wide/16", "const-wide/32", "const-wide", + "return", "return-wide", "return-object", + "const/4", "const/16", "const", "const/high16", "const-wide/16", "const-wide/32", "const-wide", "const-wide/high16", "const-string", "const-string/jumbo", "const-class", "monitor-enter", "monitor-exit", "check-cast", "instance-of", "array-length", "new-instance", "new-array", "filled-new-array", "filled-new-array/range", "fill-array-data", "throw", - "goto", "goto/16", "goto/32", "packed-switch", "sparse-switch", + "goto", "goto/16", "goto/32", + "packed-switch", "sparse-switch", "cmpl-float", "cmpg-float", "cmpl-double", "cmpg-double", "cmp-long", - "if-eq", "if-ne", "if-lt", "if-ge", "if-gt", - "if-le", "if-eqz", "if-nez", "if-ltz", "if-gez", - "if-gtz", "if-lez", "(unused)", "(unused)", "(unused)", - "(unused)", "(unused)", "(unused)", "aget", "aget-wide", + "if-eq", "if-ne", "if-lt", "if-ge", "if-gt", "if-le", + "if-eqz", "if-nez", "if-ltz", "if-gez", "if-gtz", "if-lez", + "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", + "aget", "aget-wide", "aget-object", "aget-boolean", "aget-byte", "aget-char", "aget-short", "aput", "aput-wide", "aput-object", "aput-boolean", "aput-byte", "aput-char", "aput-short", "iget", "iget-wide", "iget-object", @@ -52,6 +59,10 @@ public class MNEMONIC { "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", "(unused)", - "invoke-polymorphic", "invoke-polymorphic/range", "invoke-custom", "invoke-custom/range", "const-method-handle", + "invoke-polymorphic", + "invoke-polymorphic/range", + "invoke-custom", + "invoke-custom/range", + "const-method-handle", "const-method-type" }; } diff --git a/jadx-plugins/jadx-java-input/build.gradle b/jadx-plugins/jadx-java-input/build.gradle index fa1c146e..5665daaf 100644 --- a/jadx-plugins/jadx-java-input/build.gradle +++ b/jadx-plugins/jadx-java-input/build.gradle @@ -4,4 +4,8 @@ plugins { dependencies { api(project(":jadx-plugins:jadx-plugins-api")) + + // show bytecode disassemble + implementation 'org.ow2.asm:asm:9.2' + implementation 'org.ow2.asm:asm-util:9.2' } diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaCodeReader.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaCodeReader.java index dd2dee98..8c5783ea 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaCodeReader.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaCodeReader.java @@ -61,15 +61,16 @@ public class JavaCodeReader implements ICodeReader { state.setInsn(insn); int offset = 0; while (offset < codeSize) { + insn.setDecoded(false); + insn.setOffset(offset); + insn.setInsnStart(reader.getOffset()); + int opcode = reader.readU1(); JavaInsnInfo insnInfo = JavaInsnsRegister.get(opcode); if (insnInfo == null) { throw new JavaClassParseException("Unknown opcode: 0x" + Integer.toHexString(opcode)); } - insn.setDecoded(false); insn.setInsnInfo(insnInfo); - insn.setInsnStart(reader.getOffset()); - insn.setOffset(offset); insn.setInsnInfo(insnInfo); insn.setRegsCount(insnInfo.getRegsCount()); insn.setOpcode(insnInfo.getApiOpcode()); diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnData.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnData.java index 6478190c..0150fbc4 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnData.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnData.java @@ -12,6 +12,7 @@ import jadx.api.plugins.input.insns.InsnIndexType; import jadx.api.plugins.input.insns.Opcode; import jadx.api.plugins.input.insns.custom.ICustomPayload; import jadx.plugins.input.java.data.ConstPoolReader; +import jadx.plugins.input.java.data.DataReader; import jadx.plugins.input.java.data.code.decoders.IJavaInsnDecoder; public class JavaInsnData implements InsnData { @@ -74,9 +75,21 @@ public class JavaInsnData implements InsnData { this.opcode = opcode; } + @Override + public String getOpcodeMnemonic() { + return insnInfo.getName(); + } + @Override public byte[] getByteCode() { - return new byte[0]; + DataReader reader = state.reader(); + int startOffset = reader.getOffset(); + try { + reader.absPos(insnStart); + return reader.readBytes(1 + payloadSize); + } finally { + reader.absPos(startOffset); + } } @Override diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/DisasmUtils.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/DisasmUtils.java index f8c7ba44..5c9e0eb5 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/DisasmUtils.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/DisasmUtils.java @@ -3,27 +3,42 @@ package jadx.plugins.input.java.utils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.concurrent.TimeUnit; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.util.TraceClassVisitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DisasmUtils { private static final Logger LOG = LoggerFactory.getLogger(DisasmUtils.class); + public static String get(byte[] bytes) { + return useASM(bytes); + } + + private static String useASM(byte[] bytes) { + StringWriter out = new StringWriter(); + TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(out)); + new ClassReader(bytes).accept(tcv, 0); + return out.toString(); + } + /** * Use javap as a temporary disassembler for java bytecode + * Don't remove! Useful for debug. */ - public static String get(byte[] bytes) { + private static String useSystemJavaP(byte[] bytes) { try { Path tmpCls = null; try { tmpCls = Files.createTempFile("jadx", ".class"); Files.write(tmpCls, bytes, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); - Process process = Runtime.getRuntime().exec(new String[] { "javap", "-constants", "-v", "-p", "-c", tmpCls.toAbsolutePath().toString() diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/CallSite.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/CallSite.java index 1cb7f9b0..c2307217 100644 --- a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/CallSite.java +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/impl/CallSite.java @@ -33,4 +33,9 @@ public class CallSite implements ICallSite { public List getValues() { return values; } + + @Override + public String toString() { + return "CallSite{" + values + '}'; + } } diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java index 51181d80..ae020fc8 100644 --- a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java @@ -17,6 +17,8 @@ public interface InsnData { Opcode getOpcode(); + String getOpcodeMnemonic(); + byte[] getByteCode(); InsnIndexType getIndexType(); -- GitLab