GDB Debugging Methods (8) – Pwndbg

After using GDB for a while, you may find that some features, such as viewing stack layouts, are not very convenient to use directly with GDB. However, the core issues of operating systems often lie in memory management. For example, printing a doubly linked list is not easy to handle with GDB alone. So, is there a more user-friendly tool that can be used on top of GDB to debug some core operating system issues? The answer is yes, and that tool is Pwndbg, which allows for easier troubleshooting of core system problems.

Installing Pwndbg

Clone the repository directly and then install it.

git clone https://github.com/pwndbg/pwndbg.git
pip3 install poetry
./setup.sh

If you do not wish to recompile, you can directly download the precompiled binary according to the latest release build.

https://github.com/pwndbg/pwndbg/releases/download/2025.05.30/pwndbg_2025.05.30_arm64-portable.tar.xz

After successful installation, you can see that it loads a Python file by default through gdbinit, as shown below.

root@kylin:~# cat .gdbinit
source /root/pwndbg/gdbinit.py

Example Code

To demonstrate the advantages of Pwndbg over GDB, we will prepare a code example in malloc.c as follows:

#include <stdlib.h>
#include <string.h>

int main()
{
    void *p = malloc(100*sizeof(char));
    memset(p, 0x55, 100*sizeof(char));
    memcpy(p + 100*sizeof(char), "kylin", strlen("kylin") + 1);
    free(p);
}

After compiling, you can debug it with GDB.

Basic Debugging

When the code hits a breakpoint, Pwndbg provides all the information as shown below:

───────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────────────────────────────────────────────────────────────────
 X0   0x412304 ◂— 0x2006e696c796b/* 'kylin' */
 X1   0x400780 ◂— ldpsw x11, x30, [x11, #-0xa0] /* 'kylin' */
 X2   6
 X3   0x412304 ◂— 0x2006e696c796b/* 'kylin' */
 X4   0x400786 ◂— madd w0, w0, w1, w0
 X5   0x41230a ◂— 2
 X6   0x696c796b
 X7   0x6e696c
 X8   0x38
 X9   0x7ff7fadac0 (main_arena+96) —▸ 0x412300 ◂— 'UUUUkylin'
 X10  0
 X11  0x28
 X12  0x7ff7e43e50 ◂— udf #0
 X13  0
 X14  0
 X15  0x70
 X16  0x7ff7ec4a10 (__memcpy_generic) ◂— prfm pldl1keep, [x1] /* ' ' */
 X17  0x411000 ([email protected]) —▸ 0x7ff7ec4a10 (__memcpy_generic) ◂— prfm pldl1keep, [x1] /* ' ' */
 X18  0
 X19  0x4006d0 (__libc_csu_init) ◂— stp x29, x30, [sp, #-0x40]!
 X20  0
 X21  0x400570 (_start) ◂— mov x29, #0
 X22  0
 X23  0
 X24  0
 X25  0
 X26  0
 X27  0
 X28  0
 X29  0x7ffffff2b0 —▸ 0x7ffffff2d0 ◂— 0
 SP   0x7ffffff2b0 —▸ 0x7ffffff2d0 ◂— 0
 LR   0x4006bc (main+64) ◂— ldr x0, [sp, #0x18]
 PC   0x4006bc (main+64) ◂— ldr x0, [sp, #0x18]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM / aarch64 / set emulate on ]────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x4006bc <main+64>               ldr    x0, [sp, #0x18]     X0, [0x7ffffff2c8] => 0x4122a0 ◂— 'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU...'
   0x4006c0 <main+68>               bl     #free@plt                   <free@plt>

   0x4006c4 <main+72>               mov    w0, #0                      W0 => 0
   0x4006c8 <main+76>               ldp    x29, x30, [sp], #0x20
   0x4006cc <main+80>               ret

   0x4006d0 <__libc_csu_init>       stp    x29, x30, [sp, #-0x40]!
   0x4006d4 <__libc_csu_init+4>     mov    x29, sp
   0x4006d8 <__libc_csu_init+8>     stp    x19, x20, [sp, #0x10]
   0x4006dc <__libc_csu_init+12>    adrp   x20, #0x410000              X20 => 0x410000 ◂— 0x10102464c457f
   0x4006e0 <__libc_csu_init+16>    add    x20, x20, #0xe00
   0x4006e4 <__libc_csu_init+20>    stp    x21, x22, [sp, #0x20]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────────────────────────────────────────────────────
In file: /root/malloc.c:9
    4int main()
    5 {
    6     void *p = malloc(100*sizeof(char));
    7     memset(p, 0x55, 100*sizeof(char));
    8     memcpy(p + 100*sizeof(char), "kylin", strlen("kylin") + 1);
 ►  9     free(p);
   10 }
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ x29 sp 0x7ffffff2b0 —▸ 0x7ffffff2d0 ◂— 0
01:0008│        0x7ffffff2b8 —▸ 0x7ff7e60d90 (__libc_start_main+232) ◂— bl #0x7ff7e764b0
02:0010│        0x7ffffff2c0 —▸ 0x4006d0 (__libc_csu_init) ◂— stp x29, x30, [sp, #-0x40]!
03:0018│        0x7ffffff2c8 —▸ 0x4122a0 ◂— 'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUkylin'
04:0020│        0x7ffffff2d0 ◂— 0
05:0028│        0x7ffffff2d8 —▸ 0x4005bc (_start+76) ◂— bl #0x400550
06:0030│        0x7ffffff2e0 ◂— 0
07:0038│        0x7ffffff2e8 ◂— 0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ►0         0x4006bc main+64
   1     0x7ff7e60d90 __libc_start_main+232
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

This includes the following sections:

Registers

The first part is the values of the registers, as all traces capture the current register values at the first moment. Pwndbg provides further simplifications based on the registers, such as:

  1. Parsing memory values <span>0x412304 ◂— 0x2006e696c796b /* 'kylin' */</span>
  2. Automatic symbol resolution <span>0x7ff7ffd668 (_rtld_global_ro)</span>
  3. Automatic character resolution <span>0x7ff7fe5b28 ◂— '/lib/aarch64-linux-gnu/'</span>
  4. Automatic disassembly of instructions <span>0x4006d0 (__libc_csu_init) ◂— stp x29, x30, [sp, #-0x40]!</span>
  5. Automatic linked list resolution <span>X29 0x7fffffdf80 —▸ 0x7fffffdfb0 —▸ 0x7fffffe040 —▸ 0x7fffffe450 —▸ 0x7fffffe470 ◂— …</span>
  6. Automatic PLT calculation <span>0x411000 ([email protected]) —▸ 0x7ff7ec4a10 (__memcpy_generic) ◂— prfm pldl1keep, [x1] /* ' ' */</span>

This is much more convenient than calculating it ourselves in GDB.

Disassembly of Code Segment

The second part is the disassembly of the code corresponding to the breakpoint. Unlike GDB’s diss, it provides comments at key points, such as:

  1. Automatic comments on calculated register values <span>ldr x0, [sp, #0x18] X0, [0x7ffffff2c8] => 0x4122a0 ◂— 'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU…'</span>
  2. Automatic comments on status register values <span>cmp x20, x0 0x7ff7fffed0 - 0x0 CPSR => 0x20000000 [ n z C v q pan il d a i f el:0 sp ]</span>
  3. Automatic comments on function jump locations <span>bl #free@plt</span>
  4. Automatic comments on LR register values <span>ret <0x7ff7fd2f64></span>
  5. Automatic comments on SP register linked lists <span>mov sp, x29 SP => 0x7fffffdfb0 —▸ 0x7fffffe040 —▸ 0x7fffffe450 —▸ 0x7fffffe470 ◂— …</span>
  6. Automatic comments on general register values <span>mov w0, w28 W0 => 3</span>
  7. Automatic comments on page values <span>adrp x20, #0x410000 X20 => 0x410000 ◂— 0x10102464c457f</span>

Source Code

If GDB can find the source code, it will print the source code content directly.

In file: /root/malloc.c:9
    4 int main()
    5 {
    6     void *p = malloc(100*sizeof(char));
    7     memset(p, 0x55, 100*sizeof(char));
    8     memcpy(p + 100*sizeof(char), "kylin", strlen("kylin") + 1);
 ►  9     free(p);
   10 }

Stack Layout

The third part is the stack layout of the current function, which also provides detailed comments, as follows:

00:0000│ x29 sp 0x7ffffff2b0 —▸ 0x7ffffff2d0 ◂— 0
01:0008│        0x7ffffff2b8 —▸ 0x7ff7e60d90 (__libc_start_main+232) ◂— bl #0x7ff7e764b0
02:0010│        0x7ffffff2c0 —▸ 0x4006d0 (__libc_csu_init) ◂— stp x29, x30, [sp, #-0x40]!
03:0018│        0x7ffffff2c8 —▸ 0x4122a0 ◂— 'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUkylin'
04:0020│        0x7ffffff2d0 ◂— 0
05:0028│        0x7ffffff2d8 —▸ 0x4005bc (_start+76) ◂— bl #0x400550
06:0030│        0x7ffffff2e0 ◂— 0
07:0038│        0x7ffffff2e8 ◂— 0

Stack Frame Layout

In addition to the current stack layout, it also prints the code stack frame directly, as follows:

 ► 0         0x4006bc main+64
   1     0x7ff7e60d90 __libc_start_main+232

Printing Context

Based on the above information, by default, when a breakpoint is hit, basic information will be printed. During debugging, you can also print the context at any time. For example, after modifying certain values, you can print all information at once, as follows:

pwndbg> context

Printing Memory

Using hexdump, you can directly view the memory content of malloc without needing to dump or use x to calculate.

pwndbg> hexdump p
+0000 0x4122a0  55 55 55 55 55 55 55 55  55 55 55 55 55 55 55 55  │UUUUUUUU│UUUUUUUU│
... ↓            skipped 2 identical lines (32 bytes)
+0030 0x4122d0  55 55 55 55 55 55 55 55  55 55 55 55 55 55 55 55  │UUUUUUUU│UUUUUUUU│

More User-Friendly Memory Viewing

In the code, I intentionally appended “kylin” after p. You can see that the hexdump of p has boundaries, while telescope prints directly afterwards, as shown below:

pwndbg> telescope p
00:0000│  0x4122a0 ◂— 'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUkylin'
... ↓     7 skipped

Searching Memory

If you want to search in GDB, you must dump it out to find it. However, Pwndbg allows you to search for what you want directly using search. I marked “kylin” in p, and you can search as follows:

pwndbg> search kylin
Searching for value: 'kylin'
malloc          0x400780 ldpsw x11, x30, [x11, #-0xa0] /* 'kylin' */
malloc          0x410780 0x6e696c796b /* 'kylin' */
[heap]          0x412304 0x2006e696c796b /* 'kylin' */
libc-2.31.so    0x7ff7f665aa ldpsw x11, x30, [x11, #-0xa0]
warning: Unable to access 16000 bytes of target memory at 0x7ff7fa1d04, halting search.
[vdso]          0x7ff7ffc94d ldpsw x11, x30, [x11, #-0xa0] /* 'kylin1~20.04) 10.3.0' */

Printing GOT and PLT

The values of these two sections need to be calculated manually in GDB, while Pwndbg calculates them for you by default, as shown below:

pwndbg> got
State of the GOT of /root/malloc:
GOT protection: Partial RELRO | Found 7 GOT entries passing the filter
[0x411000] memcpy@GLIBC_2.17 -> 0x7ff7ec4a10 (__memcpy_generic) ◂— prfm pldl1keep, [x1] /* ' ' */
[0x411008] malloc@GLIBC_2.17 -> 0x7ff7eb9130 (malloc) ◂— adrp x2, #0x7ff7fac000
[0x411010] __libc_start_main@GLIBC_2.17 -> 0x7ff7e60ca8 (__libc_start_main) ◂— stp x29, x30, [sp, #-0x150]!
[0x411018] memset@GLIBC_2.17 -> 0x7ff7ec59c0 (__memset_generic) ◂— dup v0.16b, w1
[0x411020] __gmon_start__ -> 0x4004e0 ◂— stp x16, x30, [sp, #-0x10]!
[0x411028] abort@GLIBC_2.17 -> 0x4004e0 ◂— stp x16, x30, [sp, #-0x10]!
[0x411030] free@GLIBC_2.17 -> 0x4004e0 ◂— stp x16, x30, [sp, #-0x10]!
pwndbg> plt
Section .plt 0x4004e0-0x400570:
0x400500: memcpy@plt
0x400510: malloc@plt
0x400520: __libc_start_main@plt
0x400530: memset@plt
0x400540: __gmon_start__@plt
0x400550: abort@plt

Debugging Arena

Pwndbg provides convenient access to view the structure information of the arena for memory management, as shown below:

pwndbg> arena
Arena for thread 1 is located at: 0x7ff7fada60
{
  mutex = 0,
  flags = 0,
  have_fastchunks = 0,
  fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
  top = 0x412300,
  last_remainder = 0x0,
  bins = {0x7ff7fadac0 <main_arena+96>, 0x7ff7fadac0 <main_arena+96>, 0x7ff7fadad0 <main_arena+112>, 0x7ff7fadad0 <main_arena+112>, 0x7ff7fadae0 <main_arena+128>, 0x7ff7fadae0 <main_arena+128>, 0x7ff7fadaf0 <main_arena+144>, 0x7ff7fadaf0 <main_arena+144>, 0x7ff7fadb00 <main_arena+160>, 0x7ff7fadb00 <main_arena+160>, 0x7ff7fadb10 <main_arena+176>, 0x7ff7fadb10 <main_arena+176>, 0x7ff7fadb20 <main_arena+192>, 0x7ff7fadb20 <main_arena+192>, 0x7ff7fadb30 <main_arena+208>, 0x7ff7fadb30 <main_arena+208>, 0x7ff7fadb40 <main_arena+224>, 0x7ff7fadb40 <main_arena+224>, 0x7ff7fadb50 <main_arena+240>, 0x7ff7fadb50 <main_arena+240>, 0x7ff7fadb60 <main_arena+256>, 0x7ff7fadb60 <main_arena+256>, 0x7ff7fadb70 <main_arena+272>, 0x7ff7fadb70 <main_arena+272>, 0x7ff7fadb80 <main_arena+288>, 0x7ff7fadb80 <main_arena+288>, 0x7ff7fadb90 <main_arena+304>, 0x7ff7fadb90 <main_arena+304>, 0x7ff7fadba0 <main_arena+320>, 0x7ff7fadba0 <main_arena+320>, 0x7ff7fadbb0 <main_arena+336>, 0x7ff7fadbb0 <main_arena+336>, 0x7ff7fadbc0 <main_arena+352>, 0x7ff7fadbc0 <main_arena+352>, 0x7ff7fadbd0 <main_arena+368>, 0x7ff7fadbd0 <main_arena+368>, 0x7ff7fadbe0 <main_arena+384>, 0x7ff7fadbe0 <main_arena+384>, 0x7ff7fadbf0 <main_arena+400>, 0x7ff7fadbf0 <main_arena+400>, 0x7ff7fadc00 <main_arena+416>, 0x7ff7fadc00 <main_arena+416>, 0x7ff7fadc10 <main_arena+432>, 0x7ff7fadc10 <main_arena+432>, 0x7ff7fadc20 <main_arena+448>, 0x7ff7fadc20 <main_arena+448>, 0x7ff7fadc30 <main_arena+464>, 0x7ff7fadc30 <main_arena+464>, 0x7ff7fadc40 <main_arena+480>, 0x7ff7fadc40 <main_arena+480>, 0x7ff7fadc50 <main_arena+496>, 0x7ff7fadc50 <main_arena+496>, 0x7ff7fadc60 <main_arena+512>, 0x7ff7fadc60 <main_arena+512>, 0x7ff7fadc70 <main_arena+528>, 0x7ff7fadc70 <main_arena+528>, 0x7ff7fadc80 <main_arena+544>, 0x7ff7fadc80 <main_arena+544>, 0x7ff7fadc90 <main_arena+560>, 0x7ff7fadc90 <main_arena+560>, 0x7ff7fade00 <main_arena+576>, 0x7ff7fade00 <main_arena+576>, 0x7ff7fade10 <main_arena+592>, 0x7ff7fade10 <main_arena+592>, 0x7ff7fade20 <main_arena+608>, 0x7ff7fade20 <main_arena+608>, 0x7ff7fade30 <main_arena+624>, 0x7ff7fade30 <main_arena+624>, 0x7ff7fade40 <main_arena+640>, 0x7ff7fade40 <main_arena+640>, 0x7ff7fade50 <main_arena+656>, 0x7ff7fade50 <main_arena+656>, 0x7ff7fade60 <main_arena+672>, 0x7ff7fade60 <main_arena+672>, 0x7ff7fade70 <main_arena+688>, 0x7ff7fade70 <main_arena+688>, 0x7ff7fade80 <main_arena+704>, 0x7ff7fade80 <main_arena+704>, 0x7ff7fade90 <main_arena+720>, 0x7ff7fade90 <main_arena+720>, 0x7ff7fadea0 <main_arena+736>, 0x7ff7fadea0 <main_arena+736>, 0x7ff7fadeb0 <main_arena+752>, 0x7ff7fadeb0 <main_arena+752>, 0x7ff7fadec0 <main_arena+768>, 0x7ff7fadec0 <main_arena+768>, 0x7ff7faded0 <main_arena+784>, 0x7ff7faded0 <main_arena+784>, 0x7ff7fadee0 <main_arena+800>, 0x7ff7fadee0 <main_arena+800>, 0x7ff7fadef0 <main_arena+816>, 0x7ff7fadef0 <main_arena+816>, 0x7ff7fae000 <main_arena+832>, 0x7ff7fae000 <main_arena+832>, 0x7ff7fae010 <main_arena+848>, 0x7ff7fae010 <main_arena+848>, 0x7ff7fae020 <main_arena+864>, 0x7ff7fae020 <main_arena+864>, 0x7ff7fae030 <main_arena+880>, 0x7ff7fae030 <main_arena+880>, 0x7ff7fae040 <main_arena+896>, 0x7ff7fae040 <main_arena+896>, 0x7ff7fae050 <main_arena+912>, 0x7ff7fae050 <main_arena+912>, 0x7ff7fae060 <main_arena+928>, 0x7ff7fae060 <main_arena+928>, 0x7ff7fae070 <main_arena+944>, 0x7ff7fae070 <main_arena+944>, 0x7ff7fae080 <main_arena+960>, 0x7ff7fae080 <main_arena+960>, 0x7ff7fae090 <main_arena+976>, 0x7ff7fae090 <main_arena+976>, 0x7ff7fae0a0 <main_arena+992>, 0x7ff7fae0a0 <main_arena+992>, 0x7ff7fae0b0 <main_arena+1008>, 0x7ff7fae0b0 <main_arena+1008>, 0x7ff7fae0c0 <main_arena+1024>, 0x7ff7fae0c0 <main_arena+1024>, 0x7ff7fae0d0 <main_arena+1056>, 0x7ff7fae0d0 <main_arena+1056>, 0x7ff7fae0e0 <main_arena+1072>, 0x7ff7fae0e0 <main_arena+1072>, 0x7ff7fae0f0 <main_arena+1088>, 0x7ff7fae0f0 <main_arena+1088>, 0x7ff7fae100 <main_arena+1104>, 0x7ff7fae100 <main_arena+1104>, 0x7ff7fae110 <main_arena+1120>, 0x7ff7fae110 <main_arena+1120>, 0x7ff7fae120 <main_arena+1136>, 0x7ff7fae120 <main_arena+1136>, 0x7ff7fae130 <main_arena+1152>, 0x7ff7fae130 <main_arena+1152>, 0x7ff7fae140 <main_arena+1168>, 0x7ff7fae140 <main_arena+1168>, 0x7ff7fae150 <main_arena+1184>, 0x7ff7fae150 <main_arena+1184>, 0x7ff7fae160 <main_arena+1200>, 0x7ff7fae160 <main_arena+1200>, 0x7ff7fae170 <main_arena+1216>, 0x7ff7fae170 <main_arena+1216>, 0x7ff7fae180 <main_arena+1232>, 0x7ff7fae180 <main_arena+1232>, 0x7ff7fae190 <main_arena+1248>, 0x7ff7fae190 <main_arena+1248>, 0x7ff7fae1a0 <main_arena+1264>, 0x7ff7fae1a0 <main_arena+1264>, 0x7ff7fae1b0 <main_arena+1280>, 0x7ff7fae1b0 <main_arena+1280>, 0x7ff7fae1c0 <main_arena+1296>, 0x7ff7fae1c0 <main_arena+1296>, 0x7ff7fae1d0 <main_arena+1312>, 0x7ff7fae1d0 <main_arena+1312>, 0x7ff7fae1e0 <main_arena+1328>, 0x7ff7fae1e0 <main_arena+1328>, 0x7ff7fae1f0 <main_arena+1344>, 0x7ff7fae1f0 <main_arena+1344>, 0x7ff7fae200 <main_arena+1360>, 0x7ff7fae200 <main_arena+1360>, 0x7ff7fae210 <main_arena+1376>, 0x7ff7fae210 <main_arena+1376>, 0x7ff7fae220 <main_arena+1392>, 0x7ff7fae220 <main_arena+1392>, 0x7ff7fae230 <main_arena+1408>, 0x7ff7fae230 <main_arena+1408>, 0x7ff7fae240 <main_arena+1424>, 0x7ff7fae240 <main_arena+1424>, 0x7ff7fae250 <main_arena+1440>, 0x7ff7fae250 <main_arena+1440>, 0x7ff7fae260 <main_arena+1456>, 0x7ff7fae260 <main_arena+1456>, 0x7ff7fae270 <main_arena+1472>, 0x7ff7fae270 <main_arena+1472>, 0x7ff7fae280 <main_arena+1488>, 0x7ff7fae280 <main_arena+1488>, 0x7ff7fae290 <main_arena+1504>, 0x7ff7fae290 <main_arena+1504>, 0x7ff7fae2a0 <main_arena+1520>, 0x7ff7fae2a0 <main_arena+1520>, 0x7ff7fae2b0 <main_arena+1536>, 0x7ff7fae2b0 <main_arena+1536>, 0x7ff7fae2c0 <main_arena+1552>, 0x7ff7fae2c0 <main_arena+1552>, 0x7ff7fae2d0 <main_arena+1568>, 0x7ff7fae2d0 <main_arena+1568>, 0x7ff7fae2e0 <main_arena+1584>, 0x7ff7fae2e0 <main_arena+1584>, 0x7ff7fae2f0 <main_arena+1600>, 0x7ff7fae2f0 <main_arena+1600>, 0x7ff7fae300 <main_arena+1616>, 0x7ff7fae300 <main_arena+1616>, 0x7ff7fae310 <main_arena+1632>, 0x7ff7fae310 <main_arena+1632>, 0x7ff7fae320 <main_arena+1648>, 0x7ff7fae320 <main_arena+1648>, 0x7ff7fae330 <main_arena+1664>, 0x7ff7fae330 <main_arena+1664>, 0x7ff7fae340 <main_arena+1680>, 0x7ff7fae340 <main_arena+1680>...},
  binmap = {0, 0, 0, 0},
  next = 0x7ff7fada60 <main_arena>,
  next_free = 0x0,
  attached_threads = 1,
  system_mem = 135168,
  max_system_mem = 135168
}

Viewing Heap

Since you can view the arena, you can also view the program’s heap, as shown below:

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x412000
Size: 0x290 (with flag bits: 0x291)

Allocated chunk | PREV_INUSE
Addr: 0x412290
Size: 0x70 (with flag bits: 0x71)

Top chunk | IS_MMAPED | NON_MAIN_ARENA
Addr: 0x412300
Size: 0x20068 (with flag bits: 0x2006e)

Viewing Bins

Since you can view the heap, you can also view the bins inside malloc.

pwndbg> bins cache
tcachebins
empty
fastbins
0x20: 0x4132d0 —▸ 0x414578 —▸ 0x4145b8 ◂— 0x31/* '1' */
0x30: 0x413938 ◂— 0x411
0x40: 0x4145e8 ◂— 0x1fa11
0x50: 0x651
0x60: 0x414098 —▸ 0x4140d8 ◂— 0x31/* '1' */
0x70: 0x4142a8 —▸ 0x4142e8 ◂— 0x31/* '1' */
0x80: 0x414578 —▸ 0x4145b8 ◂— 0x31/* '1' */
0x90: 0x414578 —▸ 0x4145b8 ◂— 0x31/* '1' */
0xa0: 0x414368 —▸ 0x4143a8 ◂— 0x31/* '1' */
0xb0: 0x414368 —▸ 0x4143a8 ◂— 0x31/* '1' */
unsortedbin
all [corrupted]
FD: 0x413e88 —▸ 0x413ec8 ◂— 0x31/* '1' */
BK: 0x4144b8 —▸ 0x414498 ◂— 0x4c320000dd80
smallbins
0x20 [corrupted]
FD: 0x414398 —▸ 0x4143d8 ◂— 0x31/* '1' */
BK: 0x414398 —▸ 0x414378 ◂— 0x24ff000076c5
0x30 [corrupted]
FD: 0x413330 —▸ 0x413340 —▸ 0x413eb8 —▸ 0x413ef8 ◂— 0x31/* '1' */
BK: 0x413330 —▸ 0x413340 —▸ 0x413eb8 —▸ 0x413e98 ◂— 0x989b00002650/* 'P&amp;' */

Viewing Mappings

Similarly, GDB’s mappings have been optimized for better usability, as shown below:

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
          0x400000           0x401000 r-xp     1000      0 /root/malloc
          0x410000           0x411000 r--p     1000      0 /root/malloc
          0x411000           0x412000 rw-p     1000   1000 /root/malloc
          0x412000           0x433000 rw-p    21000      0 [heap]
      0x7ff7e40000       0x7ff7f9a000 r-xp   15a000      0 /usr/lib/aarch64-linux-gnu/libc-2.31.so
      0x7ff7f9a000       0x7ff7fa9000 ---p     f000 15a000 /usr/lib/aarch64-linux-gnu/libc-2.31.so
      0x7ff7fa9000       0x7ff7fad000 r--p     4000159000 /usr/lib/aarch64-linux-gnu/libc-2.31.so
      0x7ff7fad000       0x7ff7faf000 rw-p     200015d000 /usr/lib/aarch64-linux-gnu/libc-2.31.so
      0x7ff7faf000       0x7ff7fb2000 rw-p     3000      0 [anon_7ff7faf]
      0x7ff7fcc000       0x7ff7fed000 r-xp    21000      0 /usr/lib/aarch64-linux-gnu/ld-2.31.so
      0x7ff7ff8000       0x7ff7ffa000 rw-p     2000      0 [anon_7ff7ff8]
      0x7ff7ffa000       0x7ff7ffc000 r--p     2000      0 [vvar]
      0x7ff7ffc000       0x7ff7ffd000 r-xp     1000      0 [vdso]
      0x7ff7ffd000       0x7ff7ffe000 r--p     1000210000 /usr/lib/aarch64-linux-gnu/ld-2.31.so
      0x7ff7ffe000       0x7ff8000000 rw-p     200022000 /usr/lib/aarch64-linux-gnu/ld-2.31.so
      0x7ffffdf000       0x8000000000 rw-p    21000      0 [stack]

Parsing Memory Addresses

You can use xinfo to parse the information stored in memory, as shown below:

pwndbg> xinfo p
Extended information for virtual address 0x4122a0:

  Containing mapping:
          0x412000           0x433000 rw-p    21000      0 [heap]

  Offset information:
         Mapped Area 0x4122a0 = 0x412000 + 0x2a0

More Commands

Pwndbg is very powerful, but I often use it when debugging memory, focusing more on memory applications. The commands mentioned above fully meet my needs, and for more usage, you can refer to:

https://pwndbg.re/pwndbg/latest/commands/

Leave a Comment