NationalSecurityAgency / ghidra

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

[Debugger]: Show recorded register values for non-current stack frames #2868

Open chezwicker opened 3 years ago

chezwicker commented 3 years ago

Is your feature request related to a problem? Please describe.

Not really a problem, just something I noticed and thought it could be improved: register values for non-current stack frames are shown as 0 instead of what they were before the CALL.

Describe the solution you'd like

It would be nice for register values to be shown in the "Registers" component for non-current stack frames selected in the "Stack" component.

d-millar commented 3 years ago

Looking into this today - want to make sure I understand what we're currently doing so I can say something coherent. :)

d-millar commented 3 years ago

So, an interesting idea.... A couple of pieces to consider: the Register Viewer, like most of the viewers except the Objects Viewer, is intended to reflect the values at the currently selected snap for the currently selected thread. Adding a dependence on the selected stack frame adds a third variable. At a minimum, we'd have to make it very clear in the Viewer what was going on. Also, we'd definitely have to make sure that selecting a frame adjusted the current thread in the thread viewer.

The trickier issue is whether we really want to pull all the register values for all the frames for all the threads whenever the execution stops. That's relatively expensive. In the gdb case, we capture all the registers for the current frame, if I remember correctly. The Object Viewer display per-frame register trees under each frame node. We could do this for Windows (if I can remember how the GetContextStackTrace API works). My initial inclination would be to do this on demand, although we could also mirror the gdb approach.

The really really tricky question would be, assuming you wanted the Register viewer to respond to thread, snap, and frame, what would you want the snap for each frame to be. Would you want to pull all the values on each stop and mark them / record them with that snap? Would you want to record them only on the snap corresponding to the call? What if the target never stopped on that call?

Anyway, some things to think about.

d-millar commented 3 years ago

(Actually, hmmm, guess it would be GetValue2 vs GetContextStackTrace)

nsadeveloper789 commented 3 years ago

The UI and trace database does already support per-frame register values. However, as noted, we don't record non-zero frames by default. Much of this behavior depends on the debugger/connector's implementation. IIRC, in both dbgeng and GDB connectors, we record frame 0 of all live threads each time the debugger breaks. That alone can get expensive when there are many threads about. Doing that for every frame on every thread will greatly increase the delay between the debugger breaking and the user seeing updated information on screen. There are some hacks/heuristics we could consider, but they get hairy:

  1. We could, when we detect a break from completing a single-step, query only the event thread's frames.
  2. We could provide some sort of user configuration. Trouble is, we don't have a generic way of sending such options from the UI to the model, except at start time.
  3. We could stick to frame 0, but after a certain lapse of time between commands, fetch additional frames.

I don't particularly care for such heuristcs. FWIW, with the GDB connector, if you view a frame "in the present," it should fetch and record its registers, and you can review them at a later time. However, if you don't view a non-zero frame's registers, once you step forward, you can't go back and review them.

chezwicker commented 3 years ago

@nsadeveloper789: would it be an option to automatically break and record for each CALL if an according option was enabled? To be honest, I don't know how feasible this is - I assume this would require setting a cascade of software breakpoints based on a prior static analysis? If it's possible, maybe a secondary (e.g. circular) buffer could keep the according values and only if the user breaks somewhere would the relevant ones be added to the primary trace, after which the buffer could be cleared.

d-millar commented 3 years ago

@chezwicker Apologies for jumping in here given your question was addressed to @nsadeveloper789 , but...

You can actually do what you're describing - well almost. Intel processors have support for step-by-branch, and you can set-up handlers to automatically record state and resume. In fact, Intel has circular buffers that will do all that for you - and you really really want the processor to do that for you because the back-and-forth between the target and the debugger is way too expensive to do anything sizeable. So, what you really want is a debugger that supports tracing.

Have you looked at Windbg Preview by any chance? It does pretty much all this and more, at least for user-mode processes. And, if you want to interact with Ghidra, we support that too using the dbgmodel (vs dbgeng) interfaces. You can also record traces in Windbg independently and load them into Ghidra in various ways. Let us know if you want to try this out - we can certainly give you some tips for the Ghidra piece.

I should caveat this with the fact that Microsoft has just release da new version, and we're having a little difficulty getting the integration right with it. Yell, and I can point you to a version that works. In the gdb space, at some point we hope to support RR but really haven't started working on that bit yet.

chezwicker commented 3 years ago

@d-millar : oh, I'm so sorry, I didn't mean to cut you out!

I'm unfortunately spread pretty thinly at the moment, so I might not get to look into this before June (optimistically) depending on the effort required. Would you be able to give me an indication of what kind of effort would be involved in making this happen?

Thank you!

P.S. I realize I'm opening all these issues so it's totally reasonable to expect me to spend more time on this - I'm sorry I can't!

d-millar commented 3 years ago

No worries - I certainly understand. So the basic outline is this: (1) Download Windbg Preview (typically done through the Microsoft Store) (2) Copy the contents of the amd64 directory from the install directory (C:\Program Files\WindowsApps\Microsoft.Windbg.very_long_name\bin\amd64 to the bin directory in your JDK install (3) Run either the GADP or IN-VM dbgmodel versions of the Ghidra debugger rather than dbgeng

The tricky bits are for (1) you may not want the latest version until we figure out how to link it, and I'm not sure where the best place to download older versions from is, and for (2) that directory is hidden so best to get it via the command line. Everything else depends on what you want to do.

No rush in any case - we plan to be around and with luck the interface will get better not worse. :)