├── .gitignore ├── LICENSE ├── Module.manifest ├── README.md ├── build.gradle ├── data ├── README.txt ├── Testing.jpage ├── build.xml ├── gdb_ghidra_bridge_client.py ├── gdbghidra.gif └── languages │ ├── skel.cspec │ ├── skel.ldefs │ ├── skel.opinion │ ├── skel.pspec │ ├── skel.sinc │ └── skel.slaspec ├── dist ├── ghidra_9.0.1_PUBLIC_20190524_gdbghidra.zip └── ghidra_9.0.4_PUBLIC_20190526_gdbghidra.zip ├── extension.properties ├── ghidra_scripts └── README.txt ├── lib ├── README.txt └── json-simple-1.1.1.jar ├── os ├── linux64 │ └── README.txt ├── osx64 │ └── README.txt └── win64 │ └── README.txt ├── resources └── images │ ├── running.png │ └── stopped.png └── src ├── main ├── help │ └── help │ │ ├── TOC_Source.xml │ │ ├── shared │ │ └── Frontpage.css │ │ └── topics │ │ └── gdbghidra │ │ └── help.html ├── java │ └── gdbghidra │ │ ├── DeleteBreakpointAction.java │ │ ├── GDBGhidraPlugin.java │ │ ├── GDBGhidraProvider.java │ │ ├── GDBReceiver.java │ │ ├── RegisterChangeListener.java │ │ ├── RestoreBreakpointsAction.java │ │ ├── ToggleBreakpointAction.java │ │ └── events │ │ ├── BreakpointEvent.java │ │ ├── CursorEvent.java │ │ ├── Event.java │ │ ├── EventParser.java │ │ ├── EventType.java │ │ ├── HelloEvent.java │ │ ├── MemoryEvent.java │ │ ├── ParseException.java │ │ └── RegisterEvent.java └── resources │ └── images │ └── README.txt └── test └── java └── README.test.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | build/* 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Module.manifest: -------------------------------------------------------------------------------- 1 | Module.manifest||Public Domain|||END| 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gdbghidra - a visual bridge between a GDB session and GHIDRA 2 | 3 | The purpose of gdbghidra is to provide means during interactive debug sessions in 4 | gdb to quickly follow the flow in GHIDRA; similar to our [gdbida](https://github.com/Comsecuris/gdbida) plugin for IDA Pro. gdbghidra is not meant to be a full debugger. Instead, it merely serves as a small helper tool to assist during interactive debug 5 | sessions that make use of a mixture of tools. It provides simple means to quickly follow along a gdb debug 6 | session in GHIDRA. Also it does not need any dependencies on the Python side. 7 | 8 | gdbghidra consists of the following two parts: 9 | * dist/ghidra_9.0.1_PUBLIC_*_GDBGHIDRA.zip 10 | * data/gdb\_ghidra\_bridge\_client.py : gdb python script 11 | 12 | ![data/gdbghidra](data/gdbghidra.gif) 13 | 14 | Features 15 | ======== 16 | * Sync/colorize cursor inside GHIDRA to PC of GDB session 17 | * Sync stack to GHIDRA on GDB break 18 | * Automatically set register values within GHIDRA for better decompilation 19 | * GHIDRA register window 20 | * Set/Toggle/Delete breakpoints from GHIDRA 21 | * Automatic relocation 22 | 23 | Installation 24 | ============ 25 | Make a change the ~/.gdbinit configuration file to include the plugin: 26 | ``` 27 | source ~/gdb_ghidra_bridge_client.py 28 | ``` 29 | 30 | To install the plugin in GHIDRA follow these steps: 31 | 32 | * Open GHIDRA and select `File/Install Extensions`. 33 | * Press the green `+` button and select `dist/ghidra_9.0.1_PUBLIC_*_GDBGHIDRA.zip`. 34 | * Make sure the Plugin has a tick in the box left. 35 | * Start GHIDRA CodeBrowser. 36 | * Open `File/Configure` and press the adapter icon in above left oft 'Ghidra Core'. 37 | * Filter for `gdb` and make sure `GDBGhidraPlugin` is enabled. 38 | 39 | Now you should see the `GDBGhidraPlugin` window. You can now configure the listener port using the `configuration` button and start the server using the `refresh` button. 40 | 41 | Next, configure the gdb stub to connect to gdbghidras's port (either command line or gdbinit): 42 | ``` 43 | ghidrabridge 10.0.10.10:2305 44 | ``` 45 | 46 | Building 47 | ======== 48 | To build this plugin using gradle run the following command inside gdbghidra directory 49 | ```bash 50 | gradle -PGHIDRA_INSTALL_DIR= 51 | ``` 52 | This produces a fresh extension zip within the `gdbghidra/dist` folder. 53 | 54 | Development 55 | =========== 56 | If you want to build gdbghidra from source using GHIDRA's eclipse environment make sure to add `json-simple-1.1.1.jar` to the classpath as follows: 57 | 58 | * Click the `Run` Menu and select `Run Configurations`. 59 | * Navigate to `Ghidra/GDBGhidra` and select `Classpath`. 60 | * Navigate down the list to `User Entries`, select `User Entries` and click on `ADD JARS...`. 61 | * Select `lib/json-simple-1.1.1.jar` 62 | 63 | Between GHIDRA and GDB a simple JSON message format is spoken which could also be used to connect other tools/debuggers to this GHIDRA plugin. 64 | 65 | 66 | Notes 67 | ===== 68 | Please be aware that this is not considered to be finished. Specifically, the following thoughts are on my mind: 69 | * Network listening input masks untested for errors. 70 | * The network connection is not authenticated in any way. 71 | * A lot of potential for additional commands. For now, I kept it super simple. 72 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Builds a Ghidra Extension for a given Ghidra installation. 2 | // 3 | // An absolute path to the Ghidra installation directory must be supplied either by setting the 4 | // GHIDRA_INSTALL_DIR environment variable or Gradle project property: 5 | // 6 | // > export GHIDRA_INSTALL_DIR= 7 | // > gradle 8 | // 9 | // or 10 | // 11 | // > gradle -PGHIDRA_INSTALL_DIR= 12 | // 13 | // Gradle should be invoked from the directory of the project to build. Please see the 14 | // application.gradle.version property in /Ghidra/application.properties 15 | // for the correction version of Gradle to use for the Ghidra installation you specify. 16 | 17 | //----------------------START "DO NOT MODIFY" SECTION------------------------------ 18 | def ghidraInstallDir 19 | 20 | if (System.env.GHIDRA_INSTALL_DIR) { 21 | ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR 22 | } 23 | else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { 24 | ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") 25 | } 26 | 27 | if (ghidraInstallDir) { 28 | apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" 29 | } 30 | else { 31 | throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") 32 | } 33 | //----------------------END "DO NOT MODIFY" SECTION------------------------------- 34 | -------------------------------------------------------------------------------- /data/README.txt: -------------------------------------------------------------------------------- 1 | The "data" directory is intended to hold data files that will be used by this module and will 2 | not end up in the .jar file, but will be present in the zip or tar file. Typically, data 3 | files are placed here rather than in the resources directory if the user may need to edit them. 4 | 5 | An optional data/languages directory can exist for the purpose of containing various Sleigh language 6 | specification files and importer opinion files. 7 | 8 | The data/build.xml is used for building the contents of the data/languages directory. 9 | 10 | The skel language definition has been commented-out within the skel.ldefs file so that the 11 | skeleton language does not show-up within Ghidra. 12 | 13 | See the Sleigh language documentation (docs/languages/sleigh.htm or sleigh.pdf) for details 14 | on Sleigh language specification syntax. 15 | 16 | -------------------------------------------------------------------------------- /data/Testing.jpage: -------------------------------------------------------------------------------- 1 | System.out.println("test"); 2 | 3 | //var jp = new org.json.simple.parser.JSONParser(); //var r = jp.parse("{\"type\":\"CURSOR\",\"data\":[{\"ADDRESS\":\"0x23\"}]}"); 4 | //System.out.println(r); 5 | //System.out.println(((org.json.simple.JSONObject)r).get("type")); 6 | //var da = (org.json.simple.JSONArray)((org.json.simple.JSONObject)r).get("data"); 7 | //var d = (org.json.simple.JSONObject)da.get(0); 8 | //System.out.println(d.get("ADDRESS")); 9 | Syntax error, insert ";" to complete BlockStatements var l = new java.util.ArrayList(); var p = new generic.json.JSONParser(); 10 | p.parse("{\"type\":\"CURSOR\",\"data\":[{\"ADDRESS\":\"0x23\"}]}".toCharArray(), l); 11 | System.out.println(l); 12 | System.out.println(l.get(1).start); 13 | System.out.println(l.get(1).size); 14 | 15 | 16 | System.out.println(Long.decode("0x555555554000")); 17 | 18 | System.out.println("0x555555554000".substring(2)); -------------------------------------------------------------------------------- /data/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /data/gdb_ghidra_bridge_client.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | # 23 | # https://sourceware.org/gdb/onlinedocs/gdb/Events-In-Python.html#Events-In-Python 24 | 25 | from __future__ import print_function 26 | import os 27 | import socket 28 | import struct 29 | import json 30 | import base64 31 | import gzip 32 | import tempfile 33 | from threading import Thread 34 | 35 | GHIDRA_BRIGE_IP = '127.0.0.1' 36 | GHIDRA_BRIDGE_PORT = 2305 37 | GDB_BRIDGE_IP = '127.0.0.1' 38 | GDB_BRIDGE_PORT = 2306 39 | 40 | socket.setdefaulttimeout(0.1) 41 | 42 | class GhidraBridge(): 43 | def __init__(self, ip, port): 44 | self._connected = False 45 | self._socket = None 46 | self._ghidra_ip = ip 47 | self._ghidra_port = port 48 | 49 | def connect(self): 50 | if self._connected and not self._socket._closed: 51 | return 52 | 53 | socket.setdefaulttimeout(10) 54 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 55 | self._socket.connect((self._ghidra_ip, self._ghidra_port)) 56 | self._connected = True 57 | 58 | def disconnect(self): 59 | if self._connected: 60 | self._socket.close() 61 | self._connected = False 62 | 63 | def send_message(self, message): 64 | try: 65 | if not self._connected: 66 | self.connect() 67 | if not message: return 68 | 69 | self._socket.send(bytes(message + "\n", 'UTF-8')) 70 | except Exception as e: 71 | print(e) 72 | self.disconnect() 73 | self.connect() 74 | 75 | def close(self): 76 | self.disconnect() 77 | 78 | class GDBBridge(Thread): 79 | # this is the connection from GHIDRA -> GDB 80 | def __init__(self, ip, port, ghidra_bridge): 81 | Thread.__init__(self) 82 | self.exit = False 83 | self._ghidra_bridge = ghidra_bridge 84 | self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 85 | self._sock.bind((ip, port)) 86 | self._sock.listen(1) 87 | self._sock.settimeout(10) 88 | 89 | def run(self): 90 | while not self.exit: 91 | try: 92 | (con, (ghidra_ip, ghidra_port)) = self._sock.accept() 93 | line = [] 94 | while True: 95 | c = con.recv(1) 96 | if c == b'\n': 97 | msg = json.loads(b"".join(line)) 98 | if msg["type"] == "BREAKPOINT": 99 | self.handle_breakpoint(msg) 100 | elif msg['type'] == "REGISTER": 101 | self.handle_register(msg) 102 | break 103 | else: 104 | line.append(c) 105 | 106 | except socket.timeout: 107 | pass 108 | 109 | def handle_register(self, msg): 110 | data = msg["data"][0] 111 | if data["action"] == "change": 112 | r = data["register"] 113 | v = data["value"] 114 | print("[GDBBridge] setting register '%s' to '%s'\n" % (r, v)) 115 | GDBUtils.set_register(r, v) 116 | 117 | def handle_breakpoint(self, msg): 118 | data = msg["data"][0] 119 | for address in data["breakpoints"]: 120 | if not( "0x" in address): 121 | print("[GDBBridge] unknown address (missing 0x) '%s'\n" % address) 122 | continue 123 | 124 | action = data["action"] 125 | 126 | if action == "toggle": 127 | bpnr, bpenabled = GDBUtils.return_breakpoint_at(address) 128 | if not bpnr: 129 | print("[GDBBridge] adding breakpoint at address: %s\n" % address) 130 | GDBUtils.query_gdb("break *%s" % address, "set breakpoint") 131 | self._ghidra_bridge.send_message( GhidraMessages.breakpoint(address, "enable")) 132 | continue 133 | 134 | if bpenabled == "y": 135 | print("[GDBBridge] disabling breakpoint at address: %s\n" % address) 136 | GDBUtils.query_gdb("disable %s" % bpnr, "disable breakpoint") 137 | self._ghidra_bridge.send_message( GhidraMessages.breakpoint(address, "disable")) 138 | continue 139 | 140 | elif bpenabled == "n": 141 | print("[GDBBridge] enabling breakpoint at address: %s\n" % address) 142 | GDBUtils.query_gdb("enable %s" % bpnr, "enable breakpoint") 143 | self._ghidra_bridge.send_message( GhidraMessages.breakpoint(address, "enable")) 144 | continue 145 | elif action == "delete": 146 | bpnr, bpenabled = GDBUtils.return_breakpoint_at(address) 147 | 148 | GDBUtils.query_gdb("delete %s" % bpnr, "delete breakpoint\n") 149 | self._ghidra_bridge.send_message( GhidraMessages.breakpoint(address, "delete")) 150 | continue 151 | else: 152 | print("[GDBBridge] unknown breakpoint action '%s'\n" % action) 153 | continue 154 | 155 | def close(self): 156 | self.exit = True 157 | 158 | 159 | class GhidraMessages: 160 | @staticmethod 161 | def encode(msg): 162 | return json.dumps(msg) 163 | 164 | @staticmethod 165 | def update_cursor_to(address, using_relocation): 166 | # "data":[ {"address":hex(gdb.selected_frame().pc()), "relocate":relocate } ], 167 | msg = { 168 | "type":"CURSOR", 169 | "data":[ { 170 | "address":address, 171 | "relocate":using_relocation 172 | } ], 173 | } 174 | return GhidraMessages.encode(msg) 175 | 176 | @staticmethod 177 | def hello(arch, endian, gdb_ip, gdb_port): 178 | msg = { 179 | "type":"HELLO", 180 | "data":[ { 181 | "arch":arch, 182 | "endian":endian, 183 | "answer_ip":gdb_ip, 184 | "answer_port":str(gdb_port), 185 | } ], 186 | } 187 | return GhidraMessages.encode(msg) 188 | 189 | @staticmethod 190 | def breakpoint(address, action): 191 | msg = { 192 | "type":"BREAKPOINT", 193 | "data":[ { 194 | "breakpoint":address, 195 | "action":action, 196 | } ], 197 | } 198 | return GhidraMessages.encode(msg) 199 | 200 | @staticmethod 201 | def update_register(address, register, value): 202 | msg = { 203 | "type":"REGISTER", 204 | "data":[{ 205 | "address":address, 206 | "name":register, 207 | "value":value 208 | }] 209 | } 210 | return GhidraMessages.encode(msg) 211 | 212 | @staticmethod 213 | def memory(address, mapping, data, read, write, execute): 214 | if mapping and data: 215 | msg ={ 216 | "type":"MEMORY", 217 | "data":[{ 218 | "address":mapping["begin"], 219 | "name":mapping["name"], 220 | "data":data, 221 | "size":mapping["size"], 222 | "read":str(read), 223 | "write":str(write), 224 | "execute":str(execute) 225 | }] 226 | } 227 | return GhidraMessages.encode(msg) 228 | 229 | return None 230 | 231 | 232 | 233 | 234 | class GhidraBridgeCommand(gdb.Command): 235 | def __init__(self): 236 | super (GhidraBridgeCommand, self).__init__("ghidrabridge", gdb.COMMAND_USER) 237 | self._register_and_values = {} 238 | 239 | self._ghidra_ip = GHIDRA_BRIGE_IP 240 | self._ghidra_port = GHIDRA_BRIDGE_PORT 241 | 242 | self._ghidra_bridge = GhidraBridge(self._ghidra_ip, self._ghidra_port) 243 | 244 | self._gdb_ip = GDB_BRIDGE_IP 245 | self._gdb_port = GDB_BRIDGE_PORT 246 | 247 | self._gdb_bridge = GDBBridge(self._gdb_ip, self._gdb_port, self._ghidra_bridge) 248 | self._gdb_bridge.daemon = True 249 | self._gdb_bridge.start() 250 | 251 | self._ghidra_bridge.send_message( GhidraMessages.hello(GDBUtils.get_arch(), GDBUtils.get_endian(), self._gdb_ip, self._gdb_port) ) 252 | 253 | 254 | 255 | def hdl_stop_event(self, event): 256 | self._ghidra_bridge.send_message( GhidraMessages.update_cursor_to( GDBUtils.get_instruction_pointer(), GDBUtils.get_relocation()) ) 257 | self._update_register_values() 258 | self._ghidra_bridge.send_message( GhidraMessages.memory( GDBUtils.get_instruction_pointer(), GDBUtils.get_mapping("[stack]"), GDBUtils.get_encoded_stack(), True, True, False )) 259 | 260 | 261 | def _update_register_values(self): 262 | address = GDBUtils.get_instruction_pointer() 263 | 264 | for register, value in GDBUtils.get_registers_and_values(): 265 | if register in self._register_and_values and self._register_and_values[register] == value: continue 266 | 267 | self._register_and_values[register] = value 268 | self._ghidra_bridge.send_message( GhidraMessages.update_register(address, register, value)) 269 | 270 | def hdl_exit_event(self, event): 271 | self.close() 272 | 273 | def invoke(self, arg, from_tty): 274 | argv = arg.split(' ') 275 | if len(argv) < 1: 276 | print("ghidrabridge \n") 277 | return 278 | 279 | target = argv[0].split(':') 280 | 281 | if not '.' in target[0] or len(target) < 2: 282 | print("please specify ip:port combination\n") 283 | return 284 | 285 | self._ghidra_ip = target[0] 286 | self._ghidra_port = int(target[1]) 287 | print("ghidrabridge: using ip: %s port: %d\n" %(self._ghidra_ip, self._ghidra_port)) 288 | 289 | gdb.events.stop.connect(self.hdl_stop_event) 290 | gdb.events.exited.connect(self.hdl_exit_event) 291 | 292 | def close(self): 293 | self._gdb_bridge.close() 294 | self._gdb_bridge.join(2000) 295 | self._ghidra_bridge.close() 296 | 297 | class GDBUtils: 298 | @staticmethod 299 | def get_relocation(): 300 | r = GDBUtils.query_gdb('info proc stat', 'relocation', 'Start of text: ', 'End of text: ') 301 | if r == "unknown": 302 | return "0x0" 303 | 304 | return r 305 | 306 | @staticmethod 307 | def get_instruction_pointer(): 308 | return hex(gdb.selected_frame().pc()) 309 | 310 | @staticmethod 311 | def get_registers_and_values(): 312 | result = [] 313 | query_result = GDBUtils.query_gdb("info registers", "info registers") 314 | for register, value in map(lambda x: x.split()[:2], query_result.split("\n")[:-1]): 315 | result.append( [register, value] ) 316 | return result 317 | 318 | @staticmethod 319 | def query_gdb(cmd, name, extract_begin=None, extract_end=None): 320 | val = gdb.execute(cmd, to_string=True) 321 | if not( extract_begin and extract_end ): 322 | return val 323 | 324 | s_text = val.find(extract_begin) 325 | e_text = val.find(extract_end) 326 | if s_text == -1: 327 | print("[GDBGHIDRA] could not determine %s setting to 'unknown'\n" % name) 328 | return 'unknown' 329 | result = val[s_text + len(extract_begin):e_text].strip() 330 | print("[GDBGHIDRA] found %s '%s'\n" % (name, result)) 331 | return result 332 | 333 | @staticmethod 334 | def return_breakpoint_at(address): 335 | result = GDBUtils.query_gdb("info breakpoints", "info breakpoint").split("\n") 336 | for line in result: 337 | tokens = line.split() 338 | if "0x" in address: 339 | if address[2:] in line: 340 | return ( tokens[0], tokens[3] ) 341 | else: 342 | if address in line: 343 | return ( tokens[0], tokens[3] ) 344 | return (None, None) 345 | 346 | @staticmethod 347 | def get_encoded_memory(address, name, end, size): 348 | f = tempfile.NamedTemporaryFile(delete=False) 349 | gdb.execute("dump memory %s %s %s" % (f.name, address, end)) 350 | with open(f.name, "rb") as m: 351 | data = base64.b64encode(gzip.compress(m.read())).decode('utf-8') 352 | 353 | f.delete 354 | return data 355 | 356 | 357 | @staticmethod 358 | def get_mapping(named): 359 | m = GDBUtils.query_gdb("info proc mappings", "mappings") 360 | if "unable to open" in m: 361 | return None 362 | 363 | x = list(filter(lambda e: named in e, m.split("\n")))[0].split() 364 | return {"begin":x[0], "end":x[1], "size":x[2], "name":named} 365 | 366 | @staticmethod 367 | def get_encoded_stack(): 368 | mapping = GDBUtils.get_mapping("[stack]") 369 | if mapping: 370 | return GDBUtils.get_encoded_memory(mapping["begin"], "stack", mapping["end"], mapping["size"]) 371 | return None 372 | 373 | @staticmethod 374 | def get_arch(): 375 | return GDBUtils.query_gdb('show arch', 'architecture', "(currently ", ")") 376 | 377 | @staticmethod 378 | def get_endian(): 379 | return GDBUtils.query_gdb('show endian', 'endianess', "(currently ", " endian)") 380 | 381 | 382 | @staticmethod 383 | def set_register(register, value): 384 | gdb.execute("set $%s = %s" % (register, value)) 385 | 386 | GhidraBridgeCommand() -------------------------------------------------------------------------------- /data/gdbghidra.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Comsecuris/gdbghidra/139627a5b6da80b51d0e6c20a856fbc830828f60/data/gdbghidra.gif -------------------------------------------------------------------------------- /data/languages/skel.cspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /data/languages/skel.ldefs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /data/languages/skel.opinion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /data/languages/skel.pspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /data/languages/skel.sinc: -------------------------------------------------------------------------------- 1 | # sleigh include file for Skeleton language instructions 2 | 3 | define token opbyte (8) 4 | op0_8 = (0,7) 5 | op6_2 = (6,7) 6 | 7 | dRegPair4_2 = (4,5) 8 | pRegPair4_2 = (4,5) 9 | sRegPair4_2 = (4,5) 10 | qRegPair4_2 = (4,5) 11 | qRegPair4_2a = (4,5) 12 | qRegPair4_2b = (4,5) 13 | rRegPair4_2 = (4,5) 14 | 15 | reg3_3 = (3,5) 16 | bits3_3 = (3,5) 17 | 18 | bits0_4 = (0,3) 19 | 20 | reg0_3 = (0,2) 21 | bits0_3 = (0,2) 22 | ; 23 | 24 | define token data8 (8) 25 | imm8 = (0,7) 26 | sign8 = (7,7) 27 | simm8 = (0,7) signed 28 | ; 29 | 30 | define token data16 (16) 31 | timm4 = (12,15) 32 | imm16 = (0,15) 33 | sign16 = (15,15) 34 | simm16 = (0,15) signed 35 | ; 36 | 37 | attach variables [ reg0_3 reg3_3 ] [ B C D E H L _ A ]; 38 | 39 | attach variables [ sRegPair4_2 dRegPair4_2 ] [ BC DE HL SP ]; 40 | 41 | attach variables [ qRegPair4_2 ] [ BC DE HL AF ]; 42 | attach variables [ qRegPair4_2a ] [ B D H A ]; 43 | attach variables [ qRegPair4_2b ] [ C E L F ]; 44 | 45 | attach variables [ pRegPair4_2 ] [ BC DE IX SP ]; 46 | attach variables [ rRegPair4_2 ] [ BC DE IY SP ]; 47 | 48 | ################################################################ 49 | # Macros 50 | ################################################################ 51 | 52 | macro setResultFlags(result) { 53 | $(Z_flag) = (result == 0); 54 | $(S_flag) = (result s< 0); 55 | } 56 | 57 | macro setAddCarryFlags(op1,op2) { 58 | $(C_flag) = (carry(op1,zext($(C_flag))) || carry(op2,op1 + zext($(C_flag)))); 59 | } 60 | 61 | macro setAddFlags(op1,op2) { 62 | $(C_flag) = carry(op1,op2); 63 | } 64 | 65 | macro setSubtractCarryFlags(op1,op2) { 66 | notC = ~$(C_flag); 67 | $(C_flag) = ((op1 < sext(notC)) || (op2 < (op1 - sext(notC)))); 68 | } 69 | 70 | macro setSubtractFlags(op1,op2) { 71 | $(C_flag) = (op1 < op2); 72 | } 73 | 74 | macro push16(val16) { 75 | SP = SP - 2; 76 | *:2 SP = val16; 77 | } 78 | 79 | macro pop16(ret16) { 80 | ret16 = *:2 SP; 81 | SP = SP + 2; 82 | } 83 | 84 | macro push8(val8) { 85 | SP = SP - 1; 86 | ptr:2 = SP; 87 | *:1 ptr = val8; 88 | } 89 | 90 | macro pop8(ret8) { 91 | ptr:2 = SP; 92 | ret8 = *:1 ptr; 93 | SP = SP + 1; 94 | } 95 | 96 | ################################################################ 97 | 98 | ixMem8: (IX+simm8) is IX & simm8 { ptr:2 = IX + simm8; export *:1 ptr; } 99 | ixMem8: (IX-val) is IX & simm8 & sign8=1 [ val = -simm8; ] { ptr:2 = IX + simm8; export *:1 ptr; } 100 | 101 | iyMem8: (IY+simm8) is IY & simm8 { ptr:2 = IY + simm8; export *:1 ptr; } 102 | iyMem8: (IY-val) is IY & simm8 & sign8=1 [ val = -simm8; ] { ptr:2 = IY + simm8; export *:1 ptr; } 103 | 104 | Addr16: imm16 is imm16 { export *:1 imm16; } 105 | 106 | Mem16: (imm16) is imm16 { export *:2 imm16; } 107 | 108 | RelAddr8: loc is simm8 [ loc = inst_next + simm8; ] { export *:1 loc; } 109 | 110 | cc: "NZ" is bits3_3=0x0 { c:1 = ($(Z_flag) == 0); export c; } 111 | cc: "Z" is bits3_3=0x1 { c:1 = $(Z_flag); export c; } 112 | cc: "NC" is bits3_3=0x2 { c:1 = ($(C_flag) == 0); export c; } 113 | cc: "C" is bits3_3=0x3 { c:1 = $(C_flag); export c; } 114 | cc: "PO" is bits3_3=0x4 { c:1 = ($(PV_flag) == 0); export c; } 115 | cc: "PE" is bits3_3=0x5 { c:1 = $(PV_flag); export c; } 116 | cc: "P" is bits3_3=0x6 { c:1 = ($(S_flag) == 0); export c; } 117 | cc: "M" is bits3_3=0x7 { c:1 = $(S_flag); export c; } 118 | 119 | cc2: "NZ" is bits3_3=0x4 { c:1 = ($(Z_flag) == 0); export c; } 120 | cc2: "Z" is bits3_3=0x5 { c:1 = $(Z_flag); export c; } 121 | cc2: "NC" is bits3_3=0x6 { c:1 = ($(C_flag) == 0); export c; } 122 | cc2: "C" is bits3_3=0x7 { c:1 = $(C_flag); export c; } 123 | 124 | ################################################################ 125 | 126 | 127 | :LD IX,Mem16 is op0_8=0xdd & IX; op0_8=0x2a; Mem16 { 128 | IX = Mem16; 129 | } 130 | 131 | :LD IY,Mem16 is op0_8=0xfd & IY; op0_8=0x2a; Mem16 { 132 | IY = Mem16; 133 | } 134 | 135 | :LD Mem16,HL is op0_8=0x22 & HL; Mem16 { 136 | Mem16 = HL; 137 | } 138 | 139 | :LD Mem16,dRegPair4_2 is op0_8=0xed; op6_2=0x1 & dRegPair4_2 & bits0_4=0x3; Mem16 { 140 | Mem16 = dRegPair4_2; 141 | } 142 | 143 | :LD Mem16,IX is op0_8=0xdd & IX; op0_8=0x22; Mem16 { 144 | Mem16 = IX; 145 | } 146 | 147 | :LD Mem16,IY is op0_8=0xfd & IY; op0_8=0x22; Mem16 { 148 | Mem16 = IY; 149 | } 150 | 151 | :NEG is op0_8=0xed; op0_8=0x44 { 152 | $(PV_flag) = (A == 0x80); 153 | $(C_flag) = (A != 0); 154 | A = -A; 155 | setResultFlags(A); 156 | } 157 | 158 | :SET bits3_3,ixMem8 is op0_8=0xdd; op0_8=0xcb; ixMem8; op6_2=0x3 & bits3_3 & bits0_3=0x6 { 159 | mask:1 = (1 << bits3_3); 160 | val:1 = ixMem8; 161 | ixMem8 = val | mask; 162 | } 163 | 164 | :SET bits3_3,iyMem8 is op0_8=0xfd; op0_8=0xcb; iyMem8; op6_2=0x3 & bits3_3 & bits0_3=0x6 { 165 | mask:1 = (1 << bits3_3); 166 | val:1 = iyMem8; 167 | iyMem8 = val | mask; 168 | } 169 | 170 | :JP Addr16 is op0_8=0xc3; Addr16 { 171 | goto Addr16; 172 | } 173 | 174 | :JP cc,Addr16 is op6_2=0x3 & cc & bits0_3=0x2; Addr16 { 175 | if (!cc) goto Addr16; 176 | } 177 | 178 | :JR RelAddr8 is op0_8=0x18; RelAddr8 { 179 | goto RelAddr8; 180 | } 181 | 182 | :JR cc2,RelAddr8 is op6_2=0x0 & cc2 & bits0_3=0x0; RelAddr8 { 183 | if (cc2) goto RelAddr8; 184 | } 185 | 186 | :JP (HL) is op0_8=0xe9 & HL { 187 | goto [HL]; 188 | } 189 | 190 | :JP (IX) is op0_8=0xdd & IX; op0_8=0xe9 { 191 | goto [IX]; 192 | } 193 | 194 | :JP (IY) is op0_8=0xfd & IY; op0_8=0xe9 { 195 | goto [IY]; 196 | } 197 | 198 | :CALL Addr16 is op0_8=0xcd; Addr16 { 199 | push16(&:2 inst_next); 200 | call Addr16; 201 | } 202 | 203 | :CALL cc,Addr16 is op6_2=0x3 & cc & bits0_3=0x4; Addr16 { 204 | if (!cc) goto inst_next; 205 | push16(&:2 inst_next); 206 | call Addr16; 207 | } 208 | 209 | :RET is op0_8=0xc9 { 210 | pop16(PC); 211 | ptr:2 = zext(PC); 212 | return [ptr]; 213 | } 214 | 215 | :RET cc is op6_2=0x3 & cc & bits0_3=0x0 { 216 | if (!cc) goto inst_next; 217 | pop16(PC); 218 | ptr:2 = zext(PC); 219 | return [ptr]; 220 | } 221 | -------------------------------------------------------------------------------- /data/languages/skel.slaspec: -------------------------------------------------------------------------------- 1 | # sleigh specification file for Skeleton Processor 2 | # >> see docs/languages/sleigh.htm or sleigh.pdf for Sleigh syntax 3 | # Other language modules (see Ghidra/Processors) may provide better examples 4 | # when creating a new language module. 5 | 6 | define endian=little; 7 | define alignment=1; 8 | 9 | define space ram type=ram_space size=2 default; 10 | 11 | define space io type=ram_space size=2; 12 | define space register type=register_space size=1; 13 | 14 | define register offset=0x00 size=1 [ F A C B E D L H I R ]; 15 | define register offset=0x00 size=2 [ AF BC DE HL ]; 16 | define register offset=0x20 size=1 [ A_ F_ B_ C_ D_ E_ H_ L_ ]; # Alternate registers 17 | define register offset=0x20 size=2 [ AF_ BC_ DE_ HL_ ]; # Alternate registers 18 | 19 | define register offset=0x40 size=2 [ _ PC SP IX IY ]; 20 | 21 | define register offset=0x50 size=1 [ rCBAR rCBR rBBR ]; 22 | 23 | # Define context bits (if defined, size must be multiple of 4-bytes) 24 | define register offset=0xf0 size=4 contextreg; 25 | 26 | define context contextreg 27 | assume8bitIOSpace = (0,0) 28 | ; 29 | 30 | # Flag bits (?? manual is very confusing - could be typos!) 31 | @define C_flag "F[0,1]" # C: Carry 32 | @define N_flag "F[1,1]" # N: Add/Subtract 33 | @define PV_flag "F[2,1]" # PV: Parity/Overflow 34 | @define H_flag "F[4,1]" # H: Half Carry 35 | @define Z_flag "F[6,1]" # Z: Zero 36 | @define S_flag "F[7,1]" # S: Sign 37 | 38 | # Include contents of skel.sinc file 39 | @include "skel.sinc" 40 | -------------------------------------------------------------------------------- /dist/ghidra_9.0.1_PUBLIC_20190524_gdbghidra.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Comsecuris/gdbghidra/139627a5b6da80b51d0e6c20a856fbc830828f60/dist/ghidra_9.0.1_PUBLIC_20190524_gdbghidra.zip -------------------------------------------------------------------------------- /dist/ghidra_9.0.4_PUBLIC_20190526_gdbghidra.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Comsecuris/gdbghidra/139627a5b6da80b51d0e6c20a856fbc830828f60/dist/ghidra_9.0.4_PUBLIC_20190526_gdbghidra.zip -------------------------------------------------------------------------------- /extension.properties: -------------------------------------------------------------------------------- 1 | name=@extname@ 2 | description=A visual bridge between a GDB session and GHIDRA. 3 | author= 4 | createdOn= 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /ghidra_scripts/README.txt: -------------------------------------------------------------------------------- 1 | Java source directory to hold module-specific Ghidra scripts. 2 | -------------------------------------------------------------------------------- /lib/README.txt: -------------------------------------------------------------------------------- 1 | The "lib" directory is intended to hold Jar files which this module 2 | is dependent upon. This directory may be eliminated from a specific 3 | module if no other Jar files are needed. 4 | -------------------------------------------------------------------------------- /lib/json-simple-1.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Comsecuris/gdbghidra/139627a5b6da80b51d0e6c20a856fbc830828f60/lib/json-simple-1.1.1.jar -------------------------------------------------------------------------------- /os/linux64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/linux64" directory is intended to hold Linux native binaries 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /os/osx64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/osx64" directory is intended to hold macOS (OS X) native binaries 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /os/win64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/win64" directory is intended to hold MS Windows native binaries (.exe) 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /resources/images/running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Comsecuris/gdbghidra/139627a5b6da80b51d0e6c20a856fbc830828f60/resources/images/running.png -------------------------------------------------------------------------------- /resources/images/stopped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Comsecuris/gdbghidra/139627a5b6da80b51d0e6c20a856fbc830828f60/resources/images/stopped.png -------------------------------------------------------------------------------- /src/main/help/help/TOC_Source.xml: -------------------------------------------------------------------------------- 1 | 2 | 49 | 50 | 51 | 52 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/help/help/shared/Frontpage.css: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | WARNING! 18 | This file is copied to all help directories. If you change this file, you must copy it 19 | to each src/main/help/help/shared directory. 20 | 21 | 22 | Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but 23 | px (pixel) or with no type marking. 24 | 25 | */ 26 | 27 | body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */ 28 | li { font-family:times new roman; font-size:14pt; } 29 | h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; } 30 | h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; } 31 | h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; } 32 | h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; } 33 | 34 | /* 35 | P tag code. Most of the help files nest P tags inside of blockquote tags (the was the 36 | way it had been done in the beginning). The net effect is that the text is indented. In 37 | modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in 38 | blockquote tags, as well as naked P tags. The following two lines accomplish this. Note 39 | that the 'blockquote p' definition will inherit from the first 'p' definition. 40 | */ 41 | p { margin-left: 40px; font-family:times new roman; font-size:14pt; } 42 | blockquote p { margin-left: 10px; } 43 | 44 | p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } 45 | p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } 46 | p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; } 47 | p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; } 48 | 49 | /* 50 | We wish for a tables to have space between it and the preceding element, so that text 51 | is not too close to the top of the table. Also, nest the table a bit so that it is clear 52 | the table relates to the preceding text. 53 | */ 54 | table { margin-left: 20px; margin-top: 10px; width: 80%;} 55 | td { font-family:times new roman; font-size:14pt; vertical-align: top; } 56 | th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; } 57 | 58 | code { color: black; font-family: courier new; font-size: 14pt; } 59 | -------------------------------------------------------------------------------- /src/main/help/help/topics/gdbghidra/help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | Skeleton Help File for a Module 13 | 14 | 15 | 16 | 17 |

