TL;DR: With .splitlines() we splitted over universal splitlines which did not correspond to GDB's target code line splitting...
As a result we got `context code` to produce bogus out of sync lines that didn't correspond to GDB's `line` command.
See also https://docs.python.org/3/library/stdtypes.html#str.splitlines
Addresses #957 by enclosing anonymous map names printed by vmmap in square brackets.
Search still works & xinfo plays nice, but please let me know if you find anything this breaks.
Revert the change from 3e4ad60 and make the `pwndbg.proc.get_file` to strip the "target:" prefix instead of the `pwndbg.proc.exe`.
This way, we will prevent bugs when pwndbg code would use `pwndbg.proc.exe` on remote targets but not pass the returned path to `pwndbg.proc.get_file` to get the real remote file and instead use the local one (if it exists in the same path).
Additionally, we assert the `path` passed to `pwndbg.proc.get_file` so we prevent incorrect use of the function when an absolute path or not a remote path is passed to it.
Before this commit the `pwndbg.proc.exe` could return a "target:" prefix when `pwndbg.proc.exe` was executed on remote targets. This could be seen by:
1. Executing gdbserver in one terminal: gdbserver 127.0.0.1:1234 `which ps`
2. Executing `gdb -ex 'target remote :1234'` in another terminal and then invoking `pi pwndbg.proc.exe`.
This resulted in `checksec` (and some other) commands crashes which were using the `pwndbg.file.get_fille` functionality as it downloaded the remote file by using the `gdb.execute("remote get %s %s")` command passing it a path prefixed with `"target:"` which this GDB command does not support.
The `pwndbg.memoize.reset_on_new_base_address` decorator is super problematic: its start event was called before `pwndbg.arch.update` event because the pwndbg/memoize.py file is executed faster than the pwndbg/arch.py file. This happens even if we import pwndbg/arch.py as first import because it imports regs.py and events.py and those import memoize.py and so on.
TL;DR: The decorator was quite redundant and made too many calls in the end which caused some problems when executing:
1. In one console: qemu-x86_64 -g 1234 `which ps`
2. In another, attaching to this via `gdb` -> `target remote :1234`
The `explore_registers` and `clear_explored_pages` functions are currently redundant as we create an empty memory page on QEMU targets.
Also, this functions are not so useful to be called automagically on real remote and embedded targets where we may not have the memory pages information (as it may be too slow to explore stuff via remote gdbstub on a real embedded target).
E.g. for calls like this:
```
► 0x555555554a57 <main+296> call ioctl@plt <ioctl@plt>
fd: 0x3
request: 0xae01
vararg: 0x0
```
We want to display the `fd` as: `fd: 0x3 (/some/path)` fetched from
`readlink -f /proc/$PID/fd/$FD`.
The ASLR command did not work properly on QEMU kernel targets: it read /proc/sys/kernel/randomize_va_space and then /proc/<pid>/personality on local filesystem which was wrong, and returned that it couldn't read personality.
Now, this commit made so that:
- the `pwndbg.file.get_file` will print a warning if it returns a local path on remote targets
- the `check_aslr` was refactored: we don't run this on `new_objfile` or cache its result; the `pwndbg.vmmap.aslr` was also removed as it was never used
- the `pwndbg.vmmap.check_aslr` and `aslr` command will now return info if we couldn't detect ASLR on QEMU targets
There is a bug when the `pwndbg.auxv.get()` and `pwndbg.vmmap.get()` caches are not resetted when the binary is restarted. This causes an error when `disable-randomization` is set to off and the binary is restarted.
TL;DR to reproduce:
1. Run `gdb /bin/ls`
2. Invoke `entry`
3. Invoke `set disable-randomization off`
4. Invoke `starti` or `entry`
Or it can be seen here:
```
dc@dc:~$ gdb /bin/ls -q
pwndbg: loaded 195 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from /bin/ls...
(No debugging symbols found in /bin/ls)
pwndbg> set context-sections ''
Sections set to be empty. FYI valid values are: regs, disasm, args, code, stack, backtrace, expressions, ghidra
Set which context sections are displayed (controls order) to ''
pwndbg> entry
Temporary breakpoint 1 at 0x55555555a810
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Temporary breakpoint 1, 0x000055555555a810 in ?? ()
pwndbg> set exception-verbose on
Set whether to print a full stacktrace for exceptions raised in Pwndbg commands to True
pwndbg> set disable-randomization off
pwndbg> starti
Starting program: /usr/bin/ls
Traceback (most recent call last):
File "/home/dc/src/pwndbg/pwndbg/events.py", line 165, in caller
func()
File "/home/dc/src/pwndbg/pwndbg/memoize.py", line 194, in __reset_on_base
base = pwndbg.elf.exe().address if pwndbg.elf.exe() else None
File "/home/dc/src/pwndbg/pwndbg/proc.py", line 71, in wrapper
return func(*a, **kw)
File "/home/dc/src/pwndbg/pwndbg/memoize.py", line 46, in __call__
value = self.func(*args, **kwargs)
File "/home/dc/src/pwndbg/pwndbg/elf.py", line 182, in exe
return load(e)
File "/home/dc/src/pwndbg/pwndbg/elf.py", line 220, in load
return get_ehdr(pointer)[1]
File "/home/dc/src/pwndbg/pwndbg/elf.py", line 241, in get_ehdr
if pwndbg.memory.read(vmmap.start, 4) == b'\x7fELF':
File "/home/dc/src/pwndbg/pwndbg/memory.py", line 40, in read
result = gdb.selected_inferior().read_memory(addr, count)
gdb.MemoryError: Cannot access memory at address 0x555555558000
```
This commit fixes the above problem by making sure those function caches are cleared on binary start.
Before this commit the `pwndbg.elf.get_ehdr(pointer)` function searched for the ELF header by iterating through memory pages (with a step of -4kB) until the ELF magic is found (b'\x7fELF' value).
This was most likely redundant and this commit optimized this logic to look at the begining of the page and if it doesn't have the ELF magic, to look at the first page of the given objfile. Actually, maybe there was one compelling argument to do it this way which is bare metal debugging where we don't have all vmmap info. However! This situation is likely broken anyway, so let's skip doing more work than needed.
Additionally, we refactor the `pwndbg.stack.find_upper_stack_boundary` function to use the `pwndbg.memory.find_upper_boundary` function instead of the `pwndbg.elf.find_elf_magic` function, as the last one was removed in this commit. (NOTE: this actually fixes a potential bug that we could have incorrectly set the upper stack boundary if it contained an ELF magic on the beginning of one of its 4kB pages...)
Before this commit, when we did `gdb /bin/ls` and then `entry`, we could see a "Cannot find ELF base!" warning. This occured, because the `pwndbg.arch.ptrmask` was incorrectly set and then the `find_elf_magic` was using the `pwndbg.arch.ptrmask` which was 0xffFFffFF and made an early return. As a result of this early return, the "Cannot find ELF base!" warning was emitted.
The reason for incorrect early arch detection is that we used the `gdb.newest_frame().architecture().name()` API while the binary was not using in here:
```python
@pwndbg.events.start
@pwndbg.events.stop
@pwndbg.events.new_objfile
def update():
m = sys.modules[__name__]
try:
m.current = fix_arch(gdb.newest_frame().architecture().name())
except Exception:
return
m.ptrsize = pwndbg.typeinfo.ptrsize
m.ptrmask = (1 << 8*pwndbg.typeinfo.ptrsize)-1
```
And if the binary was not running, we just returned early and did not set `pwndbg.arch.ptrsize` and `pwndbg.arch.ptrmask` at all, leaving them at their default values.
Now, those values were often eventually fixed, but this occured by chance as the `new_objfile` was executed!
Anyway, starting from this commit, we will detect the arch from `gdb.newest_frame().architecture().name()` only if the process is running and if it is not, we will fallback to the `show architecture` GDB command and parse it, hoping we detect the arch properly. In case we don't, or, we don't support the given arch, we raise a `RuntimeError` currently.
Also, as a side note: the `find_elf_magic` from `elf.py` can be optimized to instead of doing 4kB steps over pages, to just look at the begining of a page. Otherwise, if this doesn't work, we are most likely on a target that may not have an ELF magic/header at all, so we shouldn't bother about it. This fix is done in the next commit.
This commit fixes an exception when pwndbg is sources after gdb is
attached to a process.
Below log is with `set exception-verbose on` and `set exception-debugger
on` hardcoded into the sources. We can see that:
1. We get an immediate exception
2. The `pwndbg.arch.current` is set incorrectly to `i386`
3. The real arch is `i386:x86-64`
```
$ gdb -q -p $(pidof a.out) ./a.out
Reading symbols from ./a.out...
(No debugging symbols found in ./a.out)
Attaching to program: /home/dc/example/a.out, process 415704
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.31.so...
Reading symbols from /lib64/ld-linux-x86-64.so.2...
(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)
0x00007f4bb94e7142 in __GI___libc_read (fd=0, buf=0x55ee576fb6b0, nbytes=1024)
at ../sysdeps/unix/sysv/linux/read.c:26
26 ../sysdeps/unix/sysv/linux/read.c: No such file or directory.
(gdb) source /home/dc/tools/pwndbg/gdbinit.py
pwndbg: loaded 195 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Traceback (most recent call last):
File "/home/dc/tools/pwndbg/pwndbg/commands/__init__.py", line 130, in __call__
return self.function(*args, **kwargs)
File "/home/dc/tools/pwndbg/pwndbg/commands/__init__.py", line 221, in _OnlyWhenRunning
return function(*a, **kw)
File "/home/dc/tools/pwndbg/pwndbg/commands/context.py", line 269, in context
result[target].extend(func(target=out,
File "/home/dc/tools/pwndbg/pwndbg/commands/context.py", line 350, in context_regs
regs = get_regs()
File "/home/dc/tools/pwndbg/pwndbg/commands/context.py", line 399, in get_regs
m = ' ' * len(change_marker) if reg not in changed else C.register_changed(change_marker)
TypeError: argument of type 'NoneType' is not iterable
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
> /home/dc/tools/pwndbg/pwndbg/commands/context.py(399)get_regs()
-> m = ' ' * len(change_marker) if reg not in changed else C.register_changed(change_marker)
(Pdb) print(changed)
None
(Pdb) print(reg)
eax
(Pdb) print(pwndbg.arch.current)
i386
(Pdb) print(gdb.execute('show arch', to_string=True))
The target architecture is set automatically (currently i386:x86-64)
(Pdb)
```
Containers created with Podman (https://podman.io/) don't have a
.dockerenv file in the root directory, so setup.sh tries to invoke sudo.
This doesn't work because podman containers can't use sudo (the
processes inside the container run as an unprivileged user).
This removes the check for .dockerenv. The other checks should already
be sufficient to detect that it's running in a container without sudo.
Because of the previous commit to this file that removed `from queue import *`, the `address_queue` on line 96 would fail by throwing an exception when running `leakfind`.
This commit adds back the required `import queue` and fixes the reference to `Queue` on line 96.