diff --git a/api/debuggerapi.h b/api/debuggerapi.h index 19cb926c..8a4407db 100644 --- a/api/debuggerapi.h +++ b/api/debuggerapi.h @@ -485,7 +485,7 @@ namespace BinaryNinjaDebuggerAPI { // target control bool Launch(); - bool LaunchAndWait(); + BNDebugStopReason LaunchAndWait(); bool Execute(); void Restart(); void Quit(); @@ -497,7 +497,8 @@ namespace BinaryNinjaDebuggerAPI { // Convenience function, either launch the target process or connect to a remote, depending on the selected // adapter void LaunchOrConnect(); - bool Attach(uint32_t pid); + bool Attach(); + DebugStopReason AttachAndWait(); bool Go(); bool StepInto(BNFunctionGraphType il = NormalFunctionGraph); @@ -528,6 +529,7 @@ namespace BinaryNinjaDebuggerAPI { std::string GetWorkingDirectory(); bool GetRequestTerminalEmulator(); std::string GetCommandLineArguments(); + int32_t GetPIDAttach(); void SetInputFile(const std::string& path); void SetExecutablePath(const std::string& path); @@ -536,6 +538,7 @@ namespace BinaryNinjaDebuggerAPI { void SetRemoteHost(const std::string& host); void SetRemotePort(uint32_t port); void SetRequestTerminalEmulator(bool requested); + void SetPIDAttach(int32_t pid); std::vector GetBreakpoints(); void DeleteBreakpoint(uint64_t address); diff --git a/api/debuggercontroller.cpp b/api/debuggercontroller.cpp index 5794498c..39bb010c 100644 --- a/api/debuggercontroller.cpp +++ b/api/debuggercontroller.cpp @@ -284,7 +284,7 @@ bool DebuggerController::Launch() } -bool DebuggerController::LaunchAndWait() +DebugStopReason DebuggerController::LaunchAndWait() { return BNDebuggerLaunchAndWait(m_object); } @@ -351,12 +351,19 @@ void DebuggerController::LaunchOrConnect() } -bool DebuggerController::Attach(uint32_t pid) +bool DebuggerController::Attach() { - return BNDebuggerAttach(m_object, pid); + return BNDebuggerAttach(m_object); } +DebugStopReason DebuggerController::AttachAndWait() +{ + return BNDebuggerAttachAndWait(m_object); +} + + + bool DebuggerController::StepInto(BNFunctionGraphType il) { return BNDebuggerStepInto(m_object, il); @@ -472,6 +479,12 @@ uint32_t DebuggerController::GetRemotePort() } +int32_t DebuggerController::GetPIDAttach() +{ + return BNDebuggerGetPIDAttach(m_object); +} + + std::string DebuggerController::GetInputFile() { char* path = BNDebuggerGetInputFile(m_object); @@ -562,6 +575,12 @@ void DebuggerController::SetRemotePort(uint32_t port) } +void DebuggerController::SetPIDAttach(int32_t port) +{ + BNDebuggerSetPIDAttach(m_object, port); +} + + void DebuggerController::SetRequestTerminalEmulator(bool requested) { BNDebuggerSetRequestTerminalEmulator(m_object, requested); diff --git a/api/ffi.h b/api/ffi.h index adc4e5f2..555a72a1 100644 --- a/api/ffi.h +++ b/api/ffi.h @@ -291,6 +291,7 @@ extern "C" enum BNDebuggerAdapterOperation { DebugAdapterLaunch, + DebugAdapterAttach, DebugAdapterGo, DebugAdapterStepInto, DebugAdapterStepOver, @@ -352,7 +353,7 @@ extern "C" // target control DEBUGGER_FFI_API bool BNDebuggerLaunch(BNDebuggerController* controller); - DEBUGGER_FFI_API bool BNDebuggerLaunchAndWait(BNDebuggerController* controller); + DEBUGGER_FFI_API BNDebugStopReason BNDebuggerLaunchAndWait(BNDebuggerController* controller); DEBUGGER_FFI_API bool BNDebuggerExecute(BNDebuggerController* controller); DEBUGGER_FFI_API void BNDebuggerRestart(BNDebuggerController* controller); DEBUGGER_FFI_API void BNDebuggerQuit(BNDebuggerController* controller); @@ -363,7 +364,8 @@ extern "C" DEBUGGER_FFI_API void BNDebuggerDetach(BNDebuggerController* controller); // Convenience function, either launch the target process or connect to a remote, depending on the selected adapter DEBUGGER_FFI_API void BNDebuggerLaunchOrConnect(BNDebuggerController* controller); - DEBUGGER_FFI_API bool BNDebuggerAttach(BNDebuggerController* controller, uint32_t pid); + DEBUGGER_FFI_API bool BNDebuggerAttach(BNDebuggerController* controller); + DEBUGGER_FFI_API BNDebugStopReason BNDebuggerAttachAndWait(BNDebuggerController* controller); DEBUGGER_FFI_API bool BNDebuggerGo(BNDebuggerController* controller); DEBUGGER_FFI_API bool BNDebuggerStepInto(BNDebuggerController* controller, BNFunctionGraphType il); @@ -391,6 +393,7 @@ extern "C" DEBUGGER_FFI_API char* BNDebuggerGetRemoteHost(BNDebuggerController* controller); DEBUGGER_FFI_API uint32_t BNDebuggerGetRemotePort(BNDebuggerController* controller); + DEBUGGER_FFI_API int32_t BNDebuggerGetPIDAttach(BNDebuggerController* controller); DEBUGGER_FFI_API char* BNDebuggerGetInputFile(BNDebuggerController* controller); DEBUGGER_FFI_API char* BNDebuggerGetExecutablePath(BNDebuggerController* controller); DEBUGGER_FFI_API char* BNDebuggerGetWorkingDirectory(BNDebuggerController* controller); @@ -399,6 +402,7 @@ extern "C" DEBUGGER_FFI_API void BNDebuggerSetRemoteHost(BNDebuggerController* controller, const char* host); DEBUGGER_FFI_API void BNDebuggerSetRemotePort(BNDebuggerController* controller, uint32_t port); + DEBUGGER_FFI_API void BNDebuggerSetPIDAttach(BNDebuggerController* controller, int32_t pid); DEBUGGER_FFI_API void BNDebuggerSetInputFile(BNDebuggerController* controller, const char* path); DEBUGGER_FFI_API void BNDebuggerSetExecutablePath(BNDebuggerController* controller, const char* path); DEBUGGER_FFI_API void BNDebuggerSetWorkingDirectory(BNDebuggerController* controller, const char* path); diff --git a/api/python/debuggercontroller.py b/api/python/debuggercontroller.py index f3e9887a..b49005d2 100644 --- a/api/python/debuggercontroller.py +++ b/api/python/debuggercontroller.py @@ -795,13 +795,21 @@ def launch_or_connect(self) -> None: """ dbgcore.BNDebuggerLaunchOrConnect(self.handle) - def attach(self, pid: int) -> bool: + def attach(self) -> bool: """ - Attach to a running process by its PID + Attach to a running process - :param pid: the PID of the process to attach to + The PID of the target process must be set via DebuggerState.pid_attach """ - return dbgcore.BNDebuggerAttach(self.handle, pid) + return dbgcore.BNDebuggerAttach(self.handle) + + def attach_and_wait(self) -> bool: + """ + Attach to a running process and wait until all debugger events are processed + + The PID of the target process must be set via DebuggerState.pid_attach + """ + return dbgcore.BNDebuggerAttachAndWait(self.handle) def go(self) -> bool: """ @@ -1065,6 +1073,22 @@ def remote_port(self) -> int: def remote_port(self, port: int) -> None: dbgcore.BNDebuggerSetRemotePort(self.handle, port) + @property + def pid_attach(self) -> int: + """ + The PID to attach to. (read/write) + + ``pid_attach`` is only useful for connecting to a running process using PID. + + :getter: returns the remote port + :setter: sets the remote port + """ + return dbgcore.BNDebuggerGetPIDAttach(self.handle) + + @remote_port.setter + def pid_attach(self, pid: int) -> None: + dbgcore.BNDebuggerSetPIDAttach(self.handle, pid) + @property def executable_path(self) -> str: """ diff --git a/cli/main.cpp b/cli/main.cpp index 2f501483..881f0b72 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -406,7 +406,8 @@ int main(int argc, const char* argv[]) uint32_t pid = std::stoi(argv[3]); if (pid == 0) return -1; - if (!debugger->Attach(pid)) + debugger->SetPIDAttach(pid); + if (!debugger->AttachAndWait()) return -1; } @@ -561,7 +562,7 @@ int main(int argc, const char* argv[]) } else if (input == "r") { - [[maybe_unused]] bool result = debugger->Launch(); + [[maybe_unused]] bool result = debugger->LaunchAndWait(); } else if (input == "force_go") { diff --git a/core/adapters/lldbadapter.cpp b/core/adapters/lldbadapter.cpp index d4177085..8049c009 100644 --- a/core/adapters/lldbadapter.cpp +++ b/core/adapters/lldbadapter.cpp @@ -272,7 +272,7 @@ bool LldbAdapter::Attach(std::uint32_t pid) if (!m_target.IsValid()) { DebuggerEvent event; - event.type = ErrorEventType; + event.type = LaunchFailureEventType; event.data.errorData.shortError = fmt::format("LLDB failed to attach to target."); event.data.errorData.error = fmt::format("LLDB failed to attach to target with \"{}\"", err.GetCString() ? err.GetCString() : ""); @@ -288,7 +288,7 @@ bool LldbAdapter::Attach(std::uint32_t pid) if (!m_process.IsValid() || (m_process.GetState() == StateType::eStateInvalid) || err.Fail()) { DebuggerEvent event; - event.type = ErrorEventType; + event.type = LaunchFailureEventType; event.data.errorData.shortError = fmt::format("LLDB failed to attach to target."); event.data.errorData.error = fmt::format("LLDB Failed to attach to target with \"{}\"", err.GetCString() ? err.GetCString() : ""); @@ -299,7 +299,7 @@ bool LldbAdapter::Attach(std::uint32_t pid) // LLDB event listener does not get an event when the attach operation completes, so we must send an event here. // This is NOT needed for Connect(), since LLDB event listener sends an event in that case. DebuggerEvent dbgevt; - dbgevt.type = TargetStoppedEventType; + dbgevt.type = AdapterStoppedEventType; dbgevt.data.targetStoppedData.reason = InitialBreakpoint; PostDebuggerEvent(dbgevt); return true; diff --git a/core/debuggercontroller.cpp b/core/debuggercontroller.cpp index 6af0bac3..f09a5670 100644 --- a/core/debuggercontroller.cpp +++ b/core/debuggercontroller.cpp @@ -147,21 +147,43 @@ DebugStopReason DebuggerController::LaunchAndWait() } -bool DebuggerController::Attach(int32_t pid) +bool DebuggerController::Attach() { - std::unique_lock lock(m_targetControlMutex); + std::thread([&]() { AttachAndWait(); }).detach(); + return true; +} + + +DebugStopReason DebuggerController::AttachAndWaitInternal() +{ + DebuggerEvent event; + event.type = LaunchEventType; + PostDebuggerEvent(event); - NotifyEvent(AttachEventType); if (!CreateDebugAdapter()) - return false; + return InternalError; m_inputFileLoaded = false; - m_initialBreakpointSeen = false; + m_initialBreakpointSeen = false; m_state->MarkDirty(); if (!CreateDebuggerBinaryView()) - return false; + return InternalError; + + return ExecuteAdapterAndWait(DebugAdapterAttach); +} + + +DebugStopReason DebuggerController::AttachAndWait() +{ + if (!m_targetControlMutex.try_lock()) + return InternalError; + + auto reason = AttachAndWaitInternal(); + if (!m_userRequestedBreak && (reason != ProcessExited) && (reason != InternalError)) + NotifyStopped(reason); - return m_adapter->Attach(pid); + m_targetControlMutex.unlock(); + return reason; } @@ -749,6 +771,7 @@ void DebuggerController::Connect() m_initialBreakpointSeen = false; m_state->MarkDirty(); m_state->SetConnectionStatus(DebugAdapterConnectingStatus); + CreateDebuggerBinaryView(); NotifyEvent(ConnectEventType); bool ok = m_adapter->Connect(m_state->GetRemoteHost(), m_state->GetRemotePort()); @@ -1747,6 +1770,9 @@ DebugStopReason DebuggerController::ExecuteAdapterAndWait(const DebugAdapterOper case DebugAdapterLaunch: resumeOK = Execute(); break; + case DebugAdapterAttach: + resumeOK = m_adapter->Attach(m_state->GetPIDAttach()); + break; default: break; } diff --git a/core/debuggercontroller.h b/core/debuggercontroller.h index bb4cc8bd..6d48b381 100644 --- a/core/debuggercontroller.h +++ b/core/debuggercontroller.h @@ -109,6 +109,8 @@ namespace BinaryNinjaDebugger { // 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 PauseAndWaitInternal(); DebugStopReason GoAndWaitInternal(); DebugStopReason StepIntoAndWaitInternal(); @@ -207,25 +209,21 @@ namespace BinaryNinjaDebugger { bool SetIP(uint64_t address); // target control - DebugStopReason LaunchAndWait(); - DebugStopReason LaunchAndWaitInternal(); - bool Launch(); bool Execute(); void Restart(); - void Quit(); - void QuitAndWait(); void Connect(); bool ConnectToDebugServer(); bool DisconnectDebugServer(); - void Detach(); - void DetachAndWait(); // Convenience function, either launch the target process or connect to a remote, depending on the selected // adapter void LaunchOrConnect(); - bool Attach(int32_t pid); + bool Attach(); // Asynchronous APIs. + bool Launch(); + void Detach(); bool Go(); + void Quit(); bool StepInto(BNFunctionGraphType il = NormalFunctionGraph); bool StepOver(BNFunctionGraphType il = NormalFunctionGraph); bool StepReturn(); @@ -235,12 +233,16 @@ namespace BinaryNinjaDebugger { DebugStopReason ExecuteAdapterAndWait(const DebugAdapterOperation operation); // Synchronous APIs + DebugStopReason LaunchAndWait(); DebugStopReason GoAndWait(); + DebugStopReason AttachAndWait(); DebugStopReason StepIntoAndWait(BNFunctionGraphType il = NormalFunctionGraph); DebugStopReason StepOverAndWait(BNFunctionGraphType il = NormalFunctionGraph); DebugStopReason StepReturnAndWait(); DebugStopReason RunToAndWait(const std::vector& remoteAddresses); DebugStopReason PauseAndWait(); + void DetachAndWait(); + void QuitAndWait(); // getters DebugAdapter* GetAdapter() { return m_adapter; } diff --git a/core/debuggerstate.cpp b/core/debuggerstate.cpp index ec813d59..14cedd2d 100644 --- a/core/debuggerstate.cpp +++ b/core/debuggerstate.cpp @@ -936,3 +936,9 @@ void DebuggerState::SetRequestTerminalEmulator(bool requested) m_requestTerminalEmulator = requested; m_controller->NotifyEvent(DebuggerSettingsChangedEvent); } + + +void DebuggerState::SetPIDAttach(int32_t pid) +{ + m_pidAttach = pid; +} diff --git a/core/debuggerstate.h b/core/debuggerstate.h index 2c0c06b6..c6c7cb61 100644 --- a/core/debuggerstate.h +++ b/core/debuggerstate.h @@ -161,6 +161,7 @@ namespace BinaryNinjaDebugger { std::string m_commandLineArgs; std::string m_remoteHost; uint32_t m_remotePort = 0; + int32_t m_pidAttach = 0; bool m_requestTerminalEmulator; std::string m_adapterType; std::vector m_availableAdapters; @@ -192,6 +193,7 @@ namespace BinaryNinjaDebugger { std::string GetRemoteHost() const { return m_remoteHost; } uint32_t GetRemotePort() const { return m_remotePort; } bool GetRequestTerminalEmulator() const { return m_requestTerminalEmulator; } + int32_t GetPIDAttach() const { return m_pidAttach; } void SetAdapterType(const std::string& adapter); void SetExecutablePath(const std::string& path); @@ -201,6 +203,7 @@ namespace BinaryNinjaDebugger { void SetRemoteHost(const std::string& host); void SetRemotePort(uint32_t port); void SetRequestTerminalEmulator(bool requested); + void SetPIDAttach(int32_t pid); // This is the center hub for adding and deleting breakpoints. It is called from DebugView, the CLI, the // DebugBreakpointsWidget, and the planned C++/Python API. diff --git a/core/ffi.cpp b/core/ffi.cpp index 0cff6a5f..42a5f616 100644 --- a/core/ffi.cpp +++ b/core/ffi.cpp @@ -398,7 +398,7 @@ bool BNDebuggerLaunch(BNDebuggerController* controller) } -bool BNDebuggerLaunchAndWait(BNDebuggerController* controller) +BNDebugStopReason BNDebuggerLaunchAndWait(BNDebuggerController* controller) { return controller->object->LaunchAndWait(); } @@ -466,9 +466,15 @@ void BNDebuggerLaunchOrConnect(BNDebuggerController* controller) } -bool BNDebuggerAttach(BNDebuggerController* controller, uint32_t pid) +bool BNDebuggerAttach(BNDebuggerController* controller) { - return controller->object->Attach(pid); + return controller->object->Attach(); +} + + +BNDebugStopReason BNDebuggerAttachAndWait(BNDebuggerController* controller) +{ + return controller->object->AttachAndWait(); } @@ -624,6 +630,12 @@ uint32_t BNDebuggerGetRemotePort(BNDebuggerController* controller) } +int32_t BNDebuggerGetPIDAttach(BNDebuggerController* controller) +{ + return controller->object->GetState()->GetPIDAttach(); +} + + char* BNDebuggerGetInputFile(BNDebuggerController* controller) { return BNDebuggerAllocString(controller->object->GetState()->GetInputFile().c_str()); @@ -666,6 +678,12 @@ void BNDebuggerSetRemotePort(BNDebuggerController* controller, uint32_t port) } +void BNDebuggerSetPIDAttach(BNDebuggerController* controller, int32_t pid) +{ + controller->object->GetState()->SetPIDAttach(pid); +} + + void BNDebuggerSetInputFile(BNDebuggerController* controller, const char* path) { controller->object->GetState()->SetInputFile(path); diff --git a/test/debugger_test.py b/test/debugger_test.py index c322373f..d27a62d3 100644 --- a/test/debugger_test.py +++ b/test/debugger_test.py @@ -306,7 +306,8 @@ def test_attach(self): self.assertIsNotNone(pid) bv = BinaryViewType.get_view_of_file(fpath) dbg = DebuggerController(bv) - self.assertTrue(dbg.attach(pid)) + dbg.pid_attach = pid + self.assertTrue(dbg.attach_and_wait()) self.assertGreater(len(dbg.regs), 0) dbg.quit_and_wait() diff --git a/ui/controlswidget.cpp b/ui/controlswidget.cpp index 7ff41cbc..c6ab9fc0 100644 --- a/ui/controlswidget.cpp +++ b/ui/controlswidget.cpp @@ -155,12 +155,13 @@ void DebugControlsWidget::performAttachPID() if (pid == 0) return; + m_controller->SetPIDAttach(pid); QString text = QString( "The debugger is %1 the target and preparing the debugger binary view. \n" "This might take a while.").arg("attaching to"); ProgressTask* task = new ProgressTask(this, "Attaching", text, "", [=](std::function progress) { - m_controller->Attach(pid); + m_controller->Attach(); // For now, this cant be canceled, as the Debugger model wasn't // designed with that in mind. This function below can return false if canceling is enabled diff --git a/ui/ui.cpp b/ui/ui.cpp index 7214d76f..a45892b5 100644 --- a/ui/ui.cpp +++ b/ui/ui.cpp @@ -484,12 +484,13 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) if (pid == 0) return; + controller->SetPIDAttach(pid); QString text = QString( "The debugger is attaching to the target and preparing the debugger binary view. \n" "This might take a while."); ProgressTask* task = new ProgressTask( ctxt.widget, "Attaching", text, "", [&](std::function progress) { - controller->Attach(pid); + controller->Attach(); // For now, this cant be canceled, as the Debugger model wasn't // designed with that in mind. This function below can return false if canceling is enabled