diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..82c2e4d --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,378 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/ghidrust/analyzer/RustStdAnalyzer.java b/src/main/java/ghidrust/analyzer/RustStdAnalyzer.java index 3e8583d..424d724 100644 --- a/src/main/java/ghidrust/analyzer/RustStdAnalyzer.java +++ b/src/main/java/ghidrust/analyzer/RustStdAnalyzer.java @@ -13,34 +13,50 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package ghidrust.analyzer; -import generic.jar.ResourceFile; import ghidra.app.services.AbstractAnalyzer; import ghidra.app.services.AnalysisPriority; import ghidra.app.services.AnalyzerType; import ghidra.app.util.importer.MessageLog; + import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.Program; + import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; + import ghidra.feature.fid.db.FidFileManager; import ghidra.framework.Application; import java.io.IOException; +import generic.jar.ResourceFile; +/** + * Ghidra analyzer plugin to find whether the program is a Rust binary. + */ public class RustStdAnalyzer extends AbstractAnalyzer { - private static final byte[][] rust_artifacts = { + private static final byte[][] rustArtifacts = { "run with `RUST_BACKTRACE=1` environment variable".getBytes(), "called `Option::unwrap()` on a `None` value".getBytes(), "called `Result::unwrap()` on an `Err` value".getBytes() }; private static final String ENABLED_PROPERTY = "DecompilerParameterAnalyzer.enabled"; + /** + * General plugin initialization. + */ public RustStdAnalyzer() { super("Detect Rust libstd functions", - "Detects Rust standard library functions from saved signatures and saves analysis time.\n\nProvided by GhidRust", + // CHECKSTYLE:OFF + """ + Detects Rust standard library functions from saved signatures and saves analysis time. + + Provided by GhidRust + """, + // CHECKSTYLE:ON AnalyzerType.FUNCTION_ANALYZER); /* @@ -56,8 +72,8 @@ public class RustStdAnalyzer extends AbstractAnalyzer { @Override public boolean getDefaultEnablement(Program program) { // Make sure the property has not been disabled - String default_enabled = System.getProperty(ENABLED_PROPERTY); - if (default_enabled != null && !Boolean.parseBoolean(default_enabled)) { + String defaultEnabled = System.getProperty(ENABLED_PROPERTY); + if (defaultEnabled != null && !Boolean.parseBoolean(defaultEnabled)) { return false; } @@ -81,15 +97,15 @@ public class RustStdAnalyzer extends AbstractAnalyzer { return false; } - ResourceFile data_dir; + ResourceFile dataDir; try { - data_dir = Application.getModuleDataSubDirectory(""); + dataDir = Application.getModuleDataSubDirectory(""); } catch (IOException exc) { log.appendException(exc); return false; } - ResourceFile[] libs = data_dir.listFiles(); + ResourceFile[] libs = dataDir.listFiles(); for (ResourceFile lib : libs) { monitor.checkCanceled(); ffm.addUserFidFile(lib.getFile(true)); @@ -103,7 +119,13 @@ public class RustStdAnalyzer extends AbstractAnalyzer { super.analysisEnded(program); } - /* For exposing the Rust checking code */ + /** + * For exposing the Rust checking code. This can be used as an library call + * by any other plugin relying on this plugin. + * + * @param program The program being analyzed. + * @return True if it is a Rust binaru, false otherwise. + */ public static boolean isRustBinary(Program program) { /* * Taken from @@ -111,10 +133,12 @@ public class RustStdAnalyzer extends AbstractAnalyzer { * with-rust.yml */ - Address start_search = program.getMinAddress(); - for (byte[] search_string : rust_artifacts) { - Address found_addr = program.getMemory().findBytes(start_search, search_string, null, true, null); - if (found_addr != null) { + Address startSearch = program.getMinAddress(); + for (byte[] searchString : rustArtifacts) { + Address foundAddr = program.getMemory().findBytes( + startSearch, searchString, null, true, null + ); + if (foundAddr != null) { return true; } } diff --git a/src/main/java/ghidrust/decompiler/RustDecPlugin.java b/src/main/java/ghidrust/decompiler/RustDecPlugin.java index e7c0fd0..aee579d 100644 --- a/src/main/java/ghidrust/decompiler/RustDecPlugin.java +++ b/src/main/java/ghidrust/decompiler/RustDecPlugin.java @@ -13,12 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package ghidrust.decompiler; +import ghidra.app.events.ProgramActivatedPluginEvent; +import ghidra.app.events.ProgramClosedPluginEvent; +import ghidra.app.events.ProgramLocationPluginEvent; import ghidra.app.plugin.PluginCategoryNames; -import docking.ActionContext; +import ghidra.app.DeveloperPluginPackage; + import docking.action.DockingAction; -import ghidra.app.events.*; +import docking.action.MenuData; +import docking.ActionContext; + import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginTool; @@ -29,39 +36,51 @@ import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; import ghidra.util.Msg; import ghidrust.analyzer.RustStdAnalyzer; -import docking.action.MenuData; -//@formatter:off +// @formatter:off +/** + * Add the PluginInfo annotation for the Rust decompiler plugin. + */ @PluginInfo( - status = PluginStatus.STABLE, - packageName = "HELLO", - category = PluginCategoryNames.ANALYSIS, - shortDescription = "Rust Decompiler", - description = "Decompile Rust binaries' assembly to Rust code", + status = PluginStatus.STABLE, + packageName = DeveloperPluginPackage.NAME, + category = PluginCategoryNames.ANALYSIS, + shortDescription = "Rust Decompiler", + description = "Decompile Rust binaries' assembly to Rust code", eventsConsumed = { - ProgramActivatedPluginEvent.class, ProgramLocationPluginEvent.class, ProgramClosedPluginEvent.class - } + ProgramActivatedPluginEvent.class, + ProgramLocationPluginEvent.class, + ProgramClosedPluginEvent.class + } ) -//@formatter:on +// @formatter:on + public class RustDecPlugin extends Plugin { Program program; RustDecProvider provider; + /** + * Adds docking actions for opening the decompiler and checking whether the + * binary is a Rust binary. + * + * @param tool PluginTool instance responsible for managing RustDecPlugin. + */ public RustDecPlugin(PluginTool tool) { super(tool); provider = new RustDecProvider(this, getName(), null); - DockingAction dec_plugin = new DockingAction("GhidRust", getName()) { + DockingAction decPlugin = new DockingAction("GhidRust", getName()) { @Override public void actionPerformed(ActionContext context) { - provider.activateProvider();; + provider.activateProvider(); + ; } }; - dec_plugin.setEnabled(true); - dec_plugin.setMenuBarData(new MenuData(new String[] { "GhidRust", "Open decompiler" })); + decPlugin.setEnabled(true); + decPlugin.setMenuBarData(new MenuData(new String[] { "GhidRust", "Open decompiler" })); - DockingAction check_plugin = new DockingAction("GhidRust", getName()) { + DockingAction checkPlugin = new DockingAction("GhidRust", getName()) { @Override public void actionPerformed(ActionContext context) { if (RustStdAnalyzer.isRustBinary(program)) { @@ -72,11 +91,13 @@ public class RustDecPlugin extends Plugin { } }; - check_plugin.setEnabled(true); - check_plugin.setMenuBarData(new MenuData(new String[] { "GhidRust", "Check if Rust binary" })); + checkPlugin.setEnabled(true); + checkPlugin.setMenuBarData(new MenuData( + new String[] { "GhidRust", "Check if Rust binary" } + )); - tool.addAction(dec_plugin); - tool.addAction(check_plugin); + tool.addAction(decPlugin); + tool.addAction(checkPlugin); } @Override diff --git a/src/main/java/ghidrust/decompiler/RustDecProvider.java b/src/main/java/ghidrust/decompiler/RustDecProvider.java index 5f00e22..b98b4af 100644 --- a/src/main/java/ghidrust/decompiler/RustDecProvider.java +++ b/src/main/java/ghidrust/decompiler/RustDecProvider.java @@ -6,6 +6,8 @@ import ghidra.program.model.listing.Function; import ghidra.app.decompiler.DecompInterface; import ghidra.app.decompiler.DecompileResults; +import ghidrust.decompiler.parser.c.CFormatter; +import ghidrust.decompiler.parser.c.gen.CParser; import javax.swing.JComponent; import javax.swing.JLabel; @@ -24,26 +26,35 @@ import java.awt.event.ActionEvent; import docking.ComponentProvider; import ghidra.util.task.ConsoleTaskMonitor; import resources.ResourceManager; -import ghidrust.decompiler.parser.c.CFormatter; -import ghidrust.decompiler.parser.c.gen.CParser; +/** + * Responsible for decompiling and showing the decompiled code in a window. + */ public class RustDecProvider extends ComponentProvider { private JPanel panel; - private JTextArea code_area; - private JLabel func_title; + private JTextArea codeArea; + private JLabel funcTitle; private Program prog; private Address addr; - private DecompInterface decomp_ifc = null; + private DecompInterface decompInterface = null; private static final String EMPTY_LABEL = ""; + /** + * Initialize the provider by creating a decompilation interface and the + * decompilation window. + * + * @param plugin Calling plugin, which in our case would be RustDecPlugin. + * @param owner Owner of the plugin. + * @param p Program on which this plugin is being used. + */ public RustDecProvider(RustDecPlugin plugin, String owner, Program p) { super(plugin.getTool(), owner, owner); setIcon(ResourceManager.loadImage("images/icon.png")); - decomp_ifc = new DecompInterface(); + decompInterface = new DecompInterface(); setProgram(p); buildPanel(); @@ -57,7 +68,7 @@ public class RustDecProvider extends ComponentProvider { private void buildPanel() { panel = new JPanel(new BorderLayout()); - func_title = new JLabel(EMPTY_LABEL); + funcTitle = new JLabel(EMPTY_LABEL); JButton reload = new JButton(ResourceManager.loadImage("images/reload.png")); reload.addActionListener(new ActionListener() { @@ -68,14 +79,14 @@ public class RustDecProvider extends ComponentProvider { JToolBar toolbar = new JToolBar("GhidRust", JToolBar.HORIZONTAL); toolbar.setFloatable(false); - toolbar.add(func_title); + toolbar.add(funcTitle); toolbar.add(Box.createHorizontalGlue()); toolbar.add(reload); - code_area = new JTextArea(); - code_area.setEditable(false); + codeArea = new JTextArea(); + codeArea.setEditable(false); - JScrollPane scroll = new JScrollPane(code_area); + JScrollPane scroll = new JScrollPane(codeArea); scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); panel.add(toolbar, BorderLayout.PAGE_START); @@ -86,12 +97,18 @@ public class RustDecProvider extends ComponentProvider { setVisible(true); } + /** + * We save the program in a private class variable to be used later, and + * open it using the decompiler interface. + * + * @param p program to be decompiled. + */ public void setProgram(Program p) { prog = p; - decomp_ifc.closeProgram(); + decompInterface.closeProgram(); if (prog != null) { - decomp_ifc.openProgram(prog); + decompInterface.openProgram(prog); } } @@ -99,48 +116,54 @@ public class RustDecProvider extends ComponentProvider { addr = a; } + /** + * When reload is called, we trigger the decompilation. + */ public void reload() { if (prog == null) { - func_title.setText(EMPTY_LABEL); - code_area.setText("[?] Open a program to see the decompilation!\n"); + funcTitle.setText(EMPTY_LABEL); + codeArea.setText("[?] Open a program to see the decompilation!\n"); return; } if (addr == null) { - func_title.setText(EMPTY_LABEL); - code_area.setText("[?] Select a memory location to decompile!\n"); + funcTitle.setText(EMPTY_LABEL); + codeArea.setText("[?] Select a memory location to decompile!\n"); return; } Function func = prog.getFunctionManager().getFunctionContaining(addr); if (func == null) { - func_title.setText(EMPTY_LABEL); - code_area.setText("[!] No function found at " + addr.toString() + "\n"); + funcTitle.setText(EMPTY_LABEL); + codeArea.setText("[!] No function found at " + addr.toString() + "\n"); return; } - func_title.setText(func.getName()); + funcTitle.setText(func.getName()); - if (decomp_ifc == null) { - code_area.setText("[!] Decompiler has not been initialized!\n"); + if (decompInterface == null) { + codeArea.setText("[!] Decompiler has not been initialized!\n"); return; } - DecompileResults results = decomp_ifc.decompileFunction(func, 0, new ConsoleTaskMonitor()); - if (results == null || results.getDecompiledFunction() == null || results.getDecompiledFunction().getC() == null) { - code_area.setText("[!] Failed to decompile " + func.getName() + "\n"); + DecompileResults results = decompInterface.decompileFunction( + func, 0, new ConsoleTaskMonitor() + ); + if (results == null || results.getDecompiledFunction() == null + || results.getDecompiledFunction().getC() == null) { + codeArea.setText("[!] Failed to decompile " + func.getName() + "\n"); return; } String decompiled = results.getDecompiledFunction().getC(); - String rust_code = ""; + String rustCode = ""; try { - rust_code = CFormatter.format(CParser.transpile(decompiled)); + rustCode = CFormatter.format(CParser.transpile(decompiled)); } catch (Exception e) { - rust_code = "/* [!] Failed to transpile " + func.getName() + " */\n" + decompiled; + rustCode = "/* [!] Failed to transpile " + func.getName() + " */\n" + decompiled; } - code_area.setText(rust_code); + codeArea.setText(rustCode); } } diff --git a/src/main/java/ghidrust/decompiler/parser/Run.java b/src/main/java/ghidrust/decompiler/parser/Run.java index cae1225..34a7c7c 100644 --- a/src/main/java/ghidrust/decompiler/parser/Run.java +++ b/src/main/java/ghidrust/decompiler/parser/Run.java @@ -3,6 +3,9 @@ package ghidrust.decompiler.parser; import ghidrust.decompiler.parser.c.gen.CParser; import ghidrust.decompiler.parser.c.CFormatter; +/** + * Run the transpiler as a command line standalone tool (for testing). + */ public class Run { public static void main(String[] args) { System.out.println(CFormatter.format(CParser.transpile(System.in))); diff --git a/src/main/java/ghidrust/decompiler/parser/c/CFormatter.java b/src/main/java/ghidrust/decompiler/parser/c/CFormatter.java index e3c2a91..2be10a3 100644 --- a/src/main/java/ghidrust/decompiler/parser/c/CFormatter.java +++ b/src/main/java/ghidrust/decompiler/parser/c/CFormatter.java @@ -1,7 +1,10 @@ package ghidrust.decompiler.parser.c; +/** + * Format decompiled code. + */ public class CFormatter { - static int indent_level; + static int indentLevel; static String indent(int level) { StringBuffer sb = new StringBuffer(""); @@ -13,38 +16,44 @@ public class CFormatter { return sb.toString(); } - public CFormatter(int initial_indent) { - indent_level = 0; + public CFormatter(int initialIndent) { + indentLevel = initialIndent; } + /** + * Format the code being passed in by adding newlines and semicolons. + * + * @param code Code as a string. + * @return Formatted code. + */ public static String format(String code) { StringBuffer pretty = new StringBuffer(""); int str_len = code.length(); - pretty.append(indent(indent_level)); + pretty.append(indent(indentLevel)); boolean disable = false; for (int i = 0; i < str_len; i++) { if (code.charAt(i) == '{') { pretty.append("{\n"); - indent_level++; - pretty.append(indent(indent_level)); + indentLevel++; + pretty.append(indent(indentLevel)); } else if (code.charAt(i) == '}') { - indent_level--; + indentLevel--; if (code.charAt(i - 1) != ';') { pretty.append("\n"); - pretty.append(indent(indent_level)); + pretty.append(indent(indentLevel)); } else { pretty.deleteCharAt(pretty.length() - 1); } pretty.append("}"); if (!(i + 1 < str_len && code.charAt(i + 1) == ' ')) { pretty.append("\n"); - pretty.append(indent(indent_level)); + pretty.append(indent(indentLevel)); } } else if (code.charAt(i) == ';') { pretty.append(";\n"); - pretty.append(indent(indent_level)); + pretty.append(indent(indentLevel)); } else if (code.charAt(i) == '@') { /* special character to denote no action for next char */ i++; diff --git a/src/main/java/ghidrust/decompiler/parser/c/CVisitor.java b/src/main/java/ghidrust/decompiler/parser/c/CVisitor.java index 661cca0..4cf8f6d 100644 --- a/src/main/java/ghidrust/decompiler/parser/c/CVisitor.java +++ b/src/main/java/ghidrust/decompiler/parser/c/CVisitor.java @@ -1,5 +1,6 @@ package ghidrust.decompiler.parser.c; +// CHECKSTYLE:OFF import ghidrust.decompiler.parser.c.gen.*; import java.util.HashMap; @@ -453,6 +454,8 @@ public class CVisitor implements CParserVisitor { return defaultSpacedVisit(node, data, ", ", false); } } +// CHECKSTYLE:ON + /* * JavaCC - OriginalChecksum=fd39d82df2a1b516298b94d6f4a5e997 (do not edit this * line) diff --git a/src/main/java/ghidrust/decompiler/parser/c/gen/c.jj b/src/main/java/ghidrust/decompiler/parser/c/gen/c.jj index 74b636e..797b05e 100644 --- a/src/main/java/ghidrust/decompiler/parser/c/gen/c.jj +++ b/src/main/java/ghidrust/decompiler/parser/c/gen/c.jj @@ -1995,4 +1995,4 @@ void Constant() : } } /*@egen*/ -} \ No newline at end of file +}