Skeleton Help File for a Module

18 | 19 |

This is a simple skeleton help topic. For a better description of what should and should not 20 | go in here, see the "sample" Ghidra extension in the Extensions/Ghidra directory, or see your 21 | favorite help topic. In general, language modules do not have their own help topics.

22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/DeleteBreakpointAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra; 25 | 26 | import docking.ActionContext; 27 | import docking.action.DockingAction; 28 | import docking.action.MenuData; 29 | import ghidra.app.context.ListingActionContext; 30 | import ghidra.program.model.address.Address; 31 | import ghidra.program.util.MarkerLocation; 32 | 33 | class DeleteBreakpointAction extends DockingAction { 34 | private GDBReceiver gdbReceiver; 35 | 36 | DeleteBreakpointAction(GDBGhidraPlugin pl) { 37 | super("Delete Breakpoint", pl.getName()); 38 | setDescription("Delete breakpoint at current location"); 39 | setPopupMenuData(new MenuData(new String[] {"Delete breakpoint"}, null, "Breakpoint")); 40 | this.gdbReceiver = null; 41 | } 42 | 43 | @Override 44 | public void actionPerformed(ActionContext context) { 45 | Address address = getAddress(context); 46 | if(this.gdbReceiver != null) { 47 | this.gdbReceiver.deleteBreakpoint(address); 48 | } 49 | } 50 | 51 | public void setGDBReceiver(GDBReceiver gdbReceiver) { 52 | this.gdbReceiver = gdbReceiver; 53 | } 54 | 55 | private Address getAddress(ActionContext context) { 56 | Object contextObject = context.getContextObject(); 57 | if(MarkerLocation.class.isAssignableFrom(contextObject.getClass())) { 58 | return ((MarkerLocation) contextObject).getAddr(); 59 | } else if (context instanceof ListingActionContext ) { 60 | return ((ListingActionContext) context).getAddress(); 61 | } 62 | return null; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/GDBGhidraPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra; 25 | 26 | import ghidra.app.plugin.PluginCategoryNames; 27 | import ghidra.app.plugin.ProgramPlugin; 28 | import ghidra.framework.plugintool.*; 29 | import ghidra.framework.plugintool.util.PluginStatus; 30 | import ghidra.program.util.ProgramLocation; 31 | 32 | //@formatter:off 33 | @PluginInfo( 34 | status = PluginStatus.STABLE, 35 | packageName = "GDBGhidra", 36 | category = PluginCategoryNames.DEBUGGER, 37 | shortDescription = "GDB to Ghidra bridge.", 38 | description = "Syncs the current instruction pointer from a gdbserver session to the current cursor of Ghidra." 39 | ) 40 | //@formatter:on 41 | public class GDBGhidraPlugin extends ProgramPlugin { 42 | 43 | GDBGhidraProvider provider; 44 | 45 | public GDBGhidraPlugin(PluginTool tool) { 46 | super(tool, true, true); 47 | 48 | String pluginName = getName(); 49 | provider = new GDBGhidraProvider(this, pluginName); 50 | } 51 | 52 | @Override 53 | public void init() { 54 | super.init(); 55 | } 56 | 57 | @Override 58 | public void locationChanged(ProgramLocation loc) { 59 | provider.locationChanged(currentProgram, loc); 60 | } 61 | 62 | public GDBGhidraProvider getProvider() { 63 | return this.provider; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/GDBGhidraProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra; 25 | 26 | import java.awt.BorderLayout; 27 | import java.awt.Color; 28 | import java.awt.event.ActionEvent; 29 | 30 | import javax.swing.AbstractAction; 31 | import javax.swing.BoxLayout; 32 | import javax.swing.JComponent; 33 | import javax.swing.JLabel; 34 | import javax.swing.JPanel; 35 | import javax.swing.JScrollPane; 36 | import javax.swing.SwingConstants; 37 | import javax.swing.border.BevelBorder; 38 | import javax.swing.table.DefaultTableModel; 39 | import docking.ActionContext; 40 | import docking.WindowPosition; 41 | import docking.action.DockingAction; 42 | import docking.action.ToolBarData; 43 | import docking.widgets.table.GTable; 44 | import ghidra.program.model.address.Address; 45 | import ghidra.program.model.listing.Program; 46 | import ghidra.program.util.ProgramLocation; 47 | import ghidra.app.script.*; 48 | import ghidra.framework.plugintool.ComponentProviderAdapter; 49 | import resources.Icons; 50 | 51 | public class GDBGhidraProvider extends ComponentProviderAdapter { 52 | private JPanel panel; 53 | private DockingAction action; 54 | private DockingAction portAction; 55 | private DockingAction stopAction; 56 | private ProgramLocation currentLocation; 57 | private GDBReceiver gdbReceiver; 58 | private Program currentProgram; 59 | private GDBGhidraPlugin plugin; 60 | private Thread gdbReceiverThread = null; 61 | private DefaultTableModel model = null; 62 | private JLabel status = new JLabel(); 63 | private Address previousAddress; 64 | private Color previousColor; 65 | 66 | 67 | public GDBGhidraProvider(GDBGhidraPlugin plugin, String owner) { 68 | super(plugin.getTool(), owner, owner); 69 | this.model = new DefaultTableModel( new String[] {"register", "value"}, 0) { 70 | @Override 71 | public boolean isCellEditable(int row, int column) { 72 | if(column == 0) { 73 | return false; 74 | } 75 | return true; 76 | } 77 | }; 78 | this.plugin = plugin; 79 | buildTable(); 80 | 81 | this.gdbReceiver = new GDBReceiver(2305, plugin, model); 82 | 83 | createActions(); 84 | setWindowGroup("core.GDBGhidra"); 85 | setIntraGroupPosition(WindowPosition.RIGHT); 86 | } 87 | 88 | public void buildTable() { 89 | panel = new JPanel(new BorderLayout()); 90 | var table = new GTable(this.model); 91 | panel.add(new JScrollPane(table)); 92 | 93 | var statusPanel = new JPanel(); 94 | statusPanel.setBorder(new BevelBorder(BevelBorder.LOWERED)); 95 | panel.add(statusPanel, BorderLayout.SOUTH); 96 | statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.X_AXIS)); 97 | 98 | status.setText("stopped"); 99 | status.setHorizontalAlignment(SwingConstants.LEFT); 100 | statusPanel.add(status); 101 | 102 | var a = new AbstractAction() { 103 | public void actionPerformed(ActionEvent a) { 104 | RegisterChangeListener l = (RegisterChangeListener)a.getSource(); 105 | if(l.getColumn() != 1) { 106 | return; 107 | } 108 | gdbReceiver.ChangeRegister((String)table.getValueAt(l.getRow(), 0), (String)l.getNewValue()); 109 | } 110 | }; 111 | 112 | new RegisterChangeListener(table, a); 113 | 114 | setVisible(true); 115 | } 116 | 117 | private void createActions() { 118 | action = new DockingAction("Run", getName()) { 119 | 120 | @Override 121 | public void actionPerformed(ActionContext context) { 122 | System.out.println("[GDBGhidra] Starting server on port " + gdbReceiver.getPort() + "\n"); 123 | gdbReceiverThread = new Thread(gdbReceiver); 124 | gdbReceiverThread.start(); 125 | status.setText("running"); 126 | } 127 | }; 128 | action.setToolBarData(new ToolBarData(Icons.REFRESH_ICON, null)); 129 | action.setEnabled(true); 130 | action.markHelpUnnecessary(); 131 | dockingTool.addLocalAction(this, action); 132 | 133 | stopAction = new DockingAction("Stop", getName()) { 134 | 135 | @Override 136 | public void actionPerformed(ActionContext context) { 137 | System.out.println("[GDBGhidra] Stopping server\n"); 138 | gdbReceiver.stop(); 139 | try { 140 | if(gdbReceiverThread != null && gdbReceiverThread.isAlive()) { 141 | gdbReceiverThread.join(2000); 142 | } 143 | gdbReceiverThread = null; 144 | } catch (InterruptedException e) { 145 | e.printStackTrace(); 146 | } 147 | gdbReceiverThread = null; 148 | status.setText("stopped"); 149 | } 150 | }; 151 | stopAction.setToolBarData(new ToolBarData(Icons.STOP_ICON, null)); 152 | stopAction.setEnabled(true); 153 | stopAction.markHelpUnnecessary(); 154 | dockingTool.addLocalAction(this, stopAction); 155 | 156 | portAction = new DockingAction("Configure", "Port") { 157 | @Override 158 | public void actionPerformed(ActionContext context) { 159 | AskDialog d = new AskDialog<>("Listener port configuration", "Please enter TCP listener port:", AskDialog.INT, gdbReceiver.getPort()); 160 | if(d.isCanceled()) { 161 | // WAAAAH! 162 | } 163 | gdbReceiver.setPort( Integer.valueOf(d.getValueAsString()).intValue() ); 164 | } 165 | }; 166 | portAction.setToolBarData(new ToolBarData(Icons.CONFIGURE_FILTER_ICON, null)); 167 | portAction.setEnabled(true); 168 | portAction.markHelpUnnecessary(); 169 | dockingTool.addLocalAction(this, portAction); 170 | 171 | DeleteBreakpointAction deleteBreakpointAction = new DeleteBreakpointAction(this.plugin); 172 | deleteBreakpointAction.setEnabled(true); 173 | deleteBreakpointAction.setGDBReceiver(gdbReceiver); 174 | dockingTool.addAction(deleteBreakpointAction); 175 | 176 | /* 177 | RestoreBreakpointsAction restoreBreakpointsAction = new RestoreBreakpointsAction(this.plugin); 178 | 179 | restoreBreakpointsAction.setEnabled(true); 180 | restoreBreakpointsAction.setGDBReceiver(gdbReceiver); 181 | dockingTool.addAction(restoreBreakpointsAction); */ 182 | 183 | 184 | ToggleBreakpointAction breakpointAction = new ToggleBreakpointAction(this.plugin); 185 | breakpointAction.setEnabled(true); 186 | breakpointAction.setGDBReceiver(gdbReceiver); 187 | dockingTool.addAction(breakpointAction); 188 | 189 | } 190 | 191 | @Override 192 | public JComponent getComponent() { 193 | return panel; 194 | } 195 | 196 | public void locationChanged(Program cp, ProgramLocation loc) { 197 | this.currentProgram = cp; 198 | this.currentLocation = loc; 199 | 200 | gdbReceiver.updateState(currentProgram, currentLocation); 201 | } 202 | 203 | public void setPreviousAddress(Address newAddress) { 204 | this.previousAddress = newAddress; 205 | } 206 | public Address getPreviousAddress() { 207 | return this.previousAddress; 208 | } 209 | 210 | public void setPreviousColor(Color previousColor) { 211 | this.previousColor = previousColor; 212 | } 213 | 214 | public Color getPreviousColor() { 215 | return this.previousColor; 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/GDBReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra; 25 | 26 | import java.io.BufferedReader; 27 | import java.io.DataOutputStream; 28 | import java.io.IOException; 29 | import java.io.InputStreamReader; 30 | import java.math.BigInteger; 31 | import java.net.ServerSocket; 32 | import java.net.Socket; 33 | import java.net.SocketException; 34 | import java.net.SocketTimeoutException; 35 | import java.util.HashMap; 36 | 37 | import javax.swing.table.DefaultTableModel; 38 | 39 | import org.json.simple.JSONObject; 40 | import gdbghidra.events.BreakpointEvent; 41 | import gdbghidra.events.CursorEvent; 42 | import gdbghidra.events.EventParser; 43 | import gdbghidra.events.HelloEvent; 44 | import gdbghidra.events.MemoryEvent; 45 | import gdbghidra.events.RegisterEvent; 46 | import ghidra.program.model.address.Address; 47 | import ghidra.program.model.listing.Program; 48 | import ghidra.program.util.ProgramLocation; 49 | 50 | public class GDBReceiver implements Runnable{ 51 | 52 | private int port; 53 | private GDBGhidraPlugin plugin; 54 | private boolean stop; 55 | private ServerSocket socket; 56 | private HelloEvent helloEvent; 57 | private long relocate = 0; 58 | private Program currentProgram; 59 | private ProgramLocation currentLocation; 60 | private HashMap registers; 61 | private DefaultTableModel model; 62 | 63 | public GDBReceiver(int port, GDBGhidraPlugin plugin, DefaultTableModel model) { 64 | this.port = port; 65 | this.plugin = plugin; 66 | this.stop = false; 67 | this.helloEvent = null; 68 | this.registers = new HashMap(); 69 | this.model = model; 70 | } 71 | 72 | @Override 73 | public void run() { 74 | this.stop = false; 75 | 76 | try { 77 | this.socket = new ServerSocket(port); 78 | while(!this.stop) { 79 | handleConnection(socket.accept()); 80 | } 81 | } catch(SocketException e) { 82 | if(!e.getMessage().contentEquals("Socket closed")) { 83 | e.printStackTrace(); 84 | } 85 | this.stop = true; 86 | return; 87 | }catch (IOException|gdbghidra.events.ParseException e) { 88 | e.printStackTrace(); 89 | this.stop = true; 90 | return; 91 | } 92 | 93 | } 94 | 95 | private void handleConnection(Socket sock) throws IOException, gdbghidra.events.ParseException { 96 | String msgBuffer; 97 | try ( 98 | var is = sock.getInputStream(); 99 | var isr = new InputStreamReader(is); 100 | var read = new BufferedReader(isr); 101 | var os = sock.getOutputStream(); 102 | ) { 103 | while(true) { 104 | msgBuffer = read.readLine(); 105 | if(msgBuffer == null || msgBuffer.length() == 0) { 106 | continue; 107 | } 108 | System.out.println("[GDBGhidra] received message: " + String.valueOf(msgBuffer.length()) + " bytes: '" + msgBuffer + "'\n"); 109 | 110 | var tmpEvent = EventParser.fromJsonString(msgBuffer); 111 | switch(tmpEvent.getType()) { 112 | case HELLO: 113 | var helloEvent = (HelloEvent)tmpEvent; 114 | this.helloEvent = helloEvent; 115 | break; 116 | case CURSOR: 117 | var cursorEvent = (CursorEvent)tmpEvent; 118 | this.relocate = CursorEvent.handleEvent(cursorEvent, currentProgram, this.plugin); 119 | 120 | break; 121 | case REGISTER: 122 | var registerEvent = (RegisterEvent)tmpEvent; 123 | RegisterEvent.handleEvent(registerEvent, currentProgram, this.plugin, currentLocation); 124 | updateTable(registerEvent); 125 | 126 | break; 127 | case BREAKPOINT: 128 | var breakpoint = (BreakpointEvent)tmpEvent; 129 | BreakpointEvent.handleEvent(breakpoint, currentProgram, this.plugin, this.relocate); 130 | 131 | break; 132 | case MEMORY: 133 | var memEvent = (MemoryEvent)tmpEvent; 134 | MemoryEvent.handleEvent(memEvent, currentProgram); 135 | break; 136 | } 137 | } 138 | } catch (SocketTimeoutException e) { 139 | return; 140 | } 141 | } 142 | 143 | private void updateTable(RegisterEvent registerEvent) { 144 | var k = registerEvent.getName(); 145 | var v = registerEvent.getValue(); 146 | if(this.registers.containsKey(k)) { 147 | this.registers.replace(k, v); 148 | }else { 149 | this.registers.put(k, v); 150 | } 151 | int i=0; 152 | boolean found = false; 153 | for(i=0; i < this.model.getRowCount(); i++) { 154 | String key = (String)this.model.getValueAt(i, 0); 155 | if(key.contentEquals(registerEvent.getName())) { 156 | this.model.setValueAt(registerEvent.getHexString(), i, 1); 157 | found = true; 158 | break; 159 | } 160 | } 161 | if(!found) { 162 | this.model.addRow(new Object[] {registerEvent.getName(), registerEvent.getHexString()}); 163 | } 164 | } 165 | 166 | public void setPort(int port) { 167 | this.port = port; 168 | } 169 | 170 | public void updateState(Program cp, ProgramLocation cl) { 171 | this.currentProgram = cp; 172 | this.currentLocation = cl; 173 | } 174 | 175 | public void stop() { 176 | this.stop = true; 177 | if(this.socket != null) { 178 | try { 179 | this.socket.close(); 180 | } catch (IOException e) { 181 | e.printStackTrace(); 182 | } 183 | } 184 | } 185 | 186 | public int getPort() { 187 | return this.port; 188 | } 189 | 190 | public void addBreakpoint(Address address) { 191 | if(this.helloEvent == null) { 192 | return; 193 | } 194 | 195 | var response = BreakpointEvent.constructJSONResponse(this.relocate + address.subtract(currentProgram.getImageBase()), "toggle"); 196 | sendResponse(response); 197 | } 198 | 199 | public void sendResponse(JSONObject response) { 200 | System.out.println("[GDBGhidra] sending message:\t"+response.toJSONString()+"\n"); 201 | 202 | try( 203 | var s = new Socket(this.helloEvent.getAnswerIp(), this.helloEvent.getAnswerPort()); 204 | var dos = new DataOutputStream(s.getOutputStream()); 205 | ) { 206 | dos.write((response.toJSONString() + "\n").getBytes()); 207 | } catch (IOException e) { 208 | e.printStackTrace(); 209 | } 210 | } 211 | 212 | public void deleteBreakpoint(Address address) { 213 | if(this.helloEvent == null) { 214 | return; 215 | } 216 | 217 | var response = BreakpointEvent.constructJSONResponse(this.relocate + address.subtract(currentProgram.getImageBase()), "delete"); 218 | sendResponse(response); 219 | } 220 | 221 | public void restoreBreakpoints() { 222 | var it = currentProgram.getBookmarkManager().getBookmarksIterator("breakpoint"); 223 | while(it.hasNext()) { 224 | var bm = it.next(); 225 | 226 | var response = BreakpointEvent.constructJSONResponse(this.relocate + bm.getAddress().subtract(currentProgram.getImageBase()), "toggle"); 227 | sendResponse(response); 228 | } 229 | } 230 | 231 | public void ChangeRegister(String register, String newValue) { 232 | if(this.helloEvent == null) { 233 | return; 234 | } 235 | 236 | var response = RegisterEvent.constructJSONResponse(register, newValue, "change"); 237 | sendResponse(response); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/RegisterChangeListener.java: -------------------------------------------------------------------------------- 1 | package gdbghidra; 2 | 3 | import java.awt.event.ActionEvent; 4 | import java.beans.PropertyChangeEvent; 5 | import java.beans.PropertyChangeListener; 6 | 7 | import javax.swing.Action; 8 | import javax.swing.JTable; 9 | import javax.swing.SwingUtilities; 10 | 11 | public class RegisterChangeListener implements PropertyChangeListener, Runnable { 12 | private JTable table; 13 | private Action action; 14 | 15 | private int row; 16 | private int column; 17 | private Object oldValue; 18 | private Object newValue; 19 | 20 | public RegisterChangeListener(JTable table, Action action) { 21 | this.table = table; 22 | this.action = action; 23 | this.table.addPropertyChangeListener(this); 24 | } 25 | 26 | private RegisterChangeListener(JTable table, int row, int column, Object oldValue, Object newValue) { 27 | this.table = table; 28 | this.row = row; 29 | this.column = column; 30 | this.oldValue = oldValue; 31 | this.newValue = newValue; 32 | } 33 | 34 | public int getColumn() { 35 | return column; 36 | } 37 | 38 | public Object getNewValue() { 39 | return newValue; 40 | } 41 | 42 | public Object getOldValue() { 43 | return oldValue; 44 | } 45 | 46 | public int getRow() { 47 | return row; 48 | } 49 | 50 | public JTable getTable() { 51 | return table; 52 | } 53 | 54 | @Override 55 | public void propertyChange(PropertyChangeEvent e) { 56 | if ("tableCellEditor".equals(e.getPropertyName())) { 57 | if (table.isEditing()) 58 | processEditingStarted(); 59 | else 60 | processEditingStopped(); 61 | } 62 | } 63 | 64 | private void processEditingStarted() { 65 | SwingUtilities.invokeLater(this); 66 | } 67 | 68 | @Override 69 | public void run() { 70 | row = table.convertRowIndexToModel(table.getEditingRow()); 71 | column = table.convertColumnIndexToModel(table.getEditingColumn()); 72 | oldValue = table.getModel().getValueAt(row, column); 73 | newValue = null; 74 | } 75 | 76 | private void processEditingStopped() { 77 | newValue = table.getModel().getValueAt(row, column); 78 | 79 | if (!newValue.equals(oldValue)) { 80 | RegisterChangeListener tcl = new RegisterChangeListener(getTable(), getRow(), getColumn(), getOldValue(), 81 | getNewValue()); 82 | 83 | ActionEvent event = new ActionEvent(tcl, ActionEvent.ACTION_PERFORMED, ""); 84 | action.actionPerformed(event); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/RestoreBreakpointsAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra; 25 | 26 | import docking.ActionContext; 27 | import docking.action.DockingAction; 28 | import docking.action.MenuData; 29 | 30 | class RestoreBreakpointsAction extends DockingAction { 31 | private GDBReceiver gdbReceiver; 32 | 33 | RestoreBreakpointsAction(GDBGhidraPlugin pl) { 34 | super("Restore Breakpoints", pl.getName()); 35 | setDescription("Restore all saved breakpoints"); 36 | setPopupMenuData(new MenuData(new String[] {"Restore breakpoints"}, null, "Breakpoint")); 37 | //setKeyBindingData(new KeyBindingData(KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK)); 38 | this.gdbReceiver = null; 39 | } 40 | 41 | @Override 42 | public void actionPerformed(ActionContext context) { 43 | if(this.gdbReceiver != null) { 44 | this.gdbReceiver.restoreBreakpoints(); 45 | } 46 | } 47 | 48 | public void setGDBReceiver(GDBReceiver gdbReceiver) { 49 | this.gdbReceiver = gdbReceiver; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/ToggleBreakpointAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra; 25 | 26 | import docking.ActionContext; 27 | import docking.action.DockingAction; 28 | import docking.action.MenuData; 29 | import ghidra.app.context.ListingActionContext; 30 | import ghidra.program.model.address.Address; 31 | import ghidra.program.util.MarkerLocation; 32 | 33 | class ToggleBreakpointAction extends DockingAction { 34 | private GDBReceiver gdbReceiver; 35 | 36 | ToggleBreakpointAction(GDBGhidraPlugin pl) { 37 | super("Toggle Breakpoint", pl.getName()); 38 | setDescription("Enable/Disable breakpoint at current location"); 39 | setPopupMenuData(new MenuData(new String[] {"Toggle breakpoint"}, null, "Breakpoint")); 40 | //setKeyBindingData(new KeyBindingData(KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK)); 41 | this.gdbReceiver = null; 42 | } 43 | 44 | @Override 45 | public void actionPerformed(ActionContext context) { 46 | Address address = getAddress(context); 47 | if(this.gdbReceiver != null) { 48 | this.gdbReceiver.addBreakpoint(address); 49 | } 50 | } 51 | 52 | public void setGDBReceiver(GDBReceiver gdbReceiver) { 53 | this.gdbReceiver = gdbReceiver; 54 | } 55 | 56 | private Address getAddress(ActionContext context) { 57 | Object contextObject = context.getContextObject(); 58 | if(MarkerLocation.class.isAssignableFrom(contextObject.getClass())) { 59 | return ((MarkerLocation) contextObject).getAddr(); 60 | } else if (context instanceof ListingActionContext ) { 61 | return ((ListingActionContext) context).getAddress(); 62 | } 63 | return null; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/BreakpointEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | import java.awt.Color; 27 | 28 | import org.json.simple.JSONArray; 29 | import org.json.simple.JSONObject; 30 | 31 | import ghidra.app.plugin.ProgramPlugin; 32 | import ghidra.app.plugin.core.colorizer.ColorizingService; 33 | import ghidra.program.model.address.Address; 34 | import ghidra.program.model.listing.Program; 35 | 36 | public class BreakpointEvent implements Event{ 37 | private String address; 38 | private BreakpointEventAction action; 39 | 40 | public BreakpointEvent(String address, String action) { 41 | this.address = address; 42 | this.action = BreakpointEventAction.valueOf(action.toUpperCase()); 43 | } 44 | 45 | public String getAddress() { 46 | return this.address; 47 | } 48 | 49 | public BreakpointEventAction getAction() { 50 | return this.action; 51 | } 52 | 53 | @Override 54 | public EventType getType() { 55 | return EventType.BREAKPOINT; 56 | } 57 | 58 | public enum BreakpointEventAction { 59 | ENABLE, 60 | DISABLE, 61 | DELETE 62 | } 63 | 64 | private static Address getBreakpointAddress(BreakpointEvent breakpoint, Program currentProgram, long relocate) { 65 | return currentProgram.getAddressFactory().getAddress(breakpoint.getAddress()).subtract(relocate).add(currentProgram.getImageBase().getOffset()); 66 | } 67 | 68 | private static void doBreakpointTransaction(String action, BreakpointEvent breakpoint, Program currentProgram, ProgramPlugin plugin, long relocate) { 69 | var caddress = getBreakpointAddress(breakpoint, currentProgram, relocate); 70 | var category = "breakpoint"; 71 | 72 | var tx = currentProgram.startTransaction(action); 73 | 74 | /*==================== Begin Transaction ====================================*/ 75 | var service = plugin.getTool().getService(ColorizingService.class); 76 | var bm = currentProgram.getBookmarkManager().getBookmark(caddress, category, category); 77 | 78 | switch(breakpoint.getAction()) { 79 | case ENABLE: 80 | service.setBackgroundColor(caddress, caddress, Color.RED); 81 | break; 82 | case DISABLE: 83 | service.setBackgroundColor(caddress, caddress, Color.LIGHT_GRAY); 84 | break; 85 | case DELETE: 86 | service.setBackgroundColor(caddress, caddress, Color.WHITE); 87 | break; 88 | } 89 | 90 | if(bm != null) { 91 | if(breakpoint.action == BreakpointEventAction.DELETE) { 92 | currentProgram.getBookmarkManager().removeBookmark(bm); 93 | service = plugin.getTool().getService(ColorizingService.class); 94 | }else { 95 | bm.set(category, action); 96 | } 97 | }else { 98 | currentProgram.getBookmarkManager().setBookmark(caddress, category, category, action); 99 | } 100 | /*==================== END Transaction ====================================*/ 101 | currentProgram.endTransaction(tx, true); 102 | 103 | } 104 | 105 | public static void handleEvent(BreakpointEvent breakpoint, Program currentProgram, ProgramPlugin plugin, long relocate) { 106 | switch(breakpoint.getAction()) { 107 | case ENABLE: 108 | doBreakpointTransaction("enabled", breakpoint, currentProgram, plugin, relocate); 109 | break; 110 | case DISABLE: 111 | doBreakpointTransaction("disabled", breakpoint, currentProgram, plugin, relocate); 112 | break; 113 | 114 | case DELETE: 115 | doBreakpointTransaction("delete", breakpoint, currentProgram, plugin, relocate); 116 | break; 117 | } 118 | 119 | } 120 | 121 | public static JSONObject constructJSONResponse(long address, String action) { 122 | var response = new JSONObject(); 123 | var datamap = new JSONObject(); 124 | var data = new JSONArray(); 125 | var jbreakpoints = new JSONArray(); 126 | 127 | response.put("type", "BREAKPOINT"); 128 | 129 | jbreakpoints.add("0x"+Long.toHexString(address)); 130 | 131 | datamap.put("breakpoints", jbreakpoints); 132 | datamap.put("action", action); 133 | data.add(datamap); 134 | response.put("data", data); 135 | 136 | return response; 137 | } 138 | } -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/CursorEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | import java.awt.Color; 27 | 28 | import gdbghidra.GDBGhidraPlugin; 29 | import ghidra.app.plugin.core.colorizer.ColorizingService; 30 | import ghidra.app.services.GoToService; 31 | import ghidra.program.model.listing.Program; 32 | 33 | public class CursorEvent implements Event { 34 | private String address; 35 | private String relocate; 36 | 37 | public CursorEvent(String address, String relocate) { 38 | this.address = address; 39 | this.relocate = relocate; 40 | } 41 | 42 | public String getAddressString() { 43 | return this.address; 44 | } 45 | 46 | public long getRelocationAddress() { 47 | return Long.decode(relocate); 48 | } 49 | 50 | public String getRelocationAddressString() { 51 | return this.relocate; 52 | } 53 | 54 | public long getOffset() { 55 | if(relocate == "unknown") { 56 | return Long.decode(address); 57 | } 58 | 59 | return Long.decode(address) - Long.decode(relocate); 60 | } 61 | 62 | @Override 63 | public EventType getType() { 64 | return EventType.CURSOR; 65 | } 66 | 67 | public static long handleEvent(CursorEvent cursor, Program currentProgram, GDBGhidraPlugin plugin) { 68 | var newAddress = currentProgram.getImageBase().add(cursor.getOffset()); 69 | plugin.getTool().getService(GoToService.class).goTo(newAddress); 70 | 71 | var tx = currentProgram.startTransaction("change cursor color"); 72 | 73 | /*==================== Begin Transaction ====================================*/ 74 | var service = plugin.getTool().getService(ColorizingService.class); 75 | var currentColor = service.getBackgroundColor(newAddress); 76 | 77 | var previousAddress = plugin.getProvider().getPreviousAddress(); 78 | 79 | service.setBackgroundColor(newAddress, newAddress, Color.GREEN); 80 | 81 | if(previousAddress != null ) { 82 | service.setBackgroundColor(plugin.getProvider().getPreviousAddress(), plugin.getProvider().getPreviousAddress(), plugin.getProvider().getPreviousColor()); 83 | } 84 | plugin.getProvider().setPreviousAddress(newAddress); 85 | if(currentColor == null) { 86 | plugin.getProvider().setPreviousColor(Color.WHITE); 87 | }else { 88 | plugin.getProvider().setPreviousColor(currentColor); 89 | } 90 | /*==================== END Transaction ====================================*/ 91 | currentProgram.endTransaction(tx, true); 92 | 93 | 94 | if(!cursor.getRelocationAddressString().equals("unknown")) { 95 | return cursor.getRelocationAddress(); 96 | } 97 | return 0; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/Event.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | public interface Event { 27 | public EventType getType(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/EventParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | import java.util.EnumSet; 27 | 28 | import org.json.simple.JSONArray; 29 | import org.json.simple.JSONObject; 30 | import org.json.simple.parser.JSONParser; 31 | 32 | public class EventParser { 33 | private static String getKeyOrThrow(JSONObject o, String key, String sectionName) throws ParseException { 34 | if(!o.containsKey(key)) { throw new ParseException("Missing '"+key+"' field in "+sectionName+" section of message."); } 35 | return (String)o.get(key); 36 | } 37 | 38 | public static Event fromJsonString(String json) throws ParseException { 39 | var jp = new JSONParser(); 40 | JSONObject r; 41 | try { 42 | r = (JSONObject)jp.parse(json); 43 | if(r == null) { throw new ParseException("Could not initialize json parser"); } 44 | 45 | if(!r.containsKey("type")) { throw new ParseException("Missing type field inside event message"); } 46 | 47 | EnumSet types = EnumSet.allOf(EventType.class); 48 | 49 | var type = EventType.valueOf((String)r.get("type")); 50 | 51 | if(!types.contains(type)) { throw new ParseException("Unknown event type. Should be one of: "); } 52 | 53 | if(!r.containsKey("data")) { throw new ParseException("Missing 'data' section inside event message."); } 54 | 55 | var da = (JSONArray)r.get("data"); 56 | 57 | if(da.size() == 0) { throw new ParseException("'data' section inside event message should not be empty"); } 58 | 59 | var d = (JSONObject)da.get(0); 60 | 61 | switch(type) { 62 | case HELLO: 63 | return new HelloEvent( 64 | getKeyOrThrow(d, "arch", "data"), 65 | getKeyOrThrow(d, "endian", "data"), 66 | getKeyOrThrow(d, "answer_ip", "data"), 67 | getKeyOrThrow(d, "answer_port", "data")); 68 | 69 | case CURSOR: 70 | return new CursorEvent( 71 | getKeyOrThrow(d, "address", "data"), 72 | getKeyOrThrow(d, "relocate", "data")); 73 | case REGISTER: 74 | return new RegisterEvent( 75 | getKeyOrThrow(d, "address", "data"), 76 | getKeyOrThrow(d, "name", "data"), 77 | getKeyOrThrow(d, "value", "data")); 78 | case BREAKPOINT: 79 | return new BreakpointEvent( 80 | getKeyOrThrow(d, "breakpoint", "data" ), 81 | getKeyOrThrow(d, "action", "data" )); 82 | case MEMORY: 83 | return new MemoryEvent( 84 | getKeyOrThrow(d, "address", "data" ), 85 | getKeyOrThrow(d, "name", "data" ), 86 | getKeyOrThrow(d, "data", "data" ), 87 | getKeyOrThrow(d, "size", "data" ), 88 | getKeyOrThrow(d, "read", "data" ).equals("True"), 89 | getKeyOrThrow(d, "write", "data" ).equals("True"), 90 | getKeyOrThrow(d, "execute", "data" ).equals("True") 91 | ); 92 | } 93 | 94 | 95 | } catch (org.json.simple.parser.ParseException e) { 96 | throw new ParseException(e.getMessage()); 97 | } 98 | 99 | // we should never reach this! 100 | return null; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/EventType.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | public enum EventType { 27 | HELLO, 28 | CURSOR, 29 | REGISTER, 30 | BREAKPOINT, 31 | MEMORY, 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/HelloEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | public class HelloEvent implements Event{ 27 | private String architecture; 28 | private Integer answerPort; 29 | private String endianess; 30 | private String answerIp; 31 | 32 | public HelloEvent(String architecture, String endianess, String answerIp, String answerPort) { 33 | this.architecture = architecture; 34 | this.endianess = endianess; 35 | this.answerIp = answerIp; 36 | this.answerPort = Integer.valueOf(answerPort); 37 | } 38 | 39 | public String getEndianess() { 40 | return this.endianess; 41 | } 42 | 43 | public String getAnswerIp() { 44 | return this.answerIp; 45 | } 46 | 47 | public Integer getAnswerPort() { 48 | return this.answerPort; 49 | } 50 | 51 | public String getArchitecture() { 52 | return this.architecture; 53 | } 54 | 55 | @Override 56 | public EventType getType() { 57 | return EventType.HELLO; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/MemoryEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | import java.io.ByteArrayInputStream; 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.util.zip.GZIPInputStream; 30 | 31 | import ch.ethz.ssh2.crypto.Base64; 32 | import ghidra.app.util.MemoryBlockUtil; 33 | import ghidra.app.util.importer.MemoryConflictHandler; 34 | import ghidra.program.model.address.Address; 35 | import ghidra.program.model.address.AddressOverflowException; 36 | import ghidra.program.model.listing.Program; 37 | import ghidra.util.task.TaskMonitor; 38 | 39 | public class MemoryEvent implements Event { 40 | private String name; 41 | private String address; 42 | private String data; 43 | private boolean readPermission; 44 | private boolean writePermission; 45 | private boolean executePermission; 46 | private long size; 47 | 48 | public MemoryEvent(String address, String name, String data, String size, boolean readPermission, boolean writePermission, boolean executePermission) { 49 | this.address = address; 50 | this.name = name; 51 | this.data = data; 52 | this.size = Long.decode(size); 53 | this.readPermission = readPermission; 54 | this.writePermission = writePermission; 55 | this.executePermission = executePermission; 56 | } 57 | 58 | public String getName() { 59 | return this.name; 60 | } 61 | 62 | @Override 63 | public EventType getType() { 64 | return EventType.MEMORY; 65 | } 66 | 67 | public Address getAddress(Program currentProgram) { 68 | return currentProgram.getAddressFactory().getAddress(address); 69 | } 70 | 71 | public boolean getReadPermission() { 72 | return readPermission; 73 | } 74 | 75 | public boolean getWritePermission() { 76 | return writePermission; 77 | } 78 | 79 | public boolean getExecutePermission() { 80 | return executePermission; 81 | } 82 | 83 | public InputStream getData() { 84 | try { 85 | var decoded = Base64.decode(this.data.toCharArray()); 86 | ByteArrayInputStream bis = new ByteArrayInputStream(decoded); 87 | return new GZIPInputStream(bis); 88 | } catch (IOException e) { 89 | e.printStackTrace(); 90 | } 91 | return null; 92 | } 93 | 94 | public long getDataSize() { 95 | return this.size; 96 | } 97 | 98 | public static void handleEvent(MemoryEvent memEvent, Program currentProgram) { 99 | MemoryConflictHandler memoryConflictHandler = MemoryConflictHandler.ALWAYS_OVERWRITE; 100 | MemoryBlockUtil mbu = new MemoryBlockUtil( currentProgram, memoryConflictHandler ); 101 | try { 102 | var tx = currentProgram.startTransaction("adding memory"); 103 | 104 | 105 | var r = mbu.createInitializedBlock( 106 | memEvent.getName(), 107 | memEvent.getAddress(currentProgram), 108 | memEvent.getData(), 109 | memEvent.getDataSize(), 110 | "", // comment? 111 | "gdb", 112 | memEvent.getReadPermission(), 113 | memEvent.getWritePermission(), 114 | memEvent.getExecutePermission(), 115 | TaskMonitor.DUMMY); 116 | if(r == null) { 117 | var msg = mbu.getMessages(); 118 | if(msg.contains("Overwrote memory")) { 119 | System.out.println("[GDBGhidra] "+ mbu.getMessages()); 120 | } else { 121 | System.err.println("[GDBGhidra] could not write new memory block: "+ mbu.getMessages()); 122 | } 123 | } else { 124 | System.out.println("[GDBGhidra]" + r.toString()); 125 | } 126 | 127 | currentProgram.endTransaction(tx, true); 128 | } catch (AddressOverflowException e) { 129 | e.printStackTrace(); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/ParseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | public class ParseException extends Exception { 27 | public ParseException(String message) { 28 | super(message); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/gdbghidra/events/RegisterEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Comsecuris UG (haftungsbeschränkt) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package gdbghidra.events; 25 | 26 | import java.math.BigInteger; 27 | 28 | import org.json.simple.JSONArray; 29 | import org.json.simple.JSONObject; 30 | 31 | import ghidra.app.cmd.register.SetRegisterCmd; 32 | import ghidra.app.plugin.ProgramPlugin; 33 | import ghidra.framework.cmd.CompoundCmd; 34 | import ghidra.program.model.lang.RegisterManager; 35 | import ghidra.program.model.listing.Program; 36 | import ghidra.program.util.ProgramLocation; 37 | 38 | public class RegisterEvent implements Event { 39 | private String name; 40 | private String value; 41 | private String address; 42 | 43 | public RegisterEvent(String address, String name, String value) { 44 | this.address = address; 45 | this.name = name; 46 | this.value = value; 47 | } 48 | 49 | public String getName() { 50 | return this.name; 51 | } 52 | 53 | public String getHexString() { 54 | return this.value; 55 | } 56 | 57 | public BigInteger getValue() { 58 | if(this.value.startsWith("0x")) { 59 | return new BigInteger(this.value.substring(2), 16); 60 | } 61 | return new BigInteger(this.value, 16); 62 | 63 | } 64 | 65 | public String getAddress() { 66 | return this.address; 67 | } 68 | 69 | @Override 70 | public EventType getType() { 71 | return EventType.REGISTER; 72 | } 73 | 74 | public static void handleEvent(RegisterEvent registerEvent, Program currentProgram, ProgramPlugin plugin, ProgramLocation currentLocation) { 75 | var register = currentProgram.getRegister(registerEvent.getName()); 76 | if(register == null) { 77 | register = currentProgram.getRegister(registerEvent.getName().toUpperCase()); 78 | if(register == null) { 79 | System.err.println("[GDBGHIDRA] Error unknown register: "+registerEvent.getName()+"\n"); 80 | return; 81 | } 82 | } 83 | var address = currentLocation.getAddress(); 84 | var cmd = new CompoundCmd("Set Register Values"); 85 | var regCmd = new SetRegisterCmd( 86 | register, 87 | address, 88 | address, 89 | registerEvent.getValue()); 90 | cmd.add(regCmd); 91 | plugin.getTool().execute(cmd, currentProgram); 92 | } 93 | 94 | public static JSONObject constructJSONResponse(String register, String newValue, String action) { 95 | var response = new JSONObject(); 96 | var datamap = new JSONObject(); 97 | var data = new JSONArray(); 98 | 99 | response.put("type", "REGISTER"); 100 | datamap.put("register", register); 101 | datamap.put("value", newValue); 102 | datamap.put("action", action); 103 | data.add(datamap); 104 | response.put("data", data); 105 | 106 | return response; 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /src/main/resources/images/README.txt: -------------------------------------------------------------------------------- 1 | The "src/resources/images" directory is intended to hold all image/icon files used by 2 | this module. 3 | -------------------------------------------------------------------------------- /src/test/java/README.test.txt: -------------------------------------------------------------------------------- 1 | The "test" directory is intended to hold unit test cases. The package structure within 2 | this folder should correspond to that found in the "src" folder. 3 | --------------------------------------------------------------------------------