diff --git a/GhidraPlugin/README.md b/GhidraPlugin/README.md new file mode 100644 index 00000000..faa75105 --- /dev/null +++ b/GhidraPlugin/README.md @@ -0,0 +1,169 @@ +# Symbol Rename Logger Plugin + +A Ghidra plugin that monitors and logs all symbol and function rename events to the console. This plugin is useful for tracking changes to your reverse engineering work and understanding how symbols are being modified during analysis. + +## Features + +- **Symbol Rename Monitoring**: Logs when any symbol is renamed, including the old and new names +- **Function Rename Monitoring**: Specifically tracks function renames with additional details like function signatures +- **Symbol Addition/Removal**: Optionally logs when symbols are added or removed +- **Detailed Logging**: Provides context including symbol type, namespace, and address information +- **Real-time Monitoring**: Events are logged as they happen during your analysis session + +## Build Instructions + +### Prerequisites + +- Java 17 or higher +- Ghidra 11.x installed +- Gradle (or use the wrapper) + +### Building the Plugin + +1. Set the `GHIDRA_INSTALL_DIR` environment variable to your Ghidra installation directory: + ```bash + export GHIDRA_INSTALL_DIR="/path/to/ghidra_11.x.x_PUBLIC" + ``` + +2. Build the plugin: + ```bash + cd GhidraPlugin + ./gradlew build + ``` + +3. The built plugin JAR will be located at `build/libs/GhidraPlugin.jar` + +## Installation + +### Method 1: Install from Ghidra + +1. Open Ghidra +2. Go to **File** → **Install Extensions...** +3. Click the **+** button to add a new extension +4. Navigate to and select the built `GhidraPlugin.jar` file +5. Restart Ghidra when prompted + +### Method 2: Manual Installation + +1. Copy the built JAR file to your Ghidra user directory: + ```bash + cp build/libs/GhidraPlugin.jar ~/ghidra_scripts/ + ``` + +2. Or copy to the Ghidra Extensions directory: + ```bash + cp build/libs/GhidraPlugin.jar $GHIDRA_INSTALL_DIR/Ghidra/Extensions/ + ``` + +## Usage + +### Enabling the Plugin + +1. Open a program in Ghidra's CodeBrowser +2. Go to **File** → **Configure...** +3. In the Configure Tool dialog, navigate to **Misc** category +4. Check the box next to **SymbolRenameLoggerPlugin** +5. Click **OK** + +### Viewing Logs + +The plugin logs all events to Ghidra's console. To view the logs: + +1. Go to **Window** → **Console** +2. The console will show messages like: + ``` + INFO REPORT: SymbolRenameLoggerPlugin initialized + INFO REPORT: Started listening for rename events in program: example.exe + INFO REPORT: SYMBOL RENAMED: 'FUN_00401000' -> 'main' at address 00401000 + INFO REPORT: Symbol type: Function, Namespace: Global + INFO REPORT: Function signature: undefined main(void) + ``` + +### Log Message Types + +The plugin generates several types of log messages: + +- **SYMBOL RENAMED**: When any symbol is renamed +- **FUNCTION RENAMED**: Specific function rename events with signatures +- **SYMBOL ADDED**: When new symbols are created +- **SYMBOL REMOVED**: When symbols are deleted + +Each message includes: +- Old and new symbol names +- Memory address +- Symbol type (Function, Label, etc.) +- Namespace information +- Function signatures (for functions) + +## Example Output + +``` +INFO REPORT: SYMBOL RENAMED: 'FUN_00401000' -> 'main' at address 00401000 +INFO REPORT: Symbol type: Function, Namespace: Global +INFO REPORT: Function signature: undefined main(void) + +INFO REPORT: SYMBOL RENAMED: 'DAT_00403000' -> 'g_config' at address 00403000 +INFO REPORT: Symbol type: Label, Namespace: Global + +INFO REPORT: FUNCTION RENAMED: 'FUN_00401234' -> 'initialize_system' at address 00401234 +INFO REPORT: Function signature: void initialize_system(int param_1) +``` + +## Development + +### Project Structure + +``` +GhidraPlugin/ +├── build.gradle # Build configuration +├── extension.properties # Plugin metadata +├── README.md # This file +└── src/main/java/symbollogger/ + └── SymbolRenameLoggerPlugin.java # Main plugin class +``` + +### Customization + +You can modify the plugin behavior by editing `SymbolRenameLoggerPlugin.java`: + +- **Filter events**: Add conditions in `handleProgramChange()` to only log certain types of symbols +- **Change log format**: Modify the `String.format()` calls in the handler methods +- **Add more details**: Extend the symbol information gathering in `handleSymbolRenamed()` +- **Export to file**: Modify the logging to write to files instead of console + +### Event Types Monitored + +The plugin currently monitors these Ghidra program events: + +- `ProgramEvent.SYMBOL_RENAMED` +- `ProgramEvent.FUNCTION_CHANGED` +- `ProgramEvent.SYMBOL_ADDED` +- `ProgramEvent.SYMBOL_REMOVED` + +You can add monitoring for additional events by modifying the `handleProgramChange()` method. + +## Troubleshooting + +### Plugin Not Loading + +- Ensure Java 17+ is being used +- Check that `GHIDRA_INSTALL_DIR` is set correctly +- Verify the plugin JAR is in the correct directory +- Check Ghidra's console for error messages + +### No Log Messages + +- Ensure the plugin is enabled in the tool configuration +- Check that the Console window is open (**Window** → **Console**) +- Verify that a program is loaded and active +- Try renaming a symbol to test if events are being captured + +### Build Errors + +- Ensure all Ghidra JAR dependencies are accessible +- Check that the Ghidra installation is complete +- Verify Java version compatibility + +## License + +This plugin is provided as-is for educational and research purposes. \ No newline at end of file diff --git a/GhidraPlugin/build.gradle b/GhidraPlugin/build.gradle new file mode 100644 index 00000000..328f0f4e --- /dev/null +++ b/GhidraPlugin/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'java' +} + +sourceCompatibility = JavaVersion.VERSION_17 +targetCompatibility = JavaVersion.VERSION_17 + +repositories { + flatDir { + dirs System.getProperty('GHIDRA_INSTALL_DIR') + '/Ghidra/Features/Base/lib' + dirs System.getProperty('GHIDRA_INSTALL_DIR') + '/Ghidra/Framework/SoftwareModeling/lib' + dirs System.getProperty('GHIDRA_INSTALL_DIR') + '/Ghidra/Framework/Docking/lib' + dirs System.getProperty('GHIDRA_INSTALL_DIR') + '/Ghidra/Framework/Generic/lib' + dirs System.getProperty('GHIDRA_INSTALL_DIR') + '/Ghidra/Framework/Project/lib' + dirs System.getProperty('GHIDRA_INSTALL_DIR') + '/Ghidra/Framework/Utility/lib' + dirs System.getProperty('GHIDRA_INSTALL_DIR') + '/Ghidra/Framework/Gui/lib' + } +} + +dependencies { + implementation name: 'Base', version: '' + implementation name: 'SoftwareModeling', version: '' + implementation name: 'Docking', version: '' + implementation name: 'Generic', version: '' + implementation name: 'Project', version: '' + implementation name: 'Utility', version: '' + implementation name: 'Gui', version: '' +} + +compileJava { + dependsOn configurations.compileClasspath +} + +jar { + manifest { + attributes( + 'Implementation-Title': 'Symbol Rename Logger Plugin', + 'Implementation-Version': '1.0' + ) + } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} \ No newline at end of file diff --git a/GhidraPlugin/extension.properties b/GhidraPlugin/extension.properties new file mode 100644 index 00000000..999fb499 --- /dev/null +++ b/GhidraPlugin/extension.properties @@ -0,0 +1,5 @@ +name=@extname@ +description=A plugin that logs symbol and function rename events +author=Your Name +createdOn= +version=@extversion@ \ No newline at end of file diff --git a/GhidraPlugin/src/main/java/symbollogger/SymbolRenameLoggerPlugin.java b/GhidraPlugin/src/main/java/symbollogger/SymbolRenameLoggerPlugin.java new file mode 100644 index 00000000..774cd317 --- /dev/null +++ b/GhidraPlugin/src/main/java/symbollogger/SymbolRenameLoggerPlugin.java @@ -0,0 +1,198 @@ +package symbollogger; + +import ghidra.app.plugin.PluginCategoryNames; +import ghidra.app.plugin.ProgramPlugin; +import ghidra.framework.plugintool.PluginInfo; +import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramChangeRecord; +import ghidra.program.util.ProgramEvent; +import ghidra.util.Msg; +import ghidra.framework.model.*; +import ghidra.program.model.symbol.*; +import ghidra.program.model.listing.Function; +import ghidra.program.util.*; + +@PluginInfo( + status = PluginStatus.STABLE, + packageName = "SymbolLogger", + category = PluginCategoryNames.MISC, + shortDescription = "Logs symbol and function renames", + description = "A plugin that monitors and logs all symbol and function rename events to the console", + servicesRequired = {}, + servicesProvided = {} +) +public class SymbolRenameLoggerPlugin extends ProgramPlugin implements DomainObjectListener { + + public SymbolRenameLoggerPlugin(PluginTool tool) { + super(tool); + } + + @Override + protected void init() { + super.init(); + Msg.info(this, "SymbolRenameLoggerPlugin initialized"); + } + + @Override + protected void programActivated(Program program) { + super.programActivated(program); + if (program != null) { + program.addListener(this); + Msg.info(this, "Started listening for rename events in program: " + program.getName()); + } + } + + @Override + protected void programDeactivated(Program program) { + if (program != null) { + program.removeListener(this); + Msg.info(this, "Stopped listening for rename events in program: " + program.getName()); + } + super.programDeactivated(program); + } + + @Override + public void domainObjectChanged(DomainObjectChangedEvent ev) { + for (DomainObjectChangeRecord record : ev) { + if (record instanceof ProgramChangeRecord) { + ProgramChangeRecord progRecord = (ProgramChangeRecord) record; + handleProgramChange(progRecord); + } + } + } + + private void handleProgramChange(ProgramChangeRecord record) { + ProgramEvent eventType = record.getEventType(); + + // Handle symbol rename events + if (eventType == ProgramEvent.SYMBOL_RENAMED) { + handleSymbolRenamed(record); + } + // Handle function rename events (functions are also symbols, but we can be more specific) + else if (eventType == ProgramEvent.FUNCTION_CHANGED) { + handleFunctionChanged(record); + } + // Handle symbol added events (in case we want to track new symbols) + else if (eventType == ProgramEvent.SYMBOL_ADDED) { + handleSymbolAdded(record); + } + // Handle symbol removed events + else if (eventType == ProgramEvent.SYMBOL_REMOVED) { + handleSymbolRemoved(record); + } + } + + private void handleSymbolRenamed(ProgramChangeRecord record) { + Object newValue = record.getNewValue(); + Object oldValue = record.getOldValue(); + + String newName = (newValue != null) ? newValue.toString() : "null"; + String oldName = (oldValue != null) ? oldValue.toString() : "null"; + + Msg.info(this, String.format("SYMBOL RENAMED: '%s' -> '%s' at address %s", + oldName, newName, record.getStart())); + + // Try to get additional information about the symbol + try { + Program program = getCurrentProgram(); + if (program != null) { + SymbolTable symbolTable = program.getSymbolTable(); + Symbol[] symbols = symbolTable.getSymbols(record.getStart()); + + for (Symbol symbol : symbols) { + if (symbol.getName().equals(newName)) { + String symbolType = getSymbolTypeDescription(symbol); + Msg.info(this, String.format(" Symbol type: %s, Namespace: %s", + symbolType, symbol.getParentNamespace().getName())); + + // If it's a function, log additional function details + if (symbol.getSymbolType() == SymbolType.FUNCTION) { + Function function = (Function) symbol.getObject(); + if (function != null) { + Msg.info(this, String.format(" Function signature: %s", + function.getPrototypeString(false, false))); + } + } + break; + } + } + } + } catch (Exception e) { + Msg.error(this, "Error getting symbol details: " + e.getMessage()); + } + } + + private void handleFunctionChanged(ProgramChangeRecord record) { + // This handles function-specific changes including renames + Object newValue = record.getNewValue(); + Object oldValue = record.getOldValue(); + + if (newValue instanceof Function && oldValue instanceof Function) { + Function newFunc = (Function) newValue; + Function oldFunc = (Function) oldValue; + + if (!newFunc.getName().equals(oldFunc.getName())) { + Msg.info(this, String.format("FUNCTION RENAMED: '%s' -> '%s' at address %s", + oldFunc.getName(), newFunc.getName(), newFunc.getEntryPoint())); + Msg.info(this, String.format(" Function signature: %s", + newFunc.getPrototypeString(false, false))); + } + } + } + + private void handleSymbolAdded(ProgramChangeRecord record) { + Object newValue = record.getNewValue(); + if (newValue instanceof Symbol) { + Symbol symbol = (Symbol) newValue; + String symbolType = getSymbolTypeDescription(symbol); + Msg.info(this, String.format("SYMBOL ADDED: '%s' (%s) at address %s", + symbol.getName(), symbolType, symbol.getAddress())); + } + } + + private void handleSymbolRemoved(ProgramChangeRecord record) { + Object oldValue = record.getOldValue(); + if (oldValue instanceof Symbol) { + Symbol symbol = (Symbol) oldValue; + String symbolType = getSymbolTypeDescription(symbol); + Msg.info(this, String.format("SYMBOL REMOVED: '%s' (%s) at address %s", + symbol.getName(), symbolType, symbol.getAddress())); + } + } + + private String getSymbolTypeDescription(Symbol symbol) { + SymbolType type = symbol.getSymbolType(); + switch (type) { + case FUNCTION: + return "Function"; + case LABEL: + return "Label"; + case CLASS: + return "Class"; + case NAMESPACE: + return "Namespace"; + case PARAMETER: + return "Parameter"; + case LOCAL_VAR: + return "Local Variable"; + case GLOBAL_VAR: + return "Global Variable"; + case LIBRARY: + return "Library"; + default: + return type.toString(); + } + } + + @Override + protected void dispose() { + Program program = getCurrentProgram(); + if (program != null) { + program.removeListener(this); + } + super.dispose(); + Msg.info(this, "SymbolRenameLoggerPlugin disposed"); + } +} \ No newline at end of file