NationalSecurityAgency / ghidra

Ghidra is a software reverse engineering (SRE) framework
https://www.nsa.gov/ghidra
Apache License 2.0
49.06k stars 5.65k forks source link

New Debugger "qemu + gdb" fails for ARM target #6635

Closed electricworry closed 3 days ago

electricworry commented 2 weeks ago

Describe the bug Starting to play around with the nice new Debugger, when using the "qemu + gdb" profile targetting an ARM static binary, the error "ValueError: Unknown register group name." is output from the gdb python extension.

To Reproduce Steps to reproduce the behavior:

  1. Compile a static arm hello world with arm-linux-gnueabi-gcc -o arm test.c -static
  2. Import binary to Ghidra
  3. Open the binary with the Debugger tool
  4. Debugger > Configure and launch arm using... > qemu + gdb
  5. Settings as follows:
    • Image: path to static arm executable
    • Arguments: NONE
    • QEMU command: qemu-arm
    • QEMU port: 12345
    • Extra qemu arguments: NONE
    • gdb command: gdb-multiarch
  6. Error as follows in Terminal pane:
    Reading symbols from /home/electricworry/projects/debugger/arm...
    (No debugging symbols found in /home/electricworry/projects/debugger/arm)
    Connected to Ghidra 11.1 at 127.0.0.1:38243
    Catchpoint 1 (syscalls 'break' [17] 'brk' [45] 'mmap' [90] 'munmap' [91] 'mprotect' [125] 'msync' [144] 'mlock' [150] 'munlock' [151] 'mlockall' [152] 'munlockall' [153] 'mremap' [163] 'mmap2' [192] 
    'mincore' [219] 'madvise' [220] 'remap_file_pages' [253] 'shmat' [305] 'shmdt' [306] 'mbind' [319] 'get_mempolicy' [320] 'set_mempolicy' [321] 'move_pages' [344])
    Remote debugging using localhost:12345
    0x00010418 in _start ()
    Traceback (most recent call last):
    File "/home/electricworry/ghidra_11.1_PUBLIC/Ghidra/Debug/Debugger-agent-gdb/pypkg/src/ghidragdb/hooks.py", line 161, in _func
    return func(*args, **kwargs)
    File "/home/electricworry/ghidra_11.1_PUBLIC/Ghidra/Debug/Debugger-agent-gdb/pypkg/src/ghidragdb/hooks.py", line 311, in on_stop
    state.record("Stopped")
    File "/home/electricworry/ghidra_11.1_PUBLIC/Ghidra/Debug/Debugger-agent-gdb/pypkg/src/ghidragdb/hooks.py", line 95, in record
    frame, util.get_register_descs(frame.architecture(), 'general'))
    File "/home/electricworry/ghidra_11.1_PUBLIC/Ghidra/Debug/Debugger-agent-gdb/pypkg/src/ghidragdb/util.py", line 382, in get_register_descs
    return arch.registers(group)
    ValueError: Unknown register group name.
    Python Exception <class 'ValueError'>: Unknown register group name.
  7. i r $pc indicates that process is suspended at _start(), however, resume and step functions are ghosted in the Ghidra Debugger
  8. Entering c in the terminal allows the process to continue until termination.

Expected behavior Ghidra Debugger should recognise that the target process is suspended and display current code position, registers, etc.

Environment (please complete the following information):

electricworry commented 2 weeks ago

I also tried my own build of qemu-arm version 9.0.1, but there was no improvement, so the QEMU version doesn't appear to be relevant.

electricworry commented 1 week ago

I've done some digging. Problem occurs on gdb v12.1, but not on v13.1.

As you know, the problem occurs in Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py, when the attempt to get the register group 'general' fails.

This was fixed between release gdb-12-branchpoint and gdb-13-branchpoint in commit e7d69e72bfd16113a6bbbebfb8a92126245a4106, and standardised the creation of the register groups. (Before that change, some of the architectures were creating the groups - e.g. intel and aarch64 - and some were not - as is the problem with arm.)

I think the solution would either be to assert that gdb version is >= 13.1 for arm targets, or to change the line to return arch.registers() if that doesn't cause any regressions.

nsadeveloper789 commented 1 week ago

Oof! It's always fun discovering all these version nuances. Thanks for pointing this one out. Can you try the following patch for us, please?

diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py
index f883d39c54..b325d04bec 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py
@@ -379,7 +379,10 @@ class RegisterDesc(namedtuple('BaseRegisterDesc', ['name'])):

 def get_register_descs(arch, group='all'):
     if hasattr(arch, "registers"):
-        return arch.registers(group)
+        try:
+            return arch.registers(group)
+        except ValueError: # No such group, or version too old
+            return arch.registers()
     else:
         descs = []
         regset = gdb.execute(

Just does a try-except and then falls back to arch.registers() as you suggested. We're curious how this performs in 12.1 when it does fall back. Does the experience get sloggish because of the high number of registers being refreshed each step?

electricworry commented 1 week ago

Sounds good. Give me a few days, I'll try it on arm, aarch64, ppc, and intel - the targets I'm interested in. I can't guarantee that I'll spot every problem but I'll at least check that it seems to behave well for old gdb and new across those targets. Thanks.

electricworry commented 1 week ago

I've tested on arm, aarch64, i386, x86_64, mipsel, and powerpc - both dynamic and static - and all appear to be fine with the change @nsadeveloper789 proposed while running gdb-multiarch GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1