Dumping memory with GDB

It's often useful to be able to grab memory from a running process. This post is going to go over how to dump memory from a live process using gdb.

Attaching to a process

The example in this post is going to dump memory from an interactive python process:

$ python
Python 2.7.5 (default, Nov  6 2016, 00:28:07)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = "hello world " * 1000
>>>

Note: a long string is assigned to the variable a, this string will be stored in memory, more specifically the heap.

The first thing to do is find the process id (PID) for the process. This can be done with pgrep:

$ pgrep python
1131

ps can be used to verify the PID matches the expected process:

$ ps -fp 1131
UID        PID  PPID  C STIME TTY          TIME CMD
user      1131  1110  0 20:45 pts/1    00:00:00 python

Once you have the correct PID, run gdb with the -p option to specify the process to attach to:

$ gdb -p 1131
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7
Copyright (C) 2013 Free Software Foundation, Inc.
...
Attaching to process 1131
...
(gdb)

If everything goes well, GDB should stop the process it's attaching to and give you a (gdb) prompt.

Memory ranges

Once GDB is attached to a process, the next thing to do is work out which memory range needs to be dumped. One way to do this is to look at the /proc/ file system. More specifically /proc/<pid>/maps and /proc/<pid>/smaps.

maps gives a single line for each block of assigned memory:

$ grep heap /proc/1131/maps
00c7d000-00d94000 rw-p 00000000 00:00 0         [heap]

smaps also gives size information for each block of memory:

$ grep -A 15 heap /proc/1131/smaps
00c7d000-00d94000 rw-p 00000000 00:00 0         [heap]
Size:               1116 kB
Rss:                1020 kB
Pss:                1020 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:      1020 kB
Referenced:         1020 kB
Anonymous:          1020 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd wr mr mw me ac sd

For more information on maps and smaps, refer to the proc file system documentation.

Note: if you want to get this information without leaving GDB, external commands can be called from inside GDB by prefixing the command with !:

(gdb) ! grep heap /proc/1131/maps
00c7d000-00d94000 rw-p 00000000 00:00 0         [heap]

Dumping the heap

Once the memory range is known, it can be dumped to a file with a GDB command similar to the following:

(gdb) dump binary memory /tmp/python-heap.bin 0x00c7d000 0x00d94000

At this point GDB can be detached by running the quit command, this will exit GDB and the process will resume:

(gdb) quit
A debugging session is active.

        Inferior 1 [process 1131] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 1131

Tools like xxd can now be used to inspect the dump of the memory range:

$ xxd /tmp/python-heap.bin |grep -m 1 -B 3 -A 6 '\.\.\.hello world'
00f7450: 0000 0000 0000 0000 112f 0000 0000 0000  ........./......
00f7460: 0100 0000 0000 0000 e06a 96d0 cb7f 0000  .........j......
00f7470: e02e 0000 0000 0000 ffff ffff ffff ffff  ................
00f7480: 0000 0000 6865 6c6c 6f20 776f 726c 6420  ....hello world
00f7490: 6865 6c6c 6f20 776f 726c 6420 6865 6c6c  hello world hell
00f74a0: 6f20 776f 726c 6420 6865 6c6c 6f20 776f  o world hello wo
00f74b0: 726c 6420 6865 6c6c 6f20 776f 726c 6420  rld hello world
00f74c0: 6865 6c6c 6f20 776f 726c 6420 6865 6c6c  hello world hell
00f74d0: 6f20 776f 726c 6420 6865 6c6c 6f20 776f  o world hello wo
00f74e0: 726c 6420 6865 6c6c 6f20 776f 726c 6420  rld hello world