0
# Process Attachment
1
2
Remote process attachment and code injection capabilities for debugging already-running Python processes. This functionality enables debugging scenarios where the target process wasn't started with debugging enabled, allowing developers to attach debuggers to production applications and long-running services.
3
4
## Capabilities
5
6
### Process Attachment
7
8
Core functions for attaching the debugger to running Python processes using platform-specific injection techniques.
9
10
```python { .api }
11
def run_python_code(pid, python_code, connect_debugger_tracing=False, show_debug_info=0):
12
"""
13
Inject and execute Python code in a running Python process.
14
15
This is a platform-specific function that routes to the appropriate implementation:
16
- Windows: run_python_code_windows
17
- Linux: run_python_code_linux
18
- macOS: run_python_code_mac
19
20
Parameters:
21
- pid (int): Process ID of the target Python process
22
- python_code (str): Python code to inject and execute (cannot contain single quotes)
23
- connect_debugger_tracing (bool): Whether to enable debugger tracing after injection
24
- show_debug_info (int): Debug information level (0=none, 1=basic, 2=detailed)
25
26
Returns:
27
None: Function executes injection but doesn't return a value
28
29
Raises:
30
- AssertionError: If python_code contains single quotes
31
- RuntimeError: If required libraries/executables are not found or injection fails
32
- Various platform-specific exceptions during injection process
33
"""
34
35
def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, show_debug_info=0):
36
"""Windows-specific implementation of code injection using winappdbg and DLL injection."""
37
38
def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show_debug_info=0):
39
"""Linux-specific implementation of code injection using gdb and shared libraries."""
40
41
def run_python_code_mac(pid, python_code, connect_debugger_tracing=False, show_debug_info=0):
42
"""macOS-specific implementation of code injection using lldb and dynamic libraries."""
43
```
44
45
### Debugger Attachment Script
46
47
High-level utilities for setting up debugger attachment to running processes.
48
49
```python { .api }
50
def attach(port, host, protocol="", debug_mode=""):
51
"""
52
Attach debugger to the current process and connect to debugger server.
53
54
This function is typically called from code injected into a running process
55
to establish debugger connection.
56
57
Parameters:
58
- port (int): Port number of the debugger server
59
- host (str): Host address of the debugger server
60
- protocol (str): Communication protocol ("", "http", "json")
61
- debug_mode (str): Debug mode configuration
62
63
Returns:
64
None
65
"""
66
67
def get_main_thread_id(unlikely_thread_id=None):
68
"""
69
Identify the main thread in a multi-threaded process.
70
71
Parameters:
72
- unlikely_thread_id: Thread ID to exclude from consideration
73
74
Returns:
75
tuple: (thread_id, critical_warning) - thread ID and any warning message
76
"""
77
```
78
79
### Platform Detection
80
81
Utilities for detecting platform capabilities and requirements for process attachment.
82
83
```python { .api }
84
def is_windows():
85
"""
86
Check if running on Windows platform.
87
88
Returns:
89
bool: True if Windows, False otherwise
90
"""
91
92
def is_linux():
93
"""
94
Check if running on Linux platform.
95
96
Returns:
97
bool: True if Linux, False otherwise
98
"""
99
100
def is_mac():
101
"""
102
Check if running on macOS platform.
103
104
Returns:
105
bool: True if macOS, False otherwise
106
"""
107
108
def get_platform_attachment_library():
109
"""
110
Get the appropriate native library for process attachment.
111
112
Returns:
113
str: Path to platform-specific attachment library (.dll, .so, or .dylib)
114
115
Raises:
116
- RuntimeError: If no compatible library is available for current platform
117
"""
118
```
119
120
## Usage Examples
121
122
### Basic Process Attachment
123
124
```python
125
import os
126
import subprocess
127
from pydevd_attach_to_process.add_code_to_python_process import run_python_code
128
129
# Start a target Python process
130
target_script = """
131
import time
132
counter = 0
133
while True:
134
counter += 1
135
print(f"Counter: {counter}")
136
time.sleep(1)
137
"""
138
139
# Write target script to file
140
with open('target_app.py', 'w') as f:
141
f.write(target_script)
142
143
# Start target process
144
process = subprocess.Popen(['python', 'target_app.py'])
145
target_pid = process.pid
146
147
print(f"Target process started with PID: {target_pid}")
148
149
# Inject debugger setup code into the running process
150
debugger_setup_code = """
151
import pydevd
152
print("Setting up debugger...")
153
pydevd.settrace(
154
host='localhost',
155
port=5678,
156
suspend=False,
157
trace_only_current_thread=False
158
)
159
print("Debugger attached successfully!")
160
"""
161
162
try:
163
result = run_python_code(
164
pid=target_pid,
165
python_code=debugger_setup_code,
166
connect_debugger_tracing=True,
167
show_debug_info=1
168
)
169
170
print(f"Code injection result: {result}")
171
print("You can now connect your IDE to port 5678")
172
173
except Exception as e:
174
print(f"Attachment error: {e}")
175
176
finally:
177
# Clean up
178
process.terminate()
179
os.remove('target_app.py')
180
```
181
182
### Code Injection for Debugging Setup
183
184
```python
185
from pydevd_attach_to_process.add_code_to_python_process import add_code_to_python_process
186
187
# Code to inject for debugger setup
188
debugger_setup_code = """
189
import pydevd
190
print("Setting up debugger...")
191
pydevd.settrace(
192
host='localhost',
193
port=5678,
194
suspend=False,
195
trace_only_current_thread=False
196
)
197
print("Debugger attached successfully!")
198
"""
199
200
# Inject debugger setup code into running process
201
result = add_code_to_python_process(
202
pid=target_pid,
203
python_code=debugger_setup_code,
204
connect_debugger_tracing=True,
205
show_debug_info=1
206
)
207
208
print(f"Code injection result: {result}")
209
```
210
211
### Advanced Process Information and Compatibility
212
213
```python
214
from pydevd_attach_to_process import attach_pydevd
215
import psutil
216
217
def attach_with_compatibility_check(pid, debugger_port):
218
"""
219
Attach to process with comprehensive compatibility checking.
220
"""
221
try:
222
# Get process information
223
process_info = attach_pydevd.get_process_info(pid)
224
225
print(f"Process Information:")
226
print(f" PID: {pid}")
227
print(f" Python Version: {process_info['python_version']}")
228
print(f" Architecture: {process_info['architecture']}")
229
print(f" Executable: {process_info['executable']}")
230
print(f" Compatible: {process_info['compatible']}")
231
232
if not process_info['compatible']:
233
print(f" Incompatibility Reason: {process_info['reason']}")
234
return False
235
236
# Check if process is still running
237
if not psutil.pid_exists(pid):
238
print(f"Process {pid} no longer exists")
239
return False
240
241
# Get additional process details
242
proc = psutil.Process(pid)
243
print(f" Process Name: {proc.name()}")
244
print(f" Memory Usage: {proc.memory_info().rss / 1024 / 1024:.1f} MB")
245
print(f" CPU Percent: {proc.cpu_percent():.1f}%")
246
247
# Attempt attachment
248
print(f"\nAttempting to attach debugger to PID {pid}...")
249
success = attach_pydevd.attach_to_process(
250
pid=pid,
251
debugger_port=debugger_port,
252
timeout=15.0
253
)
254
255
if success:
256
print(f"✓ Successfully attached debugger")
257
print(f" Connect your IDE to localhost:{debugger_port}")
258
return True
259
else:
260
print("✗ Failed to attach debugger")
261
return False
262
263
except Exception as e:
264
print(f"Error during attachment: {e}")
265
return False
266
267
# Usage
268
target_pid = 12345 # Replace with actual PID
269
attach_with_compatibility_check(target_pid, 5678)
270
```
271
272
### Platform-Specific Attachment
273
274
```python
275
from pydevd_attach_to_process import attach_pydevd
276
from pydevd_attach_to_process.add_code_to_python_process import (
277
run_python_code_windows,
278
run_python_code_linux,
279
run_python_code_mac,
280
is_windows,
281
is_linux,
282
is_mac
283
)
284
285
def platform_specific_attach(pid, debugger_code):
286
"""
287
Perform platform-specific code injection and attachment.
288
"""
289
show_debug_info = 2 # Detailed debug information
290
connect_tracing = True
291
292
try:
293
if is_windows():
294
print("Using Windows attachment method...")
295
result = run_python_code_windows(
296
pid, debugger_code, connect_tracing, show_debug_info
297
)
298
elif is_linux():
299
print("Using Linux attachment method...")
300
result = run_python_code_linux(
301
pid, debugger_code, connect_tracing, show_debug_info
302
)
303
elif is_mac():
304
print("Using macOS attachment method...")
305
result = run_python_code_mac(
306
pid, debugger_code, connect_tracing, show_debug_info
307
)
308
else:
309
raise RuntimeError("Unsupported platform for process attachment")
310
311
print(f"Platform-specific attachment result: {result}")
312
return True
313
314
except Exception as e:
315
print(f"Platform-specific attachment failed: {e}")
316
return False
317
318
# Debugger setup code
319
setup_code = """
320
import sys
321
import pydevd
322
323
# Verify Python version compatibility
324
if sys.version_info >= (3, 8):
325
print("Python version compatible, setting up debugger...")
326
pydevd.settrace(
327
host='0.0.0.0',
328
port=5678,
329
suspend=False,
330
stdout_to_server=True,
331
stderr_to_server=True
332
)
333
print("Remote debugger attached and ready!")
334
else:
335
print(f"Python version {sys.version_info} not supported")
336
"""
337
338
# Perform platform-specific attachment
339
platform_specific_attach(target_pid, setup_code)
340
```
341
342
### Production Process Debugging
343
344
```python
345
import time
346
import signal
347
from pydevd_attach_to_process import attach_pydevd
348
349
class ProductionDebugger:
350
def __init__(self, target_pid, debugger_port=5678):
351
self.target_pid = target_pid
352
self.debugger_port = debugger_port
353
self.attached = False
354
355
def safe_attach(self):
356
"""
357
Safely attach to production process with error handling.
358
"""
359
try:
360
# First, verify process exists and is compatible
361
process_info = attach_pydevd.get_process_info(self.target_pid)
362
363
if not process_info['compatible']:
364
raise RuntimeError(f"Process incompatible: {process_info['reason']}")
365
366
# Prepare minimal debugger code
367
minimal_debugger_code = """
368
import pydevd
369
import threading
370
371
def setup_debugger():
372
try:
373
pydevd.settrace(
374
host='localhost',
375
port={port},
376
suspend=False,
377
trace_only_current_thread=False,
378
patch_multiprocessing=False # Don't interfere with child processes
379
)
380
print(f"Debugger attached on port {port}")
381
except Exception as e:
382
print(f"Debugger setup failed: {{e}}")
383
384
# Run in separate thread to avoid blocking main application
385
debug_thread = threading.Thread(target=setup_debugger, daemon=True)
386
debug_thread.start()
387
""".format(port=self.debugger_port)
388
389
# Inject debugger code
390
success = attach_pydevd.attach_to_process(
391
pid=self.target_pid,
392
debugger_port=self.debugger_port,
393
timeout=5.0 # Short timeout for production
394
)
395
396
if success:
397
self.attached = True
398
print(f"✓ Production debugger attached to PID {self.target_pid}")
399
print(f" IDE connection: localhost:{self.debugger_port}")
400
return True
401
else:
402
print("✗ Failed to attach to production process")
403
return False
404
405
except Exception as e:
406
print(f"Production debugging setup failed: {e}")
407
return False
408
409
def detach(self):
410
"""
411
Detach debugger from production process.
412
"""
413
if self.attached:
414
detach_code = """
415
import pydevd
416
try:
417
pydevd.stoptrace()
418
print("Debugger detached successfully")
419
except:
420
pass
421
"""
422
try:
423
attach_pydevd.inject_debugger_code(self.target_pid, detach_code)
424
self.attached = False
425
print(f"Debugger detached from PID {self.target_pid}")
426
except Exception as e:
427
print(f"Failed to detach debugger: {e}")
428
429
# Usage for production debugging
430
production_pid = 98765 # Replace with production process PID
431
432
debugger = ProductionDebugger(production_pid, debugger_port=5679)
433
434
# Set up signal handler for clean detachment
435
def signal_handler(signum, frame):
436
print("\nDetaching debugger...")
437
debugger.detach()
438
exit(0)
439
440
signal.signal(signal.SIGINT, signal_handler)
441
442
# Attach to production process
443
if debugger.safe_attach():
444
print("Production debugging active. Press Ctrl+C to detach.")
445
try:
446
while True:
447
time.sleep(1)
448
except KeyboardInterrupt:
449
pass
450
finally:
451
debugger.detach()
452
```
453
454
## Platform Requirements
455
456
### Windows
457
- Requires `attach_windows.dll` (32-bit) or `attach_windows_amd64.dll` (64-bit)
458
- Administrator privileges may be required for some processes
459
- Compatible with Windows 7+ and Windows Server 2008+
460
461
### Linux
462
- Requires `attach_linux.so` (32-bit) or `attach_linux_amd64.so` (64-bit)
463
- Requires `ptrace` capability (may need `CAP_SYS_PTRACE`)
464
- Compatible with most Linux distributions
465
466
### macOS
467
- Requires `attach_mac.dylib` (32-bit) or `attach_mac_amd64.dylib` (64-bit)
468
- May require disabling System Integrity Protection (SIP) for some scenarios
469
- Compatible with macOS 10.9+
470
471
## Security Considerations
472
473
- **Permissions**: Process attachment requires appropriate system permissions
474
- **Code Injection**: Only inject trusted code into processes
475
- **Production Use**: Use minimal debugger setup to avoid performance impact
476
- **Network Security**: Consider firewall rules for debugger ports
477
- **Process Stability**: Attachment may affect target process performance