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.
This is the case:
```
pwndbg> show print elements
Limit on string chars or array elements to print is 200.
warning: (Internal error: pc 0x7ffff49ef495 in read in CU, but not in symtab.)
warning: (Internal error: pc 0x7ffff49ef495 in read in CU, but not in symtab.)
warning: (Internal error: pc 0x7ffff49ef495 in read in CU, but not in symtab.)
warning: (Internal error: pc 0x7ffff49ef495 in read in CU, but not in symtab.)
Exception occurred: Error: invalid literal for int() with base 10: 'symtab.)' (<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: 'symtab.)':
```
If we call `message = message.split()[-1]`, we get `symtab.)`.
Then `length = int(message)` raise an Exception.
In case the max steps are reached and the loop finished the current skip
buffer remains filled and not unrolled when the last lines are all
skipped values. Fix this by calling the collapse function and
potentially unroll the buffer in case it contains any values.
Fixes#907
The length is enough as the register column is joined with whitespaces
around it. Hence we can simply drop the increment and just use the raw
length to get rid of the double whitespace.
Skipped lines create cognitive load as it takes a bit to figure out how
many lines are actually collapsed. Instead we just create a label
showing the count of omitted lines.
Buffer all repeating lines and check the minimum value when to start
marking them with skip lines. In case the minimum value is not hit, just
unroll the buffer.
To stop skipping any lines, there is the existing bool config
telescope-skip-repeat-val so we avoid adding special values to minimum
like -1 and keep the setting separated.
Fixes#803
Currently this function is only used for the backtrace context and does
not prefix the frame pointers in hex form, which can be annoying if the
value is copied to be inspected or otherwise processed.
This can be a useful command to quickly execute some radare2 operations
in various positions in mid of a debugging session without the need to
shell out and temporarily transfer process control to radare2.
Exception driven code flow for expected code paths is not great for
readability and suffers some performance degeneration that can be
circumvented with conditional checks.
Use exceptions exclusively for fatal failure handling and either return
a simple string from the decompile function or throw an exception.
If we are trying to decompile a running binary which is a PIE, we need
to make sure to pass the appropriate base address to radare2 to be used
when loading a new binary.
Furthermore set io.cache to fix relocations in disassembly and avoid a
warning from the r2pipe.
As the source code and the decompiled sources are essentially the same
thing, lets just reuse the existing code prefix marker to indicate the
current line instead of using a hardcoded plain string.
A comment compatible marker is used before the syntax highlighter to
avoid any highlight and parsing confusion which is later replaced by the
colorized variant of the prefix marker before returning the results.
Furthermore we only replace the amount of indented spaces that is
required to fill the space for the code prefix marker.
The logic was reversed leading to not showing ghidra context if the
source could be found. Instead, we continue with ghidra decompilation
if we can't find the file.
Splitting the logic into ghidra related functionality, context
processing and plain command invocation makes the code better structured
and the individual files smaller.
* feature(radare2): add alias radare2 to r2 command
* feature(radare2): add argument to set base when loading for PIE
Depending on the use case, one may want to have either the same
addresses for PIE as in gdb or just use the non rebased plain addresses
without taking the current memory mapping into account.
* fix(radare2): fix relocations in disassembly warning by enabling io.cache
There was a quoting bug as `INSTALLFLAGS` contains both the option key and value.
This causes the subsequent commands to look something like `python ... '--target foo'...`, causing
the command to treat the entire string `--target foo` as an option key rather than a key-value pair.