Skip to content

Instantly share code, notes, and snippets.

@aldelaro5
Last active October 4, 2023 07:56
Show Gist options
  • Save aldelaro5/d532b21b5e2ec48d5f78e81846c1c1b7 to your computer and use it in GitHub Desktop.
Save aldelaro5/d532b21b5e2ec48d5f78e81846c1c1b7 to your computer and use it in GitHub Desktop.
Debugging with ghidra + mgba's GDB server

CURRENTLY WORKS MOSTLY FOR LINUX, WINDOWS INSTRUCTIONS WILL BE WRITTEN EVENTUALLY

Requirements

  • mgba BUILT FROM SOURCE, this is because you need some changes that as of this writting, aren't present in the lattest version (0.9.3). They will only be present in a release in 0.10.0+
  • Ghidra 10.0.3+ (you NEED 10.0.3+ due to fixes being applied that specifically impacts this process as well as a feature that allows to sync the static listing with the dynamic listing). I recommend however to install the lattest version at the time you are setting this up and upgrade as you feel like it (projects are usually upwards compatible, but the opposite isn't true).
  • gdb-multiarch. You need the multiarch version because it has every targets enabled includring armv4t which is the one we care about here.
  • GhidraGBA extension (you need to modify extension.properties in the zip to have the version say your ghidra's version (like "10.0.3" or "10.1") so it can be installed).
  • If you want better step over experience, you will want the ExportDwarfELFSymbols script here (please follow the setup instructions in that readme): https://github.com/aldelaro5/ghidra-ExportDwarfELFSymbols

Setup static analysis:

  • Install the GhidraGBA extension (file -> install extensions and click the +), restart ghidra for the extension change to take effect
  • Create a project at the desired location (I recommend making a new directory).
  • Import a .gba file into your newly created project, the format should say "GBA Rom", if it says binary file, you didn't properly installed the extension. Leave everything default.
  • Double click the .gba in the project, it will open the default static analysis tool which will IMMEDIATELY ask you to auto analyse, accept and you can leave everything default, but I recommend to enable the "Decompiler parameter ID" analyser since it can really help with the decomp. I also recommend to disable the "Non-Returning Functions - Discovered" because it has given me some issues where it wrongly detects non returning functions.
  • Let it do its work, your gba will be analysed for functions, strongs, data, refferences, etc..., it's magic!
  • Once it's done, save and FROM THIS POINT ON, you can use ghidra STRICTLY for static analysis.
  • This is the time I would highly recommend to use the ExportDwarfELFSymbols script because you will need these symbols for debugging later. You should be running it periodically so GDB has up to date informations.

That alone is cool because you retain every info you gather from reversing, but where this stuff really shines is when you are able to debug using the mgba's gdb stub with the integration of all the info you gathered when doing static analysis.

Setup debugging:

  • In mgba, start an emulation with the same .gba in your project and go to tools, start gdb server. Leave the server and the port default. For "Write watchpoints behavior", you should set it to "Internal change detection" OR "Break on all writes". The former will let mgba decide when to detect a changed write, but it also means ONLY changed writes will trigger on write watchpoints (unchanged ones will not). The later will take complete control of the write watchpoint logic AND break on EVERY writes. You should NEVER leave it on "Standard GDB" because this will leave control to GDB which will cause many issues with write watchpoints such as using savestates. The connexion MUST be stopped and started again to change this setting.
  • Create a text file in an easy place to find with the following content:
define info proc mappings
echo 0x0 0xFFFFFFFF 0x100000000 0x0 mem \n
end
target remote localhost:2345

This will do 2 things: it first overrides the info proc mappings commands because by default, this will return nothing since gdb can't infer how a gba ROM is layed out, but by overriding it by this, you are telling gdb that anything that is a 32 bit address is valid and mapped, it's that dumb, but it works. Finally, it connects to mgba's server via remote debugging (you can technically issue this separately, but meh typing!).

  • Open the debugger tool in ghidra (click the little blue bug icon thingy).
  • For some odd reasons, the debugger tool has a lot of stuff disabled by default and you actually want them for a good debugging experience. The way you do that is in the debugger tool, go to file, configure and click the configure thing underneath "Ghidra Core". Just...check everything that isn't checked and click ok, lol. Then go to configure underneath "Debugger" and do the same thing. Now you have access to everything in the window menu.
  • I would suggest to layout your windows, here's a good example of mine where I feel comfortable: https://i.imgur.com/DNpLnWt.png
  • In the debugger target window, click the icon to create a target, select IN-VM GNU gdb local debugger and set the gdb launch command to be the gdb-multiarch you got from devkitarm and then click connect.
  • You can now talk to GDB directly using the interpreter window. Enter "source [file]" where [file] is the file you created earlier. By issuing this command it will define the proc mappings and attempt to connect to mgba. If you see a thing like [address] in ??, it means it worked (ignore the warning about no executable specified, this is just saying gdb doesn't have symbols which is fine).
  • Optionally, run the symbol-file command with the ELF you generated with ExportDwarfELFSymbols, this will make nexti to work, but also give GDB informations about the functions you are working on.
  • At this point, you should have everything MOSTLY working, the registers, breakpoints and stuff should all work and you should see one region of memory in the region window. You can do step commands and stuff, but there's one thing left: in the modules window, there's a red "map identically" button which you must click once to have the static and dynamic listing sync each other (it also allows to set breakpoints from the static listing).

Important debugging notes

Step over in GDB actually works by GDB essentially guessing how the stack is constructed and due to several limitations, it's impossible to accurately do it, but it is possible to help GDB guess correctly by providing symbols informations. The ExportDwarfELFSymbols allows to export a very basic amount of symbols from ghidra to a format GDB can understand. Since this script as well as the symbols loading must be done on demand, it must be done periodically especially if you figure out more sensitive function boundaries or new functions. If you are curious how mgba does it, it actually has to deal with this same issue so to make sure stacktrace work, it has to actively track it as it runs which is not ideal and isn't something GDB can do.

If you issue a "c" command or press continue in the object window, it will play in mgba, but your gdb will be LOCKED, you will not be able to do commands (if you issue them anyway, it will queue them for when it's not locked anymore which is bad). You can manually unlock it by pressing "break" on mgba's gdb server window which will pause the emu and give you back control in gdb/ghidra. Upon triggering a breakpoint, this also happens so basically DON'T DO ANYTHING WHILE THE EMULATION IS RUNNING! Click Break, THEN do your stuff. You can also break from Ghidra by pressing the yellow pause button in the interpreter window.

@v1993
Copy link

v1993 commented Oct 4, 2023

Amazing tutorial, but something I'd like to ask - any reason for recommending https://github.com/SiD3W4y/GhidraGBA instead of https://github.com/pudii/gba-ghidra-loader? It seems to be better maintained and somewhat more complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment