-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathdebuggercontroller.h
More file actions
463 lines (377 loc) · 18.3 KB
/
debuggercontroller.h
File metadata and controls
463 lines (377 loc) · 18.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
/*
Copyright 2020-2026 Vector 35 Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include "binaryninjaapi.h"
#include "debuggerstate.h"
#include "debuggerevent.h"
#include <queue>
#include <list>
#include <future>
#include <functional>
#include <unordered_set>
#include "ffi_global.h"
#include "refcountobject.h"
#include "debuggerfileaccessor.h"
DECLARE_DEBUGGER_API_OBJECT(BNDebuggerController, DebuggerController);
namespace BinaryNinjaDebugger {
struct DebuggerEventCallback
{
std::function<void(const DebuggerEvent& event)> function;
size_t index;
std::string name;
};
// This is used by the debugger to track stack variables it defined. It is simpler than
// BinaryNinja::VariableNameAndType that it does not track the Variable and autoDefined.
struct StackVariableNameAndType
{
Confidence<Ref<Type>> type;
std::string name;
StackVariableNameAndType() = default;
StackVariableNameAndType(Confidence<Ref<Type>> t, const std::string& n)
{
type = t;
name = n;
}
bool operator==(const StackVariableNameAndType& other) { return (type == other.type) && (name == other.name); }
bool operator!=(const StackVariableNameAndType& other) { return !(*this == other); }
};
struct DebuggerUICallbacks
{
BNDebuggerUICallbacks* m_callbacks;
void* m_context;
void NotifyRebaseBinaryView(uint64_t base);
};
// This is the controller class of the debugger. It receives the input from the UI/API, and then route them to
// the state and UI, etc. Most actions should reach here.
class DebuggerController : public DbgRefCountObject, BinaryNinja::BinaryDataNotification
{
IMPLEMENT_DEBUGGER_API_OBJECT(BNDebuggerController);
struct PendingEvent {
DebuggerEvent event;
std::promise<void> done;
};
private:
DebugAdapter* m_adapter;
DebuggerState* m_state;
FileMetadataRef m_file;
BinaryViewRef m_data;
DebuggerFileAccessor* m_accessor {};
// This is the start address of the first file segments in the m_data. Unlike the return value of GetStart(),
// this does not change even if we add the debugger memory region. In the future, this should be provided by
// the binary view -- we will no longer need to track it ourselves
uint64_t m_viewStart;
struct ControllerState
{
std::mutex mutex;
std::vector<DbgRef<DebuggerController>> controllers;
};
static ControllerState& GetControllerState();
std::atomic<size_t> m_callbackIndex = 0;
std::list<DebuggerEventCallback> m_eventCallbacks;
std::mutex m_callbackMutex;
std::set<size_t> m_disabledCallbacks;
// m_adapterMutex is a low-level mutex that protects the adapter access. It cannot be locked recursively.
// m_targetControlMutex is a high-level mutex that prevents two threads from controlling the debugger at the
// same time
std::mutex m_adapterMutex;
std::recursive_mutex m_targetControlMutex;
// m_adapterMutex2 is similar to m_adapterMutex, but it is used to protect only the Pause/Quit/Detach operation
// These operations cannot be protected by m_adapterMutex, since if the user resume the target and it remains
// running, we need the ability to pause or kill the target
std::mutex m_adapterMutex2;
uint64_t m_lastIP = 0;
uint64_t m_currentIP = 0;
// This is only meaningful after the target exits. In the future, we should enforce a check for the target
// status before returning the value
uint32_t m_exitCode = 0;
bool m_userRequestedBreak = false;
DebugAdapterOperation m_lastOperation = DebugAdapterGo;
bool m_lastAdapterStopEventConsumed = true;
bool m_inputFileLoaded = false;
bool m_initialBreakpointSeen = false;
bool m_firstLaunch = true;
bool m_firstConnect = true;
bool m_firstConnectToDebugServer = true;
bool m_firstAttach = true;
bool m_shouldAnnotateStackVariable = false;
void EventHandler(const DebuggerEvent& event);
void UpdateStackVariables();
void AddRegisterValuesToExpressionParser();
void AddModuleValuesToExpressionParser();
bool EvaluateBreakpointCondition(uint64_t address);
bool CreateDebuggerBinaryView();
DebugStopReason StepIntoIL(BNFunctionGraphType il);
DebugStopReason StepIntoReverseIL(BNFunctionGraphType il);
DebugStopReason StepOverIL(BNFunctionGraphType il);
DebugStopReason StepOverReverseIL(BNFunctionGraphType il);
// Low-level internal synchronous APIs. They resume the target and wait for the adapter to stop.
// They do NOT dispatch the debugger event callbacks. Higher-level APIs must take care of notifying
// the callbacks.
DebugStopReason LaunchAndWaitInternal();
DebugStopReason AttachAndWaitInternal();
DebugStopReason ConnectAndWaitInternal();
DebugStopReason PauseAndWaitInternal();
DebugStopReason GoAndWaitInternal();
DebugStopReason GoReverseAndWaitInternal();
DebugStopReason StepIntoAndWaitInternal();
DebugStopReason StepIntoReverseAndWaitInternal();
DebugStopReason EmulateStepOverAndWait();
DebugStopReason EmulateStepOverReverseAndWait();
DebugStopReason StepOverAndWaitInternal();
DebugStopReason StepOverReverseAndWaitInternal();
DebugStopReason EmulateStepReturnAndWait();
DebugStopReason StepReturnAndWaitInternal();
DebugStopReason StepReturnReverseAndWaitInternal();
DebugStopReason RunToAndWaitInternal(const std::vector<uint64_t> &remoteAddresses);
DebugStopReason RunToReverseAndWaitInternal(const std::vector<uint64_t> &remoteAddresses);
// Whether we can start debugging, e.g., launch/attach/connec to a target
bool CanStartDebgging();
// Whether we can resume the execution of the target, including stepping.
bool CanResumeTarget();
bool ExpectSingleStep(DebugStopReason reason);
std::map<uint64_t, StackVariableNameAndType> m_debuggerVariables;
std::set<uint64_t> m_addressesWithVariable;
std::set<uint64_t> m_oldAddresses;
std::set<uint64_t> m_addressesWithComment;
void ProcessOneVariable(uint64_t address, Confidence<Ref<Type>> type, const std::string& name);
void DefineVariablesRecursive(uint64_t address, Confidence<Ref<Type>> type);
void ApplyBreakpoints();
std::string m_lastAdapterName;
std::string m_lastCommand;
bool m_zeroSegmentAddedByDebugger = false;
BNAnalysisState m_oldAnalysisState = IdleState;
void DetectLoadedModule();
std::mutex m_eventsMutex;
std::condition_variable m_cv;
std::queue<std::shared_ptr<PendingEvent>> m_eventQueue;
std::thread::id m_dispatcherThreadId;
std::atomic_bool m_shouldExit;
std::thread m_debuggerEventThread;
void DebuggerMainThread();
std::unique_ptr<DebuggerUICallbacks> m_uiCallbacks;
uint64_t m_oldViewBase, m_newViewBase;
std::vector<BNAddressRange> m_ranges;
BinaryNinja::Ref<BinaryNinja::AnalysisCompletionEvent> m_rebaseCompletionEvent;
// TTD Code Coverage Analysis
std::unordered_map<uint64_t, uint32_t> m_executedInstructionCounts;
bool m_codeCoverageAnalysisRun = false;
// TTD Position History for back/forward navigation
std::vector<TTDPosition> m_ttdPositionHistory;
int m_ttdPositionHistoryIndex = -1;
bool m_suppressTTDPositionRecording = false;
void RecordTTDPosition();
public:
DebuggerController(BinaryViewRef data);
static DbgRef<DebuggerController> GetController(BinaryViewRef data);
static void DeleteController(BinaryViewRef data);
static bool ControllerExists(BinaryViewRef data);
static DbgRef<DebuggerController> GetController(FileMetadataRef file);
static void DeleteController(FileMetadataRef file);
static bool ControllerExists(FileMetadataRef file);
// Explicitly destroy the current controller, so a new controller on the same binaryview will be brand new.
// I am not super sure that this is the correct way of doing things, but it addresses the controller reuse
// problem.
void Destroy();
~DebuggerController();
// breakpoints
void AddBreakpoint(uint64_t address);
void AddBreakpoint(const ModuleNameAndOffset& address);
void DeleteBreakpoint(uint64_t address);
void DeleteBreakpoint(const ModuleNameAndOffset& address);
void EnableBreakpoint(uint64_t address);
void EnableBreakpoint(const ModuleNameAndOffset& address);
void DisableBreakpoint(uint64_t address);
void DisableBreakpoint(const ModuleNameAndOffset& address);
bool ContainsBreakpoint(const ModuleNameAndOffset& address);
DebugBreakpoint GetAllBreakpoints();
bool SetBreakpointCondition(uint64_t address, const std::string& condition);
bool SetBreakpointCondition(const ModuleNameAndOffset& address, const std::string& condition);
std::string GetBreakpointCondition(uint64_t address);
std::string GetBreakpointCondition(const ModuleNameAndOffset& address);
// hardware breakpoints
// Hardware breakpoint methods - absolute address
bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size);
bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size);
bool EnableHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size);
bool DisableHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size);
// Hardware breakpoint methods - module+offset (ASLR-safe)
bool AddHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size);
bool RemoveHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size);
bool EnableHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size);
bool DisableHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size);
// registers
intx::uint512 GetRegisterValue(const std::string& name);
bool SetRegisterValue(const std::string& name, intx::uint512 value);
std::vector<DebugRegister> GetAllRegisters();
// processes
std::vector<DebugProcess> GetProcessList();
// threads
DebugThread GetActiveThread() const;
void SetActiveThread(const DebugThread& thread);
std::vector<DebugThread> GetAllThreads();
std::vector<DebugFrame> GetFramesOfThread(uint64_t tid);
bool SuspendThread(std::uint32_t tid);
bool ResumeThread(std::uint32_t tid);
// modules
std::vector<DebugModule> GetAllModules();
DebugModule GetModuleByName(const std::string& module);
bool GetModuleBase(const std::string& name, uint64_t& address);
DebugModule GetModuleForAddress(uint64_t remoteAddress);
ModuleNameAndOffset AbsoluteAddressToRelative(uint64_t absoluteAddress);
uint64_t RelativeAddressToAbsolute(const ModuleNameAndOffset& relativeAddress);
// rebasing
// Note: Returns true immediately in UI mode (rebase completes asynchronously via UI callback)
bool RebaseToRemoteBase();
bool RebaseToAddress(uint64_t address);
bool GetRemoteBase(uint64_t& address);
// arch
ArchitectureRef GetRemoteArchitecture();
// status
DebugAdapterConnectionStatus GetConnectionStatus();
DebugAdapterTargetStatus GetExecutionStatus();
// memory
DataBuffer ReadMemory(std::uintptr_t address, std::size_t size);
bool WriteMemory(std::uintptr_t address, const DataBuffer& buffer);
// debugger events
size_t RegisterEventCallback(
std::function<void(const DebuggerEvent& event)> callback, const std::string& name = "");
bool RemoveEventCallback(size_t index);
bool RemoveEventCallbackInternal(size_t index);
void NotifyStopped(DebugStopReason reason, void* data = nullptr);
void NotifyError(const std::string& error, const std::string& shortError, void* data = nullptr);
void NotifyEvent(DebuggerEventType event);
void PostDebuggerEvent(const DebuggerEvent& event);
void CleanUpDisabledEvent();
// shortcut for instruction pointer
uint64_t GetLastIP() const { return m_lastIP; }
uint64_t GetCurrentIP() const { return m_currentIP; }
bool SetIP(uint64_t address);
// target control
bool Execute();
bool Restart();
bool ConnectToDebugServer();
bool DisconnectDebugServer();
bool IsConnectedToDebugServer();
// Convenience function, either launch the target process or connect to a remote, depending on the selected
// adapter
void LaunchOrConnect();
// Asynchronous APIs.
bool Launch();
bool Connect();
bool Attach();
void Detach();
bool Go();
bool GoReverse();
void Quit();
bool StepInto(BNFunctionGraphType il = NormalFunctionGraph);
bool StepIntoReverse(BNFunctionGraphType il = NormalFunctionGraph);
bool StepOver(BNFunctionGraphType il = NormalFunctionGraph);
bool StepOverReverse(BNFunctionGraphType il);
bool StepReturn();
bool StepReturnReverse();
bool RunTo(const std::vector<uint64_t>& remoteAddresses);
bool RunToReverse(const std::vector<uint64_t>& remoteAddresses);
bool Pause();
DebugStopReason ExecuteAdapterAndWait(const DebugAdapterOperation operation);
// Synchronous APIs
DebugStopReason LaunchAndWait();
DebugStopReason GoAndWait();
DebugStopReason GoReverseAndWait();
DebugStopReason AttachAndWait();
DebugStopReason RestartAndWait();
DebugStopReason ConnectAndWait();
DebugStopReason StepIntoAndWait(BNFunctionGraphType il = NormalFunctionGraph);
DebugStopReason StepIntoReverseAndWait(BNFunctionGraphType il = NormalFunctionGraph);
DebugStopReason StepOverAndWait(BNFunctionGraphType il = NormalFunctionGraph);
DebugStopReason StepOverReverseAndWait(BNFunctionGraphType il);
DebugStopReason StepReturnAndWait();
DebugStopReason StepReturnReverseAndWait();
DebugStopReason RunToAndWait(const std::vector<uint64_t>& remoteAddresses);
DebugStopReason RunToReverseAndWait(const std::vector<uint64_t>& remoteAddresses);
DebugStopReason PauseAndWait();
void DetachAndWait();
void QuitAndWait();
// getters
DebugAdapter* GetAdapter() { return m_adapter; }
DebuggerState* GetState() { return m_state; }
BinaryViewRef GetData() { return m_data; }
FileMetadataRef GetFile() { return m_file; }
void SetData(BinaryViewRef view) {}
DebuggerFileAccessor* GetMemoryAccessor() const { return m_accessor; }
uint32_t GetExitCode();
uint32_t GetActivePID();
void WriteStdIn(const std::string message);
std::string InvokeBackendCommand(const std::string& cmd);
static std::string GetStopReasonString(DebugStopReason);
DebugStopReason StopReason() const;
BinaryNinja::Ref<BinaryNinja::Metadata> GetAdapterProperty(const std::string& name);
bool SetAdapterProperty(const std::string& name, const BinaryNinja::Ref<BinaryNinja::Metadata>& value);
bool ActivateDebugAdapter();
// Dereference an address and check for printable strings, functions, symbols, etc
std::string GetAddressInformation(intx::uint512 value);
bool IsFirstLaunch();
bool IsFirstConnect();
bool IsFirstConnectToDebugServer();
bool IsFirstAttach();
bool IsTTD();
// TTD Memory Analysis Methods
std::vector<TTDMemoryEvent> GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead);
std::vector<TTDPositionRangeIndexedMemoryEvent> GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime);
std::vector<TTDCallEvent> GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress = 0, uint64_t endReturnAddress = 0);
std::vector<TTDEvent> GetTTDEvents(TTDEventType eventType);
std::vector<TTDEvent> GetAllTTDEvents();
TTDPosition GetCurrentTTDPosition();
bool SetTTDPosition(const TTDPosition& position);
std::pair<bool, TTDMemoryEvent> GetTTDNextMemoryAccess(uint64_t address, uint64_t size, TTDMemoryAccessType accessType);
std::pair<bool, TTDMemoryEvent> GetTTDPrevMemoryAccess(uint64_t address, uint64_t size, TTDMemoryAccessType accessType);
std::vector<TTDStringEntry> GetTTDStrings(const std::string& pattern = "", uint64_t maxResults = 0);
// TTD Position History Navigation
bool TTDNavigateBack();
bool TTDNavigateForward();
bool CanTTDNavigateBack() const;
bool CanTTDNavigateForward() const;
void ClearTTDPositionHistory();
// TTD Bookmark Methods
std::vector<TTDBookmark> GetTTDBookmarks();
bool AddTTDBookmark(const TTDPosition& position, const std::string& note = "", uint64_t viewAddress = 0);
bool RemoveTTDBookmark(const TTDPosition& position);
bool UpdateTTDBookmark(const TTDPosition& position, const std::string& note, uint64_t viewAddress);
void ClearTTDBookmarks();
// TTD Code Coverage Analysis Methods
bool IsInstructionExecuted(uint64_t address);
bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime);
size_t GetInstructionExecutionCount(uint64_t address);
size_t GetExecutedInstructionCount() const;
bool SaveCodeCoverageToFile(const std::string& filePath) const;
bool LoadCodeCoverageFromFile(const std::string& filePath);
void OnRebased(BinaryView* oldView, BinaryView* newView);
bool RemoveDebuggerMemoryRegion();
bool ReAddDebuggerMemoryRegion();
uint64_t GetViewFileSegmentsStart() { return m_viewStart; }
bool ComputeExprValueAPI(const LowLevelILInstruction& instr, intx::uint512& value);
bool ComputeExprValue(const LowLevelILInstruction& instr, intx::uint512& value);
intx::uint512 GetValueFromComparison(const BNLowLevelILOperation op, intx::uint512 left, intx::uint512 right, size_t size);
bool ComputeExprValueAPI(const MediumLevelILInstruction& instr, intx::uint512& value);
bool ComputeExprValue(const MediumLevelILInstruction& instr, intx::uint512& value);
intx::uint512 GetValueFromComparison(const BNMediumLevelILOperation op, intx::uint512 left, intx::uint512 right, size_t size);
bool ComputeExprValueAPI(const HighLevelILInstruction& instr, intx::uint512& value);
bool ComputeExprValue(const HighLevelILInstruction& instr, intx::uint512& value);
intx::uint512 GetValueFromComparison(const BNHighLevelILOperation op, intx::uint512 left, intx::uint512 right, size_t size);
bool GetVariableValueAPI(const Variable& var, uint64_t address, size_t size, intx::uint512& value);
bool GetVariableValue(const Variable& var, uint64_t address, size_t size, intx::uint512& value);
Ref<Settings> GetAdapterSettings();
bool CreateDebugAdapter();
void SetDebuggerUICallbacks(BNDebuggerUICallbacks* cb, void* ctxt);
bool FunctionExistsInOldView(uint64_t address);
};
}; // namespace BinaryNinjaDebugger