未验证 提交 4705194a 编写于 作者: L LBJ-the-GOAT 提交者: GitHub

feat(gui): add a smali debugger (#1136) (PR #1137)

* add a smali debugger
* debugger: support android 11, support 9(may be) & 10 if debug_info available, add rerun.
* debugger: support get/set fields of this, change icons, fix bugs.
* debugger: add timeout to attach

Co-authored-by: tobias <tobias.hotmail.com>
上级 19572a67
......@@ -451,6 +451,40 @@ public final class JadxDecompiler implements Closeable {
.orElse(null);
}
@Nullable
public ClassNode searchClassNodeByOrigFullName(String fullName) {
return getRoot().getClasses().stream()
.filter(cls -> cls.getClassInfo().getFullName().equals(fullName))
.findFirst()
.orElse(null);
}
// returns parent if class contains DONT_GENERATE flag.
@Nullable
public JavaClass searchJavaClassOrItsParentByOrigFullName(String fullName) {
ClassNode node = getRoot().getClasses().stream()
.filter(cls -> cls.getClassInfo().getFullName().equals(fullName))
.findFirst()
.orElse(null);
if (node != null) {
if (node.contains(AFlag.DONT_GENERATE)) {
return getJavaClassByNode(node.getTopParentClass());
} else {
return getJavaClassByNode(node);
}
}
return null;
}
@Nullable
public JavaClass searchJavaClassByAliasFullName(String fullName) {
return getRoot().getClasses().stream()
.filter(cls -> cls.getClassInfo().getAliasFullName().equals(fullName))
.findFirst()
.map(this::getJavaClassByNode)
.orElse(null);
}
@Nullable
JavaNode convertNode(Object obj) {
if (!(obj instanceof LineAttrNode)) {
......
......@@ -54,7 +54,7 @@ public class InsnDecoder {
}
@NotNull
private InsnNode decode(InsnData insn) throws DecodeException {
protected InsnNode decode(InsnData insn) throws DecodeException {
switch (insn.getOpcode()) {
case NOP:
return new InsnNode(InsnType.NOP, 0);
......
......@@ -575,27 +575,8 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
sb.append(this.clsData.getDisassembledCode());
}
public String getSmaliV2() {
StringBuilder sb = new StringBuilder();
getSmaliV2(sb);
sb.append(System.lineSeparator());
Set<ClassNode> allInlinedClasses = new LinkedHashSet<>();
getInnerAndInlinedClassesRecursive(allInlinedClasses);
for (ClassNode innerClass : allInlinedClasses) {
innerClass.getSmaliV2(sb);
sb.append(System.lineSeparator());
}
return sb.toString();
}
private void getSmaliV2(StringBuilder sb) {
if (this.clsData == null) {
sb.append(String.format("###### Class %s is created by jadx", getFullName()));
return;
}
sb.append(String.format("###### Class %s (%s)", getFullName(), getRawName()));
sb.append(System.lineSeparator());
sb.append(this.clsData.getDisassembledCodeV2());
public IClassData getClsData() {
return clsData;
}
public ProcessState getState() {
......
package jadx.core.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.jetbrains.annotations.Nullable;
import jadx.api.JadxArgs;
......@@ -314,4 +317,8 @@ public class StringUtils {
return WORD_SEPARATORS.indexOf(chr) != -1;
}
public static String getDateText() {
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
}
......@@ -21,7 +21,8 @@ dependencies {
implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
implementation "com.github.akarnokd:rxjava2-swing:0.3.7"
implementation 'com.android.tools.build:apksig:4.1.2'
implementation 'com.android.tools.build:apksig:4.1.1'
implementation 'io.github.hqktech:jdwp:1.0'
}
application {
......
package jadx.gui.device.debugger;
public class ArtAdapter {
public interface Debugger {
int getRuntimeRegNum(int smaliNum, int regCount, int paramStart);
boolean readNullObject();
String typeForNull();
}
public static Debugger getAdapter(int androidReleaseVer) {
if (androidReleaseVer <= 8) {
return new AndroidOreoAndBelow();
} else {
return new AndroidPieAndAbove();
}
}
public static class AndroidOreoAndBelow implements Debugger {
@Override
public int getRuntimeRegNum(int smaliNum, int regCount, int paramStart) {
int localRegCount = regCount - paramStart;
return (smaliNum + localRegCount) % regCount;
}
@Override
public boolean readNullObject() {
return true;
}
@Override
public String typeForNull() {
return "";
}
}
public static class AndroidPieAndAbove implements Debugger {
@Override
public int getRuntimeRegNum(int smaliNum, int regCount, int paramStart) {
return smaliNum;
}
@Override
public boolean readNullObject() {
return false;
}
@Override
public String typeForNull() {
return "zero value";
}
}
}
package jadx.gui.device.debugger;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import jadx.core.dex.nodes.ClassNode;
import jadx.gui.device.debugger.smali.Smali;
import jadx.gui.treemodel.JClass;
public class BreakpointManager {
private static Gson gson = null;
private static final Type TYPE_TOKEN = new TypeToken<Map<String, List<FileBreakpoint>>>() {
}.getType();
private static Map<String, List<FileBreakpoint>> bpm;
private static Path savePath;
private static DebugController debugController;
private static Map<String, Entry<ClassNode, Listener>> listeners = Collections.emptyMap(); // class full name as key
public static void saveAndExit() {
if (bpm != null) {
if (bpm.size() == 0 && !Files.exists(savePath)) {
return; // user didn't do anything with breakpoint so don't output breakpoint file.
}
sync();
bpm = null;
savePath = null;
listeners = Collections.emptyMap();
}
}
public static void init(Path dirPath) {
if (gson == null) {
gson = new GsonBuilder()
.setPrettyPrinting()
.create();
}
savePath = dirPath.resolve("breakpoints.json");
if (Files.exists(savePath)) {
try {
byte[] bytes = Files.readAllBytes(savePath);
bpm = gson.fromJson(new String(bytes, StandardCharsets.UTF_8), TYPE_TOKEN);
} catch (IOException e) {
e.printStackTrace();
}
}
if (bpm == null) {
bpm = Collections.emptyMap();
}
}
/**
* @param listener When breakpoint is failed to set during debugging, this listener will be called.
*/
public static void addListener(JClass topCls, Listener listener) {
if (listeners == Collections.EMPTY_MAP) {
listeners = new HashMap<>();
}
listeners.put(DbgUtils.getRawFullName(topCls),
new SimpleEntry<>(topCls.getCls().getClassNode(), listener));
}
public static void removeListener(JClass topCls) {
listeners.remove(DbgUtils.getRawFullName(topCls));
}
public static List<Integer> getPositions(JClass topCls) {
List<FileBreakpoint> bps = bpm.get(DbgUtils.getRawFullName(topCls));
if (bps != null && bps.size() > 0) {
Smali smali = DbgUtils.getSmali(topCls.getCls().getClassNode());
if (smali != null) {
List<Integer> posList = new ArrayList<>(bps.size());
for (FileBreakpoint bp : bps) {
int pos = smali.getInsnPosByCodeOffset(bp.getFullMthRawID(), bp.codeOffset);
if (pos > -1) {
posList.add(pos);
}
}
return posList;
}
}
return Collections.emptyList();
}
public static boolean set(JClass topCls, int line) {
Entry<String, Integer> lineInfo = DbgUtils.getCodeOffsetInfoByLine(topCls, line);
if (lineInfo != null) {
if (bpm.isEmpty()) {
bpm = new HashMap<>();
}
String name = DbgUtils.getRawFullName(topCls);
List<FileBreakpoint> list = bpm.computeIfAbsent(name, k -> new ArrayList<>());
FileBreakpoint bkp = list.stream()
.filter(bp -> bp.codeOffset == lineInfo.getValue() && bp.getFullMthRawID().equals(lineInfo.getKey()))
.findFirst()
.orElse(null);
boolean ok = true;
if (bkp == null) {
String[] sigs = DbgUtils.sepClassAndMthSig(lineInfo.getKey());
if (sigs != null && sigs.length == 2) {
FileBreakpoint bp = new FileBreakpoint(sigs[0], sigs[1], lineInfo.getValue());
list.add(bp);
if (debugController != null) {
ok = debugController.setBreakpoint(bp);
}
}
}
return ok;
}
return false;
}
public static boolean remove(JClass topCls, int line) {
Entry<String, Integer> lineInfo = DbgUtils.getCodeOffsetInfoByLine(topCls, line);
if (lineInfo != null) {
List<FileBreakpoint> bps = bpm.get(DbgUtils.getRawFullName(topCls));
for (Iterator<FileBreakpoint> it = bps.iterator(); it.hasNext();) {
FileBreakpoint bp = it.next();
if (bp.codeOffset == lineInfo.getValue() && bp.getFullMthRawID().equals(lineInfo.getKey())) {
it.remove();
if (debugController != null) {
return debugController.removeBreakpoint(bp);
}
break;
}
}
}
return true;
}
private static void sync() {
try {
Files.write(savePath, gson.toJson(bpm).getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
}
public interface Listener {
void breakpointDisabled(int codeOffset);
}
protected static class FileBreakpoint {
final String cls;
final String mth;
final long codeOffset;
private FileBreakpoint(String cls, String mth, long codeOffset) {
this.cls = cls;
this.mth = mth;
this.codeOffset = codeOffset;
}
protected String getFullMthRawID() {
return cls + "." + mth;
}
@Override
public int hashCode() {
return (int) (31 * codeOffset + 31 * cls.hashCode() + 31 * mth.hashCode());
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FileBreakpoint) {
if (obj == this) {
return true;
}
FileBreakpoint fbp = (FileBreakpoint) obj;
return fbp.codeOffset == codeOffset && fbp.cls.equals(cls) && fbp.mth.equals(mth);
}
return false;
}
}
protected static List<FileBreakpoint> getAllBreakpoints() {
List<FileBreakpoint> bpList = new ArrayList<>();
for (Entry<String, List<FileBreakpoint>> entry : bpm.entrySet()) {
bpList.addAll(entry.getValue());
}
return bpList;
}
protected static void failBreakpoint(FileBreakpoint bp) {
Entry<ClassNode, Listener> entry = listeners.get(bp.cls);
if (entry != null) {
int pos = DbgUtils.getSmali(entry.getKey())
.getInsnPosByCodeOffset(bp.getFullMthRawID(), bp.codeOffset);
pos = Math.max(0, pos);
entry.getValue().breakpointDisabled(pos);
}
}
protected static void setDebugController(DebugController controller) {
debugController = controller;
}
}
package jadx.gui.device.debugger;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.jetbrains.annotations.Nullable;
import jadx.api.JavaClass;
import jadx.api.ResourceFile;
import jadx.api.ResourceType;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.nodes.ClassNode;
import jadx.gui.device.debugger.smali.Smali;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.MainWindow;
public class DbgUtils {
private static Map<ClassInfo, Smali> smaliCache = Collections.emptyMap();
protected static Smali getSmali(ClassNode topCls) {
if (smaliCache == Collections.EMPTY_MAP) {
smaliCache = new HashMap<>();
}
return smaliCache.computeIfAbsent(topCls.getTopParentClass().getClassInfo(),
c -> Smali.disassemble(topCls));
}
public static String getSmaliCode(ClassNode topCls) {
Smali smali = getSmali(topCls);
if (smali != null) {
return smali.getCode();
}
return null;
}
public static Entry<String, Integer> getCodeOffsetInfoByLine(JClass cls, int line) {
Smali smali = getSmali(cls.getCls().getClassNode().getTopParentClass());
if (smali != null) {
return smali.getMthFullIDAndCodeOffsetByLine(line);
}
return null;
}
public static String[] sepClassAndMthSig(String fullSig) {
int pos = fullSig.indexOf("(");
if (pos != -1) {
pos = fullSig.lastIndexOf(".", pos);
if (pos != -1) {
String[] sigs = new String[2];
sigs[0] = fullSig.substring(0, pos);
sigs[1] = fullSig.substring(pos + 1);
return sigs;
}
}
return null;
}
// doesn't replace $
public static String classSigToRawFullName(String clsSig) {
if (clsSig != null && clsSig.startsWith("L") && clsSig.endsWith(";")) {
clsSig = clsSig.substring(1, clsSig.length() - 1)
.replace("/", ".");
}
return clsSig;
}
// replaces $
public static String classSigToFullName(String clsSig) {
if (clsSig != null && clsSig.startsWith("L") && clsSig.endsWith(";")) {
clsSig = clsSig.substring(1, clsSig.length() - 1)
.replace("/", ".")
.replace("$", ".");
}
return clsSig;
}
public static String getRawFullName(JClass topCls) {
return topCls.getCls().getClassNode().getClassInfo().makeRawFullName();
}
public static boolean isStringObjectSig(String objectSig) {
return objectSig.equals("Ljava/lang/String;");
}
public static JClass getTopClassBySig(String clsSig, MainWindow mainWindow) {
clsSig = DbgUtils.classSigToFullName(clsSig);
JavaClass cls = mainWindow.getWrapper().getDecompiler().searchJavaClassOrItsParentByOrigFullName(clsSig);
if (cls != null) {
JClass jc = (JClass) mainWindow.getCacheObject().getNodeCache().makeFrom(cls);
return jc.getRootClass();
}
return null;
}
public static ClassNode getClassNodeBySig(String clsSig, MainWindow mainWindow) {
clsSig = DbgUtils.classSigToFullName(clsSig);
return mainWindow.getWrapper().getDecompiler().searchClassNodeByOrigFullName(clsSig);
}
public static String searchPackageName(MainWindow mainWindow) {
String content = getManifestContent(mainWindow);
int pos = content.indexOf("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" ");
if (pos > -1) {
pos = content.lastIndexOf(">", pos);
if (pos > -1) {
pos = content.indexOf(" package=\"", pos);
if (pos > -1) {
pos += " package=\"".length();
return content.substring(pos, content.indexOf("\"", pos));
}
}
}
return "";
}
/**
* @return the Activity class for android.intent.action.MAIN.
*/
@Nullable
public static JClass searchMainActivity(MainWindow mainWindow) {
String content = getManifestContent(mainWindow);
int pos = content.indexOf("<action android:name=\"android.intent.action.MAIN\"");
if (pos > -1) {
pos = content.lastIndexOf("<activity ", pos);
if (pos > -1) {
pos = content.indexOf(" android:name=\"", pos);
if (pos > -1) {
pos += " android:name=\"".length();
String classFullName = content.substring(pos, content.indexOf("\"", pos));
// in case the MainActivity class has been renamed before, we need raw name.
JavaClass cls = mainWindow.getWrapper().getDecompiler().searchJavaClassByAliasFullName(classFullName);
JNode jNode = mainWindow.getCacheObject().getNodeCache().makeFrom(cls);
if (jNode != null) {
return jNode.getRootClass();
}
}
}
}
return null;
}
// TODO: parse AndroidManifest.xml instead of looking for keywords
private static String getManifestContent(MainWindow mainWindow) {
try {
ResourceFile androidManifest = mainWindow.getWrapper().getDecompiler().getResources()
.stream()
.filter(res -> res.getType() == ResourceType.MANIFEST)
.findFirst()
.orElse(null);
if (androidManifest != null) {
return androidManifest.loadContent().getText().getCodeStr();
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public static boolean isPrintableChar(int c) {
return 32 <= c && c <= 126;
}
}
package jadx.gui.device.debugger;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import io.reactivex.annotations.Nullable;
import jadx.core.dex.instructions.args.ArgType;
import jadx.gui.device.debugger.SmaliDebugger.RuntimeVarInfo;
import jadx.gui.device.debugger.smali.RegisterInfo;
import jadx.gui.device.debugger.smali.SmaliRegister;
public class RegisterObserver {
private Map<Long, List<Info>> infoMap;
private final List<Entry<SmaliRegister, List<RuntimeVarInfo>>> regList;
private boolean hasDbgInfo = false;
private RegisterObserver() {
regList = new ArrayList<>();
infoMap = Collections.emptyMap();
}
public static RegisterObserver merge(List<RuntimeVarInfo> rtRegs, List<SmaliRegister> smaliRegs) {
RegisterObserver adapter = new RegisterObserver();
adapter.hasDbgInfo = rtRegs.size() > 0;
if (adapter.hasDbgInfo) {
adapter.infoMap = new HashMap<>();
}
for (SmaliRegister sr : smaliRegs) {
adapter.regList.add(new SimpleEntry<>(sr, Collections.emptyList()));
}
adapter.regList.sort(Comparator.comparingInt(r -> r.getKey().getRuntimeRegNum()));
for (RuntimeVarInfo rt : rtRegs) {
Entry<SmaliRegister, List<RuntimeVarInfo>> entry = adapter.regList.get(rt.getRegNum());
if (entry.getValue().isEmpty()) {
entry.setValue(new ArrayList<>());
}
entry.getValue().add(rt);
String type = rt.getSignature();
if (type.isEmpty()) {
type = rt.getType();
}
ArgType at = ArgType.parse(type);
if (at != null) {
type = at.toString();
}
Info load = new Info(entry.getKey().getRegNum(), true,
new SimpleEntry<>(rt.getName(), type));
Info unload = new Info(entry.getKey().getRegNum(), false, null);
adapter.infoMap.computeIfAbsent((long) rt.getStartOffset(), k -> new ArrayList<>())
.add(load);
adapter.infoMap.computeIfAbsent((long) rt.getEndOffset(), k -> new ArrayList<>())
.add(unload);
}
return adapter;
}
public List<SmaliRegister> getInitializedList(long codeOffset) {
List<SmaliRegister> ret = Collections.emptyList();
for (Entry<SmaliRegister, List<RuntimeVarInfo>> info : regList) {
if (info.getKey().isInitialized(codeOffset)) {
if (ret.isEmpty()) {
ret = new ArrayList<>();
}
ret.add(info.getKey());
}
}
return ret;
}
@Nullable
public Entry<String, String> getInfo(int runtimeNum, long codeOffset) {
Entry<SmaliRegister, List<RuntimeVarInfo>> list = regList.get(runtimeNum);
for (RegisterInfo info : list.getValue()) {
if (info.getStartOffset() > codeOffset) {
break;
}
if (info.isInitialized(codeOffset)) {
return new SimpleEntry<>(info.getName(), info.getType());
}
}
return null;
}
public List<Info> getInfoAt(long codeOffset) {
if (hasDbgInfo) {
List<Info> list = infoMap.get(codeOffset);
if (list != null) {
return list;
}
}
return Collections.emptyList();
}
public static class Info {
private final int smaliRegNum;
private final boolean load;
private final Entry<String, String> info;
private Info(int smaliRegNum, boolean load, Entry<String, String> info) {
this.smaliRegNum = smaliRegNum;
this.load = load;
this.info = info;
}
public int getSmaliRegNum() {
return smaliRegNum;
}
public boolean isLoad() {
return load;
}
public Entry<String, String> getInfo() {
return info;
}
}
}
package jadx.gui.device.debugger.smali;
public class MNEMONIC {
public 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",
"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",
"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",
"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",
"iget-boolean", "iget-byte", "iget-char", "iget-short", "iput",
"iput-wide", "iput-object", "iput-boolean", "iput-byte", "iput-char",
"iput-short", "sget", "sget-wide", "sget-object", "sget-boolean",
"sget-byte", "sget-char", "sget-short", "sput", "sput-wide",
"sput-object", "sput-boolean", "sput-byte", "sput-char", "sput-short",
"invoke-virtual", "invoke-super", "invoke-direct", "invoke-static", "invoke-interface",
"(unused)", "invoke-virtual/range", "invoke-super/range", "invoke-direct/range", "invoke-static/range",
"invoke-interface/range", "(unused)", "(unused)", "neg-int", "not-int",
"neg-long", "not-long", "neg-float", "neg-double", "int-to-long",
"int-to-float", "int-to-double", "long-to-int", "long-to-float", "long-to-double",
"float-to-int", "float-to-long", "float-to-double", "double-to-int", "double-to-long",
"double-to-float", "int-to-byte", "int-to-char", "int-to-short", "add-int",
"sub-int", "mul-int", "div-int", "rem-int", "and-int",
"or-int", "xor-int", "shl-int", "shr-int", "ushr-int",
"add-long", "sub-long", "mul-long", "div-long", "rem-long",
"and-long", "or-long", "xor-long", "shl-long", "shr-long",
"ushr-long", "add-float", "sub-float", "mul-float", "div-float",
"rem-float", "add-double", "sub-double", "mul-double", "div-double",
"rem-double", "add-int/2addr", "sub-int/2addr", "mul-int/2addr", "div-int/2addr",
"rem-int/2addr", "and-int/2addr", "or-int/2addr", "xor-int/2addr", "shl-int/2addr",
"shr-int/2addr", "ushr-int/2addr", "add-long/2addr", "sub-long/2addr", "mul-long/2addr",
"div-long/2addr", "rem-long/2addr", "and-long/2addr", "or-long/2addr", "xor-long/2addr",
"shl-long/2addr", "shr-long/2addr", "ushr-long/2addr", "add-float/2addr", "sub-float/2addr",
"mul-float/2addr", "div-float/2addr", "rem-float/2addr", "add-double/2addr", "sub-double/2addr",
"mul-double/2addr", "div-double/2addr", "rem-double/2addr", "add-int/lit16", "rsub-int",
"mul-int/lit16", "div-int/lit16", "rem-int/lit16", "and-int/lit16", "or-int/lit16",
"xor-int/lit16", "add-int/lit8", "rsub-int/lit8", "mul-int/lit8", "div-int/lit8",
"rem-int/lit8", "and-int/lit8", "or-int/lit8", "xor-int/lit8", "shl-int/lit8",
"shr-int/lit8", "ushr-int/lit8", "(unused)", "(unused)", "(unused)",
"(unused)", "(unused)", "(unused)", "(unused)", "(unused)",
"(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",
"const-method-type" };
}
package jadx.gui.device.debugger.smali;
import jadx.api.plugins.input.data.ILocalVar;
public abstract class RegisterInfo implements ILocalVar {
public boolean isInitialized(long codeOffset) {
return codeOffset >= getStartOffset() && codeOffset < getEndOffset();
}
public boolean isUnInitialized(long codeOffset) {
return codeOffset < getStartOffset() || codeOffset >= getEndOffset();
}
}
package jadx.gui.device.debugger.smali;
import java.util.*;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.InsnNode;
class SmaliMethodNode {
private Map<Long, InsnNode> nodes; // codeOffset: InsnNode
private List<SmaliRegister> regList;
private int[] insnPos;
private int defPos;
private Map<Integer, Integer> lineMapping = Collections.emptyMap(); // line: codeOffset
private int paramRegStart;
private int regCount;
public int getParamRegStart() {
return this.paramRegStart;
}
public int getRegCount() {
return this.regCount;
}
public Map<Integer, Integer> getLineMapping() {
return lineMapping;
}
public void initRegInfoList(int regCount, int insnCount) {
regList = new ArrayList<>(regCount);
for (int i = 0; i < regCount; i++) {
regList.add(new SmaliRegister(i, insnCount));
}
}
public int getInsnPos(long codeOffset) {
if (insnPos != null && codeOffset < insnPos.length) {
return insnPos[(int) codeOffset];
}
return -1;
}
public int getDefPos() {
return defPos;
}
public InsnNode getInsnNode(long codeOffset) {
return nodes.get(codeOffset);
}
public List<SmaliRegister> getRegList() {
return regList;
}
protected SmaliMethodNode() {
}
protected void setRegCount(int regCount) {
this.regCount = regCount;
}
protected void attachLine(int line, int codeOffset) {
if (lineMapping.isEmpty()) {
lineMapping = new HashMap<>();
}
lineMapping.put(line, codeOffset);
}
protected void setInsnInfo(int codeOffset, int pos) {
if (insnPos != null && codeOffset < insnPos.length) {
insnPos[codeOffset] = pos;
}
InsnNode insn = getInsnNode(codeOffset);
RegisterArg r = insn.getResult();
if (r != null) {
regList.get(r.getRegNum()).setStartOffset(codeOffset);
}
for (InsnArg arg : insn.getArguments()) {
if (arg instanceof RegisterArg) {
regList.get(((RegisterArg) arg).getRegNum()).setStartOffset(codeOffset);
}
}
}
protected void setDefPos(int pos) {
defPos = pos;
}
protected void setParamReg(int regNum, String name) {
SmaliRegister r = regList.get(regNum);
r.setParam(name);
r.setStartOffset(-1);
}
protected void setParamRegStart(int paramRegStart) {
this.paramRegStart = paramRegStart;
}
protected void setInsnNodes(Map<Long, InsnNode> nodes, int insnCount) {
this.nodes = nodes;
insnPos = new int[insnCount];
}
}
package jadx.gui.device.debugger.smali;
public class SmaliRegister extends RegisterInfo {
private final int num;
private String paramName;
private final int endOffset;
private int startOffset;
private boolean isParam;
private int runtimeNum;
public SmaliRegister(int num, int insnCount) {
this.num = num;
this.endOffset = insnCount;
this.startOffset = insnCount;
}
public int getRuntimeRegNum() {
return runtimeNum;
}
public void setRuntimeRegNum(int runtimeNum) {
this.runtimeNum = runtimeNum;
}
@Override
public boolean isInitialized(long codeOffset) {
return codeOffset > getStartOffset() && codeOffset < getEndOffset();
}
protected void setParam(String name) {
paramName = name;
isParam = true;
}
protected void setStartOffset(int off) {
if (startOffset == -1 && !isParam) {
startOffset = off;
return;
}
if (off < startOffset) {
startOffset = off;
}
}
@Override
public String getName() {
return paramName != null ? paramName : "v" + num;
}
@Override
public int getRegNum() {
return num;
}
@Override
public String getType() {
return "";
}
@Override
public String getSignature() {
return null;
}
@Override
public int getStartOffset() {
return startOffset;
}
@Override
public int getEndOffset() {
return endOffset;
}
}
......@@ -107,10 +107,6 @@ public class JClass extends JLoadableNode {
return cls.getSmali();
}
public String getSmaliV2() {
return cls.getClassNode().getSmaliV2();
}
@Override
public String getSyntaxName() {
return SyntaxConstants.SYNTAX_STYLE_JAVA;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -86,4 +86,8 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel {
public AbstractCodeArea getSmaliCodeArea() {
return smaliCodePanel.getCodeArea();
}
public void showSmaliPane() {
areaTabbedPane.setSelectedComponent(smaliCodePanel);
}
}
此差异已折叠。
......@@ -78,6 +78,11 @@ public class UiUtils {
comp.getActionMap().put(id, action);
}
public static void removeKeyBinding(JComponent comp, KeyStroke key, String id) {
comp.getInputMap().remove(key);
comp.getActionMap().remove(id);
}
public static String typeFormat(String name, ArgType type) {
return name + " " + typeStr(type);
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册