Replies: 3 comments 5 replies
-
@astrelsky @nsadeveloper789 is doubtless the best person to answer your questions, but he's out-of-office for the next two weeks so I'll take a stab. As you probably already have deduced, the trace-rmi.proto spec is used to generate TraceRmi.java in Ghidra/Debug/Debugger-rmi-trace/build/generated/source/proto/main/java. (I believe this happens as part of "gradle assemblePyPackage", but don't hold me to that.) If you peek at this class, you'll see a slew of subclasses. Most of these are either builders for various objects that might get passed over the wire (addresses, primitives, arrays, object) or request/reply pairs. Most of the request/reply pairs relate to what I would call state methods - read/write registers, read/write memory, various bookkeeping functions, e.g. create trace. These also handle "objects" which are very generic. We're following the dbgmodel approach here, i.e. you specify a schema describing parents and their children and you populate objects with elements or attributes. Elements are generally keyed objects of a single type; attributes are a mixed bag, key/value pairs of different types. Processes, threads, modules, etc. are all objects in this sense. Process, thread, module containers have processes, threads, and modules as elements. Things like processes typically have only attributes. It should be noted that almost all of the request/reply pairs follow a push model. The target pushes information to Ghidra, based on either some event, such as a break, or a request from the Ghidra, such as refresh. Data is generally not returned directly in the reply. In the agent implementations, the event logic is generally in a file like hooks.py, whereas the refresh logic is kept in commands.py. Hooks will reference methods in commands. Methods.py comprises all of the logic we used to refer to as control/extended methods. Methods (to finally get to your initial question) includes step, break, continue, set a breakpoint, etc. The methods are not predetermined. Methods are registered on start-up by the agent and made available to various objects, e.g. in the tree, or to the command line. The annotation logic directs the registration process, i.e. specifies parameters, display characteristics, object associations, etc. In the python logic, look for @REGISTRY.method refs. Some of the "action" annotations are "special', i.e. have builtin behaviors, such as "refresh". Also of note, the XRequest/ReplayInvokeMethod pair is handled slightly differently than the other pairs (hence the X). As a bit of history, we used to split methods into state (read/write), control, and extended. "Extended" were target-specific commands not generally supported by all debuggers. It turns out most of the control commands really fall into the same bucket, i.e. they don't generalize very well. Breakpoints are the worst case in this respect. Even for something like gdb, the low-level RSP interface is not well standardized. The new design is considerably more flexible and extensible, but also a bit harder to understand because of its genericness. Similarly, we used to always have specific objects for process, thread, module, breakpoint, but the more generic model adapts to new debuggers, debugger variants, and targets more seemlessly. For more info, if you haven't already, take a look at the B5 sections of the class. I tried to document the process of writing a new agent as I implemented it (vs after), the example being "drgn". Also, we have one non-python agent, which still uses protobuf despite technically not needing it: Debugger-jpda which handles Java/Dalvik. Of course, am happy to answer more questions - you might be the first person to attempt writing a custom debugger agent, so we're super-interested in your feedback. |
Beta Was this translation helpful? Give feedback.
-
Let me see if I can simplify things... In our current working model, there are effectively always two communication streams, one between the GUI and the agent and one between the agent and the target. If the agent is guaranteed to be on the same machine as Ghidra proper (i.e. the GUI) and you plan on writing the agent in Java, there is technically no need for the first stream. That said (having done this for Java/Dalvik), it's still MUCH easier to implement that stream. Why - because the GUI already assumes that model. In the case of the second stream, we by-and-large do not implement the protocol. These protocols are very brittle in most cases and change frequently. (I will avoid horror stories, here....) We used to implement the agents in Java, but this implementation was also inherently brittle, i.e. we used JNA to wrap dbgeng DLLs, JNI/Swig to talk to the lldb libraries, and the MI/2 interface to talk with gdb. All of these target have well-defined python3 interfaces, however, so we made the shift. From your description, sounds like you may be in the position of implementing both streams. I think you'll still want to avoid making the first stream direct. The first-stream logic you'll want to implement will parallel that in Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda, i.e. you'll probably want JdiCommands/Methods/Hooks equivalents. The code in ghidra/dbg/jdi/manager is essentially the second stream, i.e. all the actual JDI/JPDA logic. For Java, obviously, this second stream is virtual, no protocol involved, just direct access. |
Beta Was this translation helpful? Give feedback.
-
Oh, and, perhaps obvious (?), ghidratrace is the middleman for the first stream. (And the type stuff is mostly there to keep us from shooting ourselves in the foot with python.) |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I've been looking through this and am a bit confused because there doesn't seem to be anything for the server to know when to create breakpoints, step and stuff like that.
Using protobuf provides a unique opportunity for systems where python is not supported, gdb isn't supported and/or a proprietary debug library/protocol is present. I can run golang on the system without a problem because I ported the tool chain so this seems like the obvious thing to do. I'm just really confused about how this system is supposed to work.
I'm still mostly just thinking out loud at this point, so no rush on answering this. I have to fully reverse engineer the debug protocol for my target and find out if I can even use the breakpoints without causing a system panic.
Beta Was this translation helpful? Give feedback.
All reactions