@@ -79,8 +79,57 @@ bool EsrevenAdapter::ExecuteWithArgs(const std::string &path, const std::string
7979
8080bool EsrevenAdapter::Attach (std::uint32_t pid)
8181{
82- LogWarn (" EsrevenAdapter does not support Attach()" );
83- return false ;
82+ if (!m_rspConnector)
83+ {
84+ LogWarn (" EsrevenAdapter::Attach called without an active connection. "
85+ " Please connect to the debug server first." );
86+ return false ;
87+ }
88+
89+ // Send rvn:select-process to activate process filtering at runtime
90+ auto response = m_rspConnector->TransmitAndReceive (
91+ RspData (fmt::format (" rvn:select-process:{}" , pid)));
92+ std::string responseStr = response.AsString ();
93+
94+ if (responseStr != " OK" )
95+ {
96+ LogWarn (" Failed to select process %u: %s" , pid, responseStr.c_str ());
97+ DebuggerEvent event;
98+ event.type = LaunchFailureEventType;
99+ event.data .errorData .shortError = " Process selection failed" ;
100+ event.data .errorData .error =
101+ fmt::format (" Failed to select process with PID {}: {}" , pid, responseStr);
102+ PostDebuggerEvent (event);
103+ return false ;
104+ }
105+
106+ m_processPid = pid;
107+ InvalidateCache ();
108+ ClearCachedBreakpoints ();
109+
110+ // Query the initial stop reason after selecting the process
111+ const auto reply = m_rspConnector->TransmitAndReceive (RspData (" ?" ));
112+ auto map = RspConnector::PacketToUnorderedMap (reply);
113+ m_lastActiveThreadId = (uint32_t )map[" thread" ];
114+ m_isTargetRunning = false ;
115+
116+ // Apply any pending breakpoints
117+ BNSettingsScope scope = SettingsResourceScope;
118+ auto data = GetData ();
119+ auto adapterSettings = GetAdapterSettings ();
120+ auto inputFile = adapterSettings->Get <std::string>(" common.inputFile" , data, &scope);
121+
122+ CheckApplyPendingBreakpoints ();
123+
124+ if (Settings::Instance ()->Get <bool >(" debugger.stopAtEntryPoint" ) && m_hasEntryFunction)
125+ AddBreakpoint (ModuleNameAndOffset (inputFile, m_entryPoint - m_start));
126+
127+ DebuggerEvent dbgevt;
128+ dbgevt.type = AdapterStoppedEventType;
129+ dbgevt.data .targetStoppedData .reason = InitialBreakpoint;
130+ PostDebuggerEvent (dbgevt);
131+
132+ return true ;
84133}
85134
86135bool EsrevenAdapter::LoadRegisterInfo ()
@@ -297,8 +346,90 @@ bool EsrevenAdapter::Connect(const std::string& server, std::uint32_t port)
297346
298347bool EsrevenAdapter::ConnectToDebugServer (const std::string &server, std::uint32_t port)
299348{
300- LogWarn (" DbgAdapter does not support connecting to a debug server, please use connect to remote process instead" );
301- return false ;
349+ m_canReverseContinue = false ;
350+ m_canReverseStep = false ;
351+
352+ BNSettingsScope scope = SettingsResourceScope;
353+ auto data = GetData ();
354+ auto adapterSettings = GetAdapterSettings ();
355+ scope = SettingsResourceScope;
356+ auto ipAddress = adapterSettings->Get <std::string>(" debugServer.ipAddress" , data, &scope);
357+ scope = SettingsResourceScope;
358+ auto serverPort = adapterSettings->Get <uint64_t >(" debugServer.port" , data, &scope);
359+
360+ bool connected = false ;
361+ for (std::uint8_t index{}; index < 30 ; index++)
362+ {
363+ this ->m_socket = new Socket (AF_INET, SOCK_STREAM, 0 );
364+
365+ sockaddr_in address{};
366+ address.sin_family = (u_short)AF_INET;
367+ address.sin_addr .s_addr = inet_addr (ipAddress.c_str ());
368+ address.sin_port = htons ((u_short)serverPort);
369+
370+ if (this ->m_socket ->Connect (address))
371+ {
372+ connected = true ;
373+ break ;
374+ }
375+
376+ m_socket->Close ();
377+ std::this_thread::sleep_for (std::chrono::milliseconds (500 ));
378+ }
379+
380+ if (!connected)
381+ {
382+ DebuggerEvent event;
383+ event.type = LaunchFailureEventType;
384+ event.data .errorData .shortError = " Connection failed" ;
385+ event.data .errorData .error =
386+ fmt::format (" Failed to connect to debug server at {}:{}" , ipAddress, serverPort);
387+ PostDebuggerEvent (event);
388+ return false ;
389+ }
390+
391+ this ->m_rspConnector = new RspConnector (this ->m_socket );
392+ this ->m_rspConnector ->TransmitAndReceive (RspData (" Hg0" ));
393+ this ->m_rspConnector ->NegotiateCapabilities (
394+ { " swbreak+" , " hwbreak+" , " qRelocInsn+" , " fork-events+" , " vfork-events+" , " exec-events+" ,
395+ " vContSupported+" , " QThreadEvents+" , " no-resumed+" , " xmlRegisters=i386" });
396+
397+ auto capacities = m_rspConnector->GetServerCapabilities ();
398+ if (std::find (capacities.begin (), capacities.end (), " ReverseContinue" ) != capacities.end ())
399+ m_canReverseContinue = true ;
400+ if (std::find (capacities.begin (), capacities.end (), " ReverseStep" ) != capacities.end ())
401+ m_canReverseStep = true ;
402+
403+ if (!this ->LoadRegisterInfo ())
404+ {
405+ DebuggerEvent event;
406+ event.type = LaunchFailureEventType;
407+ event.data .errorData .shortError = " Invalid Register Info" ;
408+ event.data .errorData .error = fmt::format (" Failed to read register info from the server" );
409+ PostDebuggerEvent (event);
410+ return false ;
411+ }
412+
413+ m_isTargetRunning = false ;
414+ return true ;
415+ }
416+
417+
418+ bool EsrevenAdapter::DisconnectDebugServer ()
419+ {
420+ if (!m_rspConnector)
421+ return true ;
422+
423+ this ->m_rspConnector ->SendPayload (RspData (" D" ));
424+ this ->m_socket ->Kill ();
425+ m_isTargetRunning = false ;
426+ InvalidateCache ();
427+ ClearCachedBreakpoints ();
428+
429+ delete m_rspConnector;
430+ m_rspConnector = nullptr ;
431+
432+ return true ;
302433}
303434
304435bool EsrevenAdapter::Detach ()
@@ -2596,7 +2727,9 @@ bool EsrevenAdapterType::CanConnect(BinaryNinja::BinaryView *data)
25962727
25972728bool EsrevenAdapterType::CanExecute (BinaryNinja::BinaryView *data)
25982729{
2599- return false ;
2730+ // Return true so that the "Attach to Process..." button is enabled in the UI,
2731+ // allowing the two-step workflow: Connect to Debug Server → Attach to Process.
2732+ return true ;
26002733}
26012734
26022735void BinaryNinjaDebugger::InitEsrevenAdapterType ()
@@ -2656,6 +2789,34 @@ Ref<Settings> EsrevenAdapterType::RegisterAdapterSettings()
26562789 "readOnly" : false
26572790 })" );
26582791
2792+ settings->RegisterSetting (" attach.pid" ,
2793+ R"( {
2794+ "title" : "PID to attach to",
2795+ "type" : "number",
2796+ "default" : 0,
2797+ "description" : "PID of the process to attach to",
2798+ "readOnly" : false
2799+ })" );
2800+
2801+ settings->RegisterSetting (" debugServer.ipAddress" ,
2802+ R"( {
2803+ "title" : "Debug Server IP Address",
2804+ "type" : "string",
2805+ "default" : "127.0.0.1",
2806+ "description" : "IP address of the REVEN GDB stub to connect to",
2807+ "readOnly" : false
2808+ })" );
2809+ settings->RegisterSetting (" debugServer.port" ,
2810+ R"( {
2811+ "title" : "Debug Server Port",
2812+ "type" : "number",
2813+ "default" : 31337,
2814+ "minValue" : 0,
2815+ "maxValue" : 65535,
2816+ "description" : "Port of the REVEN GDB stub to connect to",
2817+ "readOnly" : false
2818+ })" );
2819+
26592820 settings->RegisterSetting (" ttd.queryTimeout" ,
26602821 R"JSON( {
26612822 "title" : "TTD Query Timeout",
0 commit comments