Skip to content

Commit c8a9a61

Browse files
committed
Add details per arch
Signed-off-by: Pablo Galindo <[email protected]>
1 parent 1730c05 commit c8a9a61

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

Doc/howto/remote_debugging.rst

+68
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,30 @@ The following is an example implementation::
9494
return base_address + section_offset
9595

9696

97+
On Linux systems, there are two main approaches to read memory from another
98+
process. The first is through the ``/proc`` filesystem, specifically by reading from
99+
``/proc/[pid]/mem`` which provides direct access to the process's memory. This
100+
requires appropriate permissions - either being the same user as the target
101+
process or having root access. The second approach is using the
102+
``process_vm_readv()`` system call which provides a more efficient way to copy
103+
memory between processes. While ptrace's ``PTRACE_PEEKTEXT`` operation can also be
104+
used to read memory, it is significantly slower as it only reads one word at a
105+
time and requires multiple context switches between the tracer and tracee
106+
processes.
107+
108+
For parsing ELF sections, the process involves reading and interpreting the ELF
109+
file format structures from the binary file on disk. The ELF header contains a
110+
pointer to the section header table. Each section header contains metadata about
111+
a section including its name (stored in a separate string table), offset, and
112+
size. To find a specific section like .PyRuntime, you need to walk through these
113+
headers and match the section name. The section header then provdes the offset
114+
where that section exists in the file, which can be used to calculate its
115+
runtime address when the binary is loaded into memory.
116+
117+
You can read more about the ELF file format in the `ELF specification
118+
<https://en.wikipedia.org/wiki/Executable_and_Linkable_Format>`_.
119+
120+
97121
.. rubric:: macOS (Mach-O)
98122

99123
To find the ``PyRuntime`` structure on macOS:
@@ -134,6 +158,29 @@ The following is an example implementation::
134158
# Step 5: Compute the PyRuntime address in memory
135159
return base_address + section_offset
136160

161+
On macOS, accessing another process's memory requires using Mach-O specific APIs
162+
and file formats. The first step is obtaining a ``task_port`` handle via
163+
``task_for_pid()``, which provides access to the target process's memory space.
164+
This handle enables memory operations through APIs like
165+
``mach_vm_read_overwrite()``.
166+
167+
The process memory can be examined using ``mach_vm_region()`` to scan through the
168+
virtual memory space, while ``proc_regionfilename()`` helps identify which binary
169+
files are loaded at each memory region. When the Python binary or library is
170+
found, its Mach-O headers need to be parsed to locate the ``PyRuntime`` structure.
171+
172+
The Mach-O format organizes code and data into segments and sections. The
173+
``PyRuntime`` structure lives in a section named ``__PyRuntime`` within the
174+
``__DATA`` segment. The actual runtime address calculation involves finding the
175+
``__TEXT`` segment which serves as the binary's base address, then locating the
176+
``__DATA`` segment containing our target section. The final address is computed by
177+
combining the base address with the appropriate section offsets from the Mach-O
178+
headers.
179+
180+
Note that accessing another process's memory on macOS typically requires
181+
elevated privileges - either root access or special security entitlements
182+
granted to the debugging process.
183+
137184

138185
.. rubric:: Windows (PE)
139186

@@ -180,6 +227,27 @@ The following is an example implementation::
180227
return base_address + section_rva
181228

182229

230+
On Windows, accessing another process's memory requires using the Windows API
231+
functions like ``CreateToolhelp32Snapshot()`` and ``Module32First()/Module32Next()``
232+
to enumerate loaded modules. The ``OpenProcess()`` function provides a handle to
233+
access the target process's memory space, enabling memory operations through
234+
``ReadProcessMemory()``.
235+
236+
The process memory can be examined by enumerating loaded modules to find the
237+
Python binary or DLL. When found, its PE headers need to be parsed to locate the
238+
``PyRuntime`` structure.
239+
240+
The PE format organizes code and data into sections. The ``PyRuntime`` structure
241+
lives in a section named "PyRuntim" (truncated from "PyRuntime" due to PE's
242+
8-character name limit). The actual runtime address calculation involves finding
243+
the module's base address from the module entry, then locating our target
244+
section in the PE headers. The final address is computed by combining the base
245+
address with the section's virtual address from the PE section headers.
246+
247+
Note that accessing another process's memory on Windows typically requires
248+
appropriate privileges - either administrative access or the ``SeDebugPrivilege``
249+
privilege granted to the debugging process.
250+
183251

184252
Reading _Py_DebugOffsets
185253
========================

0 commit comments

Comments
 (0)