diff --git a/api/debuggerapi.h b/api/debuggerapi.h index 2d3cf9e5..26b6d1df 100644 --- a/api/debuggerapi.h +++ b/api/debuggerapi.h @@ -359,6 +359,18 @@ namespace BinaryNinjaDebuggerAPI { uint64_t offset; uint64_t address; bool enabled; + DebugBreakpointType type = SoftwareBreakpoint; + }; + + + // Breakpoint types - used to specify the type of breakpoint to set + enum DebugBreakpointType + { + SoftwareBreakpoint = 0, // Default software breakpoint + HardwareExecuteBreakpoint = 1, // Hardware execution breakpoint + HardwareReadBreakpoint = 2, // Hardware read watchpoint + HardwareWriteBreakpoint = 3, // Hardware write watchpoint + HardwareAccessBreakpoint = 4 // Hardware read/write watchpoint }; @@ -647,6 +659,10 @@ namespace BinaryNinjaDebuggerAPI { bool ContainsBreakpoint(uint64_t address); bool ContainsBreakpoint(const ModuleNameAndOffset& breakpoint); + // Hardware breakpoint and watchpoint support + bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1); + bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1); + uint64_t IP(); uint64_t GetLastIP(); bool SetIP(uint64_t address); diff --git a/api/debuggercontroller.cpp b/api/debuggercontroller.cpp index 859995ab..3495cadb 100644 --- a/api/debuggercontroller.cpp +++ b/api/debuggercontroller.cpp @@ -766,6 +766,18 @@ bool DebuggerController::ContainsBreakpoint(const ModuleNameAndOffset& breakpoin } +bool DebuggerController::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) +{ + return BNDebuggerAddHardwareBreakpoint(m_object, address, (BNDebugBreakpointType)type, size); +} + + +bool DebuggerController::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) +{ + return BNDebuggerRemoveHardwareBreakpoint(m_object, address, (BNDebugBreakpointType)type, size); +} + + uint64_t DebuggerController::RelativeAddressToAbsolute(const ModuleNameAndOffset& address) { return BNDebuggerRelativeAddressToAbsolute(m_object, address.module.c_str(), address.offset); diff --git a/api/ffi.h b/api/ffi.h index b2f489ae..d91887cb 100644 --- a/api/ffi.h +++ b/api/ffi.h @@ -218,6 +218,16 @@ extern "C" } BNDebugAdapterTargetStatus; + typedef enum BNDebugBreakpointType + { + BNSoftwareBreakpoint = 0, // Default software breakpoint + BNHardwareExecuteBreakpoint = 1, // Hardware execution breakpoint + BNHardwareReadBreakpoint = 2, // Hardware read watchpoint + BNHardwareWriteBreakpoint = 3, // Hardware write watchpoint + BNHardwareAccessBreakpoint = 4 // Hardware read/write watchpoint + } BNDebugBreakpointType; + + typedef enum BNDebuggerEventType { LaunchEventType, @@ -522,6 +532,12 @@ extern "C" DEBUGGER_FFI_API bool BNDebuggerContainsRelativeBreakpoint( BNDebuggerController* controller, const char* module, uint64_t offset); + // Hardware breakpoint and watchpoint support + DEBUGGER_FFI_API bool BNDebuggerAddHardwareBreakpoint(BNDebuggerController* controller, uint64_t address, + BNDebugBreakpointType type, size_t size); + DEBUGGER_FFI_API bool BNDebuggerRemoveHardwareBreakpoint(BNDebuggerController* controller, uint64_t address, + BNDebugBreakpointType type, size_t size); + DEBUGGER_FFI_API uint64_t BNDebuggerGetIP(BNDebuggerController* controller); DEBUGGER_FFI_API uint64_t BNDebuggerGetLastIP(BNDebuggerController* controller); DEBUGGER_FFI_API bool BNDebuggerSetIP(BNDebuggerController* controller, uint64_t address); diff --git a/breakpoint_widget_enhanced.png b/breakpoint_widget_enhanced.png new file mode 100644 index 00000000..00897fac Binary files /dev/null and b/breakpoint_widget_enhanced.png differ diff --git a/core/adapters/dbgengadapter.cpp b/core/adapters/dbgengadapter.cpp index f58635f9..dd467649 100644 --- a/core/adapters/dbgengadapter.cpp +++ b/core/adapters/dbgengadapter.cpp @@ -996,6 +996,15 @@ bool DbgEngAdapter::ResumeThread(std::uint32_t tid) DebugBreakpoint DbgEngAdapter::AddBreakpoint(const std::uintptr_t address, unsigned long breakpoint_flags) { + // Handle hardware breakpoint types + if (breakpoint_flags != SoftwareBreakpoint) + { + if (AddHardwareBreakpoint(address, (DebugBreakpointType)breakpoint_flags)) + return DebugBreakpoint(address, 0, true, (DebugBreakpointType)breakpoint_flags); + else + return DebugBreakpoint{}; + } + IDebugBreakpoint2* debug_breakpoint {}; /* attempt to read at breakpoint location to confirm its valid */ @@ -1021,7 +1030,7 @@ DebugBreakpoint DbgEngAdapter::AddBreakpoint(const std::uintptr_t address, unsig if (debug_breakpoint->SetFlags(DEBUG_BREAKPOINT_ENABLED | breakpoint_flags) != S_OK) return {}; - const auto new_breakpoint = DebugBreakpoint(address, id, true); + const auto new_breakpoint = DebugBreakpoint(address, id, true, SoftwareBreakpoint); this->m_debug_breakpoints.push_back(new_breakpoint); return new_breakpoint; @@ -1125,6 +1134,88 @@ std::vector DbgEngAdapter::GetBreakpointList() const return {}; } + +bool DbgEngAdapter::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) +{ + if (!m_dbgengInitialized) + return false; + + std::string command; + switch (type) + { + case HardwareExecuteBreakpoint: + // ba e
: hardware execution breakpoint + command = fmt::format("ba e{} 0x{:x}", size, address); + break; + case HardwareReadBreakpoint: + // ba r
: hardware read breakpoint + command = fmt::format("ba r{} 0x{:x}", size, address); + break; + case HardwareWriteBreakpoint: + // ba w
: hardware write breakpoint + command = fmt::format("ba w{} 0x{:x}", size, address); + break; + case HardwareAccessBreakpoint: + // ba a
: hardware access (read/write) breakpoint + command = fmt::format("ba a{} 0x{:x}", size, address); + break; + default: + return false; + } + + // Execute the command and check if it succeeded + auto result = InvokeBackendCommand(command); + // DbgEng typically returns an empty string or specific success message for successful ba commands + // If the command fails, it usually contains an error message + return result.find("error") == std::string::npos && result.find("Error") == std::string::npos; +} + + +bool DbgEngAdapter::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) +{ + if (!m_dbgengInitialized) + return false; + + // List all breakpoints to find the ID of the hardware breakpoint at this address + auto result = InvokeBackendCommand("bl"); + + // Parse the breakpoint list to find the ID + // DbgEng breakpoint list format is typically: + // 0 e
+ // 1 r
etc. + std::stringstream ss(result); + std::string line; + + while (std::getline(ss, line)) + { + // Look for lines containing our address + if (line.find(fmt::format("{:x}", address)) != std::string::npos) + { + // Extract breakpoint ID (first number in the line) + std::istringstream iss(line); + std::string id_str; + if (iss >> id_str) + { + try + { + int bp_id = std::stoi(id_str); + // Remove the breakpoint using bc (breakpoint clear) command + auto clear_result = InvokeBackendCommand(fmt::format("bc {}", bp_id)); + return clear_result.find("error") == std::string::npos && + clear_result.find("Error") == std::string::npos; + } + catch (...) + { + // Continue searching if this line doesn't contain a valid ID + continue; + } + } + } + } + + return false; +} + void DbgEngAdapter::ApplyBreakpoints() { for (const auto bp : m_pendingBreakpoints) diff --git a/core/adapters/dbgengadapter.h b/core/adapters/dbgengadapter.h index 6e7f1eaf..aa7db2d9 100644 --- a/core/adapters/dbgengadapter.h +++ b/core/adapters/dbgengadapter.h @@ -200,6 +200,10 @@ namespace BinaryNinjaDebugger { std::vector GetBreakpointList() const override; + // Hardware breakpoint and watchpoint support + bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override; + bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override; + std::string GetRegisterNameByIndex(std::uint32_t index) const; std::unordered_map ReadAllRegisters() override; DebugRegister ReadRegister(const std::string& reg) override; diff --git a/core/adapters/esrevenadapter.cpp b/core/adapters/esrevenadapter.cpp index ccc6a3a5..7b3c8faf 100644 --- a/core/adapters/esrevenadapter.cpp +++ b/core/adapters/esrevenadapter.cpp @@ -411,6 +411,15 @@ DebugBreakpoint EsrevenAdapter::AddBreakpoint(const std::uintptr_t address, unsi DebugBreakpoint(address)) != this->m_debugBreakpoints.end()) return {}; + // Handle hardware breakpoint types + if (breakpoint_type != SoftwareBreakpoint) + { + if (AddHardwareBreakpoint(address, (DebugBreakpointType)breakpoint_type)) + return DebugBreakpoint(address, 0, true, (DebugBreakpointType)breakpoint_type); + else + return DebugBreakpoint{}; + } + /* TODO: replace %d with the actual breakpoint size as it differs per architecture */ size_t kind = 1; if (m_remoteArch == "aarch64") @@ -421,7 +430,7 @@ DebugBreakpoint EsrevenAdapter::AddBreakpoint(const std::uintptr_t address, unsi if (this->m_rspConnector->TransmitAndReceive(RspData("Z0,{:x},{}", address, kind)).AsString() != "OK" ) return DebugBreakpoint{}; - const auto new_breakpoint = DebugBreakpoint(address, this->m_internalBreakpointId++, true); + const auto new_breakpoint = DebugBreakpoint(address, this->m_internalBreakpointId++, true, SoftwareBreakpoint); this->m_debugBreakpoints.push_back(new_breakpoint); return new_breakpoint; @@ -1068,20 +1077,80 @@ bool EsrevenAdapter::StepOverReverse() return status != InternalError; } -bool EsrevenAdapter::AddHardwareWriteBreakpoint(uint64_t address) +bool EsrevenAdapter::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) { if (m_isTargetRunning || !m_rspConnector) return false; - return this->m_rspConnector->TransmitAndReceive(RspData("Z2,{:x},{}", address, 1)).AsString() != "OK"; + std::string command; + switch (type) + { + case HardwareExecuteBreakpoint: + // Z1 = hardware execution breakpoint + command = fmt::format("Z1,{:x},{}", address, size); + break; + case HardwareReadBreakpoint: + // Z3 = hardware read watchpoint + command = fmt::format("Z3,{:x},{}", address, size); + break; + case HardwareWriteBreakpoint: + // Z2 = hardware write watchpoint + command = fmt::format("Z2,{:x},{}", address, size); + break; + case HardwareAccessBreakpoint: + // Z4 = hardware access watchpoint (read/write) + command = fmt::format("Z4,{:x},{}", address, size); + break; + default: + return false; + } + + return m_rspConnector->TransmitAndReceive(RspData(command)).AsString() == "OK"; } -bool EsrevenAdapter::RemoveHardwareWriteBreakpoint(uint64_t address) + +bool EsrevenAdapter::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) { if (m_isTargetRunning || !m_rspConnector) return false; - return this->m_rspConnector->TransmitAndReceive(RspData("Z2,{:x},{}", address, 1)).AsString() != "OK"; + std::string command; + switch (type) + { + case HardwareExecuteBreakpoint: + // z1 = remove hardware execution breakpoint + command = fmt::format("z1,{:x},{}", address, size); + break; + case HardwareReadBreakpoint: + // z3 = remove hardware read watchpoint + command = fmt::format("z3,{:x},{}", address, size); + break; + case HardwareWriteBreakpoint: + // z2 = remove hardware write watchpoint + command = fmt::format("z2,{:x},{}", address, size); + break; + case HardwareAccessBreakpoint: + // z4 = remove hardware access watchpoint (read/write) + command = fmt::format("z4,{:x},{}", address, size); + break; + default: + return false; + } + + return m_rspConnector->TransmitAndReceive(RspData(command)).AsString() == "OK"; +} + + +bool EsrevenAdapter::AddHardwareWriteBreakpoint(uint64_t address) +{ + // Delegate to new standardized method + return AddHardwareBreakpoint(address, HardwareWriteBreakpoint, 1); +} + +bool EsrevenAdapter::RemoveHardwareWriteBreakpoint(uint64_t address) +{ + // Delegate to new standardized method + return RemoveHardwareBreakpoint(address, HardwareWriteBreakpoint, 1); } bool EsrevenAdapter::StepReturnReverse() diff --git a/core/adapters/esrevenadapter.h b/core/adapters/esrevenadapter.h index d2f10845..9f4ad42c 100644 --- a/core/adapters/esrevenadapter.h +++ b/core/adapters/esrevenadapter.h @@ -156,7 +156,11 @@ namespace BinaryNinjaDebugger bool ResumeThread(std::uint32_t tid) override; DebugBreakpoint AddBreakpoint(const ModuleNameAndOffset& address, unsigned long breakpoint_type = 0) override; - // Temporary internal methods + // Hardware breakpoint and watchpoint support + bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override; + bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override; + + // Legacy methods - kept for backward compatibility bool AddHardwareWriteBreakpoint(uint64_t address); bool RemoveHardwareWriteBreakpoint(uint64_t address); diff --git a/core/adapters/gdbadapter.cpp b/core/adapters/gdbadapter.cpp index b7f66bed..89b7b910 100644 --- a/core/adapters/gdbadapter.cpp +++ b/core/adapters/gdbadapter.cpp @@ -409,6 +409,15 @@ DebugBreakpoint GdbAdapter::AddBreakpoint(const std::uintptr_t address, unsigned DebugBreakpoint(address)) != this->m_debugBreakpoints.end()) return {}; + // Handle hardware breakpoint types + if (breakpoint_type != SoftwareBreakpoint) + { + if (AddHardwareBreakpoint(address, (DebugBreakpointType)breakpoint_type)) + return DebugBreakpoint(address, 0, true, (DebugBreakpointType)breakpoint_type); + else + return DebugBreakpoint{}; + } + /* TODO: replace %d with the actual breakpoint size as it differs per architecture */ size_t kind = 1; if (m_remoteArch == "aarch64") @@ -419,7 +428,7 @@ DebugBreakpoint GdbAdapter::AddBreakpoint(const std::uintptr_t address, unsigned if (this->m_rspConnector->TransmitAndReceive(RspData("Z0,{:x},{}", address, kind)).AsString() != "OK" ) return DebugBreakpoint{}; - const auto new_breakpoint = DebugBreakpoint(address, this->m_internalBreakpointId++, true); + const auto new_breakpoint = DebugBreakpoint(address, this->m_internalBreakpointId++, true, SoftwareBreakpoint); this->m_debugBreakpoints.push_back(new_breakpoint); return new_breakpoint; @@ -1116,20 +1125,80 @@ bool GdbAdapter::StepOverReverse() return status != InternalError; } -bool GdbAdapter::AddHardwareWriteBreakpoint(uint64_t address) +bool GdbAdapter::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) { if (m_isTargetRunning || !m_rspConnector) return false; - return this->m_rspConnector->TransmitAndReceive(RspData("Z2,{:x},{}", address, 1)).AsString() != "OK"; + std::string command; + switch (type) + { + case HardwareExecuteBreakpoint: + // Z1 = hardware execution breakpoint + command = fmt::format("Z1,{:x},{}", address, size); + break; + case HardwareReadBreakpoint: + // Z3 = hardware read watchpoint + command = fmt::format("Z3,{:x},{}", address, size); + break; + case HardwareWriteBreakpoint: + // Z2 = hardware write watchpoint + command = fmt::format("Z2,{:x},{}", address, size); + break; + case HardwareAccessBreakpoint: + // Z4 = hardware access watchpoint (read/write) + command = fmt::format("Z4,{:x},{}", address, size); + break; + default: + return false; + } + + return m_rspConnector->TransmitAndReceive(RspData(command)).AsString() == "OK"; } -bool GdbAdapter::RemoveHardwareWriteBreakpoint(uint64_t address) + +bool GdbAdapter::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) { if (m_isTargetRunning || !m_rspConnector) return false; - return this->m_rspConnector->TransmitAndReceive(RspData("Z2,{:x},{}", address, 1)).AsString() != "OK"; + std::string command; + switch (type) + { + case HardwareExecuteBreakpoint: + // z1 = remove hardware execution breakpoint + command = fmt::format("z1,{:x},{}", address, size); + break; + case HardwareReadBreakpoint: + // z3 = remove hardware read watchpoint + command = fmt::format("z3,{:x},{}", address, size); + break; + case HardwareWriteBreakpoint: + // z2 = remove hardware write watchpoint + command = fmt::format("z2,{:x},{}", address, size); + break; + case HardwareAccessBreakpoint: + // z4 = remove hardware access watchpoint (read/write) + command = fmt::format("z4,{:x},{}", address, size); + break; + default: + return false; + } + + return m_rspConnector->TransmitAndReceive(RspData(command)).AsString() == "OK"; +} + + +bool GdbAdapter::AddHardwareWriteBreakpoint(uint64_t address) +{ + // Delegate to new standardized method + return AddHardwareBreakpoint(address, HardwareWriteBreakpoint, 1); +} + +bool GdbAdapter::RemoveHardwareWriteBreakpoint(uint64_t address) +{ + // Delegate to new standardized method + return RemoveHardwareBreakpoint(address, HardwareWriteBreakpoint, 1); } bool GdbAdapter::StepReturnReverse() diff --git a/core/adapters/gdbadapter.h b/core/adapters/gdbadapter.h index ebcd2e0f..40d2dae9 100644 --- a/core/adapters/gdbadapter.h +++ b/core/adapters/gdbadapter.h @@ -156,7 +156,11 @@ namespace BinaryNinjaDebugger bool ResumeThread(std::uint32_t tid) override; DebugBreakpoint AddBreakpoint(const ModuleNameAndOffset& address, unsigned long breakpoint_type = 0) override; - // Temporary internal methods + // Hardware breakpoint and watchpoint support + bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override; + bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override; + + // Legacy methods - kept for backward compatibility bool AddHardwareWriteBreakpoint(uint64_t address); bool RemoveHardwareWriteBreakpoint(uint64_t address); diff --git a/core/adapters/lldbadapter.cpp b/core/adapters/lldbadapter.cpp index a2b79dfb..ecad2cfb 100644 --- a/core/adapters/lldbadapter.cpp +++ b/core/adapters/lldbadapter.cpp @@ -908,11 +908,21 @@ std::vector LldbAdapter::GetFramesOfThread(uint32_t tid) DebugBreakpoint LldbAdapter::AddBreakpoint(const std::uintptr_t address, unsigned long breakpoint_type) { + // Check if this is a hardware breakpoint type + if (breakpoint_type == HardwareExecuteBreakpoint) + { + if (AddHardwareBreakpoint(address, HardwareExecuteBreakpoint)) + return DebugBreakpoint(address, 0, true, HardwareExecuteBreakpoint); + else + return DebugBreakpoint {}; + } + + // Default software breakpoint SBBreakpoint bp = m_target.BreakpointCreateByAddress(address); if (!bp.IsValid()) return DebugBreakpoint {}; - return DebugBreakpoint(address, bp.GetID(), bp.IsEnabled()); + return DebugBreakpoint(address, bp.GetID(), bp.IsEnabled(), SoftwareBreakpoint); } @@ -984,6 +994,96 @@ std::vector LldbAdapter::GetBreakpointList() const } +bool LldbAdapter::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) +{ + if (!m_targetActive) + return false; + + switch (type) + { + case HardwareExecuteBreakpoint: + { + // Use LLDB command to set hardware execution breakpoint + std::string command = fmt::format("breakpoint set --address 0x{:x} -H", address); + auto result = InvokeBackendCommand(command); + return result.find("Breakpoint") != std::string::npos; + } + case HardwareReadBreakpoint: + { + // Use LLDB watchpoint command for read + std::string command = fmt::format("watchpoint set expression -w read -s {} -- 0x{:x}", size, address); + auto result = InvokeBackendCommand(command); + return result.find("Watchpoint") != std::string::npos; + } + case HardwareWriteBreakpoint: + { + // Use LLDB watchpoint command for write + std::string command = fmt::format("watchpoint set expression -w write -s {} -- 0x{:x}", size, address); + auto result = InvokeBackendCommand(command); + return result.find("Watchpoint") != std::string::npos; + } + case HardwareAccessBreakpoint: + { + // Use LLDB watchpoint command for read/write + std::string command = fmt::format("watchpoint set expression -w read_write -s {} -- 0x{:x}", size, address); + auto result = InvokeBackendCommand(command); + return result.find("Watchpoint") != std::string::npos; + } + default: + return false; + } +} + + +bool LldbAdapter::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) +{ + if (!m_targetActive) + return false; + + switch (type) + { + case HardwareExecuteBreakpoint: + { + // Find and delete hardware breakpoint at address + for (size_t i = 0; i < m_target.GetNumBreakpoints(); i++) + { + auto bp = m_target.GetBreakpointAtIndex(i); + if (bp.IsHardware()) + { + for (size_t j = 0; j < bp.GetNumLocations(); j++) + { + auto location = bp.GetLocationAtIndex(j); + auto bpAddress = location.GetAddress().GetLoadAddress(m_target); + if (address == bpAddress) + { + return m_target.BreakpointDelete(bp.GetID()); + } + } + } + } + return false; + } + case HardwareReadBreakpoint: + case HardwareWriteBreakpoint: + case HardwareAccessBreakpoint: + { + // Find and delete watchpoint at address + for (size_t i = 0; i < m_target.GetNumWatchpoints(); i++) + { + auto wp = m_target.GetWatchpointAtIndex(i); + if (wp.GetWatchAddress() == address) + { + return m_target.DeleteWatchpoint(wp.GetID()); + } + } + return false; + } + default: + return false; + } +} + + static intx::uint512 SBValueToUint512(lldb::SBValue& reg_val) { using namespace lldb; using namespace intx; diff --git a/core/adapters/lldbadapter.h b/core/adapters/lldbadapter.h index e1f42984..f8588a08 100644 --- a/core/adapters/lldbadapter.h +++ b/core/adapters/lldbadapter.h @@ -94,6 +94,10 @@ namespace BinaryNinjaDebugger { std::vector GetBreakpointList() const override; + // Hardware breakpoint and watchpoint support + bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override; + bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override; + std::unordered_map ReadAllRegisters() override; DebugRegister ReadRegister(const std::string& reg) override; diff --git a/core/debugadapter.cpp b/core/debugadapter.cpp index 52360905..c32d16b6 100644 --- a/core/debugadapter.cpp +++ b/core/debugadapter.cpp @@ -205,3 +205,17 @@ bool DebugAdapter::SetTTDPosition(const TTDPosition& position) // Default implementation returns false for adapters that don't support TTD return false; } + + +bool DebugAdapter::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) +{ + // Default implementation returns false for adapters that don't support hardware breakpoints + return false; +} + + +bool DebugAdapter::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size) +{ + // Default implementation returns false for adapters that don't support hardware breakpoints + return false; +} diff --git a/core/debugadapter.h b/core/debugadapter.h index 548c023a..e2e38fc5 100644 --- a/core/debugadapter.h +++ b/core/debugadapter.h @@ -118,12 +118,14 @@ namespace BinaryNinjaDebugger { std::uintptr_t m_address {}; unsigned long m_id {}; bool m_is_active {}; + DebugBreakpointType m_type = SoftwareBreakpoint; - DebugBreakpoint(std::uintptr_t address, unsigned long id, bool active) : - m_address(address), m_id(id), m_is_active(active) + DebugBreakpoint(std::uintptr_t address, unsigned long id, bool active, DebugBreakpointType type = SoftwareBreakpoint) : + m_address(address), m_id(id), m_is_active(active), m_type(type) {} - DebugBreakpoint(std::uintptr_t address) : m_address(address) {} + DebugBreakpoint(std::uintptr_t address, DebugBreakpointType type = SoftwareBreakpoint) : + m_address(address), m_type(type) {} DebugBreakpoint() {} @@ -264,6 +266,10 @@ namespace BinaryNinjaDebugger { virtual std::vector GetBreakpointList() const = 0; + // Hardware breakpoint and watchpoint support + virtual bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1); + virtual bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1); + virtual std::unordered_map ReadAllRegisters() = 0; virtual DebugRegister ReadRegister(const std::string& reg) = 0; diff --git a/core/debuggercommon.h b/core/debuggercommon.h index 22fafc3f..458acbd5 100644 --- a/core/debuggercommon.h +++ b/core/debuggercommon.h @@ -152,4 +152,14 @@ namespace BinaryNinjaDebugger { TTDCallEvent() : threadId(0), uniqueThreadId(0), functionAddress(0), returnAddress(0), returnValue(0), hasReturnValue(false) {} }; + + // Breakpoint types - used to specify the type of breakpoint to set + enum DebugBreakpointType + { + SoftwareBreakpoint = 0, // Default software breakpoint + HardwareExecuteBreakpoint = 1, // Hardware execution breakpoint + HardwareReadBreakpoint = 2, // Hardware read watchpoint + HardwareWriteBreakpoint = 3, // Hardware write watchpoint + HardwareAccessBreakpoint = 4 // Hardware read/write watchpoint + }; }; // namespace BinaryNinjaDebugger diff --git a/core/ffi.cpp b/core/ffi.cpp index c3f77a86..1c8b1b5b 100644 --- a/core/ffi.cpp +++ b/core/ffi.cpp @@ -897,6 +897,34 @@ bool BNDebuggerContainsRelativeBreakpoint(BNDebuggerController* controller, cons } +bool BNDebuggerAddHardwareBreakpoint(BNDebuggerController* controller, uint64_t address, BNDebugBreakpointType type, size_t size) +{ + DebuggerState* state = controller->object->GetState(); + if (!state) + return false; + + DebugAdapter* adapter = state->GetAdapter(); + if (!adapter) + return false; + + return adapter->AddHardwareBreakpoint(address, (DebugBreakpointType)type, size); +} + + +bool BNDebuggerRemoveHardwareBreakpoint(BNDebuggerController* controller, uint64_t address, BNDebugBreakpointType type, size_t size) +{ + DebuggerState* state = controller->object->GetState(); + if (!state) + return false; + + DebugAdapter* adapter = state->GetAdapter(); + if (!adapter) + return false; + + return adapter->RemoveHardwareBreakpoint(address, (DebugBreakpointType)type, size); +} + + uint64_t BNDebuggerRelativeAddressToAbsolute(BNDebuggerController* controller, const char* module, uint64_t offset) { DebuggerState* state = controller->object->GetState(); diff --git a/docs/examples/hardware_breakpoints.py b/docs/examples/hardware_breakpoints.py new file mode 100644 index 00000000..09c2148d --- /dev/null +++ b/docs/examples/hardware_breakpoints.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +""" +Example script demonstrating hardware breakpoint usage + +This script shows how to use the new hardware breakpoint functionality +introduced in the debugger. +""" + +try: + from binaryninja import load + from debugger import DebuggerController, DebugBreakpointType +except ImportError: + from binaryninja import load + from binaryninja.debugger import DebuggerController, DebugBreakpointType + + +def hardware_breakpoint_example(binary_path): + """ + Example showing how to use hardware breakpoints + """ + # Load the binary + bv = load(binary_path) + if not bv: + print(f"Failed to load binary: {binary_path}") + return + + # Get the debugger controller + controller = DebuggerController(bv) + + print("Setting up hardware breakpoints...") + + # Example 1: Hardware execution breakpoint at entry point + entry_point = bv.entry_point + print(f"Setting hardware execution breakpoint at entry point: 0x{entry_point:x}") + success = controller.add_hardware_breakpoint( + entry_point, + DebugBreakpointType.HardwareExecuteBreakpoint + ) + if success: + print("✓ Hardware execution breakpoint set successfully") + else: + print("✗ Failed to set hardware execution breakpoint") + + # Example 2: Hardware write watchpoint on a data address + # In a real scenario, you'd find a data address from your binary analysis + data_address = 0x1000 # Example address + print(f"Setting hardware write watchpoint at 0x{data_address:x} (4 bytes)") + success = controller.add_hardware_breakpoint( + data_address, + DebugBreakpointType.HardwareWriteBreakpoint, + 4 # Watch 4 bytes + ) + if success: + print("✓ Hardware write watchpoint set successfully") + else: + print("✗ Failed to set hardware write watchpoint") + + # Example 3: Hardware read watchpoint + print(f"Setting hardware read watchpoint at 0x{data_address + 8:x} (8 bytes)") + success = controller.add_hardware_breakpoint( + data_address + 8, + DebugBreakpointType.HardwareReadBreakpoint, + 8 # Watch 8 bytes + ) + if success: + print("✓ Hardware read watchpoint set successfully") + else: + print("✗ Failed to set hardware read watchpoint") + + # Example 4: Hardware access (read/write) watchpoint + print(f"Setting hardware access watchpoint at 0x{data_address + 16:x} (1 byte)") + success = controller.add_hardware_breakpoint( + data_address + 16, + DebugBreakpointType.HardwareAccessBreakpoint, + 1 # Watch 1 byte + ) + if success: + print("✓ Hardware access watchpoint set successfully") + else: + print("✗ Failed to set hardware access watchpoint") + + print("\nLaunching target...") + stop_reason = controller.launch_and_wait() + print(f"Target stopped with reason: {stop_reason}") + + # Continue execution to test breakpoints + print("Continuing execution...") + stop_reason = controller.go_and_wait() + print(f"Target stopped with reason: {stop_reason}") + + # Clean up - remove hardware breakpoints + print("\nCleaning up hardware breakpoints...") + + controller.remove_hardware_breakpoint( + entry_point, + DebugBreakpointType.HardwareExecuteBreakpoint + ) + + controller.remove_hardware_breakpoint( + data_address, + DebugBreakpointType.HardwareWriteBreakpoint, + 4 + ) + + controller.remove_hardware_breakpoint( + data_address + 8, + DebugBreakpointType.HardwareReadBreakpoint, + 8 + ) + + controller.remove_hardware_breakpoint( + data_address + 16, + DebugBreakpointType.HardwareAccessBreakpoint, + 1 + ) + + print("Hardware breakpoints removed") + + # Quit the debugger + controller.quit_and_wait() + print("Debugging session ended") + + +def backend_command_example(binary_path): + """ + Example showing how to use hardware breakpoints via backend commands + (useful for advanced scenarios or when the API is not sufficient) + """ + bv = load(binary_path) + controller = DebuggerController(bv) + + print("Using backend commands for hardware breakpoints...") + + # Launch the target first + controller.launch_and_wait() + + # For LLDB adapter: + if controller.get_adapter_type() == "LLDB": + print("Using LLDB commands:") + + # Hardware execution breakpoint + result = controller.send_command("breakpoint set --address 0x100000000 -H") + print(f"LLDB hardware execution breakpoint: {result}") + + # Hardware write watchpoint + result = controller.send_command("watchpoint set expression -w write -s 4 -- 0x100001000") + print(f"LLDB hardware write watchpoint: {result}") + + # List breakpoints and watchpoints + result = controller.send_command("breakpoint list") + print(f"LLDB breakpoints: {result}") + + result = controller.send_command("watchpoint list") + print(f"LLDB watchpoints: {result}") + + # For GDB RSP adapter: + elif "GDB" in controller.get_adapter_type(): + print("Using GDB RSP commands:") + + # Hardware execution breakpoint (Z1) + result = controller.send_command("Z1,100000000,1") + print(f"GDB hardware execution breakpoint: {result}") + + # Hardware write watchpoint (Z2) + result = controller.send_command("Z2,100001000,4") + print(f"GDB hardware write watchpoint: {result}") + + controller.quit_and_wait() + + +if __name__ == "__main__": + import sys + + if len(sys.argv) != 2: + print("Usage: python hardware_breakpoints.py ") + sys.exit(1) + + binary_path = sys.argv[1] + + print("=== Hardware Breakpoint API Example ===") + hardware_breakpoint_example(binary_path) + + print("\n=== Backend Command Example ===") + backend_command_example(binary_path) \ No newline at end of file diff --git a/docs/guide/index.md b/docs/guide/index.md index 932ad24b..4c6a4450 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -504,9 +504,44 @@ dbg.execute_backend_command('image list') ### Hardware Breakpoints/Watchpoints -Hardware breakpoints and watchpoints are very useful and we plan to add better support for it soon. It is tracked by -this [issue](https://github.com/Vector35/debugger/issues/53). For now, we can run a backend command directly to set -hardware breakpoints/watchpoints. +Hardware breakpoints and watchpoints are now supported through both the debugger API and direct backend commands. + +#### Using the Debugger API + +Hardware breakpoints can be set using the following methods in Python: + +```python +from debugger import DebuggerController, DebugBreakpointType + +# Get the controller for your binary view +controller = DebuggerController(bv) + +# Set hardware execution breakpoint +controller.add_hardware_breakpoint(0x12345678, DebugBreakpointType.HardwareExecuteBreakpoint) + +# Set hardware read watchpoint (1 byte) +controller.add_hardware_breakpoint(0x12345678, DebugBreakpointType.HardwareReadBreakpoint, 1) + +# Set hardware write watchpoint (4 bytes) +controller.add_hardware_breakpoint(0x12345678, DebugBreakpointType.HardwareWriteBreakpoint, 4) + +# Set hardware access (read/write) watchpoint (8 bytes) +controller.add_hardware_breakpoint(0x12345678, DebugBreakpointType.HardwareAccessBreakpoint, 8) + +# Remove hardware breakpoint +controller.remove_hardware_breakpoint(0x12345678, DebugBreakpointType.HardwareExecuteBreakpoint) +``` + +The supported breakpoint types are: +- `SoftwareBreakpoint`: Regular software breakpoint (default) +- `HardwareExecuteBreakpoint`: Hardware execution breakpoint +- `HardwareReadBreakpoint`: Hardware read watchpoint +- `HardwareWriteBreakpoint`: Hardware write watchpoint +- `HardwareAccessBreakpoint`: Hardware read/write watchpoint + +#### Using Backend Commands + +For cases where you need more control or the API is not available, you can use backend commands directly. #### WinDbg/DbgEng diff --git a/hardware_breakpoint_dialog.png b/hardware_breakpoint_dialog.png new file mode 100644 index 00000000..89694aad Binary files /dev/null and b/hardware_breakpoint_dialog.png differ diff --git a/ui/breakpointswidget.cpp b/ui/breakpointswidget.cpp index 0ab904dd..b3337dc8 100644 --- a/ui/breakpointswidget.cpp +++ b/ui/breakpointswidget.cpp @@ -17,7 +17,10 @@ limitations under the License. #include #include #include +#include +#include #include "breakpointswidget.h" +#include "hardwarebreakpointdialog.h" #include "ui.h" #include "menus.h" #include "fmt/format.h" @@ -26,11 +29,31 @@ using namespace BinaryNinjaDebuggerAPI; using namespace BinaryNinja; using namespace std; -BreakpointItem::BreakpointItem(bool enabled, const ModuleNameAndOffset location, uint64_t address) : - m_enabled(enabled), m_location(location), m_address(address) +BreakpointItem::BreakpointItem(bool enabled, const ModuleNameAndOffset location, uint64_t address, DebugBreakpointType type) : + m_enabled(enabled), m_location(location), m_address(address), m_type(type) {} +std::string BreakpointItem::typeString() const +{ + switch (m_type) + { + case SoftwareBreakpoint: + return "Software"; + case HardwareExecuteBreakpoint: + return "Hardware Exec"; + case HardwareReadBreakpoint: + return "Hardware Read"; + case HardwareWriteBreakpoint: + return "Hardware Write"; + case HardwareAccessBreakpoint: + return "Hardware Access"; + default: + return "Unknown"; + } +} + + bool BreakpointItem::operator==(const BreakpointItem& other) const { return (m_enabled == other.enabled()) && (m_location == other.location()) && (m_address == other.address()); @@ -132,6 +155,14 @@ QVariant DebugBreakpointsListModel::data(const QModelIndex& index, int role) con return QVariant(text); } + case DebugBreakpointsListModel::TypeColumn: + { + QString text = QString::fromStdString(item->typeString()); + if (role == Qt::SizeHintRole) + return QVariant((qulonglong)text.size()); + + return QVariant(text); + } } return QVariant(); } @@ -153,6 +184,8 @@ QVariant DebugBreakpointsListModel::headerData(int column, Qt::Orientation orien return "Location"; case DebugBreakpointsListModel::AddressColumn: return "Remote Address"; + case DebugBreakpointsListModel::TypeColumn: + return "Type"; } return QVariant(); } @@ -195,6 +228,7 @@ void DebugBreakpointsItemDelegate::paint( // case DebugBreakpointsListModel::EnabledColumn: case DebugBreakpointsListModel::LocationColumn: case DebugBreakpointsListModel::AddressColumn: + case DebugBreakpointsListModel::TypeColumn: { painter->setFont(m_font); painter->setPen(option.palette.color(QPalette::WindowText).rgba()); @@ -340,26 +374,46 @@ void DebugBreakpointsWidget::add() if (!view) return; - uint64_t address = 0; - if (!ViewFrame::getAddressFromInput(frame, view, address, - frame->getCurrentOffset(), "Add Breakpoint", "The address of the breakpoint:", true)) + // Show options for software or hardware breakpoint + QMenu menu(this); + QAction* softwareAction = menu.addAction("Software Breakpoint"); + QAction* hardwareAction = menu.addAction("Hardware Breakpoint..."); + + QAction* chosen = menu.exec(QCursor::pos()); + if (!chosen) return; - bool isAbsoluteAddress = false; - auto controller = DebuggerController::GetController(view); - if (controller->IsConnected()) - isAbsoluteAddress = true; - - if (isAbsoluteAddress) + if (chosen == softwareAction) { - m_controller->AddBreakpoint(address); + // Original software breakpoint logic + uint64_t address = 0; + if (!ViewFrame::getAddressFromInput(frame, view, address, + frame->getCurrentOffset(), "Add Breakpoint", "The address of the breakpoint:", true)) + return; + + bool isAbsoluteAddress = false; + auto controller = DebuggerController::GetController(view); + if (controller->IsConnected()) + isAbsoluteAddress = true; + + if (isAbsoluteAddress) + { + m_controller->AddBreakpoint(address); + } + else + { + std::string filename = m_controller->GetInputFile(); + uint64_t offset = address - m_controller->GetViewFileSegmentsStart(); + ModuleNameAndOffset info = {filename, offset}; + m_controller->AddBreakpoint(info); + } } - else + else if (chosen == hardwareAction) { - std::string filename = m_controller->GetInputFile(); - uint64_t offset = address - m_controller->GetViewFileSegmentsStart(); - ModuleNameAndOffset info = {filename, offset}; - m_controller->AddBreakpoint(info); + // Hardware breakpoint dialog + uint64_t suggestedAddress = frame->getCurrentOffset(); + HardwareBreakpointDialog dialog(this, m_controller, suggestedAddress); + dialog.exec(); } } @@ -392,7 +446,7 @@ void DebugBreakpointsWidget::updateContent() ModuleNameAndOffset info; info.module = bp.module; info.offset = bp.offset; - bps.emplace_back(bp.enabled, info, bp.address); + bps.emplace_back(bp.enabled, info, bp.address, bp.type); } m_model->updateRows(bps); diff --git a/ui/breakpointswidget.h b/ui/breakpointswidget.h index 0c717d2f..33e98fca 100644 --- a/ui/breakpointswidget.h +++ b/ui/breakpointswidget.h @@ -38,12 +38,15 @@ class BreakpointItem bool m_enabled; ModuleNameAndOffset m_location; uint64_t m_address; + DebugBreakpointType m_type; public: - BreakpointItem(bool enabled, const ModuleNameAndOffset location, uint64_t remoteAddress); + BreakpointItem(bool enabled, const ModuleNameAndOffset location, uint64_t remoteAddress, DebugBreakpointType type = SoftwareBreakpoint); bool enabled() const { return m_enabled; } ModuleNameAndOffset location() const { return m_location; } uint64_t address() const { return m_address; } + DebugBreakpointType type() const { return m_type; } + std::string typeString() const; bool operator==(const BreakpointItem& other) const; bool operator!=(const BreakpointItem& other) const; bool operator<(const BreakpointItem& other) const; @@ -67,6 +70,7 @@ class DebugBreakpointsListModel : public QAbstractTableModel //EnabledColumn, LocationColumn, AddressColumn, + TypeColumn, }; DebugBreakpointsListModel(QWidget* parent, ViewFrame* view); @@ -82,7 +86,7 @@ class DebugBreakpointsListModel : public QAbstractTableModel virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override { (void)parent; - return 2; + return 3; } BreakpointItem getRow(int row) const; virtual QVariant data(const QModelIndex& i, int role) const override; diff --git a/ui/hardwarebreakpointdialog.cpp b/ui/hardwarebreakpointdialog.cpp new file mode 100644 index 00000000..b4f3a85a --- /dev/null +++ b/ui/hardwarebreakpointdialog.cpp @@ -0,0 +1,186 @@ +/* +Copyright 2020-2025 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. +*/ + +#include "hardwarebreakpointdialog.h" +#include +#include + +HardwareBreakpointDialog::HardwareBreakpointDialog(QWidget* parent, DbgRef controller, uint64_t suggestedAddress) : + QDialog(parent), m_controller(controller), m_suggestedAddress(suggestedAddress) +{ + setWindowTitle("Add Hardware Breakpoint"); + setModal(true); + resize(400, 200); + + // Create form layout + QFormLayout* formLayout = new QFormLayout(); + + // Address input + m_addressEdit = new QLineEdit(); + if (suggestedAddress != 0) + m_addressEdit->setText(QString("0x%1").arg(suggestedAddress, 0, 16)); + formLayout->addRow("Address:", m_addressEdit); + + // Type selection + m_typeCombo = new QComboBox(); + m_typeCombo->addItem("Hardware Execute", static_cast(HardwareExecuteBreakpoint)); + m_typeCombo->addItem("Hardware Read", static_cast(HardwareReadBreakpoint)); + m_typeCombo->addItem("Hardware Write", static_cast(HardwareWriteBreakpoint)); + m_typeCombo->addItem("Hardware Access (Read/Write)", static_cast(HardwareAccessBreakpoint)); + formLayout->addRow("Type:", m_typeCombo); + + // Size selection (for watchpoints) + m_sizeSpin = new QSpinBox(); + m_sizeSpin->setMinimum(1); + m_sizeSpin->setMaximum(8); + m_sizeSpin->setValue(1); + m_sizeSpin->setSuffix(" byte(s)"); + // Only allow powers of 2 + m_sizeSpin->setSpecialValueText("1 byte"); + formLayout->addRow("Size:", m_sizeSpin); + + // Help label + m_helpLabel = new QLabel(); + m_helpLabel->setWordWrap(true); + m_helpLabel->setStyleSheet("QLabel { color: gray; font-size: 10px; }"); + formLayout->addRow(m_helpLabel); + + // Button box + m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + // Main layout + QVBoxLayout* mainLayout = new QVBoxLayout(); + mainLayout->addLayout(formLayout); + mainLayout->addWidget(m_buttonBox); + setLayout(mainLayout); + + // Connect signals + connect(m_buttonBox, &QDialogButtonBox::accepted, this, &HardwareBreakpointDialog::addBreakpoint); + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(m_addressEdit, &QLineEdit::textChanged, this, &HardwareBreakpointDialog::validateInput); + connect(m_typeCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &HardwareBreakpointDialog::typeChanged); + + // Initial setup + typeChanged(); + validateInput(); +} + +uint64_t HardwareBreakpointDialog::getAddress() const +{ + QString text = m_addressEdit->text().trimmed(); + if (text.startsWith("0x") || text.startsWith("0X")) + text = text.mid(2); + + bool ok; + uint64_t address = text.toULongLong(&ok, 16); + return ok ? address : 0; +} + +DebugBreakpointType HardwareBreakpointDialog::getType() const +{ + return static_cast(m_typeCombo->currentData().toInt()); +} + +size_t HardwareBreakpointDialog::getSize() const +{ + return static_cast(m_sizeSpin->value()); +} + +void HardwareBreakpointDialog::addBreakpoint() +{ + uint64_t address = getAddress(); + if (address == 0) + { + QMessageBox::warning(this, "Invalid Address", "Please enter a valid hexadecimal address."); + return; + } + + DebugBreakpointType type = getType(); + size_t size = getSize(); + + // Validate size for powers of 2 + if (type != HardwareExecuteBreakpoint && (size & (size - 1)) != 0) + { + QMessageBox::warning(this, "Invalid Size", "Watchpoint size must be a power of 2 (1, 2, 4, or 8 bytes)."); + return; + } + + if (m_controller) + { + bool success = m_controller->AddHardwareBreakpoint(address, type, size); + if (success) + { + accept(); + } + else + { + QMessageBox::warning(this, "Failed to Add Breakpoint", + "Failed to add hardware breakpoint. The target may not support hardware breakpoints or all hardware breakpoint slots may be in use."); + } + } +} + +void HardwareBreakpointDialog::validateInput() +{ + uint64_t address = getAddress(); + bool valid = (address != 0); + + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); + + if (!valid && !m_addressEdit->text().isEmpty()) + { + m_helpLabel->setText("Please enter a valid hexadecimal address (e.g., 0x401000)"); + m_helpLabel->setStyleSheet("QLabel { color: red; font-size: 10px; }"); + } + else + { + typeChanged(); // Update help text + } +} + +void HardwareBreakpointDialog::typeChanged() +{ + DebugBreakpointType type = getType(); + + // Enable/disable size control based on type + bool needSize = (type != HardwareExecuteBreakpoint); + m_sizeSpin->setEnabled(needSize); + + // Update help text + QString helpText; + switch (type) + { + case HardwareExecuteBreakpoint: + helpText = "Hardware execution breakpoint will trigger when the CPU executes code at the specified address."; + m_sizeSpin->setValue(1); // Execution breakpoints are always 1 byte + break; + case HardwareReadBreakpoint: + helpText = "Hardware read watchpoint will trigger when the CPU reads from the specified memory range."; + break; + case HardwareWriteBreakpoint: + helpText = "Hardware write watchpoint will trigger when the CPU writes to the specified memory range."; + break; + case HardwareAccessBreakpoint: + helpText = "Hardware access watchpoint will trigger when the CPU reads from or writes to the specified memory range."; + break; + default: + helpText = ""; + break; + } + + m_helpLabel->setText(helpText); + m_helpLabel->setStyleSheet("QLabel { color: gray; font-size: 10px; }"); +} \ No newline at end of file diff --git a/ui/hardwarebreakpointdialog.h b/ui/hardwarebreakpointdialog.h new file mode 100644 index 00000000..41458810 --- /dev/null +++ b/ui/hardwarebreakpointdialog.h @@ -0,0 +1,58 @@ +/* +Copyright 2020-2025 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debuggerapi.h" + +using namespace BinaryNinjaDebuggerAPI; + +class HardwareBreakpointDialog : public QDialog +{ + Q_OBJECT + +private: + DbgRef m_controller; + QLineEdit* m_addressEdit; + QComboBox* m_typeCombo; + QSpinBox* m_sizeSpin; + QLabel* m_helpLabel; + QDialogButtonBox* m_buttonBox; + + uint64_t m_suggestedAddress; + +public: + HardwareBreakpointDialog(QWidget* parent, DbgRef controller, uint64_t suggestedAddress = 0); + + uint64_t getAddress() const; + DebugBreakpointType getType() const; + size_t getSize() const; + +private Q_SLOTS: + void addBreakpoint(); + void validateInput(); + void typeChanged(); +}; \ No newline at end of file