Skip to content

Instantly share code, notes, and snippets.

@w568w
Created November 7, 2025 05:53
Show Gist options
  • Select an option

  • Save w568w/9a705c9545335129960a047216bf06ce to your computer and use it in GitHub Desktop.

Select an option

Save w568w/9a705c9545335129960a047216bf06ce to your computer and use it in GitHub Desktop.
Modify the command line of a running process shown in `top`, `ps`, etc.
import os
import sys
import ctypes
from typing import Tuple, List
def get_argv_address(pid: int) -> Tuple[int, int, bytes]:
"""
Get the memory address of the argv area of a given process.
Args:
pid (int): Target process ID.
Returns:
Tuple[int, int, bytes]: A tuple containing:
- start_addr (int): Starting address of argv in memory
- end_addr (int): Ending address of argv in memory
- argv_bytes (bytes): Raw argv bytes read from /proc/[pid]/cmdline
"""
# 1. Read current cmdline — the raw argv contents
cmdline_path = f"/proc/{pid}/cmdline"
with open(cmdline_path, 'rb') as f:
cmdline_bytes = f.read()
# Parse arguments from cmdline (null-separated)
args = cmdline_bytes.rstrip(b'\x00').split(b'\x00')
print(f"Current arguments: {[arg.decode() for arg in args]}")
# 2. Locate the stack region from /proc/[pid]/maps
maps_path = f"/proc/{pid}/maps"
stack_start: int | None = None
stack_end: int | None = None
with open(maps_path, 'r') as f:
for line in f:
if '[stack]' in line:
addr_range = line.split()[0]
start, end = addr_range.split('-')
stack_start = int(start, 16)
stack_end = int(end, 16)
break
if not stack_start:
raise RuntimeError("Stack region not found in memory map.")
print(f"Stack region: 0x{stack_start:x} - 0x{stack_end:x}")
# 3. Search for argv bytes in the stack memory range
mem_path = f"/proc/{pid}/mem"
chunk_size = 4096
with open(mem_path, 'rb') as mem:
addr = stack_start
while addr < stack_end:
try:
mem.seek(addr)
chunk = mem.read(chunk_size)
offset = chunk.find(cmdline_bytes)
if offset != -1:
found_addr = addr + offset
print(f"Found argv address: 0x{found_addr:x}")
return found_addr, found_addr + len(cmdline_bytes), cmdline_bytes
addr += chunk_size
except OSError:
# Skip unreadable pages
addr += chunk_size
continue
raise RuntimeError("Unable to locate argv bytes in process memory.")
def modify_argv(pid: int, argv_addr: int, new_args: List[str], original_length: int) -> None:
"""
Modify the argv of a running process.
Args:
pid (int): Process ID.
argv_addr (int): Starting memory address of argv.
new_args (List[str]): List of new argument strings.
original_length (int): Length (in bytes) of the original argv data.
"""
# Construct new cmdline data
new_cmdline = b'\x00'.join(arg.encode('utf-8') for arg in new_args) + b'\x00'
print(f"\nModifying argv to: {new_args}")
print(f"New data length: {len(new_cmdline)} bytes")
# Pad with null bytes if the new data is shorter
if len(new_cmdline) < original_length:
padding = original_length - len(new_cmdline)
new_cmdline += b'\x00' * padding
print(f"Padded with {padding} null bytes")
PTRACE_ATTACH = 16
PTRACE_DETACH = 17
libc = ctypes.CDLL("libc.so.6")
# Attach to the process (requires root privileges)
result = libc.ptrace(PTRACE_ATTACH, pid, 0, 0)
if result == 0:
os.waitpid(pid, 0)
print("Attached to target process.")
try:
# Write new argv data into process memory
mem_path = f"/proc/{pid}/mem"
with open(mem_path, 'r+b') as mem:
mem.seek(argv_addr)
mem.write(new_cmdline)
print("✓ argv successfully modified.")
finally:
# Detach to resume normal execution
libc.ptrace(PTRACE_DETACH, pid, 0, 0)
print("Detached from process.")
def main() -> None:
if len(sys.argv) < 2:
print("Usage:")
print(f" View argv: {sys.argv[0]} <pid>")
print(f" Modify argv: {sys.argv[0]} <pid> <new_arg0> <new_arg1> ...")
sys.exit(1)
pid = int(sys.argv[1])
try:
# Locate argv in process memory
addr, end_addr, old_data = get_argv_address(pid)
print(f"argv memory range: 0x{addr:x} - 0x{end_addr:x} ({end_addr - addr} bytes)")
# If additional args provided, perform modification
if len(sys.argv) > 2:
new_args = sys.argv[2:]
new_len = sum(len(arg.encode()) for arg in new_args) + len(new_args)
old_len = end_addr - addr
if new_len > old_len:
print(f"\nWarning: New argv ({new_len} bytes) exceeds original size ({old_len} bytes)")
print("This may overwrite adjacent data! Aborting.")
return
modify_argv(pid, addr, new_args, old_len)
# Verify modification
print("\nVerifying modification...")
new_addr, new_end, new_data = get_argv_address(pid)
except PermissionError:
print("Error: Root privileges are required.")
sys.exit(1)
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment