67 Memory Maps
The ability to understand and manipulate the memory maps of a debugged program is important for many different Reverse Engineering tasks. Rizin offers a rich set of commands to handle memory maps in the binary. This includes listing the memory maps of the currently debugged binary, removing memory maps, handling loaded libraries and more.
First, let’s see the help message for dm
, the command which is responsible for handling memory maps:
[0x55f2104cf620]> dm?
Usage: dm[?] # Memory map commands
| dm[j*qt] # List memory maps
| dm+ <size> # Allocate <size> bytes at current offset
| dm= # List memory maps of current process with ASCII art bars
| dm. # Show map name of current address
| dmm[j*.] # Module memory map commands
| dm- # Deallocate memory map at current offset
| dmd[aw] # Dump debug map regions to a file (from-to.dmp)
| dmh[?] # Glibc heap commands
| dmi[?] # List/Load symbols
| dml <file> # Load contents of file into current map region
| dmp <perms> [<size>] # Change page at current offset with <size>, protection <perms> / Change dbg.map permissions
to <perms>
| dmL <size> # Allocate <size> bytes at current offset and promote to huge page
| dmS[*] [<addr|libname> [<sectname>]] # List sections of target lib
| dmw[jb?] # Windows heap commands
| dmx[?] # Jemalloc heap commands
In this chapter, we’ll go over some of the most useful subcommands of dm
using simple examples. For the following examples, we’ll use a simple hello_world for Linux, but it’ll be the same for every binary.
First things first - open a program in debugging mode:
$ rizin -d hello_world
Process with PID 4760 started...
[0x7f12fa1debb0]>
Note that we passed “hello_world” to rizin without “./”. rizin will try to find this program in the current directory and then in $PATH, even if no “./” is passed. This is contradictory with UNIX systems, but makes the behaviour consistent for Windows users
Let’s use dm
to print the memory maps of the binary we’ve just opened:
[0x7f133f022fb0]> dm
0x000055ca0f426000 - 0x000055ca0f427000 - usr 4K s r-- /tmp/hello_world /tmp/hello_world ; tmp_hello_world.r
0x000055ca0f427000 - 0x000055ca0f428000 - usr 4K s r-x /tmp/hello_world /tmp/hello_world ; tmp_hello_world.r_x
0x000055ca0f428000 - 0x000055ca0f429000 - usr 4K s r-- /tmp/hello_world /tmp/hello_world ; tmp_hello_world.r.55ca0f428000
0x000055ca0f429000 - 0x000055ca0f42b000 - usr 8K s rw- /tmp/hello_world /tmp/hello_world ; tmp_hello_world.rw
0x00007f52c4ae0000 - 0x00007f52c4ae1000 - usr 4K s r-- /usr/lib64/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2 ; usr_lib64_ld_linux_x86_64.so.2.r
0x00007f52c4ae1000 - 0x00007f52c4b06000 * usr 148K s r-x /usr/lib64/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2 ; usr_lib64_ld_linux_x86_64.so.2.r_x
0x00007f52c4b06000 - 0x00007f52c4b11000 - usr 44K s r-- /usr/lib64/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2 ; usr_lib64_ld_linux_x86_64.so.2.r.7f52c4b06000
0x00007f52c4b11000 - 0x00007f52c4b15000 - usr 16K s rw- /usr/lib64/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2 ; usr_lib64_ld_linux_x86_64.so.2.rw
0x00007fff03836000 - 0x00007fff03858000 - usr 136K s rw- [stack] [stack] ; stack_.rw
0x00007fff038bc000 - 0x00007fff038c0000 - usr 16K s r-- [vvar] [vvar] ; vvar_.r
0x00007fff038c0000 - 0x00007fff038c2000 - usr 8K s r-x [vdso] [vdso] ; vdso_.r_x
0xffffffffff600000 - 0xffffffffff601000 - usr 4K s --x [vsyscall] [vsyscall] ; vsyscall_.__x
For those of you who prefer a more visual way, you can use dm=
to see the memory maps using an ASCII-art bars. This will be handy when you want to see how these maps are located in the memory.
[0x7f52c4afbbb0]> dm=
map 4K - 0x00007f52c4ae0000 |------------------------------| 0x00007f52c4ae1000 r-- /usr/lib64/ld-linux-x86-64.so.2
map 148K * 0x00007f52c4ae1000 |------------------------------| 0x00007f52c4b06000 r-x /usr/lib64/ld-linux-x86-64.so.2
map 44K - 0x00007f52c4b06000 |------------------------------| 0x00007f52c4b11000 r-- /usr/lib64/ld-linux-x86-64.so.2
map 16K - 0x00007f52c4b11000 |------------------------------| 0x00007f52c4b15000 rw- /usr/lib64/ld-linux-x86-64.so.2
map 4K - 0xffffffffff600000 |------------------------------| 0xffffffffff601000 --x [vsyscall]
map 136K - 0x00007fff03836000 |------------------------------| 0x00007fff03858000 rw- [stack]
map 16K - 0x00007fff038bc000 |------------------------------| 0x00007fff038c0000 r-- [vvar]
map 8K - 0x00007fff038c0000 |------------------------------| 0x00007fff038c2000 r-x [vdso]
map 4K - 0x000055ca0f426000 |#######-----------------------| 0x000055ca0f427000 r-- /tmp/hello_world
map 4K - 0x000055ca0f427000 |------#######-----------------| 0x000055ca0f428000 r-x /tmp/hello_world
map 4K - 0x000055ca0f428000 |------------#######-----------| 0x000055ca0f429000 r-- /tmp/hello_world
map 8K - 0x000055ca0f429000 |------------------############| 0x000055ca0f42b000 rw- /tmp/hello_world
If you want to know the memory-map you are currently in, use dm.
:
[0x7f52c4afbbb0]> dm.
0x00007f52c4ae1000 - 0x00007f52c4b06000 * usr 148K s r-x /usr/lib64/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2 ; usr_lib64_ld_linux_x86_64.so.2.r_x
Using dmm
we can “List modules (libraries, binaries loaded in memory)”, this is quite a handy command to see which modules were loaded.
[0x7f52c4afbbb0]> dmm
0x55ca0f426000 0x55ca0f427000 /tmp/hello_world
0x7f52c4ae0000 0x7f52c4ae1000 /usr/lib64/ld-linux-x86-64.so.2
Note that the output of
dm
subcommands, anddmm
specifically, might be different in various systems and different binaries.
We can see that along with our hello_world
binary itself, another library was loaded which is ld-linux-x86-64.so.2
. We don’t see libc
yet and this is because Rizin breaks before libc
is loaded to memory. Let’s use dcu
(debug continue until) to execute our program until the entry point of the program, which Rizin flags as entry0
:
[0x7f52c4afbbb0]> dcu entry0
Continue until 0x55ca0f427100
hit breakpoint at: 0x55ca0f427100
[0x55ca0f427100]> dmm
0x55ca0f426000 0x55ca0f427000 /tmp/hello_world
0x7f52c48c8000 0x7f52c48ec000 /usr/lib64/libc.so.6
0x7f52c4ae0000 0x7f52c4ae1000 /usr/lib64/ld-linux-x86-64.so.2
Now we can see that libc.so.6
was loaded as well, great!
Speaking of libc
, a popular task for binary exploitation is to find the address of a specific symbol in a library. With this information in hand, you can build, for example, an exploit which uses ROP. This can be achieved using the dmi
command. So if we want, for example, to find the address of system()
in the loaded libc
, we can simply execute the following command:
[0x55ca0f427100]> dmi libc system
[Symbols]
nth paddr vaddr bind type size lib name
---------------------------------------------------------
1052 0x0004d2f0 0x7f52c49152f0 WEAK FUNC 45 system
Similar to the dm.
command, with dmi.
you can see the closest symbol to the current address:
[0x55ca0f427100]> dmi. libc system
[Symbols]
nth paddr vaddr bind type size lib name
------------------------------------------------------
20 ---------- 0x00004018 GLOBAL NOTYPE 0 _end
Another useful command is to list the sections of a specific library. In the following example we’ll list the sections of ld-linux-x86-64.so.2
:
[0x55ca0f427100]> dmS ld-linux-x86-64.so.2
[Sections]
paddr size vaddr vsize align perm name type flags
--------------------------------------------------------------------------------------------------------------------
0x00000000 0x0 ---------- 0x0 0x0 ---- ld-linux-x86-64.so.2. NULL
0x00000270 0x1e8 0x7f52c4ae0270 0x1e8 0x0 -r-- ld-linux-x86-64.so.2..gnu.hash GNU_HASH alloc
0x00000458 0x3c0 0x7f52c4ae0458 0x3c0 0x0 -r-- ld-linux-x86-64.so.2..dynsym DYNSYM alloc
0x00000818 0x2ca 0x7f52c4ae0818 0x2ca 0x0 -r-- ld-linux-x86-64.so.2..dynstr STRTAB alloc
0x00000ae2 0x50 0x7f52c4ae0ae2 0x50 0x0 -r-- ld-linux-x86-64.so.2..gnu.version VERSYM alloc
0x00000b38 0xec 0x7f52c4ae0b38 0xec 0x0 -r-- ld-linux-x86-64.so.2..gnu.version_d VERDEF alloc
0x00000c28 0x18 0x7f52c4ae0c28 0x18 0x0 -r-- ld-linux-x86-64.so.2..rela.dyn RELA alloc
0x00000c40 0x18 0x7f52c4ae0c40 0x18 0x0 -r-- ld-linux-x86-64.so.2..relr.dyn NUM alloc
0x00001000 0x24ebb 0x7f52c4ae1000 0x24ebb 0x0 -r-x ld-linux-x86-64.so.2..text PROGBITS alloc,execute
0x00026000 0x64e8 0x7f52c4b06000 0x64e8 0x0 -r-- ld-linux-x86-64.so.2..rodata PROGBITS alloc
0x0002c4e8 0x9a4 0x7f52c4b0c4e8 0x9a4 0x0 -r-- ld-linux-x86-64.so.2..eh_frame_hdr PROGBITS alloc
0x0002ce90 0x36f8 0x7f52c4b0ce90 0x36f8 0x0 -r-- ld-linux-x86-64.so.2..eh_frame PROGBITS alloc
0x00030588 0x40 0x7f52c4b10588 0x40 0x0 -r-- ld-linux-x86-64.so.2..note.gnu.property NOTE alloc
0x000312e0 0x1ba0 0x7f52c4b112e0 0x1ba0 0x0 -rw- ld-linux-x86-64.so.2..data.rel.ro PROGBITS write,alloc
0x00032e80 0x170 0x7f52c4b12e80 0x170 0x0 -rw- ld-linux-x86-64.so.2..dynamic DYNAMIC write,alloc
0x00033000 0x1104 0x7f52c4b13000 0x1104 0x0 -rw- ld-linux-x86-64.so.2..data PROGBITS write,alloc
0x00034104 0x0 0x7f52c4b14110 0x1d0 0x0 -rw- ld-linux-x86-64.so.2..bss NOBITS write,alloc
0x00034104 0x33 ---------- 0x33 0x0 ---- ld-linux-x86-64.so.2..comment PROGBITS merge,strings
0x00034138 0x40f8 ---------- 0x40f8 0x0 ---- ld-linux-x86-64.so.2..symtab SYMTAB
0x00038230 0x23f4 ---------- 0x23f4 0x0 ---- ld-linux-x86-64.so.2..strtab STRTAB
0x0003a624 0xc8 ---------- 0xc8 0x0 ---- ld-linux-x86-64.so.2..shstrtab STRTAB