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