Visual GDB + Microsofts VS Community Edition+ Ghidra + QEMU ??

Started by heder, February 08, 2021, 03:02:18 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

heder

Hi all, I need a debugger setup for debugging QEMU.

I never liked gdb text interface, it's rubbish! I'm used to work with Microsoft debugger for windows programs and Segger's Ozone debugger with the J-Trace PRO debugger for arm development, and nothing compares to this combo, tried many others, but the last combo for embedded is really power full. Here's an long list of visual frontends for GDB is long https://sourceware.org/gdb/wiki/GDB%20Front%20Ends but if you are used to working with said tools, you end up not debugging at all on QEMU, and qprintf becomes you best friend. But in 2020 (at work) we're started to use Visual GDB, a good alternative. It ain't free, but there's and 30 day free trail, and after that you'll need to pay 79€, that's cheap. It can be used with Microsofts free VS Community Edition.

How about this setup
* Microsofts free VS Community Edition.
* Visual GDB
* Ghidra (or IDA)
* QEMU (ML version)

Work flow

Ghidra (or IDA for that matter)
* Disassemble/analyze the camera ROM with Ghidra and let Ghdira auto name all function stubs & data
* Create a Ghidra script to perform advanced function renaming, auto rename function that has a DebugMsg(x,y,"[??] ...")
* Do you own renaming ...
* Use Ghidra2Dwarf to export symbol file


Scripts
* convert Ghidra text file into dwarf debugging file
* convert the camera ROM into a camera elf file
* inject the newly created dwarf file into camera elf file

QEMU
* run the camera elf file in QEMU

Visual GDB
* Connect to remote target
* Debug ...

Anyone tried this combo ? suggestion, ideas are welcome ..   

... some text here ..

names_are_hard

Yes, debugging with Qemu is a pain.  I haven't tried that combo, but I share your dislike of the GDB interface.  It's a powerful debugger, but it's very slow and frustrating to use with every frontend I've tried.  I wasn't aware of Visual GDB, and it gives me some hope.

I will give the free trial of Visual GDB a go, if I can get VS working in Wine (looks like older versions may work well enough).

Exporting function names from Ghidra should be quite easy.  You can also export the decompilation, it may be possible to integrate this with VS, to get "source code" browsing working.  Since there is elf/dwarf integration maybe this only requires mapping Ghidra decomp into dwarf format?  That should be plausible.  If we have to deal with some undocumented MS format it will be much harder.

heder

From https://reposhub.com/android/utils/cesena-ghidra2dwarf.html :

Ghidra2Dwarf is a ghidra plugin that allows to exports informations (such as functions, decompiled code, types) from ghidra to dwarf sections inside ELF binaries.
More specifically it exports inside a source file named ${program}.c all the decompiled functions, and create an ELF binary named ${program}_dbg that can be used to do source code level debugging.


This is much easier that I thought.

Tomorrow to install the Ghidra2Dwarf plugin, export to elf file and run the elf file in QEMU.
... some text here ..

names_are_hard

Always nice when it's already been done :)  Ghidra has a well documented scripting interface and is open source, so it's pretty easy to work with.

Simply running an elf in Qemu won't work as well as existing ML qemu-eos, since that's a patched version of Qemu with extra support for EOS style devices / peripherals.  Definitely still useful for some things, but ideally we can find some way of joining up the Ghidra source view, qemu-eos, and a nice debugger frontend.  Possibly by structuring an ELF so that when it loads, the code gets mapped to mem in the same way that DryOS does it.  Maybe Ghidra2Dwarf already does this for the regions in the memory map.  I can't look at this for several hours.  Maybe you don't need the ELF part, since fundamentally the debug symbols are mapping addresses to lines of source.

heder

Breif update.

After fixing a ordinary python issue in the ghidra2dwarf module, then learning how to re-create ELF files the properly way (ghidra and ghidra2dwarf wants proper ELF files) it looks good.  :)

I took the stripped flat binary bootloader thats is used on the 40D, recreated a ELF file, loaded it into ghidra, analysed it, and then ran the ghidra2dwarf pluging, and got a ELF file with dwarf symbols and the ghidra decompiled source code. The ghidra source code ain't that bad after all, its looks abit like the real source code. Next step is try this on 40D ROM1.BIN, and the try to get GDB to load the dwarf information so we can debug with the ghidra source code in QEMU.


... some text here ..

names_are_hard

Was your "ordinary python issue" this?
Quote
  File "/home/user/ghidra_scripts/ghidra2dwarf.py", line 43, in get_real_address
    return addr.offset - image_base + orig_base
TypeError: unsupported operand type(s) for +: 'long' and 'NoneType'

orig_base is None because the file Ghidra is working from is not ELF.  The ROM file was never an ELF, so, we may have to create one (from within Ghidra?).

names_are_hard

I made a small script to turn the ROM into a mostly valid ELF.  Without this step, ghidra2dwarf doesn't understand how to work with the ROM in Ghidra.

