From df38a6424f51ad04588be2a6ef017da9af2889e2 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sun, 25 Dec 2022 18:31:44 +0000 Subject: [PATCH] fix(gui): make `bytecode` output closer to smali (#1739) --- .../gui/device/debugger/SmaliDebugger.java | 10 +- .../jadx/gui/device/debugger/smali/Smali.java | 108 ++++++++++-------- .../debugger/smali/SmaliMethodNode.java | 1 - .../device/debugger/smali/SmaliRegister.java | 9 +- .../device/debugger/smali/DbgSmaliTest.java | 5 +- .../sections/debuginfo/DebugInfoParser.java | 5 +- .../dex/sections/debuginfo/DexLocalVar.java | 11 ++ .../attributes/debuginfo/JavaLocalVar.java | 5 + .../api/plugins/input/data/ILocalVar.java | 6 + 9 files changed, 101 insertions(+), 59 deletions(-) diff --git a/jadx-gui/src/main/java/jadx/gui/device/debugger/SmaliDebugger.java b/jadx-gui/src/main/java/jadx/gui/device/debugger/SmaliDebugger.java index a07063f8..9f529465 100644 --- a/jadx-gui/src/main/java/jadx/gui/device/debugger/SmaliDebugger.java +++ b/jadx-gui/src/main/java/jadx/gui/device/debugger/SmaliDebugger.java @@ -1286,7 +1286,10 @@ public class SmaliDebugger { @Override public String getType() { String gen = getSignature(); - return gen.isEmpty() ? this.slot.signature : gen; + if (gen == null || gen.isEmpty()) { + return this.slot.signature; + } + return gen; } @NonNull @@ -1304,6 +1307,11 @@ public class SmaliDebugger { public int getEndOffset() { return (int) (slot.codeIndex + slot.length); } + + @Override + public boolean isMarkedAsParameter() { + return false; + } } public static class RuntimeDebugInfo { 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 1d606e43..f3a1103d 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 @@ -285,24 +285,25 @@ public class Smali { writeMethodDef(smali, mth, line); ICodeReader codeReader = mth.getCodeReader(); if (codeReader != null) { + int regsCount = codeReader.getRegistersCount(); line.smaliMthNode.setParamRegStart(getParamStartRegNum(mth)); - line.smaliMthNode.setRegCount(codeReader.getRegistersCount()); + line.smaliMthNode.setRegCount(regsCount); Map nodes = new HashMap<>(codeReader.getUnitsCount() / 2); line.smaliMthNode.setInsnNodes(nodes, codeReader.getUnitsCount()); - line.smaliMthNode.initRegInfoList(codeReader.getRegistersCount(), codeReader.getUnitsCount()); + line.smaliMthNode.initRegInfoList(regsCount, codeReader.getUnitsCount()); smali.incIndent(); - smali.startLine(".registers ") - .add("" + codeReader.getRegistersCount()) - .startLine(); + smali.startLine(".registers ").add(Integer.toString(regsCount)); + writeTries(codeReader, line); - if (formatMthParamInfo(mth, smali, codeReader, line)) { - smali.startLine(); + IDebugInfo debugInfo = codeReader.getDebugInfo(); + List localVars = debugInfo != null ? debugInfo.getLocalVars() : Collections.emptyList(); + formatMthParamInfo(mth, smali, line, regsCount, localVars); + if (debugInfo != null) { + formatDbgInfo(debugInfo, localVars, line); } + smali.newLine(); smali.startLine(); - if (codeReader.getDebugInfo() != null) { - formatDbgInfo(codeReader.getDebugInfo(), line); - } // first pass to fill payload offsets for switch instructions codeReader.visitInstructions(insn -> { Opcode opcode = insn.getOpcode(); @@ -458,20 +459,11 @@ public class Smali { } } - private boolean formatMthParamInfo(IMethodData mth, SmaliWriter smali, ICodeReader codeReader, LineInfo line) { + private void formatMthParamInfo(IMethodData mth, SmaliWriter smali, LineInfo line, + int regsCount, List localVars) { List types = mth.getMethodRef().getArgTypes(); if (types.isEmpty()) { - return false; - } - ILocalVar[] params = new ILocalVar[codeReader.getRegistersCount()]; - IDebugInfo dbgInfo = codeReader.getDebugInfo(); - if (dbgInfo != null) { - for (ILocalVar var : dbgInfo.getLocalVars()) { - // collect only method parameters - if (var.getStartOffset() <= 0) { - params[var.getRegNum()] = var; - } - } + return; } int paramStart = 0; int regNum = line.smaliMthNode.getParamRegStart(); @@ -482,26 +474,30 @@ public class Smali { regNum++; paramStart++; } + if (localVars.isEmpty()) { + return; + } + ILocalVar[] params = new ILocalVar[regsCount]; + for (ILocalVar var : localVars) { + if (var.isMarkedAsParameter()) { + params[var.getRegNum()] = var; + } + } + smali.newLine(); for (String paramType : types) { - String name; - String type; ILocalVar param = params[regNum]; if (param != null) { - name = Utils.getOrElse(param.getName(), ""); - type = Utils.getOrElse(param.getSignature(), paramType); - } else { - name = ""; - type = paramType; + String name = Utils.getOrElse(param.getName(), ""); + String type = Utils.getOrElse(param.getSignature(), paramType); + String varName = "p" + paramStart; + smali.startLine(String.format(".param %s, \"%s\" # %s", varName, name, type)); + line.addRegName(regNum, varName); + line.smaliMthNode.setParamReg(regNum, varName); } - String varName = "p" + paramStart; - smali.startLine(String.format(".param %s, \"%s\" # %s", varName, name, type)); - line.addRegName(regNum, varName); - line.smaliMthNode.setParamReg(regNum, varName); int regSize = isWideType(paramType) ? 2 : 1; regNum += regSize; paramStart += regSize; } - return true; } private static int getParamStartRegNum(IMethodData mth) { @@ -558,30 +554,42 @@ public class Smali { smali.startLine(".end annotation"); } - private void formatDbgInfo(IDebugInfo dbgInfo, LineInfo line) { + private void formatDbgInfo(IDebugInfo dbgInfo, List localVars, LineInfo line) { dbgInfo.getSourceLineMapping().forEach((codeOffset, srcLine) -> { if (codeOffset > -1) { line.addDebugLineTip(codeOffset, String.format(".line %d", srcLine), ""); } }); - for (ILocalVar localVar : dbgInfo.getLocalVars()) { - String type = localVar.getSignature(); - if (type == null || type.trim().isEmpty()) { - type = localVar.getType(); - } - if (localVar.getStartOffset() > -1) { - line.addTip( - localVar.getStartOffset(), - String.format(".local v%d", localVar.getRegNum()), - String.format(", \"%s\":%s", localVar.getName(), type)); + for (ILocalVar localVar : localVars) { + if (localVar.isMarkedAsParameter()) { + continue; } - if (localVar.getEndOffset() > -1) { - line.addTip( - localVar.getEndOffset(), - String.format(".end local v%d", localVar.getRegNum()), - String.format(" # \"%s\":%s", localVar.getName(), type)); + String type = localVar.getType(); + String sign = localVar.getSignature(); + String longTypeStr; + if (sign == null || sign.trim().isEmpty()) { + longTypeStr = String.format(", \"%s\":%s", localVar.getName(), type); + } else { + longTypeStr = String.format(", \"%s\":%s, \"%s\"", localVar.getName(), type, localVar.getSignature()); } + line.addTip( + localVar.getStartOffset(), + ".local " + formatVarName(line.smaliMthNode, localVar), + longTypeStr); + line.addTip( + localVar.getEndOffset(), + ".end local " + formatVarName(line.smaliMthNode, localVar), + String.format(" # \"%s\":%s", localVar.getName(), type)); + } + } + + private String formatVarName(SmaliMethodNode smaliMthNode, ILocalVar localVar) { + int paramRegStart = smaliMthNode.getParamRegStart(); + int regNum = localVar.getRegNum(); + if (regNum < paramRegStart) { + return "v" + regNum; } + return "p" + (regNum - paramRegStart); } private void writeEncodedValue(SmaliWriter smali, EncodedValue value, boolean wrapArray) { diff --git a/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliMethodNode.java b/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliMethodNode.java index f0e287d1..4a9f4c61 100644 --- a/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliMethodNode.java +++ b/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliMethodNode.java @@ -94,7 +94,6 @@ class SmaliMethodNode { protected void setParamReg(int regNum, String name) { SmaliRegister r = regList.get(regNum); r.setParam(name); - r.setStartOffset(-1); } protected void setParamRegStart(int paramRegStart) { diff --git a/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliRegister.java b/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliRegister.java index 59097938..31ff7e38 100644 --- a/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliRegister.java +++ b/jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliRegister.java @@ -33,10 +33,6 @@ public class SmaliRegister extends RegisterInfo { } protected void setStartOffset(int off) { - if (startOffset == -1 && !isParam) { - startOffset = off; - return; - } if (off < startOffset) { startOffset = off; } @@ -71,4 +67,9 @@ public class SmaliRegister extends RegisterInfo { public int getEndOffset() { return endOffset; } + + @Override + public boolean isMarkedAsParameter() { + return isParam; + } } 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 index 43e6c542..60c9ea1b 100644 --- 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 @@ -33,6 +33,9 @@ class DbgSmaliTest extends SmaliTest { Smali disasm = Smali.disassemble(cls); String code = disasm.getCode(); LOG.debug("{}", code); - assertThat(code).doesNotContain("Failed to write method"); + assertThat(code) + .doesNotContain("Failed to write method") + .doesNotContain(".param p1") + .contains(".local p1, \"arg0\":Landroid/widget/AdapterView;, \"Landroid/widget/AdapterView<*>;\""); } } diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DebugInfoParser.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DebugInfoParser.java index fada61ab..adee2f08 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DebugInfoParser.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DebugInfoParser.java @@ -96,8 +96,9 @@ public class DebugInfoParser { int nameId = in.readUleb128p1(); String name = ext.getString(nameId); if (name != null && i < argsCount) { - int regNum = argRegs[i]; - startVar(new DexLocalVar(regNum, name, argTypes.get(i)), -1); + DexLocalVar paramVar = new DexLocalVar(argRegs[i], name, argTypes.get(i)); + startVar(paramVar, addr); + paramVar.markAsParameter(); varsInfoFound = true; } } diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DexLocalVar.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DexLocalVar.java index 4458fc24..2a5efe64 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DexLocalVar.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DexLocalVar.java @@ -7,6 +7,8 @@ import jadx.api.plugins.utils.Utils; import jadx.plugins.input.dex.sections.SectionReader; public class DexLocalVar implements ILocalVar { + private static final int PARAM_START_OFFSET = -1; + private final int regNum; private final String name; private final String type; @@ -78,6 +80,15 @@ public class DexLocalVar implements ILocalVar { return startOffset; } + public void markAsParameter() { + startOffset = PARAM_START_OFFSET; + } + + @Override + public boolean isMarkedAsParameter() { + return startOffset == PARAM_START_OFFSET; + } + @Override public int getEndOffset() { return endOffset; diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/debuginfo/JavaLocalVar.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/debuginfo/JavaLocalVar.java index 6edaf5d3..864e96c5 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/debuginfo/JavaLocalVar.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/debuginfo/JavaLocalVar.java @@ -61,6 +61,11 @@ public class JavaLocalVar implements ILocalVar { return endOffset; } + @Override + public boolean isMarkedAsParameter() { + return false; + } + @Override public int hashCode() { int result = regNum; diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/ILocalVar.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/ILocalVar.java index 24a52b28..3e68013e 100644 --- a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/ILocalVar.java +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/ILocalVar.java @@ -15,4 +15,10 @@ public interface ILocalVar { int getStartOffset(); int getEndOffset(); + + /** + * Hint if variable is a method parameter. + * Can be incorrect and shouldn't be trusted. + */ + boolean isMarkedAsParameter(); } -- GitLab