* improve smali printer to show bytecode * set insnStart position before start decoding * swithed line 62 and line 63, to get the proper bytes, insnStart must to be set before start to decode. Co-authored-by: tobias <tobias.hotmail.com>
This commit is contained in:
@@ -576,6 +576,29 @@ 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 ProcessState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
private boolean checkForUpdates = false;
|
||||
private List<Path> recentProjects = new ArrayList<>();
|
||||
private String fontStr = "";
|
||||
private String smaliFontStr = "";
|
||||
private String editorThemePath = "";
|
||||
private LangLocale langLocale = NLS.defaultLocale();
|
||||
private boolean autoStartJobs = false;
|
||||
@@ -68,6 +69,7 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
private int srhResourceSkipSize = 1000;
|
||||
private String srhResourceFileExt = ".xml|.html|.js|.json|.txt";
|
||||
private boolean keepCommonDialogOpen = false;
|
||||
private boolean smaliAreaShowBytecode = false;
|
||||
|
||||
/**
|
||||
* UI setting: the width of the tree showing the classes, resources, ...
|
||||
@@ -377,6 +379,27 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
}
|
||||
}
|
||||
|
||||
public Font getSmaliFont() {
|
||||
if (smaliFontStr.isEmpty()) {
|
||||
return DEFAULT_FONT;
|
||||
}
|
||||
try {
|
||||
return FontUtils.loadByStr(smaliFontStr);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to load font: {} for smali, reset to default", smaliFontStr, e);
|
||||
setSmaliFont(DEFAULT_FONT);
|
||||
return DEFAULT_FONT;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSmaliFont(@Nullable Font font) {
|
||||
if (font == null) {
|
||||
this.smaliFontStr = "";
|
||||
} else {
|
||||
this.smaliFontStr = FontUtils.convertToStr(font);
|
||||
}
|
||||
}
|
||||
|
||||
public void setLogLevel(LogHelper.LogLevelEnum level) {
|
||||
this.logLevel = level;
|
||||
}
|
||||
@@ -430,6 +453,14 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
return keepCommonDialogOpen;
|
||||
}
|
||||
|
||||
public void setSmaliAreaShowBytecode(boolean yes) {
|
||||
smaliAreaShowBytecode = yes;
|
||||
}
|
||||
|
||||
public boolean getSmaliAreaShowBytecode() {
|
||||
return smaliAreaShowBytecode;
|
||||
}
|
||||
|
||||
private void upgradeSettings(int fromVersion) {
|
||||
LOG.debug("upgrade settings from version: {} to {}", fromVersion, CURRENT_SETTINGS_VERSION);
|
||||
if (fromVersion == 0) {
|
||||
|
||||
@@ -7,8 +7,7 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
@@ -292,6 +291,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
|
||||
private SettingsGroup makeEditorGroup() {
|
||||
JButton fontBtn = new JButton(NLS.str("preferences.select_font"));
|
||||
JButton smaliFontBtn = new JButton(NLS.str("preferences.select_smali_font"));
|
||||
|
||||
EditorTheme[] editorThemes = EditorTheme.getAllThemes();
|
||||
JComboBox<EditorTheme> themesCbx = new JComboBox<>(editorThemes);
|
||||
@@ -311,6 +311,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
SettingsGroup group = new SettingsGroup(NLS.str("preferences.editor"));
|
||||
JLabel fontLabel = group.addRow(getFontLabelStr(), fontBtn);
|
||||
group.addRow(NLS.str("preferences.theme"), themesCbx);
|
||||
JLabel smaliFontLabel = group.addRow(getSmaliFontLabelStr(), smaliFontBtn);
|
||||
|
||||
fontBtn.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
@@ -327,6 +328,22 @@ public class JadxSettingsWindow extends JDialog {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
smaliFontBtn.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
JFontChooser fontChooser = new JPreferredFontChooser();
|
||||
fontChooser.setSelectedFont(settings.getSmaliFont());
|
||||
int result = fontChooser.showDialog(JadxSettingsWindow.this);
|
||||
if (result == JFontChooser.OK_OPTION) {
|
||||
Font font = fontChooser.getSelectedFont();
|
||||
LOG.debug("Selected Font: {} for smali", font);
|
||||
settings.setSmaliFont(font);
|
||||
mainWindow.loadSettings();
|
||||
smaliFontLabel.setText(getSmaliFontLabelStr());
|
||||
}
|
||||
}
|
||||
});
|
||||
return group;
|
||||
}
|
||||
|
||||
@@ -336,6 +353,12 @@ public class JadxSettingsWindow extends JDialog {
|
||||
return NLS.str("preferences.font") + ": " + font.getFontName() + ' ' + fontStyleName + ' ' + font.getSize();
|
||||
}
|
||||
|
||||
private String getSmaliFontLabelStr() {
|
||||
Font font = settings.getSmaliFont();
|
||||
String fontStyleName = FontUtils.convertFontStyleToString(font.getStyle());
|
||||
return NLS.str("preferences.smali_font") + ": " + font.getFontName() + ' ' + fontStyleName + ' ' + font.getSize();
|
||||
}
|
||||
|
||||
private SettingsGroup makeDecompilationGroup() {
|
||||
JCheckBox fallback = new JCheckBox();
|
||||
fallback.setSelected(settings.isFallbackMode());
|
||||
@@ -578,4 +601,42 @@ public class JadxSettingsWindow extends JDialog {
|
||||
add(Box.createVerticalGlue());
|
||||
}
|
||||
}
|
||||
|
||||
private static class JPreferredFontChooser extends JFontChooser {
|
||||
private static final String[] PREFERRED_FONTS = new String[] {
|
||||
"Monospaced", "Consolas", "Courier", "Courier New",
|
||||
"Lucida Sans Typewriter", "Lucida Console",
|
||||
"SimSun", "SimHei",
|
||||
};
|
||||
|
||||
private String[] filteredFonts;
|
||||
|
||||
@Override
|
||||
protected String[] getFontFamilies() {
|
||||
if (filteredFonts == null) {
|
||||
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
Set<String> fontSet = new HashSet<>();
|
||||
Collections.addAll(fontSet, env.getAvailableFontFamilyNames());
|
||||
ArrayList<String> found = new ArrayList<>(PREFERRED_FONTS.length);
|
||||
for (String font : PREFERRED_FONTS) {
|
||||
if (fontSet.contains(font)) {
|
||||
found.add(font);
|
||||
}
|
||||
}
|
||||
if (found.size() == PREFERRED_FONTS.length) {
|
||||
filteredFonts = PREFERRED_FONTS;
|
||||
} else if (found.size() > 0) {
|
||||
filteredFonts = new String[found.size()];
|
||||
for (int i = 0; i < found.size(); i++) {
|
||||
filteredFonts[i] = found.get(i);
|
||||
}
|
||||
} else {
|
||||
// this machine is crazy.
|
||||
LOG.warn("Can't found any preferred fonts for smali, use all available.");
|
||||
filteredFonts = env.getAvailableFontFamilyNames();
|
||||
}
|
||||
}
|
||||
return filteredFonts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,10 @@ public class JClass extends JLoadableNode {
|
||||
return cls.getSmali();
|
||||
}
|
||||
|
||||
public String getSmaliV2() {
|
||||
return cls.getClassNode().getSmaliV2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntaxName() {
|
||||
return SyntaxConstants.SYNTAX_STYLE_JAVA;
|
||||
|
||||
@@ -55,9 +55,10 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
contentPanel.getTabbedPane().getOpenTabs().values().forEach(v -> {
|
||||
if (v instanceof AbstractCodeContentPanel) {
|
||||
AbstractCodeArea codeArea = ((AbstractCodeContentPanel) v).getCodeArea();
|
||||
codeArea.setLineWrap(wrap);
|
||||
if (codeArea.isVisible()) {
|
||||
codeArea.repaint();
|
||||
setCodeAreaLineWrap(codeArea, wrap);
|
||||
if (v instanceof ClassCodeContentPanel) {
|
||||
codeArea = ((ClassCodeContentPanel) v).getSmaliCodeArea();
|
||||
setCodeAreaLineWrap(codeArea, wrap);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -116,6 +117,13 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
});
|
||||
}
|
||||
|
||||
private void setCodeAreaLineWrap(AbstractCodeArea codeArea, boolean wrap) {
|
||||
codeArea.setLineWrap(wrap);
|
||||
if (codeArea.isVisible()) {
|
||||
codeArea.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private String highlightCaretWord(String lastText, int pos) {
|
||||
String text = getWordByPosition(pos);
|
||||
if (StringUtils.isEmpty(text)) {
|
||||
|
||||
@@ -1,26 +1,106 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.TextNode;
|
||||
import jadx.gui.ui.ContentPanel;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
public final class SmaliArea extends AbstractCodeArea {
|
||||
private static final long serialVersionUID = 1334485631870306494L;
|
||||
|
||||
private final JNode textNode;
|
||||
|
||||
private SmaliV2Style smaliV2Style;
|
||||
private boolean curVersion = false;
|
||||
private final JCheckBoxMenuItem cbUseSmaliV2;
|
||||
|
||||
SmaliArea(ContentPanel contentPanel) {
|
||||
super(contentPanel);
|
||||
this.textNode = new TextNode(node.getName());
|
||||
setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
||||
|
||||
cbUseSmaliV2 = new JCheckBoxMenuItem(NLS.str("popup.bytecode_col"), shouldUseSmaliPrinterV2());
|
||||
cbUseSmaliV2.setAction(new AbstractAction(NLS.str("popup.bytecode_col")) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
||||
boolean usingV2 = shouldUseSmaliPrinterV2();
|
||||
JadxSettings settings = getContentPanel().getTabbedPane().getMainWindow().getSettings();
|
||||
settings.setSmaliAreaShowBytecode(!usingV2);
|
||||
contentPanel.getTabbedPane().getOpenTabs().values().forEach(v -> {
|
||||
if (v instanceof ClassCodeContentPanel) {
|
||||
((ClassCodeContentPanel) v).getSmaliCodeArea().refresh();
|
||||
}
|
||||
});
|
||||
settings.sync();
|
||||
}
|
||||
});
|
||||
getPopupMenu().add(cbUseSmaliV2);
|
||||
if (shouldUseSmaliPrinterV2()) {
|
||||
loadV2Style();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font getFont() {
|
||||
if (smaliV2Style != null && shouldUseSmaliPrinterV2()) {
|
||||
return smaliV2Style.getFont();
|
||||
}
|
||||
return super.getFont();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font getFontForTokenType(int type) {
|
||||
if (shouldUseSmaliPrinterV2()) {
|
||||
return smaliV2Style.getFont();
|
||||
}
|
||||
return super.getFontForTokenType(type);
|
||||
}
|
||||
|
||||
private boolean shouldUseSmaliPrinterV2() {
|
||||
return getContentPanel().getTabbedPane().getMainWindow().getSettings().getSmaliAreaShowBytecode();
|
||||
}
|
||||
|
||||
private void loadV2Style() {
|
||||
if (smaliV2Style == null) {
|
||||
smaliV2Style = new SmaliV2Style(this);
|
||||
addPropertyChangeListener(SYNTAX_SCHEME_PROPERTY, evt -> {
|
||||
if (smaliV2Style.refreshTheme() && shouldUseSmaliPrinterV2()) {
|
||||
setSyntaxScheme(smaliV2Style);
|
||||
}
|
||||
});
|
||||
}
|
||||
setSyntaxScheme(smaliV2Style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (getText().isEmpty()) {
|
||||
setText(node.getSmali());
|
||||
boolean useSmaliV2 = shouldUseSmaliPrinterV2();
|
||||
if (useSmaliV2 != cbUseSmaliV2.getState()) {
|
||||
cbUseSmaliV2.setState(useSmaliV2);
|
||||
}
|
||||
if (getText().isEmpty() || curVersion != useSmaliV2) {
|
||||
curVersion = useSmaliV2;
|
||||
if (!useSmaliV2) {
|
||||
if (getSyntaxScheme() == smaliV2Style) {
|
||||
Theme theme = getContentPanel().getTabbedPane().getMainWindow().getEditorTheme();
|
||||
setSyntaxScheme(theme.scheme);
|
||||
}
|
||||
setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
||||
setText(node.getSmali());
|
||||
} else {
|
||||
loadV2Style();
|
||||
setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_ASSEMBLER_6502);
|
||||
setText(((JClass) node).getSmaliV2());
|
||||
}
|
||||
setCaretPosition(0);
|
||||
}
|
||||
}
|
||||
@@ -35,4 +115,57 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
// this area contains only smali without other node attributes
|
||||
return textNode;
|
||||
}
|
||||
|
||||
private static class SmaliV2Style extends SyntaxScheme {
|
||||
|
||||
SmaliArea smaliArea;
|
||||
Theme curTheme;
|
||||
|
||||
public SmaliV2Style(SmaliArea smaliArea) {
|
||||
super(true);
|
||||
this.smaliArea = smaliArea;
|
||||
curTheme = smaliArea.getContentPanel().getTabbedPane().getMainWindow().getEditorTheme();
|
||||
updateTheme();
|
||||
}
|
||||
|
||||
public Font getFont() {
|
||||
return smaliArea.getContentPanel().getTabbedPane().getMainWindow().getSettings().getSmaliFont();
|
||||
}
|
||||
|
||||
public boolean refreshTheme() {
|
||||
Theme theme = smaliArea.getContentPanel().getTabbedPane().getMainWindow().getEditorTheme();
|
||||
boolean refresh = theme != curTheme;
|
||||
if (refresh) {
|
||||
curTheme = theme;
|
||||
updateTheme();
|
||||
}
|
||||
return refresh;
|
||||
}
|
||||
|
||||
private void updateTheme() {
|
||||
Style[] mainStyles = curTheme.scheme.getStyles();
|
||||
Style[] styles = new Style[mainStyles.length];
|
||||
for (int i = 0; i < mainStyles.length; i++) {
|
||||
Style mainStyle = mainStyles[i];
|
||||
if (mainStyle == null) {
|
||||
styles[i] = new Style();
|
||||
} else {
|
||||
// font will be hijacked by getFont & getFontForTokenType,
|
||||
// so it doesn't need to be set here.
|
||||
styles[i] = new Style(mainStyle.foreground, mainStyle.background, null);
|
||||
}
|
||||
}
|
||||
setStyles(styles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreDefaults(Font baseFont) {
|
||||
restoreDefaults(baseFont, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreDefaults(Font baseFont, boolean fontStyles) {
|
||||
// Note: it's a hook for continue using the editor theme, better don't remove it.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +115,11 @@ preferences.excludedPackages.editDialog=<html>Liste der durch Leerzeichen getren
|
||||
preferences.cfg=Methoden generieren CFG-Grafiken (im 'Punkt'-Format)
|
||||
preferences.raw_cfg=RAW CFG-Grafiken generieren
|
||||
preferences.font=Schrift ändern
|
||||
#preferences.smali_font=
|
||||
preferences.theme=Thema ändern
|
||||
preferences.start_jobs=Autom. Hintergrunddekompilierung starten
|
||||
preferences.select_font=Ändern
|
||||
#preferences.select_smali_font=
|
||||
preferences.deobfuscation_on=Deobfuscation aktivieren
|
||||
preferences.deobfuscation_force=Deobfuscationskartendatei umschreiben erzwingen
|
||||
preferences.deobfuscation_min_len=Minimale Namenlänge
|
||||
@@ -154,6 +156,7 @@ msg.cmd_select_class_error=Klasse\n%s auswählen nicht möglich\nSie existiert n
|
||||
#msg.rename_node_disabled=
|
||||
#msg.rename_node_failed=
|
||||
|
||||
#popup.bytecode_col=
|
||||
#popup.line_wrap=
|
||||
popup.undo=Rückgängig
|
||||
popup.redo=Wiederholen
|
||||
|
||||
@@ -115,9 +115,11 @@ preferences.excludedPackages.editDialog=<html>List of space separated package na
|
||||
preferences.cfg=Generate methods CFG graphs (in 'dot' format)
|
||||
preferences.raw_cfg=Generate RAW CFG graphs
|
||||
preferences.font=Editor font
|
||||
preferences.smali_font=Smali Editor font
|
||||
preferences.theme=Editor theme
|
||||
preferences.start_jobs=Auto start background decompilation
|
||||
preferences.select_font=Change
|
||||
preferences.select_smali_font=Change
|
||||
preferences.deobfuscation_on=Enable deobfuscation
|
||||
preferences.deobfuscation_force=Force rewrite deobfuscation map file
|
||||
preferences.deobfuscation_min_len=Minimum name length
|
||||
@@ -154,6 +156,7 @@ msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not ex
|
||||
msg.rename_node_disabled=Can't rename this node
|
||||
msg.rename_node_failed=Can't rename %s
|
||||
|
||||
popup.bytecode_col=Show Bytecode
|
||||
popup.line_wrap=Line Wrap
|
||||
popup.undo=Undo
|
||||
popup.redo=Redo
|
||||
|
||||
@@ -115,9 +115,11 @@ preferences.threads=Número de hilos a procesar
|
||||
preferences.cfg=Generar methods CFG graphs (in 'dot' format)
|
||||
preferences.raw_cfg=Generate RAW CFG graphs
|
||||
preferences.font=Fuente del editor
|
||||
#preferences.smali_font=
|
||||
preferences.theme=Tema del editor
|
||||
preferences.start_jobs=Inicio autom. descompilación de fondo
|
||||
preferences.select_font=Seleccionar
|
||||
#preferences.select_smali_font=
|
||||
preferences.deobfuscation_on=Activar desobfuscación
|
||||
preferences.deobfuscation_force=Forzar reescritura del fichero de ofuscación
|
||||
preferences.deobfuscation_min_len=Longitud mínima del nombre
|
||||
@@ -154,6 +156,7 @@ msg.index_not_initialized=Índice no inicializado, ¡la bósqueda se desactivar
|
||||
#msg.rename_node_disabled=
|
||||
#msg.rename_node_failed=
|
||||
|
||||
#popup.bytecode_col=
|
||||
#popup.line_wrap=
|
||||
popup.undo=Deshacer
|
||||
popup.redo=Rehacer
|
||||
|
||||
@@ -115,9 +115,11 @@ preferences.excludedPackages.editDialog=<html>RAM 절약을 위해 디컴파일
|
||||
preferences.cfg=메소드 CFG 그래프 생성 ('dot' 포맷)
|
||||
preferences.raw_cfg=RAW CFG 그래프 생성
|
||||
preferences.font=에디터 글씨체
|
||||
#preferences.smali_font=
|
||||
preferences.theme=에디터 테마
|
||||
preferences.start_jobs=백그라운드에서 디컴파일 자동 시작
|
||||
preferences.select_font=변경
|
||||
#preferences.select_smali_font=
|
||||
preferences.deobfuscation_on=난독 해제 활성화
|
||||
preferences.deobfuscation_force=난독 해제 맵 파일 다시 쓰기
|
||||
preferences.deobfuscation_min_len=최소 이름 길이
|
||||
@@ -154,6 +156,7 @@ msg.cmd_select_class_error=클래스를 선택하지 못했습니다.\n%s\n클
|
||||
msg.rename_node_disabled=이 노드의 이름을 바꿀 수 없습니다.
|
||||
msg.rename_node_failed=%s의 이름을 바꿀 수 없습니다.
|
||||
|
||||
#popup.bytecode_col=
|
||||
popup.line_wrap=줄 바꿈
|
||||
popup.undo=실행 취소
|
||||
popup.redo=다시 실행
|
||||
|
||||
@@ -115,9 +115,11 @@ preferences.excludedPackages.editDialog=<html>将不被解压或索引的以空
|
||||
preferences.cfg=生成方法的 CFG 图(以 .dot 格式保存)
|
||||
preferences.raw_cfg=生成原始的 CFG 图
|
||||
preferences.font=编辑器字体
|
||||
#preferences.smali_font=
|
||||
preferences.theme=编辑器主题
|
||||
preferences.start_jobs=自动进行后台反编译
|
||||
preferences.select_font=更改
|
||||
#preferences.select_smali_font=
|
||||
preferences.deobfuscation_on=启用反混淆
|
||||
preferences.deobfuscation_force=强制覆盖反混淆映射文件
|
||||
preferences.deobfuscation_min_len=最小命名长度
|
||||
@@ -154,6 +156,7 @@ msg.cmd_select_class_error=无法选择类\n%s\n该类不存在。
|
||||
#msg.rename_node_disabled=
|
||||
#msg.rename_node_failed=
|
||||
|
||||
#popup.bytecode_col=
|
||||
#popup.line_wrap=
|
||||
popup.undo=撤销
|
||||
popup.redo=重做
|
||||
|
||||
+30
-3
@@ -2,9 +2,7 @@ package jadx.plugins.input.dex.insns;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.plugins.input.data.ICallSite;
|
||||
import jadx.api.plugins.input.data.IFieldData;
|
||||
import jadx.api.plugins.input.data.IMethodRef;
|
||||
import jadx.api.plugins.input.data.*;
|
||||
import jadx.api.plugins.input.insns.InsnData;
|
||||
import jadx.api.plugins.input.insns.InsnIndexType;
|
||||
import jadx.api.plugins.input.insns.Opcode;
|
||||
@@ -21,6 +19,7 @@ public class DexInsnData implements InsnData {
|
||||
private boolean decoded;
|
||||
private int opcodeUnit;
|
||||
private int length;
|
||||
private int insnStart;
|
||||
|
||||
private int offset;
|
||||
private int[] argsReg = new int[5];
|
||||
@@ -49,6 +48,11 @@ public class DexInsnData implements InsnData {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileOffset() {
|
||||
return insnStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Opcode getOpcode() {
|
||||
DexInsnInfo info = this.insnInfo;
|
||||
@@ -58,6 +62,11 @@ public class DexInsnData implements InsnData {
|
||||
return info.getApiOpcode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getByteCode() {
|
||||
return externalReader.getByteCode(insnStart, length * 2); // a unit is 2 bytes
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawOpcodeUnit() {
|
||||
return opcodeUnit;
|
||||
@@ -118,6 +127,20 @@ public class DexInsnData implements InsnData {
|
||||
return externalReader.getCallSite(index, secondExtReader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently, protoIndex is either being stored at index or target, index for const-method-type,
|
||||
* target for invoke-polymorphic(/range)
|
||||
*/
|
||||
@Override
|
||||
public IMethodProto getIndexAsProto(int protoIndex) {
|
||||
return externalReader.getMethodProto(protoIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMethodHandle getIndexAsMethodHandle() {
|
||||
return externalReader.getMethodHandle(index);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ICustomPayload getPayload() {
|
||||
@@ -144,6 +167,10 @@ public class DexInsnData implements InsnData {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public void setInsnStart(int start) {
|
||||
this.insnStart = start;
|
||||
}
|
||||
|
||||
public void setLiteral(long literal) {
|
||||
this.literal = literal;
|
||||
}
|
||||
|
||||
+56
@@ -230,4 +230,60 @@ public class DexOpcodes {
|
||||
public static final int PACKED_SWITCH_PAYLOAD = 0x0100;
|
||||
public static final int SPARSE_SWITCH_PAYLOAD = 0x0200;
|
||||
public static final int FILL_ARRAY_DATA_PAYLOAD = 0x0300;
|
||||
|
||||
public static final String MNE_UNUSED = "(unused)";
|
||||
|
||||
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" };
|
||||
}
|
||||
|
||||
+6
@@ -13,6 +13,7 @@ import jadx.api.plugins.input.data.IMethodData;
|
||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||
import jadx.plugins.input.dex.sections.annotations.AnnotationsParser;
|
||||
import jadx.plugins.input.dex.smali.SmaliPrinter;
|
||||
import jadx.plugins.input.dex.utils.SmaliUtils;
|
||||
|
||||
public class DexClassData implements IClassData {
|
||||
@@ -193,6 +194,11 @@ public class DexClassData implements IClassData {
|
||||
return SmaliUtils.getSmaliCode(dexBuf, getClassDefOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisassembledCodeV2() {
|
||||
return SmaliPrinter.printClass(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getType();
|
||||
|
||||
+2
@@ -59,8 +59,10 @@ public class DexCodeReader implements ICodeReader {
|
||||
int size = in.readInt();
|
||||
int offset = 0; // in code units (2 byte)
|
||||
while (offset < size) {
|
||||
int insnStart = in.getAbsPos();
|
||||
int opcodeUnit = in.readUShort();
|
||||
DexInsnInfo insnInfo = DexInsnInfo.get(opcodeUnit);
|
||||
insnData.setInsnStart(insnStart);
|
||||
insnData.setOffset(offset);
|
||||
insnData.setInsnInfo(insnInfo);
|
||||
insnData.setOpcodeUnit(opcodeUnit);
|
||||
|
||||
+8
@@ -45,6 +45,14 @@ public class SectionReader {
|
||||
return new SectionReader(this, off);
|
||||
}
|
||||
|
||||
public byte[] getByteCode(int start, int len) {
|
||||
int pos = buf.position();
|
||||
buf.position(start);
|
||||
byte[] bytes = readByteArray(len);
|
||||
buf.position(pos);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static ByteBuffer duplicate(ByteBuffer baseBuffer, int off) {
|
||||
ByteBuffer dupBuf = baseBuffer.duplicate();
|
||||
dupBuf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
+771
-2
@@ -1,10 +1,21 @@
|
||||
package jadx.plugins.input.dex.smali;
|
||||
|
||||
import jadx.api.plugins.input.data.AccessFlags;
|
||||
import jadx.api.plugins.input.data.ICodeReader;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import jadx.api.plugins.input.data.*;
|
||||
import jadx.api.plugins.input.data.annotations.AnnotationVisibility;
|
||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||
import jadx.api.plugins.input.insns.InsnData;
|
||||
import jadx.api.plugins.input.insns.InsnIndexType;
|
||||
import jadx.plugins.input.dex.insns.DexOpcodes;
|
||||
import jadx.plugins.input.dex.insns.payloads.DexSwitchPayload;
|
||||
import jadx.plugins.input.dex.sections.DexFieldData;
|
||||
import jadx.plugins.input.dex.sections.DexMethodData;
|
||||
import jadx.plugins.input.dex.sections.DexMethodRef;
|
||||
|
||||
import static jadx.api.plugins.input.data.AccessFlagsScope.FIELD;
|
||||
import static jadx.api.plugins.input.data.AccessFlagsScope.METHOD;
|
||||
|
||||
// TODO: not finished
|
||||
@@ -37,4 +48,762 @@ public class SmaliPrinter {
|
||||
codeWriter.startLine(".end method");
|
||||
return codeWriter.getCode();
|
||||
}
|
||||
|
||||
public static String printClass(IClassData cls) {
|
||||
SmaliCodeWriter smali = new SmaliCodeWriter();
|
||||
smali.startLine("Class: " + cls.getType())
|
||||
.startLine("AccessFlags: " + AccessFlags.format(cls.getAccessFlags(), AccessFlagsScope.CLASS))
|
||||
.startLine("SuperType: " + cls.getSuperType())
|
||||
.startLine("Interfaces: " + cls.getInterfacesTypes())
|
||||
.startLine("SourceFile: " + cls.getSourceFile());
|
||||
|
||||
if (cls.getAnnotations().size() > 0) {
|
||||
smali.startLine().startLine("# annotations");
|
||||
printAnnotations(smali, cls.getAnnotations());
|
||||
}
|
||||
List<Entry<DexFieldData, List<IAnnotation>>> flds = new ArrayList<>();
|
||||
cls.visitFieldsAndMethods(
|
||||
f -> {
|
||||
DexFieldData fld = new DexFieldData(null);
|
||||
fld.setParentClassType(f.getParentClassType());
|
||||
fld.setAccessFlags(f.getAccessFlags());
|
||||
fld.setName(f.getName());
|
||||
fld.setType(f.getType());
|
||||
flds.add(new AbstractMap.SimpleEntry<>(fld, f.getAnnotations()));
|
||||
},
|
||||
m -> {
|
||||
if (!flds.isEmpty()) {
|
||||
printField(smali, flds, cls.getStaticFieldInitValues());
|
||||
flds.clear();
|
||||
smali.startLine("# methods");
|
||||
}
|
||||
printMethod(smali, m);
|
||||
});
|
||||
if (!flds.isEmpty()) { // in case there are no methods.
|
||||
printField(smali, flds, cls.getStaticFieldInitValues());
|
||||
flds.clear();
|
||||
}
|
||||
return smali.getCode();
|
||||
}
|
||||
|
||||
private static void printField(SmaliCodeWriter smali,
|
||||
List<Entry<DexFieldData, List<IAnnotation>>> flds,
|
||||
List<EncodedValue> staticFieldInitValues) {
|
||||
int staticIdx = 0;
|
||||
int accessColWidth = 0;
|
||||
int nameColWidth = 0;
|
||||
List<String> accesses = new ArrayList<>(flds.size());
|
||||
for (Entry<DexFieldData, List<IAnnotation>> fld : flds) { // calc width of cols
|
||||
String temp = fld.getKey().getName();
|
||||
if (temp.length() > nameColWidth) {
|
||||
nameColWidth = temp.length();
|
||||
}
|
||||
temp = AccessFlags.format(fld.getKey().getAccessFlags(), FIELD);
|
||||
accesses.add(temp);
|
||||
if (temp.length() > accessColWidth) {
|
||||
accessColWidth = temp.length();
|
||||
}
|
||||
}
|
||||
smali.startLine().startLine("# fields");
|
||||
String whites = new String(new byte[Math.max(accessColWidth, nameColWidth)]).replace("\0", " ");
|
||||
for (int i = 0; i < flds.size(); i++) {
|
||||
smali.startLine();
|
||||
Entry<DexFieldData, List<IAnnotation>> fld = flds.get(i);
|
||||
String access = accesses.get(i);
|
||||
int pad = accessColWidth - access.length();
|
||||
if (pad > 0) {
|
||||
access += whites.substring(0, pad);
|
||||
}
|
||||
smali.add(".field ").add(access);
|
||||
String name = fld.getKey().getName();
|
||||
pad = nameColWidth - name.length();
|
||||
if (pad > 0) {
|
||||
name += whites.substring(0, pad);
|
||||
}
|
||||
smali.add(name).add(" ");
|
||||
smali.add(": ").add(fld.getKey().getType());
|
||||
if ((fld.getKey().getAccessFlags() & AccessFlags.STATIC) != 0) { // static field
|
||||
if (staticIdx < staticFieldInitValues.size()) {
|
||||
smali.add(" # init val = ");
|
||||
printEncodedValue(smali, staticFieldInitValues.get(staticIdx++), false);
|
||||
}
|
||||
}
|
||||
smali.incIndent();
|
||||
printAnnotations(smali, fld.getValue());
|
||||
smali.decIndent();
|
||||
}
|
||||
smali.startLine();
|
||||
}
|
||||
|
||||
private static void printMethod(SmaliCodeWriter smali, IMethodData mth) {
|
||||
smali.startLine()
|
||||
.startLine(mth.isDirect() ? "# direct method" : " # virtual method")
|
||||
.startLine(".method ");
|
||||
printMethodDef(smali, mth);
|
||||
smali.incIndent();
|
||||
ICodeReader codeReader = mth.getCodeReader();
|
||||
if (codeReader != null) {
|
||||
smali.startLine(".registers ")
|
||||
.add(codeReader.getRegistersCount())
|
||||
.startLine();
|
||||
Map<Integer, String> paramMap = formatMthParamInfo(mth, smali, codeReader);
|
||||
if (paramMap.size() > 0) {
|
||||
smali.startLine();
|
||||
}
|
||||
SmaliGen smaliGen = new SmaliGen(paramMap, codeReader.getDebugInfo(), true, true);
|
||||
codeReader.visitInstructions(insn -> {
|
||||
insn.decode();
|
||||
smaliGen.format(insn);
|
||||
});
|
||||
smaliGen.gen(smali);
|
||||
}
|
||||
smali.decIndent();
|
||||
smali.startLine(".end method");
|
||||
}
|
||||
|
||||
private static void printMethodDef(SmaliCodeWriter smali, IMethodData mth) {
|
||||
smali.add(AccessFlags.format(mth.getAccessFlags(), METHOD));
|
||||
|
||||
IMethodRef methodRef = mth.getMethodRef();
|
||||
methodRef.load();
|
||||
smali.add(methodRef.getName());
|
||||
smali.add('(').addArgs(methodRef.getArgTypes()).add(')');
|
||||
smali.add(methodRef.getReturnType());
|
||||
if (mth.getAnnotations().size() > 0) {
|
||||
smali.incIndent();
|
||||
printAnnotations(smali, mth.getAnnotations());
|
||||
smali.decIndent();
|
||||
smali.startLine();
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<Integer, String> formatMthParamInfo(IMethodData mth, SmaliCodeWriter smali, ICodeReader codeReader) {
|
||||
List<String> types = mth.getMethodRef().getArgTypes();
|
||||
if (types.size() == 0) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
int i = 0;
|
||||
int paramCount = 0;
|
||||
int paramStart = isStaticMethod(mth) ? 0 : 1;
|
||||
int regNum = getParamStartRegNum(mth);
|
||||
Map<Integer, String> paramMap = new HashMap<>(types.size());
|
||||
IDebugInfo dbgInfo = codeReader.getDebugInfo();
|
||||
if (dbgInfo != null) {
|
||||
for (ILocalVar var : dbgInfo.getLocalVars()) {
|
||||
if (var.getStartOffset() == -1) {
|
||||
smali.startLine(String.format(".param p%d, \"%s\":%s",
|
||||
paramStart + i, var.getName(), var.getType()));
|
||||
paramMap.put(regNum + i, "p" + (paramStart + i));
|
||||
paramCount++;
|
||||
i += 1;
|
||||
if (isWideType(var.getType())) {
|
||||
paramMap.put(regNum + i, "p" + (paramStart + i));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (paramCount + 1 == types.size()) {
|
||||
return paramMap;
|
||||
}
|
||||
}
|
||||
for (; paramCount < types.size(); paramCount++) {
|
||||
String type = types.get(paramCount);
|
||||
smali.startLine(String.format(".param p%d, \"\":%s", paramStart + i, type));
|
||||
paramMap.put(regNum + i, "p" + (paramStart + i));
|
||||
i += 1;
|
||||
if (isWideType(type)) {
|
||||
paramMap.put(regNum + i, "p" + (paramStart + i));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
private static int getParamStartRegNum(IMethodData mth) {
|
||||
ICodeReader codeReader = mth.getCodeReader();
|
||||
if (codeReader != null) {
|
||||
int startNum = codeReader.getRegistersCount();
|
||||
if (startNum > 0) {
|
||||
for (String argType : mth.getMethodRef().getArgTypes()) {
|
||||
if (isWideType(argType)) {
|
||||
startNum -= 2;
|
||||
} else {
|
||||
startNum -= 1;
|
||||
}
|
||||
}
|
||||
if (!isStaticMethod(mth)) {
|
||||
startNum--;
|
||||
}
|
||||
return startNum;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean isWideType(String type) {
|
||||
return type.equals("D") || type.equals("J");
|
||||
}
|
||||
|
||||
private static boolean isStaticMethod(IMethodData mth) {
|
||||
return (mth.getAccessFlags() & AccessFlags.STATIC) != 0;
|
||||
}
|
||||
|
||||
private static void printAnnotations(SmaliCodeWriter smali, List<IAnnotation> annoList) {
|
||||
if (annoList.size() > 0) {
|
||||
for (int i = 0; i < annoList.size(); i++) {
|
||||
smali.startLine();
|
||||
printAnnotation(smali, annoList.get(i));
|
||||
if (i != annoList.size() - 1) {
|
||||
smali.startLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printAnnotation(SmaliCodeWriter smali, IAnnotation anno) {
|
||||
smali.add(".annotation")
|
||||
.add(" ");
|
||||
AnnotationVisibility vby = anno.getVisibility();
|
||||
if (vby != null) {
|
||||
smali.add(vby.toString().toLowerCase()).add(" ");
|
||||
}
|
||||
smali.add(anno.getAnnotationClass());
|
||||
anno.getValues().forEach((k, v) -> {
|
||||
smali.incIndent();
|
||||
smali.startLine(k).add(" = ");
|
||||
printEncodedValue(smali, v, true);
|
||||
smali.decIndent();
|
||||
});
|
||||
smali.startLine(".end annotation");
|
||||
}
|
||||
|
||||
private static void printEncodedValue(SmaliCodeWriter smali, EncodedValue value, boolean wrapArray) {
|
||||
switch (value.getType()) {
|
||||
case ENCODED_ARRAY:
|
||||
smali.add("{");
|
||||
if (wrapArray) {
|
||||
smali.incIndent();
|
||||
smali.startLine();
|
||||
}
|
||||
List<EncodedValue> values = (List<EncodedValue>) value.getValue();
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
printEncodedValue(smali, values.get(i), wrapArray);
|
||||
if (i != values.size() - 1) {
|
||||
smali.add(",");
|
||||
if (wrapArray) {
|
||||
smali.startLine();
|
||||
} else {
|
||||
smali.add(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wrapArray) {
|
||||
smali.decIndent();
|
||||
smali.startLine("}");
|
||||
}
|
||||
break;
|
||||
case ENCODED_STRING:
|
||||
smali.add("\"").add(value.getValue()).add("\"");
|
||||
break;
|
||||
case ENCODED_NULL:
|
||||
smali.add("null");
|
||||
break;
|
||||
case ENCODED_ANNOTATION:
|
||||
printAnnotation(smali, (IAnnotation) value.getValue());
|
||||
break;
|
||||
default:
|
||||
smali.add(value.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private static final int CODE_OFFSET_COLUMN_WIDTH = 4;
|
||||
private static final int BYTECODE_COLUMN_WIDTH = 20 + 3; // 3 for ellipses.
|
||||
private static final String FMT_BYTECODE_COL = "%-" + (BYTECODE_COLUMN_WIDTH - 3) + "s";
|
||||
|
||||
private static final int INSN_COL_WIDTH = "const-method-handle".length();
|
||||
private static final String FMT_INSN_COL = "%-" + INSN_COL_WIDTH + "s";
|
||||
private static final String FMT_FILE_OFFSET = "%08x:";
|
||||
private static final String FMT_CODE_OFFSET = "%04x:";
|
||||
private static final String FMT_TARGET_OFFSET = "%04x";
|
||||
private static final String FMT_GOTO = ":goto_" + FMT_TARGET_OFFSET;
|
||||
private static final String FMT_COND = ":cond_" + FMT_TARGET_OFFSET;
|
||||
private static final String FMT_DATA = ":data_" + FMT_TARGET_OFFSET;
|
||||
private static final String FMT_P_SWITCH = ":p_switch_" + FMT_TARGET_OFFSET;
|
||||
private static final String FMT_S_SWITCH = ":s_switch_" + FMT_TARGET_OFFSET;
|
||||
private static final String FMT_P_SWITCH_CASE = ":p_case_" + FMT_TARGET_OFFSET;
|
||||
private static final String FMT_S_SWITCH_CASE = ":s_case_" + FMT_TARGET_OFFSET;
|
||||
|
||||
private static final String FMT_GOTO_TAG = "goto_" + FMT_TARGET_OFFSET + ":";
|
||||
private static final String FMT_COND_TAG = "cond_" + FMT_TARGET_OFFSET + ":";
|
||||
private static final String FMT_DATA_TAG = "data_" + FMT_TARGET_OFFSET + ":";
|
||||
private static final String FMT_P_SWITCH_TAG = "p_switch_" + FMT_TARGET_OFFSET + ":";
|
||||
private static final String FMT_S_SWITCH_TAG = "s_switch_" + FMT_TARGET_OFFSET + ":";
|
||||
private static final String FMT_P_SWITCH_CASE_TAG = "p_case_" + FMT_TARGET_OFFSET + ":";
|
||||
private static final String FMT_S_SWITCH_CASE_TAG = "s_case_" + FMT_TARGET_OFFSET + ":";
|
||||
|
||||
public static class SmaliGen {
|
||||
static class SmaliLine {
|
||||
Object line;
|
||||
List<Entry<String, String>> tips = Collections.emptyList();
|
||||
|
||||
void setLine(String str) {
|
||||
line = str;
|
||||
}
|
||||
|
||||
void addLine(String str) {
|
||||
if (!(line instanceof List)) {
|
||||
line = new ArrayList<String>();
|
||||
}
|
||||
((ArrayList<String>) this.line).add(str);
|
||||
}
|
||||
|
||||
void addLineTip(String tip, String extra) {
|
||||
if (tips.isEmpty()) {
|
||||
tips = new ArrayList<>();
|
||||
}
|
||||
tips.add(new AbstractMap.SimpleEntry<>(tip, extra));
|
||||
}
|
||||
|
||||
private void fmtLineTip(int lineOffset, SmaliCodeWriter smali) {
|
||||
for (Entry<String, String> tip : tips) {
|
||||
int start = Math.max(0, lineOffset - tip.getKey().length());
|
||||
if (start > 0) {
|
||||
smali.add(new String(new byte[start]).replace("\0", " "));
|
||||
}
|
||||
smali.add(tip.getKey() + tip.getValue()).startLine();
|
||||
}
|
||||
}
|
||||
|
||||
private void gen(int lineOffset, SmaliCodeWriter smali) {
|
||||
fmtLineTip(lineOffset, smali);
|
||||
if (line instanceof List) {
|
||||
int size = ((List<String>) line).size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
smali.add(((List<String>) line).get(i));
|
||||
if (i != size - 1) {
|
||||
smali.startLine();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
smali.add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder lineWriter = new StringBuilder(50);
|
||||
Map<Integer, SmaliLine> targetMap = new HashMap<>();
|
||||
Map<Integer, Integer> payloadOffsetMap = new HashMap<>();
|
||||
Map<Integer, String> paramMap;
|
||||
List<SmaliLine> smaliList = new ArrayList<>();
|
||||
boolean fileOffset;
|
||||
boolean bytecode;
|
||||
boolean hasDbgInfo;
|
||||
|
||||
/**
|
||||
* @param fileOffset adds file offset column to smali output
|
||||
* @param bytecode adds bytecode column to smali output
|
||||
*/
|
||||
public SmaliGen(Map<Integer, String> paramMap, IDebugInfo dbgInfo,
|
||||
boolean fileOffset, boolean bytecode) {
|
||||
this.fileOffset = fileOffset;
|
||||
this.bytecode = bytecode;
|
||||
this.paramMap = paramMap;
|
||||
this.hasDbgInfo = dbgInfo != null;
|
||||
if (hasDbgInfo) {
|
||||
fmtDbgInfo(dbgInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isParamReg(int regNum) {
|
||||
return paramMap.containsKey(regNum);
|
||||
}
|
||||
|
||||
private String getRegName(int regNum) {
|
||||
String text = paramMap.get(regNum);
|
||||
if (text == null || text.isEmpty()) {
|
||||
return "v" + regNum;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public void gen(SmaliCodeWriter smali) {
|
||||
removeDupTips();
|
||||
int lineOffset = getInsnColStart();
|
||||
for (SmaliLine smaliLine : smaliList) {
|
||||
smali.startLine();
|
||||
smaliLine.gen(lineOffset, smali);
|
||||
}
|
||||
}
|
||||
|
||||
public void format(InsnData insnData) {
|
||||
SmaliLine line = targetMap.computeIfAbsent(insnData.getOffset(), k -> new SmaliLine());
|
||||
smaliList.add(line);
|
||||
fmt(insnData, line);
|
||||
}
|
||||
|
||||
private void fmt(InsnData insn, SmaliLine line) {
|
||||
fmtCols(insn);
|
||||
if (!fmtPayloadInsn(insn, line)) {
|
||||
fmtInsn(insn);
|
||||
line.line = lineWriter.toString();
|
||||
}
|
||||
lineWriter.delete(0, lineWriter.length());
|
||||
}
|
||||
|
||||
private void fmtDbgInfo(IDebugInfo dbgInfo) {
|
||||
dbgInfo.getSourceLineMapping().forEach((codeOffset, srcLine) -> {
|
||||
if (codeOffset > -1) {
|
||||
SmaliLine line = targetMap.computeIfAbsent(codeOffset, k -> new SmaliLine());
|
||||
line.addLineTip(String.format(".line %d", srcLine), "");
|
||||
}
|
||||
});
|
||||
for (ILocalVar localVar : dbgInfo.getLocalVars()) {
|
||||
if (localVar.getStartOffset() > -1) {
|
||||
SmaliLine line = targetMap.computeIfAbsent(localVar.getStartOffset(), k -> new SmaliLine());
|
||||
line.addLineTip(String.format(".local v%d", localVar.getRegNum()),
|
||||
String.format(", \"%s\":%s", localVar.getName(), localVar.getType()));
|
||||
}
|
||||
if (localVar.getEndOffset() > -1) {
|
||||
if (isParamReg(localVar.getRegNum())) {
|
||||
return; // no need to add .end local for parameters.
|
||||
}
|
||||
SmaliLine line = targetMap.computeIfAbsent(localVar.getEndOffset(), k -> new SmaliLine());
|
||||
line.addLineTip(String.format(".end local v%d", localVar.getRegNum()),
|
||||
String.format(" # \"%s\":%s", localVar.getName(), localVar.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fmtInsn(InsnData insn) {
|
||||
int opcode = insn.getRawOpcodeUnit();
|
||||
opcode = opcode & 0xff;
|
||||
String mne = DexOpcodes.MNEMONICS[opcode];
|
||||
lineWriter.append(String.format(FMT_INSN_COL, mne)).append(" ");
|
||||
fmtRegs(opcode, insn, lineWriter);
|
||||
if (hasTarget(opcode)) {
|
||||
if (isGotoIns(opcode)) {
|
||||
lineWriter.append(String.format(FMT_GOTO, insn.getTarget()));
|
||||
addTarget(FMT_GOTO_TAG, insn.getTarget());
|
||||
return;
|
||||
}
|
||||
lineWriter.append(", ");
|
||||
if (isConditionIns(opcode)) {
|
||||
lineWriter.append(String.format(FMT_COND, insn.getTarget()));
|
||||
addTarget(FMT_COND_TAG, insn.getTarget());
|
||||
|
||||
} else if (opcode == DexOpcodes.PACKED_SWITCH) {
|
||||
payloadOffsetMap.put(insn.getTarget(), insn.getOffset());
|
||||
lineWriter.append(String.format(FMT_P_SWITCH, insn.getTarget()));
|
||||
addTarget(FMT_P_SWITCH_TAG, insn.getTarget());
|
||||
|
||||
} else if (opcode == DexOpcodes.SPARSE_SWITCH) {
|
||||
payloadOffsetMap.put(insn.getTarget(), insn.getOffset());
|
||||
lineWriter.append(String.format(FMT_S_SWITCH, insn.getTarget()));
|
||||
addTarget(FMT_S_SWITCH_TAG, insn.getTarget());
|
||||
|
||||
} else {
|
||||
lineWriter.append(String.format(FMT_DATA, insn.getTarget()));
|
||||
addTarget(FMT_DATA_TAG, insn.getTarget());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isInvokeIns(opcode)) {
|
||||
lineWriter.append(", ").append(method(insn));
|
||||
return;
|
||||
}
|
||||
if (insn.getIndexType() == InsnIndexType.TYPE_REF) {
|
||||
lineWriter.append(", ").append(type(insn));
|
||||
return;
|
||||
}
|
||||
if (insn.getIndexType() == InsnIndexType.FIELD_REF) {
|
||||
lineWriter.append(", ").append(field(insn));
|
||||
return;
|
||||
}
|
||||
if (insn.getIndexType() == InsnIndexType.STRING_REF) {
|
||||
lineWriter.append(", ").append(str(insn));
|
||||
return;
|
||||
}
|
||||
if (hasLiteral(opcode)) {
|
||||
lineWriter.append(", ").append(literal(insn, opcode));
|
||||
return;
|
||||
}
|
||||
if (opcode == DexOpcodes.CONST_METHOD_HANDLE) {
|
||||
lineWriter.append(", ").append(methodHandle(insn));
|
||||
return;
|
||||
}
|
||||
if (opcode == DexOpcodes.CONST_METHOD_TYPE) {
|
||||
lineWriter.append(", ").append(proto(insn, insn.getIndex()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void addTarget(String fmtTag, int target) {
|
||||
addTarget(fmtTag, target, "");
|
||||
}
|
||||
|
||||
private void addTarget(String fmtTag, int target, String extraTip) {
|
||||
targetMap.computeIfAbsent(target, k -> new SmaliLine())
|
||||
.addLineTip(String.format(fmtTag, target), extraTip);
|
||||
}
|
||||
|
||||
private void fmtRegs(int opcode, InsnData insn, StringBuilder smali) {
|
||||
boolean appendBrace = isRegList(opcode);
|
||||
if (appendBrace) {
|
||||
smali.append("{");
|
||||
}
|
||||
if (isRangeRegIns(opcode)) {
|
||||
smali.append(getRegName(insn.getReg(0)))
|
||||
.append(" .. ")
|
||||
.append(getRegName(insn.getReg(insn.getRegsCount() - 1)));
|
||||
|
||||
} else if (insn.getRegsCount() > 0) {
|
||||
for (int i = 0; i < insn.getRegsCount(); i++) {
|
||||
if (i > 0) {
|
||||
smali.append(", ");
|
||||
}
|
||||
smali.append(getRegName(insn.getReg(i)));
|
||||
}
|
||||
}
|
||||
if (appendBrace) {
|
||||
smali.append("}");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean fmtPayloadInsn(InsnData insn, SmaliLine line) {
|
||||
int opcode = insn.getRawOpcodeUnit();
|
||||
if (opcode == DexOpcodes.PACKED_SWITCH_PAYLOAD) {
|
||||
lineWriter.append("packed-switch-payload");
|
||||
line.addLine(lineWriter.toString());
|
||||
DexSwitchPayload payload = (DexSwitchPayload) insn.getPayload();
|
||||
if (payload != null) {
|
||||
fmtSwitchPayload(FMT_P_SWITCH_CASE, FMT_P_SWITCH_CASE_TAG, line, payload, insn.getOffset());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (opcode == DexOpcodes.SPARSE_SWITCH_PAYLOAD) {
|
||||
lineWriter.append("sparse-switch-payload");
|
||||
line.addLine(lineWriter.toString());
|
||||
DexSwitchPayload payload = (DexSwitchPayload) insn.getPayload();
|
||||
if (payload != null) {
|
||||
fmtSwitchPayload(FMT_S_SWITCH_CASE, FMT_S_SWITCH_CASE_TAG, line, payload, insn.getOffset());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (opcode == DexOpcodes.FILL_ARRAY_DATA_PAYLOAD) {
|
||||
lineWriter.append("fill-array-data-payload");
|
||||
line.setLine(lineWriter.toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void fmtSwitchPayload(String fmtTarget, String fmtTag, SmaliLine line,
|
||||
DexSwitchPayload payload, int curOffset) {
|
||||
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 = SmaliCodeWriter.INDENT_STR + basicIndent;
|
||||
int[] keys = payload.getKeys();
|
||||
int[] targets = payload.getTargets();
|
||||
int opcodeOffset = payloadOffsetMap.get(curOffset);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
int target = opcodeOffset + targets[i];
|
||||
line.addLine(String.format("%scase %d: -> " + fmtTarget, indent, keys[i], target));
|
||||
addTarget(fmtTag, target, String.format(" # case %d", keys[i]));
|
||||
}
|
||||
line.addLine(basicIndent + ".end payload");
|
||||
}
|
||||
|
||||
private void removeDupTips() {
|
||||
List<Entry<Integer, Entry<String, String>>> dbgLines = null; // line num: tip
|
||||
if (hasDbgInfo) {
|
||||
dbgLines = new ArrayList<>();
|
||||
}
|
||||
for (int i = 0; i < smaliList.size(); i++) {
|
||||
SmaliLine line = smaliList.get(i);
|
||||
Map<String, Integer> tipSet = Collections.emptyMap(); // tip: reference count
|
||||
for (Iterator<Entry<String, String>> it = line.tips.iterator(); it.hasNext();) {
|
||||
Entry<String, String> tip = it.next();
|
||||
if (hasDbgInfo && removeDupSourceLine(tip, i, dbgLines)) { // debug info source line.
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
if (tipSet.containsKey(tip.getKey())) { // remove dup tips like cond_:/goto_:.
|
||||
it.remove();
|
||||
tipSet.computeIfPresent(tip.getKey(), (k, v) -> v + 1);
|
||||
} else {
|
||||
if (tipSet.isEmpty()) {
|
||||
tipSet = new HashMap<>();
|
||||
}
|
||||
tipSet.computeIfAbsent(tip.getKey(), k -> 1);
|
||||
}
|
||||
}
|
||||
tipSet.forEach((k, v) -> {
|
||||
if (v > 1) {
|
||||
for (int j = 0; j < line.tips.size(); j++) {
|
||||
if (line.tips.get(j).getKey().equals(k)) {
|
||||
line.tips.set(j, new AbstractMap.SimpleEntry<>(k, " # " + v + " refs"));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private boolean removeDupSourceLine(Entry<String, String> tip, int i,
|
||||
List<Entry<Integer, Entry<String, String>>> dbgLines) {
|
||||
boolean removeIt = false;
|
||||
if (tip.getKey().startsWith(".line ")) { // debug info source line.
|
||||
if (dbgLines.size() > 0) {
|
||||
Entry<Integer, Entry<String, String>> entry = dbgLines.get(dbgLines.size() - 1);
|
||||
if (i - entry.getKey() == 1 && entry.getValue().getKey().equals(tip.getKey())) {
|
||||
removeIt = true; // duplicated.
|
||||
}
|
||||
}
|
||||
dbgLines.add(new AbstractMap.SimpleEntry<>(i, tip));
|
||||
}
|
||||
return removeIt;
|
||||
}
|
||||
|
||||
private int getInsnColStart() {
|
||||
int start = 0;
|
||||
if (fileOffset) {
|
||||
start += 8 + 1 + 1; // plus 1s for space and the ':'
|
||||
}
|
||||
if (bytecode) {
|
||||
start += BYTECODE_COLUMN_WIDTH + 1; // plus 1 for space
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
private void fmtCols(InsnData insn) {
|
||||
if (fileOffset) {
|
||||
lineWriter.append(String.format(FMT_FILE_OFFSET + " ", insn.getFileOffset()));
|
||||
}
|
||||
if (bytecode) {
|
||||
formatByteCode(lineWriter, insn.getByteCode());
|
||||
lineWriter.append(" ");
|
||||
lineWriter.append(String.format(FMT_CODE_OFFSET + " ", insn.getOffset()));
|
||||
}
|
||||
}
|
||||
|
||||
private static 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; i++) {
|
||||
int temp = ((bytes[i++] & 0xff) << 8) | (bytes[i] & 0xff);
|
||||
inHex.append(String.format("%04x ", temp));
|
||||
}
|
||||
smali.append(String.format(FMT_BYTECODE_COL, inHex));
|
||||
if (maxLen < bytes.length) {
|
||||
smali.append("...");
|
||||
} else {
|
||||
smali.append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
private static String literal(InsnData insn, int opcode) {
|
||||
long it = insn.getLiteral();
|
||||
String tip = "";
|
||||
if (it > Integer.MAX_VALUE) {
|
||||
if (isWideIns(opcode)) {
|
||||
tip = " # double: " + Double.longBitsToDouble(it);
|
||||
} else if (opcode == DexOpcodes.CONST_HIGH16) {
|
||||
tip = " # float: " + Float.intBitsToFloat((int) it);
|
||||
}
|
||||
} else if (it <= 0) {
|
||||
return "" + it + tip;
|
||||
}
|
||||
return "0x" + Long.toHexString(it) + tip;
|
||||
}
|
||||
|
||||
private static String str(InsnData insn) {
|
||||
return String.format("\"%s\" # string@%04x",
|
||||
insn.getIndexAsString()
|
||||
.replace("\n", "\\n")
|
||||
.replace("\t", "\\t"),
|
||||
insn.getIndex());
|
||||
}
|
||||
|
||||
private static String type(InsnData insn) {
|
||||
return String.format("%s # type@%04x", insn.getIndexAsType(), insn.getIndex());
|
||||
}
|
||||
|
||||
private static String field(InsnData insn) {
|
||||
return String.format("%s # field@%04x", insn.getIndexAsField().toString(), insn.getIndex());
|
||||
}
|
||||
|
||||
private static String method(InsnData insn) {
|
||||
int rawOpcodeUnit = insn.getRawOpcodeUnit();
|
||||
int opcode = rawOpcodeUnit & 0xFF;
|
||||
if (opcode == DexOpcodes.INVOKE_CUSTOM || opcode == DexOpcodes.INVOKE_CUSTOM_RANGE) {
|
||||
insn.getIndexAsCallSite().load();
|
||||
return String.format("%s # call_site@%04x", insn.getIndexAsCallSite().toString(), insn.getIndex());
|
||||
}
|
||||
IMethodRef mthRef = insn.getIndexAsMethod();
|
||||
mthRef.load();
|
||||
if (opcode == DexOpcodes.INVOKE_POLYMORPHIC || opcode == DexOpcodes.INVOKE_POLYMORPHIC_RANGE) {
|
||||
return String.format("%s, %s # method@%04x, proto@%04x",
|
||||
mthRef.toString(), insn.getIndexAsProto(insn.getTarget()).toString(),
|
||||
insn.getIndex(), insn.getTarget());
|
||||
}
|
||||
return String.format("%s # method@%04x", mthRef.toString(), insn.getIndex());
|
||||
}
|
||||
|
||||
private static String proto(InsnData insn, int protoIndex) {
|
||||
return String.format("%s # proto@%04x", insn.getIndexAsProto(protoIndex).toString(), protoIndex);
|
||||
}
|
||||
|
||||
private static String methodHandle(InsnData insn) {
|
||||
return String.format("%s # method_handle@%04x",
|
||||
insn.getIndexAsMethodHandle().toString(), insn.getIndex());
|
||||
}
|
||||
|
||||
private static boolean isGotoIns(int opcode) {
|
||||
return opcode >= DexOpcodes.GOTO && opcode <= DexOpcodes.GOTO_32;
|
||||
}
|
||||
|
||||
private static boolean isInvokeIns(int opcode) {
|
||||
return (opcode >= DexOpcodes.INVOKE_VIRTUAL && opcode <= DexOpcodes.INVOKE_INTERFACE)
|
||||
|| (opcode >= DexOpcodes.INVOKE_VIRTUAL_RANGE && opcode <= DexOpcodes.INVOKE_INTERFACE_RANGE)
|
||||
|| (opcode >= DexOpcodes.INVOKE_POLYMORPHIC && opcode <= DexOpcodes.INVOKE_CUSTOM_RANGE);
|
||||
}
|
||||
|
||||
private static boolean isRangeRegIns(int opcode) {
|
||||
if (opcode >= DexOpcodes.INVOKE_VIRTUAL_RANGE && opcode <= DexOpcodes.INVOKE_INTERFACE_RANGE) {
|
||||
return true;
|
||||
}
|
||||
switch (opcode) {
|
||||
case DexOpcodes.FILLED_NEW_ARRAY_RANGE:
|
||||
case DexOpcodes.INVOKE_CUSTOM_RANGE:
|
||||
case DexOpcodes.INVOKE_POLYMORPHIC_RANGE:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isWideIns(int opcode) {
|
||||
return (opcode >= DexOpcodes.CONST_WIDE_16 && opcode <= DexOpcodes.CONST_WIDE_HIGH16);
|
||||
}
|
||||
|
||||
private static boolean hasLiteral(int opcode) {
|
||||
return (opcode >= DexOpcodes.CONST_4 && opcode <= DexOpcodes.CONST_WIDE_HIGH16)
|
||||
|| (opcode >= DexOpcodes.ADD_INT_LIT16 && opcode <= DexOpcodes.USHR_INT_LIT8);
|
||||
}
|
||||
|
||||
private static boolean isConditionIns(int opcode) {
|
||||
return opcode >= DexOpcodes.IF_EQ && opcode <= DexOpcodes.IF_LEZ;
|
||||
}
|
||||
|
||||
private static boolean hasTarget(int opcode) {
|
||||
return (opcode >= DexOpcodes.IF_EQ && opcode <= DexOpcodes.IF_LEZ)
|
||||
|| (opcode >= DexOpcodes.GOTO && opcode <= DexOpcodes.SPARSE_SWITCH)
|
||||
|| (opcode == DexOpcodes.FILL_ARRAY_DATA);
|
||||
}
|
||||
|
||||
private static boolean isRegList(int opcode) {
|
||||
return isInvokeIns(opcode)
|
||||
|| (opcode >= DexOpcodes.FILLED_NEW_ARRAY && opcode <= DexOpcodes.FILLED_NEW_ARRAY_RANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -31,4 +31,6 @@ public interface IClassData {
|
||||
List<IAnnotation> getAnnotations();
|
||||
|
||||
String getDisassembledCode();
|
||||
|
||||
String getDisassembledCodeV2();
|
||||
}
|
||||
|
||||
+5
@@ -34,4 +34,9 @@ public class FieldRefHandle implements IMethodHandle {
|
||||
public void load() {
|
||||
// already loaded
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type + ": " + fieldRef;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-4
@@ -2,19 +2,21 @@ package jadx.api.plugins.input.insns;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.plugins.input.data.ICallSite;
|
||||
import jadx.api.plugins.input.data.IFieldData;
|
||||
import jadx.api.plugins.input.data.IMethodRef;
|
||||
import jadx.api.plugins.input.data.*;
|
||||
import jadx.api.plugins.input.insns.custom.ICustomPayload;
|
||||
|
||||
public interface InsnData {
|
||||
|
||||
void decode();
|
||||
|
||||
int getOffset();
|
||||
int getOffset(); // offset within method
|
||||
|
||||
int getFileOffset(); // offset within dex file
|
||||
|
||||
Opcode getOpcode();
|
||||
|
||||
byte[] getByteCode();
|
||||
|
||||
InsnIndexType getIndexType();
|
||||
|
||||
int getRawOpcodeUnit();
|
||||
@@ -39,6 +41,10 @@ public interface InsnData {
|
||||
|
||||
ICallSite getIndexAsCallSite();
|
||||
|
||||
IMethodProto getIndexAsProto(int protoIndex);
|
||||
|
||||
IMethodHandle getIndexAsMethodHandle();
|
||||
|
||||
@Nullable
|
||||
ICustomPayload getPayload();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user