System, Uncategorized, Web Server

Dump A Linux Process’s Memory To File

I’m not sure how you dump all the memory to a file without doing this repeatedly (if anyone knows an automated way to get gdb to do this please let me know), but the following works for any one batch of memory assuming you know the pid:

$ cat /proc/[pid]/maps

This will be in the format (example):

00400000-00421000 r-xp 00000000 08:01 592398 /usr/libexec/dovecot/pop3-login
00621000-00622000 rw-p 00021000 08:01 592398 /usr/libexec/dovecot/pop3-login
00622000-0066a000 rw-p 00622000 00:00 0 [heap]
3e73200000-3e7321c000 r-xp 00000000 08:01 229378 /lib64/ld-2.5.so
3e7341b000-3e7341c000 r--p 0001b000 08:01 229378 /lib64/ld-2.5.so

Pick one batch of memory (so for example 00621000-00622000) then use gdb as root to attach to the process and dump that memory:

$ gdb --pid [pid]
(gdb) dump memory /root/output 0x00621000 0x00622000

Then analyse /root/output with the strings command, less you want the PuTTY all over your screen.

The following quick-and-dirty python script dumps the memory of a process to stdout. This has the side effect of loading any swapped out page or mapped file. Call it as cat_proc_mem 123 456 789 where the arguments are process IDs.

This script is completely specific to Linux. It may be adaptable to other systems with a similar /proc structure (Solaris?), but forget about running it on e.g. *BSD. Even on Linux, you may need to change the definition of c_pid_t and the values of PTRACE_ATTACH and PTRACE_DETACH. This is a proof-of-principle script, not meant as an example of good programming practices. Use at your own risk.

Linux makes the memory of a process available as /proc/$pid/mem. Only certain address ranges are readable. These ranges can be found by reading the memory mapping information from the text file /proc/$pid/maps. The pseudo-file /proc/$pid/mem cannot be read by all processes that have the permission to read it: the reader process must have called ptrace(PTRACE_ATTACH, $pid)

#! /usr/bin/env python
import ctypes, re, sys

## Partial interface to ptrace(2), only for PTRACE_ATTACH and PTRACE_DETACH.
c_ptrace = ctypes.CDLL("libc.so.6").ptrace
c_pid_t = ctypes.c_int32 # This assumes pid_t is int32_t
c_ptrace.argtypes = [ctypes.c_int, c_pid_t, ctypes.c_void_p, ctypes.c_void_p]
def ptrace(attach, pid):
 op = ctypes.c_int(16 if attach else 17) #PTRACE_ATTACH or PTRACE_DETACH
 c_pid = c_pid_t(pid)
 null = ctypes.c_void_p()
 err = c_ptrace(op, c_pid, null, null)
 if err != 0: raise SysError, 'ptrace', err

## Parse a line in /proc/$pid/maps. Return the boundaries of the chunk
## the read permission character.
def maps_line_range(line):
 m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
 return [int(m.group(1), 16), int(m.group(2), 16), m.group(3)]

## Dump the readable chunks of memory mapped by a process
def cat_proc_mem(pid):
 ## Apparently we need to ptrace(PTRACE_ATTACH, $pid) to read /proc/$pid/mem
 ptrace(True, int(pid))
 ## Read the memory maps to see what address ranges are readable
 maps_file = open("/proc/" + pid + "/maps", 'r')
 ranges = map(maps_line_range, maps_file.readlines())
 maps_file.close()
 ## Read the readable mapped ranges
 mem_file = open("/proc/" + pid + "/mem", 'r', 0)
 for r in ranges:
 if r[2] == 'r':
 mem_file.seek(r[0])
 chunk = mem_file.read(r[1] - r[0])
 print chunk,
 mem_file.close()
 ## Cleanup
 ptrace(False, int(pid))

if __name__ == "__main__":
 for pid in sys.argv[1:]:
 cat_proc_mem(pid)

In this case, we are going to get dump information of a running apache. Append below code and execute.

#!/bin/bash
grep rw-p /proc/$1/maps | sed -n 's/^\([0-9a-f]*\)-\([0-9a-f]*\) .*$/\1 \2/p' | while read start stop; do gdb --batch --pid $1 -ex "dump memory $1-$start-$stop.dump 0x$start 0x$stop"; done

Get the pid of your apache process.

pgrep -uroot apache2

Dump the process’s memory

mkdir /tmp/apache_dump && cd /tmp/apache_dump
sh /path/to/dump-all-memory-of-pid.sh <PID>

grep all of the dump files for something that you expect to be in the apache config file.

grep DocumentRoot*

open the matched dump file(s) in vim and search for the string.

vim 24374-7f159d56c000-7f159d72c000.dump

Search by typing “/”, eg “/DocumentRoot”, then simply copy out the text that you want.

Thank me later.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s