Skip to content

Instantly share code, notes, and snippets.

@aldelaro5
Last active October 4, 2023 07:56
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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.

@tommai78101
Copy link

tommai78101 commented Feb 18, 2023

Windows 10 / 11 Instructions

System Requirements

Terminology

Windows = The host Windows operating system that will be running WSL.
WSL = The Windows Subsystem for Linux instance of your choice.
Ghidra = The extracted Ghidra build from the ZIP archive for Windows.

Steps

  1. Install WSL from the Microsoft Store.
  2. Make sure to set WSL version to 2. If it's not yet set to version 2, use the command wsl --set-version <distro name> 2, replacing <distro name> with the name of the Linux distribution that you want to update.
  3. On Windows, grab the latest Ghidra release and extract it to some desired location of your choice.
  4. In WSL, make sure to run the commands:
sudo apt install gdb-multiarch net-tools build-essentials git openjdk-17-jdk openjdk-17-jre
  1. On Windows, head over to Gradle Installation webpage and download the Gradle 7.6 release (required for building Ghidra extensions, as of February 18, 2023).
  2. Extract the Gradle ZIP file, and the ExportDwarfELFSymbols scripts to a desired location of your choice.
  3. On Windows, open PowerShell.
  4. Use the command to set the following environment variables, replacing the paths to the respective directory locations. If you have a different configuration method for setting up environment variables for WSL to access Windows applications, feel free to use it:
setx GRADLE_HOME "<path/to/gradle root directory>"
setx JAVA_HOME "<path/to/java root directory>"
setx WSLENV "$env:WSLENV:GRADLE_HOME"/p
setx WSLENV "$env:WSLENV:JAVA_HOME"/p
  1. On Windows, navigate to the GhidraGBA extension source code directory.
  2. Run gradle -PGHIDRA_INSTALL_DIR=<path/to/ghidra 10.2.3+> so Gradle will build the extension off of Ghidra 10.2.3 with Java 17. Replace the path to Ghidra 10.2.3 with the absolute path to the Ghidra root directory on Windows.
  3. In WSL, navigate to Ghidra and launch the ./ghidraRun script. You should now be running Ghidra in WSLg.
  4. In Ghidra, load the GhidraGBA extension through `File -> Install Extensions...".
  5. At the top right corner, click on the green + icon. and add the GhidraGBA ZIP file from the /mnt/<drive>/path/to/GhidraGBA/dist/ location.
  6. Enable the GhidraGBA extension by ticking the checkbox, and hit OK.
  7. In Ghidra, add the ExportDwarfELFSymbols JAR file in the Edit -> Plugin Path... -> Add Jar.
  8. In WSL, restart Ghidra.
  9. On Windows, launch mGBA, load the ROM, and enable the GDB server with the default settings.
  10. Follow the instructions for the static analysis setup and the setup debugging sections in the README above. Move to the next step when it is time for you to create a new file to put the define info proc mappings...
  11. In WSL, do ip route. Grab the IP address that comes after the default via text. That is your localhost IP address binding from WSL to Windows.
  12. In the file for define info proc mappings..., change it to the following contents and replace <IP Address> with the IP address you copied from WSL:
define info proc mappings
	echo 0x0 0xFFFFFFFF 0x100000000 0x0 mem \n
end

define next_multi
	set $next_mult_max = 1000
	if $argc >= 1
		set $next_mult_max = $arg0
	end

	set $next_mult_count = 0
	while ($next_mult_count < $next_mult_max)
		set $next_mult_count = $next_mult_count + 1
		printf "nexti #%d\n", $next_mult_count
		nexti
	end
end

target remote <IP Address>:2345
  1. Continue with the Ghidra GDB setup and set the GDB to your gdb-multiarch installed in WSL.
  2. When you are ready and the GDB is connected to the mGBA instance running in Windows, in the interpreter, type the command source to the file given above.
    • This will allow you to automatic your instructions stepping by specifying (gdb) next_multi N where N is the number of instructions to step. It's great for animating the code execution on mGBA.

From this point onwards, you should now be able to debug and disassemble the GBA ROM file using Ghidra in WSL.

@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