like-dbg icon indicating copy to clipboard operation
like-dbg copied to clipboard

WIP: support pwndbg as a GDB extension

Open 0xricksanchez opened this issue 1 year ago • 3 comments

NOT MERGABLE YET

I'm currently running into a

pwndbg: loaded 196 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
The target architecture is set to "i386:x86-64:intel".
Reading symbols from /io/vmlinux...
add symbol table from file "/io/vmlinux"
Reading symbols from /io/vmlinux...
Breakpoint 1 at 0xffffffff82cff9b4: file init/main.c, line 849.
The program is not being run.
loading vmlinux
Remote debugging using :1234
0x000000000000fff0 in exception_stacks ()
------- tip of the day (disable with set show-tips off) -------
GDB's follow-fork-mode parameter can be used to set whether to trace parent or child after fork() calls
Exception occurred: Error: invalid literal for int() with base 10: '' (<class 'ValueError'>)
For more info invoke `set exception-verbose on` and rerun the command
or debug it by yourself with `set exception-debugger on`
Python Exception <class 'ValueError'> invalid literal for int() with base 10: '':
pwndbg> set exception-verbose on
Set whether to print a full stacktrace for exceptions raised in Pwndbg commands to True
Traceback (most recent call last):
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 164, in caller
    func()
  File "/home/user/pwndbg/pwndbg/symbol.py", line 96, in autofetch
    for mapping in pwndbg.vmmap.get():
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 62, in get
    pages.extend(kernel_vmmap_via_page_tables())
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 358, in kernel_vmmap_via_page_tables
    p.lazy_init()
  File "/home/user/pwndbg/gdb-pt-dump/pt.py", line 162, in lazy_init
    pid = int(proc.read().strip(), 10)
ValueError: invalid literal for int() with base 10: ''

If that is an issue, you can report it on https://github.com/pwndbg/pwndbg/issues
(Please don't forget to search if it hasn't been reported before)
To generate the report and open a browser, you may run `bugreport --run-browser`
PS: Pull requests are welcome
Traceback (most recent call last):
  File "/home/user/pwndbg/pwndbg/gdblib/prompt.py", line 47, in initial_hook
    prompt_hook(*a)
  File "/home/user/pwndbg/pwndbg/gdblib/prompt.py", line 57, in prompt_hook
    pwndbg.gdblib.events.after_reload(start=cur is None)
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 234, in after_reload
    f()
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 169, in caller
    raise e
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 164, in caller
    func()
  File "/home/user/pwndbg/pwndbg/symbol.py", line 96, in autofetch
    for mapping in pwndbg.vmmap.get():
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 62, in get
    pages.extend(kernel_vmmap_via_page_tables())
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 358, in kernel_vmmap_via_page_tables
    p.lazy_init()
  File "/home/user/pwndbg/gdb-pt-dump/pt.py", line 162, in lazy_init
    pid = int(proc.read().strip(), 10)
ValueError: invalid literal for int() with base 10: ''

0xricksanchez avatar Sep 09 '22 15:09 0xricksanchez

The latest change (which made sense in the first place) shifts the error in a different direction:

pwndbg: loaded 196 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
The target architecture is set to "i386:x86-64:intel".
Reading symbols from /io/vmlinux...
add symbol table from file "/io/vmlinux"
Reading symbols from /io/vmlinux...
Remote debugging using :1234
0x000000000000fff0 in exception_stacks ()
Breakpoint 1 at 0xffffffff82cff9b4: file init/main.c, line 849.
Continuing.
Exception occurred: Error: invalid literal for int() with base 10: '' (<class 'ValueError'>)
For more info invoke `set exception-verbose on` and rerun the command
or debug it by yourself with `set exception-debugger on`
Python Exception <class 'ValueError'> invalid literal for int() with base 10: '':

Breakpoint 1, start_kernel () at init/main.c:849
849     {
loading vmlinux
------- tip of the day (disable with set show-tips off) -------
Use GDB's pi command to run an interactive Python console where you can use Pwndbg APIs like pwndbg.gdblib.memory.read(addr, len), pwndbg.gdblib.memory.write(addr, data), pwndbg.gdb.vmmap.get() and so on!
Exception occurred: Error: invalid literal for int() with base 10: '' (<class 'ValueError'>)
For more info invoke `set exception-verbose on` and rerun the command
or debug it by yourself with `set exception-debugger on`
Python Exception <class 'ValueError'> invalid literal for int() with base 10: '':

After hitting continue in pwndbg the kernel boots but gdb is still broken:

^C
Program received signal SIGINT, Interrupt.
default_idle () at arch/x86/kernel/process.c:689
689     }
Exception occurred: Error: invalid literal for int() with base 10: '' (<class 'ValueError'>)
For more info invoke `set exception-verbose on` and rerun the command
or debug it by yourself with `set exception-debugger on`
Python Exception <class 'ValueError'> invalid literal for int() with base 10: '':
pwndbg> set exception-verbose on
Set whether to print a full stacktrace for exceptions raised in Pwndbg commands to True
Traceback (most recent call last):
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 164, in caller
    func()
  File "/home/user/pwndbg/pwndbg/symbol.py", line 96, in autofetch
    for mapping in pwndbg.vmmap.get():
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 62, in get
    pages.extend(kernel_vmmap_via_page_tables())
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 358, in kernel_vmmap_via_page_tables
    p.lazy_init()
  File "/home/user/pwndbg/gdb-pt-dump/pt.py", line 162, in lazy_init
    pid = int(proc.read().strip(), 10)
