[HN Gopher] Show HN: Ghidra Plays Mario
       ___________________________________________________________________
        
       Show HN: Ghidra Plays Mario
        
       I've been exploring new ways of testing Ghidra processor modules.
       In this repo, I was able to emulate NES ROMs in Ghidra to test its
       6502 specification, which resulted in finding and fixing some bugs.
       Context: Ghidra is used for reverse engineering binary executables,
       complementing the usual disassembly view with function
       decompilation. Each supported architecture has a SLEIGH
       specification, which provides semantics for parsing and emulating
       instructions, not unlike the dispatch handlers you would find in
       interpreters written for console emulators.  Emulator devs have
       long had extensive test ROMs for popular consoles, but Ghidra only
       provides CPU emulation, so it can't run them without additional
       setup. What I did here is bridge the gap: by modifying a console
       emulator to instead delegate CPU execution to Ghidra, we can now
       use these same ROMs to validate Ghidra processor modules.
       Previously [1], I went with a trace log diffing approach, where any
       hardware specific behaviour that affected CPU execution was also
       encoded in trace logs. However, it required writing hardware
       specific logic, and is still not complete. With the delegation
       approach, most of this effort is avoided, since it's easier to hook
       and delegate memory accesses.  I plan on continuing research in
       this space and generalizing my approaches, since it shows potencial
       for complementing existing test coverage provided by pcodetest. If
       a simple architecture like 6502 had a few bugs, who knows how many
       are in more complex architectures! I wasn't able to find similar
       attempts (outside of diffing and coverage analysis from trace
       logs), please let me know if I missed something, and any
       suggestions for improvements.  [1]:
       https://github.com/nevesnunes/ghidra-tlcs900h#emulation
        
       Author : 0d0a
       Score  : 146 points
       Date   : 2023-09-09 12:42 UTC (2 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | msla wrote:
       | Is it a 6502 processor model in specific? Because the NES used
       | the 2A03 in NTSC regions:
       | 
       | https://www.nesdev.org/wiki/2A03
        
         | 0d0a wrote:
         | I think it's closer to the 2A03. Unless I missed something,
         | there isn't any support implemented for binary-coded decimal
         | mode.
        
       | ComputerGuru wrote:
       | This is great. I'm not clear on if the bugs you are finding are
       | in Ghidra's processor model or in the emulator? (Though I think
       | it's the latter?) Also, why would Ghidra have the best (most
       | accurate?) processor model vs some of the highest quality
       | emulators?
       | 
       | One other question: when the cpu is being emulated at a 50th of
       | its actual speed (or less!) how does replaying recorded input
       | work? Do all games strictly use interrupts to read input or do
       | any poll the state instead (or maybe just at certain sequences or
       | for certain portions of the gameplay)? If the latter, did you
       | have to adjust the key down/key up events you were replaying to
       | avoid a slow-executing cpu missing inputs? (As you might be able
       | to guess, I'm an embedded dev but haven't dabbled with emulators
       | beyond using them.)
       | 
       | Thanks in advance and again, awesome work!
        
         | wgjordan wrote:
         | > I'm not clear on if the bugs you are finding are in Ghidra's
         | processor model or in the emulator? (Though I think it's the
         | latter?)
         | 
         | The project README includes a link to a commit fixing bugs in
         | Ghidra's processor model, here is the author's PR submitting
         | those fixes upstream:
         | https://github.com/NationalSecurityAgency/ghidra/pull/5740
        
         | 0d0a wrote:
         | From what I've seen, it's usually read at the vblank interrupt.
         | 
         | The input recording has entries in format "<instruction_number>
         | <buttons_bitmask>". If I press a button and it's read from the
         | hardware register after let's say 0x1000 instructions have been
         | stepped, it is stored as "0x1000 0x80", and in the Ghidra
         | emulator script, I only need to count up to 0x1000 instructions
         | before I send that memory write to the other emulator. While
         | the real timings are vastly different, the input will be read
         | after roughly the same number of vblank calls. I say "roughly"
         | because indeed I found a differential on the expected call
         | where it should be read, but it isn't yet clear if that's a
         | logic bug on my side, I'll have to eventually look into it
         | again.
        
       | [deleted]
        
       | nneonneo wrote:
       | The emulator in Ghidra is really cool. I've been improving my
       | Wasm processor module to support better emulation, and I've made
       | use of their comprehensive specification tests to validate the
       | implementation.
       | 
       | One thing that I run into a fair bit is the tension between
       | keeping the decompiler output sane vs. implementing every nuance
       | of a particular instruction. Trying to emulate every quirk turns
       | into very complex P-code, which can clutter up the decompiled
       | output. One strategy is to use custom operations (pcodeops) plus
       | an emulator helper, but this makes the operation totally opaque
       | to the decompiler, so it's not suitable for common instructions.
       | 
       | In general though it's super cool to have this kind of
       | functionality available. It will be awesome if Ghidra can someday
       | be a powerful tool for dynamic reverse engineering, not just
       | static reversing.
        
         | 0d0a wrote:
         | Nice to see another CTF enjoyer :) I've always thought about
         | using Ghidra for vm challenges, but I'm still not sure if it
         | fits the typical timeframe. Although I never used it, something
         | like binja seems more favourable to quick and dirty scripting.
         | 
         | About custom pcodeops, yeah I was really tempted to use them
         | for TLCS-900. For example, instruction `daa` adjusts the
         | execution result of an add or subtract as binary-coded decimal,
         | and the pcode for that is just inglorious (but I'm sure there's
         | worse out there): https://github.com/nevesnunes/ghidra-
         | tlcs900h/blob/5ff4eb851...
         | 
         | Pretty amusing how a single instruction takes more than a dozen
         | lines in the decompilation:
         | https://gist.github.com/nevesnunes/7417e8bec2cddfcaf8d7653c9...
        
         | [deleted]
        
       | hatsunearu wrote:
       | so glad Ghidra was released for free!
        
       | ipnon wrote:
       | Excellent results. As I'm sure you'll agree there are many stones
       | left to overturn in researching how to play video games without
       | direct human input. I'm looking forward to your next
       | developments.
        
         | 0d0a wrote:
         | Thanks, but I think I'm going to disappoint you: the demo is
         | using pre-recorded manual inputs, which are then replayed when
         | emulating in Ghidra. The only logic involved is checking when
         | we are at the right instruction to then send the input. I
         | mentioned it briefly in the README but maybe I wasn't very
         | clear, sorry!
        
       | dlandau wrote:
       | I thought I recognized the GitHub username, you contributed to my
       | GNOME Shell extension years ago!
        
         | askiiart wrote:
         | Just wondering, what was the extension?
        
       | mohn wrote:
       | Cool project! I'm very interested in accurate preservation of the
       | behavior of these old systems (chip decapping and scanning, FPGA
       | reimplementation, accuracy-focused emulators) and using Ghidra to
       | reverse engineer old games, especially on the 6502 and m68k
       | architectures. Just an enthusiastic spectator at this point, but
       | I hope to contribute something to the field eventually.
       | 
       | A sidenote: the action at 0:19 in the 50x-speed demo is
       | intriguing. I've played many hours of Super Mario Brothers and
       | watched various tool-assisted speedruns of it, but I don't recall
       | seeing a Goomba reverse direction like that instead of just
       | plowing into Mario. Is that a game glitch that you intended to
       | show off with your recorded keyboard inputs? I haven't played in
       | a long time, so I also wouldn't be surprised to hear that such
       | behavior is common. I didn't find an obvious reference to it in
       | the TAS info here [0].
       | 
       | Edit: there is precedent for that Goomba behavior [1].
       | 
       | [0] https://tasvideos.org/GameResources/NES/SuperMarioBros
       | 
       | [1]
       | https://www.reddit.com/r/Mario/comments/add1fx/changing_goom...
        
         | cylon13 wrote:
         | I think that goomba bumped into the squished goomba Mario had
         | just squished. Mario was just a bit to the left so the flat
         | goombas hit box stuck out to the right a bit and the other
         | goomba hit it.
        
           | mohn wrote:
           | Ahh, yes indeed.
        
       | JohnMakin wrote:
       | Very old ML project from a master's student's thesis which is
       | what originally got me into CS. He taught it to play NES games
       | other than mario and had a good breakdown of his results.
       | 
       | http://tom7.org/mario/
        
         | [deleted]
        
         | _joel wrote:
         | Jeez, I remember when that came out, "very old", feels like
         | yesterday
        
       ___________________________________________________________________
       (page generated 2023-09-11 22:00 UTC)