Skip to content

Commit d1cde4f

Browse files
committed
More documentation
1 parent 4522868 commit d1cde4f

File tree

1 file changed

+119
-13
lines changed

1 file changed

+119
-13
lines changed

documentation/Manual.md

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
# Binary Ninja Debugger Manual
22

3+
## Document Conventions
4+
5+
Terms in **bold** can be found in the Glossary section. Strings `highlighted` refer to code, paths, or commands executed at a terminal.
6+
7+
`BNDP_ROOT` refers to the path where the debugger is installed.
8+
9+
## Table of Contents
10+
311
[toc]
412
# Introduction
513

6-
Binary Ninja Debugger Plugin (BNDP) is a plugin for Binary Ninja implementing debug functionality. It can be used from within the GUI or outside (headless). It officially supports 64-bit Windows, Linux, and MacOS targets. Its extensible design makes it easily adapted to other platforms.
14+
Binary Ninja Debugger Plugin (**BNDP**) is a plugin for Binary Ninja implementing debug functionality. It can be used from within the GUI or outside (headless). It officially supports 64-bit Windows, Linux, and MacOS targets. Its extensible design makes it easily adapted to other platforms.
715

816
# Installation
917

@@ -131,43 +139,133 @@ The python package `colorama` is required and can be installed with: `pip3 insta
131139

132140
Simply execute `BNDP_ROOT/test.py`.
133141

134-
# Internals
142+
# Design
143+
144+
## Overview
145+
146+
BNDP is a convenient means to interact with various debugger **backends**. It does not instrument targets itself.
147+
148+
The backends vary considerably, so a large part of BNDP's task is to present a unified interface by which these backends can be used.
135149

136-
## Multiplatform Architecture
150+
This is achieved using debug **adapters**, named so because they adapt whatever ideosyncracies a backend has to the ideal interface we envision in `DebugAdapter.py`. Each adaptation of a backend is a subclass of `DebugAdapter`, for example `DebugAdapterGdb` and `DebugAdapterLLDB` communicate with gdb and lldb backends, respectively.
137151

138-
BNDP can connect to three backends: gdb, lldb, and dbgeng. The target is decoupled from BNDP in the first two modes, communicating using the [RSP protocol](https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html) over a socket. Theoretically any platform for which a gdbserver exists can be debugged. In dbgeng mode, BNDP is runtime linked to [Windows debugger engine](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/introduction) and can debug local binaries.
152+
## Standard Backends
153+
154+
BNDP can connect to three **backends**: gdb/gdbserver, lldb/debugserver, and **dbgeng**. The target is decoupled from BNDP in the first two modes, communicating using the [RSP protocol](https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html) over a socket. Theoretically any platform for which a gdbserver exists can be debugged. In dbgeng mode, BNDP is runtime linked to [Windows debugger engine](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/introduction) and can debug local binaries.
155+
156+
The gdb backend isn't a single piece of software or even a reliable behavior. It's a specification, and developers who wish to expose debug functionality are free to implement whatever subset of the specification they choose. For example, the Multiple Arcade Machine Emulator (MAME), BOCHS emulator, and VMware have the option to expose a gdb interface. So further adaptation is require depending on the gdb backend implementation. For example, we have `DebugAdapterMameColeco` for the gdb backend implemented by MAME when it's emulating a ColecoVision system with Z80 processor.
139157

140158
![](./resources/backend_arch.png)
141159

142160
BNDP is tested on **x64-linux**, **arm-android** and **aarch64-android** binaries in gdb mode, **x64-macos** binaries in lldb mode, and **x64-windows** binaries in dbgeng mode.
143161

144162
You should have success on the 32-bit x86 versions of the above binaries, but they're not as rigorously tested as 64-bit.
145163

146-
## DebugAdapter And Its Subclasses
164+
## DebugAdapter and its Subclasses
147165

148166
Each of the adapters is expected to provide some primitive operations, like reading registers, stepping, breakpoints, etc. The comprehensive, but still short, list is the abstract functions in class DebugAdapter in [DebugAdapter.py](https://github.com/Vector35/debugger/blob/master/DebugAdapter.py).
149167

150-
With classes and inheritance, we're able to factor out common behavior among adapters. For instance, GDB and LLDB have much in common, with LLDB speaking [an augmented RSP protocol](https://github.com/llvm-mirror/lldb/blob/master/docs/lldb-gdb-remote.txt). The current class diagram has plenty of room for an additional adapter and its corresponding backend:
168+
With classes and inheritance, we're able to factor out common behavior among adapters. For instance, GDB and LLDB have much in common, with LLDB speaking [an augmented RSP protocol](https://github.com/llvm-mirror/lldb/blob/master/docs/lldb-gdb-remote.txt). The current class diagram has plenty of room for an additional adapter and its corresponding **backend**:
151169

152170
![](./resources/adapter_class_diagram.png)
153171

154-
Higher level operations like "step over" are provided by some backends (like dbgeng) but not others (like gdb and lldb). For these, the operation is synthesized with primitive operations. "Step over" might involve disassembling to detect call or loop instructions and setting a one-shot breakpoint. "Go up" might involve reading the stack and setting a one-shot breakpoint at the return address.
172+
Higher level operations like "step over" are provided by some backends (like **dbgeng**) but not others (like gdb and lldb). For these, the operation is synthesized with primitive operations. "Step over" might involve disassembling to detect call or loop instructions and setting a one-shot breakpoint. "Go up" might involve reading the stack and setting a one-shot breakpoint at the return address.
155173

156-
## DebugProcessView
174+
## UI: DebugProcessView
157175

158176
`DebugProcessView` is a specialized `BinaryView` that reads and writes its memory from the connected `DebugAdapter`. When the adapter is not present, all reads/writes will return `None` to indicate an error. To save on data transfer, `DebugProcessView` caches all reads from the adapter. Whenever the debugger executes instructions or writes data it will call `mark_dirty` on the `DebugProcessView` and clear the cached data.
159177

160178
`DebugProcessView` provides two functions, `local_addr_to_remote` and `remote_addr_to_local` which will translate addresses for use in binaries that are compiled with Position Independent Code. **Local addresses** correspond to the loaded `BinaryView` analysis and **remote addresses** represent addresses in the debugged binary, which may be relocated in PIE executables.
161179

162-
# For Developers
180+
# API
181+
182+
Prerequisite reading: "Design" section
183+
184+
The API is centered around the `DebugAdapter` class. You can subclass it to interface with a new **backend**.
185+
186+
You can use existing subclasses `DebugAdapterGdbLike`, `DebugAdapterGdb`, `DebugAdapterLLDB`, and `DebugAdapterMameColeco` to connect to already tested backends.
187+
188+
You can programmatically debug targets by using `DebugAdapter` methods.
163189

164-
## The DebugAdapter Ideal
190+
## DebugAdapter Methods
165191

166-
The DebugAdapter wants to present a general debug interface, hiding the ideosyncracies of whatever backend it is connected to. Often you will have to "shape" the behavior of the backend to aim for this ideal.
192+
### Initialization Methods
193+
194+
Some **backends** need to perform some initialization before acquiring a target. For example, **dbgeng** needs to acquire COM interfaces.
195+
196+
```
197+
def setup()
198+
def teardown()
199+
```
200+
201+
### Session Start/Stop Methods
202+
203+
These functions start and end a session. A session can be started by executing a target,, attaching to a target, or connecting to a listening **backend**. Detaching, when possible, means disconnecting the debugger and allowing the target to resume execution. Quitting means to end the session entirely.
204+
205+
```
206+
def exec(self, path, args=[])
207+
def attach(self, pid)
208+
def connect(self, server, port)
209+
def detach(self)
210+
def quit(self)
211+
```
212+
213+
### Target Info Methods
214+
215+
Acquire the target's architecture, execution path, process ID, and load address. Note that these are not always applicable for certain targets. For example, pid does not make sense when connected to a full system BOCHS emulation.
216+
217+
```
218+
def target_arch(self)
219+
def target_path(self)
220+
def target_pid(self)
221+
def target_base(self)
222+
```
223+
224+
See `BNDP_ROOT/DebugAdapter.py`.
225+
226+
### Thread Methods
227+
```
228+
def thread_list(self):
229+
def thread_selected(self):
230+
def thread_select(self, tidx):
231+
```
232+
233+
### Breakpoint Methods
234+
```
235+
def breakpoint_set(self, address)
236+
def breakpoint_clear(self, address)
237+
def breakpoint_list(self)
238+
```
239+
240+
### Register Methods
241+
```
242+
def reg_read(self, reg)
243+
def reg_write(self, reg, value)
244+
def reg_list(self)
245+
def reg_bits(self, reg)
246+
```
247+
248+
### Memory Methods
249+
```
250+
def mem_read(self, address, length)
251+
def mem_write(self, address, data)
252+
def mem_modules(self, cache_ok=True)
253+
```
254+
255+
### Execution Control Methods
256+
```
257+
def go(self):
258+
def step_into(self):
259+
def step_over(self):
260+
```
261+
262+
## Subclassing DebugAdapter
263+
264+
The DebugAdapter wants to present a general debug interface, hiding the ideosyncracies of whatever **backend** it is connected to. Often you will have to "shape" the behavior of the backend to aim for this ideal.
167265

168266
For example, in Windows, setting a breakpoint always succeeds, no matter the address. It's only at the next go/step/step that bad addresses will raise a write exception because that's when the engine actually writes the 0xCC byte. But the designed behavior of `breakpoint_set()` in DebugAdapter is to write immediately, and provide instant response to the caller if the breakpoint wasn't set correctly. So a `WriteProcessMemory()` is used as a probe, and if it succeeds, the adapter pretends the breakpoint is as good as set.
169267

170-
On Windows, `step over` is available from the dbgeng backend, but with gdbserver and debugserver as backends, it is not. To attain this behavior, `step over` is synthesized by setting a temporary breakpoint at the following instruction, which requires analyzing the current instruction for length and branch behavior.
268+
On Windows, `step over` is available from the **dbgeng** backend, but with gdbserver and debugserver as backends, it is not. To attain this behavior, `step over` is synthesized by setting a temporary breakpoint at the following instruction, which requires analyzing the current instruction for length and branch behavior.
171269

172270
The stubs implemented by gdbserver and debugserver will not step over an address that has a breakpoint. The program counter will remain the same and a breakpoint event will occur. The breakpoint must be temporarily cleared. The MAME stub, however, has no such restriction.
173271

@@ -183,7 +281,7 @@ lldb has single reg writes with 'P' packet, gdb doesn't, and registers must be w
183281

184282
lldb can list solibs and executable image with 'jGetLoadedDynamicLibrariesInfos' packet, gdb still looks to /proc/pid/maps.
185283

186-
## Debugging the Debugger
284+
## Troubleshooting new GDB Backends
187285

188286
### General Tips
189287

@@ -211,3 +309,11 @@ Monitoring, then mimicing behavior from working tools is often faster than start
211309

212310
`(lldb) process connect connect://localhost:31337`
213311

312+
# Glossary
313+
314+
**backend** - This is the code that is actually instrumenting (eg: writing breakpoint bytes, setting the trap flag) in targets. Examples are Microsoft's [debugger engine](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-engine-overview), gdbserver, debugserver, or MAME.
315+
316+
**BNDP** - Binary Ninja Debugger Plugin
317+
318+
**dbgeng** - Microsoft's [debugger engine](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-engine-overview).
319+

0 commit comments

Comments
 (0)