ValueError: invalid literal for int() with base 10: ''

If that is an issue, you can report it on https://github.com/pwndbg/pwndbg/issues
(Please don't forget to search if it hasn't been reported before)
To generate the report and open a browser, you may run `bugreport --run-browser`
PS: Pull requests are welcome
Traceback (most recent call last):
  File "/home/user/pwndbg/pwndbg/gdblib/prompt.py", line 47, in initial_hook
    prompt_hook(*a)
  File "/home/user/pwndbg/pwndbg/gdblib/prompt.py", line 57, in prompt_hook
    pwndbg.gdblib.events.after_reload(start=cur is None)
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 234, in after_reload
    f()
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 169, in caller
    raise e
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 164, in caller
    func()
  File "/home/user/pwndbg/pwndbg/symbol.py", line 96, in autofetch
    for mapping in pwndbg.vmmap.get():
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 62, in get
    pages.extend(kernel_vmmap_via_page_tables())
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 358, in kernel_vmmap_via_page_tables
    p.lazy_init()
  File "/home/user/pwndbg/gdb-pt-dump/pt.py", line 162, in lazy_init
    pid = int(proc.read().strip(), 10)
ValueError: invalid literal for int() with base 10: ''

0xricksanchez avatar Sep 09 '22 16:09 0xricksanchez

We spoke about this on Discord, but I will document this here as well.

This fails because gdb-pt-dump that Pwndbg rely on looks for the qemu-system process PID in here: https://github.com/martinradev/gdb-pt-dump/blob/f25898adc61d60e5f30c6452b15700bbf1bd630c/pt.py#L161-L162

And you launch the two - qemu-system (the linux vm) - and the GDB - in two separate containers. As a result, they end up in two different PID Linux namespaces and so they cannot see each other's PIDs/processes.

Running the containers with --pid=host would mitigate this issue but then we end up with permission errors:

pwndbg> set exception-verbose on
Set whether to print a full stacktrace for exceptions raised in Pwndbg commands to True
Traceback (most recent call last):
  File "/home/user/pwndbg/pwndbg/gdblib/events.py", line 164, in caller
    func()
  File "/home/user/pwndbg/pwndbg/symbol.py", line 96, in autofetch
    for mapping in pwndbg.vmmap.get():
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 62, in get
    pages.extend(kernel_vmmap_via_page_tables())
  File "/home/user/pwndbg/pwndbg/lib/memoize.py", line 49, in __call__
    value = self.func(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 358, in kernel_vmmap_via_page_tables
    p.lazy_init()
  File "/home/user/pwndbg/gdb-pt-dump/pt.py", line 164, in lazy_init
    self.phys_mem = VMPhysMem(pid)
  File "/home/user/pwndbg/gdb-pt-dump/pt.py", line 18, in __init__
    self.file = os.open(f"/proc/{pid}/mem", os.O_RDONLY)
PermissionError: [Errno 13] Permission denied: '/proc/72600/mem'

That's likely because of AppArmor profile blocking write access to /proc/$pid/* files. Fwiw this policy can be seen here: https://github.com/moby/moby/blob/924edb948c2731df3b77697a8fcc85da3f6eef57/profiles/apparmor/template.go#L37-L38

So now running the container additionally with --security-opt apparmor=unconfined (basically: disabling AppArmor) should probably fix this.

But this isn't really a great solution. We don't want people to do all this.

A potential solution could be using set kernel-vmmap-via-page-tables off but this will make Pwndbg to fetch memory map information from QEMU's GDB stub and its monitor info mem command. This... should work, but may be painfully slow, as QEMU renders LOTS of those memory map information (I think it does not merge them) and also it is not super accurate as iirc they don't show whether a page is writable or executable (I don't remember which one, I think exec).

disconnect3d avatar Sep 09 '22 16:09 disconnect3d

Added -ex 'set kernel-vmmap-via-page-tables off' -ex 'set exception-verbose on' for debugging purposes to the GDB invocation, leaving us with the follow command:

gdb-multiarch -q /io/vmlinux -iex 'set architecture i386:x86-64:intel' -ex 'add-symbol-file /io/vmlinux' -ex 'set kernel-vmmap-via-page-tables off' -ex 'set exception-verbose on' -ex 'target remote :1234' -ex 'break start_kernel' -ex continue -ex lx-symbols

This seems to run "fine", as in it does not fully crash. However, there's still the problem that the add-symbol-file fails for some reason. While pwndbg itself is still causing exceptions as seen here:

unknown

Single-stepping through the instructions was not painfully slow for me. Neither solution, hacking more flags into the the docker run command, nor bypassing an intended pwndbg feature seem like a good idea just to make this work.

Holding off a merge until a clean solution is found.

0xricksanchez avatar Sep 10 '22 08:09 0xricksanchez