Ghidra can load my faked ELF and ghidra2dwarf almost works to export it.  I think it fails due to some section structures that it doesn't understand how to parse.  However, I don't think this is the best approach.  When you're working in Ghidra you may well want to map some extra memory into your project.  If you do that, the ELF parser is going to get very confused, because that memory isn't mapped by the ELF headers.

I think a better approach would be to extend ghidra2dwarf so it recognises that the project is a ROM, not an ELF, and it generates a fake ELF (with debug info) from the mapped memory.  That way, if you update the project, you can regenerate the ELF + debug.  And, your Ghidra project is actually based on the ROM, not some faked ELF file.

heder

Quote from: names_are_hard on February 15, 2021, 01:12:31 PM
Was your "ordinary python issue" this?
orig_base is None because the file Ghidra is working from is not ELF.  The ROM file was never an ELF, so, we may have to create one (from within Ghidra?).

I got ... "ImportError" on elf.py, so I neded up forcing all code from elf.py into the ghidra2dwarf script, python refused to find elf.py.


Here is my conversion from rom to elf


# strip the first 0x10000, entry is at 0xff810000, and remove the useless end part
cp ROM1.BIN ROM_tmp0.BIN
truncate -s 8388608 ROM_tmp0.BIN
dd if="ROM_tmp0.BIN" of="ROM_tmp1.BIN" bs=65536 skip=1
arm-none-eabi-objcopy --input-target=binary ROM_tmp1.BIN --output-target=elf32-littlearm -B ARM --change-addresses=0xff800000 --rename-section .data=.text ROM1.elf


and I tested this on my bootloader which was a success, but on the real 40D ROM/elf file it failed.
... some text here ..

names_are_hard

objcopy is simpler than the way I was doing it, although it is giving me the wrong arch:

Quote
arm-none-eabi-objdump -f ROM0.elf

ROM0.elf:     file format elf32-littlearm
architecture: armv3m

Don't know if that's important.  Mine does the arch correctly (.ARM.attributes section, couldn't see an easy way to do this with objcopy).

I don't see the elf.py import error.  Did you copy elf.py, ghidra2dwarf.py and ghidra2dwarf.sh into one of your Ghidra script paths?

What was your error from Qemu?

heder

Regardless what I did, copy scripts into ghidra script paths, or just running from ghidra2dwarfs directory, I get ImportError.
I ran ghidra2draw headless (on my 40D.elf) from bash prompt and got:

Traceback (most recent call last):
  File /home/heder/magic-root/ghidra/ghidra2dwarf/src/gd.py, line 1088, in <module>
    add_debug_info()
  File /home/heder/magic-root/ghidra/ghidra2dwarf/src/gd.py, line 132, in add_debug_info
    add_function(cu, f, file_index)
  File /home/heder/magic-root/ghidra/ghidra2dwarf/src/gd.py, line 316, in add_function
    d = res.decompiledFunction.c
AttributeError: 'NoneType' object has no attribute 'c'
INFO  ANALYZING changes made by post scripts: /ROM1.elf (HeadlessAnalyzer)
INFO  REPORT: Post-analysis succeeded for file: /ROM1.elf (HeadlessAnalyzer)
INFO  REPORT: Save succeeded for processed file: /ROM1.elf (HeadlessAnalyzer)

I have'nt had the time to analyze the failure cause.
... some text here ..

names_are_hard

The import thing is a bit strange.  The way import paths work changed with python3, but I would have guessed here it's Ghidra's Jython that is doing the import, so I don't know what could go wrong.

I've seen that "no attribute 'c'" error as well, from GUI.  Might be related to auto-analysis being incomplete when ghidra2dwarf runs.  In GUI, if I manually cancel the analysis, then run ghidra2dwarf script, it works okay.  But Qemu asserts trying to parse the ELF produced via your objcopy method.  I'm not totally sure, but I think this is due to missing Phdr information.  My method (makeelf python module, manually creating an ELF from the ROM), gives a valid Phdr and loads okay in Qemu (after adding a line to eos/dbi/logging.c, near existing load_symbol() usage).  But I don't see any extra symbols in the logs.

names_are_hard

Got it working.



This is using qemu-eos on a normal ROM dump, but pulling the symbols / source code from a converted ELF - use add-symbol-file to bring in the ELF symbols.  Something is off with the source generation / mapping, as the lines don't match up.  Unsure if this is a ghidra2dwarf bug, or some artifact of using one file to give me symbols for another process.  They should be at equivalent addresses but I haven't checked very thoroughly yet.

Using https://github.com/cyrus-and/gdb-dashboard/blob/master/.gdbinit for the fancy GDB interface.  The register window not working should be their bug.  Should work with other debuggers as this is a mostly normal ELF file.

Needed to make a small addition to ghidra2dwarf's elf.py, like this:

Quote
521     SHT_LOPROC = 0x70000000, ''
522     SHT_ARM_ATTRIBUTES = 0x70000003, 'section holds ARM attributes'
523     SHT_HIPROC = 0x7fffffff, ''

That's to parse the .ARM.attributes section in my ELF.  Not sure I need to make that section in the first place.