You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
+
3
11
[toc]
4
12
# Introduction
5
13
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.
7
15
8
16
# Installation
9
17
@@ -131,43 +139,133 @@ The python package `colorama` is required and can be installed with: `pip3 insta
131
139
132
140
Simply execute `BNDP_ROOT/test.py`.
133
141
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.
135
149
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.
137
151
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.
139
157
140
158

141
159
142
160
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.
143
161
144
162
You should have success on the 32-bit x86 versions of the above binaries, but they're not as rigorously tested as 64-bit.
145
163
146
-
## DebugAdapter And Its Subclasses
164
+
## DebugAdapter and its Subclasses
147
165
148
166
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).
149
167
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**:
151
169
152
170

153
171
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.
155
173
156
-
## DebugProcessView
174
+
## UI: DebugProcessView
157
175
158
176
`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.
159
177
160
178
`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.
161
179
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.
163
189
164
-
## The DebugAdapter Ideal
190
+
## DebugAdapter Methods
165
191
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.
167
265
168
266
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.
169
267
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.
171
269
172
270
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.
173
271
@@ -183,7 +281,7 @@ lldb has single reg writes with 'P' packet, gdb doesn't, and registers must be w
183
281
184
282
lldb can list solibs and executable image with 'jGetLoadedDynamicLibrariesInfos' packet, gdb still looks to /proc/pid/maps.
185
283
186
-
## Debugging the Debugger
284
+
## Troubleshooting new GDB Backends
187
285
188
286
### General Tips
189
287
@@ -211,3 +309,11 @@ Monitoring, then mimicing behavior from working tools is often faster than start
211
309
212
310
`(lldb) process connect connect://localhost:31337`
213
311
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.
0 commit comments