Port a lot of tests to aglib (#2519)

* nix: proper use of `version` in jemalloc

* tests: remove gdblib from tests where can

* workflows: add check build pwndbg-lldb

* tests: fix test_commands[jemalloc_heap]

* tests: remove unused JEMALLOC_PATH

* nix: fix jemalloc-static

* tests: fix failing test_commands[bugreport]
pull/2529/head
patryk4815 1 year ago committed by GitHub
parent f26453884f
commit a534af1c28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -16,7 +16,7 @@ on:
- '!*.md'
jobs:
check_release_build-x86_64:
check_release_build-gdb-x86_64:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
@ -26,7 +26,25 @@ jobs:
nix_path: nixpkgs=channel:nixos-unstable
- name: build pwndbg
run: nix build '.#pwndbg' -o result-pwndbg
run: nix build '.#pwndbg' -o result1
- name: simple run pwndbg
run: ./result1/bin/pwndbg <<< 'exit'
check_release_build-lldb-x86_64:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # @v3
- uses: cachix/install-nix-action@6ed004b9ccb68dbc28e7c85bee15fa93dbd214ac # @v22
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: build pwndbg
run: nix build '.#pwndbg-lldb' -o result2
- name: simple run pwndbg
run: ./result2/bin/pwndbg-lldb <<< 'exit'
lock_flake:
runs-on: ubuntu-latest

@ -26,9 +26,13 @@ let
;
isDev = true;
};
jemalloc-static = pkgs.jemalloc.overrideAttrs (oldAttrs: {
jemalloc-static = pkgs.jemalloc.overrideAttrs (finalAttrs: previousAttrs: {
version = "5.3.0"; # version match setup-dev.sh
configureFlags = (oldAttrs.configureFlags or [ ]) ++ [
src = pkgs.fetchurl {
url = "https://github.com/jemalloc/jemalloc/releases/download/${finalAttrs.version}/${finalAttrs.pname}-${finalAttrs.version}.tar.bz2";
sha256 = "sha256-LbgtHnEZ3z5xt2QCGbbf6EeJvAU3mDw7esT3GJrs/qo=";
};
configureFlags = (previousAttrs.configureFlags or [ ]) ++ [
"--enable-static"
"--disable-shared"
];
@ -37,7 +41,7 @@ let
makeFlagsArray+=(CFLAGS="-O0 -g")
'';
postInstall = ''
${oldAttrs.postInstall or ""}
${previousAttrs.postInstall or ""}
cp -v lib/libjemalloc.a $out/lib/
'';
dontStrip = true; # don't strip the debug symbols we added
@ -89,7 +93,6 @@ in
shellHook = ''
export PWNDBG_VENV_PATH="PWNDBG_PLEASE_SKIP_VENV"
export ZIGPATH="${pkgs.lib.getBin pkgs.zig_0_10}/bin/"
export JEMALLOC_PATH="${jemalloc-static}/lib/libjemalloc.a"
'';
};
}

@ -1639,11 +1639,18 @@ def jemalloc_heap() -> None:
print("This command was tested only for jemalloc 5.3.0 and does not support lower versions")
print()
rtree = jemalloc.RTree.get_rtree()
try:
rtree = jemalloc.RTree.get_rtree()
except pwndbg.dbg_mod.Error as what:
# Fixes test_commands[jemalloc_heap] test case
print(message.warn(f"{what}"))
return
extents = rtree.extents
if len(extents) == 0:
print(message.warn("No extents found"))
return
for extent in extents:
# TODO: refactor so not create copies
jemalloc_extent_info(extent.extent_address, header=False)

@ -175,12 +175,16 @@ If it is somehow unavailable, use:
charset_info = sys.getdefaultencoding()
current_setup += f"Charset: {charset_info}\n"
# 8. showing width
width_info = os.get_terminal_size().columns
current_setup += f"Width: {width_info}\n"
# 8. showing width, height
try:
width_info = os.get_terminal_size().columns
height_info = os.get_terminal_size().lines
except OSError:
# Terminal size may not be available in non-interactive environments (e.g., scripts, IDEs)
width_info = "no terminal size"
height_info = "no terminal size"
# 9. showing height
height_info = os.get_terminal_size().lines
current_setup += f"Width: {width_info}\n"
current_setup += f"Height: {height_info}\n"
current_setup += "\n".join(all_info)
@ -248,7 +252,6 @@ If it is somehow unavailable, use:
check_call(["gh", "issue", "create", "--body-file", f.name])
except Exception:
print(please_please_submit + github_issue_url)
raise
elif run_browser:
try:
check_output(["xdg-open", github_issue_url + github_issue_body])

@ -21,10 +21,6 @@ GO = go
SOURCES_GO = $(wildcard *.go)
COMPILED_GO = $(SOURCES_GO:.go=.x86) $(SOURCES_GO:.go=.x64)
ifndef JEMALLOC_PATH
JEMALLOC_PATH = /usr/local/lib/libjemalloc.a
endif
ifeq ($(TARGET), x86)
CFLAGS += -m32
endif
@ -101,13 +97,13 @@ heap_jemalloc_extent_info.out: heap_jemalloc_extent_info.c
@echo "[+] Building heap_jemalloc_extent_info.out"
${CC} -g -O0 -Wno-nonnull -Wno-unused-result \
-o heap_jemalloc_extent_info.out heap_jemalloc_extent_info.c \
-lpthread ${JEMALLOC_PATH} -lm -lstdc++ -pthread -ldl
-Wl,-Bstatic -ljemalloc -Wl,-Bdynamic -lpthread -lm -lstdc++ -pthread -ldl
heap_jemalloc_heap.out: heap_jemalloc_heap.c
@echo "[+] Building heap_jemalloc_heap.out"
${CC} -g -O0 -Wno-nonnull -Wno-unused-result \
-o heap_jemalloc_heap.out heap_jemalloc_heap.c \
-lpthread ${JEMALLOC_PATH} \-lm -lstdc++ -pthread -ldl
-Wl,-Bstatic -ljemalloc -Wl,-Bdynamic -lpthread -lm -lstdc++ -pthread -ldl
multiple_threads.out: multiple_threads.c

@ -4,7 +4,10 @@ import re
import gdb
import pwndbg
import pwndbg.aglib.arch
import pwndbg.aglib.heap
import pwndbg.aglib.memory
import pwndbg.dbg
import tests
HEAP_FIND_FAKE_FAST = tests.binaries.get("heap_find_fake_fast.out")
@ -51,12 +54,12 @@ def test_find_fake_fast_command(start_binary):
unmapped_heap_info = pwndbg.aglib.heap.ptmalloc.heap_for_ptr(
int(gdb.lookup_global_symbol("fake_chunk").value())
)
assert pwndbg.gdblib.memory.peek(unmapped_heap_info) is None
assert pwndbg.aglib.memory.peek(unmapped_heap_info) is None
# A gdb.MemoryError raised here indicates a regression from PR #1145
gdb.execute("find_fake_fast fake_chunk+0x80")
target_address = pwndbg.gdblib.symbol.address("target_address")
target_address = pwndbg.dbg.selected_inferior().symbol_address_from_name("target_address")
assert target_address is not None
print(hex(target_address))

@ -5,11 +5,10 @@ import re
import gdb
import pytest
import pwndbg
import pwndbg.aglib.heap
import pwndbg.aglib.memory
import pwndbg.aglib.typeinfo
import pwndbg.gdblib.symbol
import pwndbg.dbg
import tests
from pwndbg.aglib.heap.ptmalloc import SymbolUnresolvableError
@ -158,7 +157,7 @@ def test_malloc_chunk_command(start_binary):
gdb.execute("continue")
# Print main thread's chunk from another thread
assert gdb.selected_thread().num == 2
assert pwndbg.dbg.selected_thread().index() == 2
results["large"] = gdb.execute("malloc_chunk large_chunk", to_string=True).splitlines()
expected = generate_expected_malloc_chunk_output(chunks)
assert results["large"] == expected["large"]
@ -183,7 +182,7 @@ def test_malloc_chunk_command(start_binary):
# Print another thread's chunk from the main thread
gdb.execute("thread 1")
assert gdb.selected_thread().num == 1
assert pwndbg.dbg.selected_thread().index() == 1
results["large"] = gdb.execute("malloc_chunk large_chunk", to_string=True).splitlines()
assert results["large"] == expected["large"]
@ -211,7 +210,7 @@ def test_malloc_chunk_command_heuristic(start_binary):
gdb.execute("continue")
# Print main thread's chunk from another thread
assert gdb.selected_thread().num == 2
assert pwndbg.dbg.selected_thread().index() == 2
results["large"] = gdb.execute("malloc_chunk large_chunk", to_string=True).splitlines()
expected = generate_expected_malloc_chunk_output(chunks)
assert results["large"] == expected["large"]
@ -235,7 +234,7 @@ def test_malloc_chunk_command_heuristic(start_binary):
# Print another thread's chunk from the main thread
gdb.execute("thread 1")
assert gdb.selected_thread().num == 1
assert pwndbg.dbg.selected_thread().index() == 1
results["large"] = gdb.execute("malloc_chunk large_chunk", to_string=True).splitlines()
assert results["large"] == expected["large"]
@ -281,14 +280,11 @@ class mock_for_heuristic:
mock_symbols # every symbol's address in the list will be mocked to `None`
)
self.mock_all = mock_all # all symbols will be mocked to `None`
# Save `pwndbg.gdblib.symbol.address` and `pwndbg.gdblib.symbol.static_linkage_symbol_address` before mocking
self.saved_address_func = pwndbg.gdblib.symbol.address
self.saved_static_linkage_symbol_address_func = (
pwndbg.gdblib.symbol.static_linkage_symbol_address
)
# Save `selected_inferior` before mocking
self.saved_func = pwndbg.dbg.selected_inferior
def __enter__(self):
def mock(original):
def mock_symbol_address_from_name(original):
def _mock(symbol, *args, **kwargs):
if self.mock_all:
return None
@ -299,18 +295,22 @@ class mock_for_heuristic:
return _mock
# Mock `pwndbg.gdblib.symbol.address` and `pwndbg.gdblib.symbol.static_linkage_symbol_address`
pwndbg.gdblib.symbol.address = mock(pwndbg.gdblib.symbol.address)
pwndbg.gdblib.symbol.static_linkage_symbol_address = mock(
pwndbg.gdblib.symbol.static_linkage_symbol_address
)
def mock_interior(original):
def _mock(*args, **kwargs):
inst = original(*args, **kwargs)
inst.symbol_address_from_name = mock_symbol_address_from_name(
inst.symbol_address_from_name
)
return inst
return _mock
# Mock `symbol_address_from_name` from `selected_inferior`
pwndbg.dbg.selected_inferior = mock_interior(pwndbg.dbg.selected_inferior)
def __exit__(self, exc_type, exc_value, traceback):
# Restore `pwndbg.gdblib.symbol.address` and `pwndbg.gdblib.symbol.static_linkage_symbol_address`
pwndbg.gdblib.symbol.address = self.saved_address_func
pwndbg.gdblib.symbol.static_linkage_symbol_address = (
self.saved_static_linkage_symbol_address_func
)
# Restore `selected_inferior`
pwndbg.dbg.selected_inferior = self.saved_func
def test_main_arena_heuristic(start_binary):
@ -320,9 +320,9 @@ def test_main_arena_heuristic(start_binary):
gdb.execute("continue")
# Use the debug symbol to get the address of `main_arena`
main_arena_addr_via_debug_symbol = pwndbg.gdblib.symbol.static_linkage_symbol_address(
"main_arena"
) or pwndbg.gdblib.symbol.address("main_arena")
main_arena_addr_via_debug_symbol = pwndbg.dbg.selected_inferior().symbol_address_from_name(
"main_arena", prefer_static=True
)
# Check if we can get the address of `main_arena` from debug symbols and the struct of `main_arena` is correct
assert pwndbg.aglib.heap.current.main_arena is not None
@ -349,9 +349,9 @@ def test_mp_heuristic(start_binary):
gdb.execute("continue")
# Use the debug symbol to get the address of `mp_`
mp_addr_via_debug_symbol = pwndbg.gdblib.symbol.static_linkage_symbol_address(
"mp_"
) or pwndbg.gdblib.symbol.address("mp_")
mp_addr_via_debug_symbol = pwndbg.dbg.selected_inferior().symbol_address_from_name(
"mp_", prefer_static=True
)
# Check if we can get the address of `mp_` from debug symbols and the struct of `mp_` is correct
assert pwndbg.aglib.heap.current.mp is not None
@ -382,12 +382,12 @@ def test_thread_cache_heuristic(start_binary, is_multi_threaded):
gdb.execute("continue")
if is_multi_threaded:
gdb.execute("continue")
assert gdb.selected_thread().num == 2
assert pwndbg.dbg.selected_thread().index() == 2
# Use the debug symbol to find the address of `thread_cache`
tcache_addr_via_debug_symbol = pwndbg.gdblib.symbol.static_linkage_symbol_address(
"tcache"
) or pwndbg.gdblib.symbol.address("tcache")
tcache_addr_via_debug_symbol = pwndbg.dbg.selected_inferior().symbol_address_from_name(
"tcache", prefer_static=True
)
thread_cache_addr_via_debug_symbol = pwndbg.aglib.memory.u(tcache_addr_via_debug_symbol)
# Check if we can get the address of `thread_cache` from debug symbols and the struct of `thread_cache` is correct
@ -426,12 +426,12 @@ def test_thread_arena_heuristic(start_binary, is_multi_threaded):
gdb.execute("continue")
if is_multi_threaded:
gdb.execute("continue")
assert gdb.selected_thread().num == 2
assert pwndbg.dbg.selected_thread().index() == 2
# Use the debug symbol to find the value of `thread_arena`
thread_arena_via_debug_symbol = pwndbg.gdblib.symbol.static_linkage_symbol_address(
"thread_arena"
) or pwndbg.gdblib.symbol.address("thread_arena")
thread_arena_via_debug_symbol = pwndbg.dbg.selected_inferior().symbol_address_from_name(
"thread_arena", prefer_static=True
)
assert thread_arena_via_debug_symbol is not None
thread_arena_via_debug_symbol = pwndbg.aglib.memory.u(thread_arena_via_debug_symbol)
assert thread_arena_via_debug_symbol > 0
@ -459,9 +459,9 @@ def test_global_max_fast_heuristic(start_binary):
gdb.execute("continue")
# Use the debug symbol to find the address of `global_max_fast`
global_max_fast_addr_via_debug_symbol = pwndbg.gdblib.symbol.static_linkage_symbol_address(
"global_max_fast"
) or pwndbg.gdblib.symbol.address("global_max_fast")
global_max_fast_addr_via_debug_symbol = pwndbg.dbg.selected_inferior().symbol_address_from_name(
"global_max_fast", prefer_static=True
)
assert global_max_fast_addr_via_debug_symbol is not None
# Check if we can get the address of `global_max_fast` from debug symbols and the value of `global_max_fast` is correct
@ -488,7 +488,7 @@ def test_heuristic_fail_gracefully(start_binary, is_multi_threaded):
gdb.execute("continue")
if is_multi_threaded:
gdb.execute("continue")
assert gdb.selected_thread().num == 2
assert pwndbg.dbg.selected_thread().index() == 2
def _test_heuristic_fail_gracefully(name):
try:

@ -4,9 +4,9 @@ import gdb
import pytest
import pwndbg.aglib.heap
import pwndbg.gdblib.memory
import pwndbg.gdblib.symbol
import pwndbg.gdblib.vmmap
import pwndbg.aglib.memory
import pwndbg.aglib.vmmap
import pwndbg.dbg
import tests
from pwndbg.aglib.heap.ptmalloc import BinType
@ -25,22 +25,22 @@ def test_heap_bins(start_binary):
gdb.execute("continue")
allocator = pwndbg.aglib.heap.current
addr = pwndbg.gdblib.symbol.address("tcache_size")
tcache_size = allocator._request2size(pwndbg.gdblib.memory.u64(addr))
addr = pwndbg.gdblib.symbol.address("tcache_count")
tcache_count = pwndbg.gdblib.memory.u64(addr)
addr = pwndbg.gdblib.symbol.address("fastbin_size")
fastbin_size = allocator._request2size(pwndbg.gdblib.memory.u64(addr))
addr = pwndbg.gdblib.symbol.address("fastbin_count")
fastbin_count = pwndbg.gdblib.memory.u64(addr)
addr = pwndbg.gdblib.symbol.address("smallbin_size")
smallbin_size = allocator._request2size(pwndbg.gdblib.memory.u64(addr))
addr = pwndbg.gdblib.symbol.address("smallbin_count")
smallbin_count = pwndbg.gdblib.memory.u64(addr)
addr = pwndbg.gdblib.symbol.address("largebin_size")
largebin_size = allocator._request2size(pwndbg.gdblib.memory.u64(addr))
addr = pwndbg.gdblib.symbol.address("largebin_count")
largebin_count = pwndbg.gdblib.memory.u64(addr)
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("tcache_size")
tcache_size = allocator._request2size(pwndbg.aglib.memory.u64(addr))
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("tcache_count")
tcache_count = pwndbg.aglib.memory.u64(addr)
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("fastbin_size")
fastbin_size = allocator._request2size(pwndbg.aglib.memory.u64(addr))
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("fastbin_count")
fastbin_count = pwndbg.aglib.memory.u64(addr)
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("smallbin_size")
smallbin_size = allocator._request2size(pwndbg.aglib.memory.u64(addr))
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("smallbin_count")
smallbin_count = pwndbg.aglib.memory.u64(addr)
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("largebin_size")
largebin_size = allocator._request2size(pwndbg.aglib.memory.u64(addr))
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("largebin_count")
largebin_count = pwndbg.aglib.memory.u64(addr)
result = allocator.tcachebins()
assert result.bin_type == BinType.TCACHE
@ -87,7 +87,7 @@ def test_heap_bins(start_binary):
and len(result.bins[tcache_size].fd_chain) == tcache_count + 1
)
for addr in result.bins[tcache_size].fd_chain[:-1]:
assert pwndbg.gdblib.vmmap.find(addr)
assert pwndbg.aglib.vmmap.find(addr)
# check fastbin
gdb.execute("continue")
@ -98,7 +98,7 @@ def test_heap_bins(start_binary):
len(result.bins[fastbin_size].fd_chain) == fastbin_count + 1
)
for addr in result.bins[fastbin_size].fd_chain[:-1]:
assert pwndbg.gdblib.vmmap.find(addr)
assert pwndbg.aglib.vmmap.find(addr)
# check unsortedbin
gdb.execute("continue")
@ -111,9 +111,9 @@ def test_heap_bins(start_binary):
)
assert not result.bins["all"].is_corrupted
for addr in result.bins["all"].fd_chain[:-1]:
assert pwndbg.gdblib.vmmap.find(addr)
assert pwndbg.aglib.vmmap.find(addr)
for addr in result.bins["all"].bk_chain[:-1]:
assert pwndbg.gdblib.vmmap.find(addr)
assert pwndbg.aglib.vmmap.find(addr)
# check smallbins
gdb.execute("continue")
@ -126,9 +126,9 @@ def test_heap_bins(start_binary):
)
assert not result.bins[smallbin_size].is_corrupted
for addr in result.bins[smallbin_size].fd_chain[:-1]:
assert pwndbg.gdblib.vmmap.find(addr)
assert pwndbg.aglib.vmmap.find(addr)
for addr in result.bins[smallbin_size].bk_chain[:-1]:
assert pwndbg.gdblib.vmmap.find(addr)
assert pwndbg.aglib.vmmap.find(addr)
# check largebins
gdb.execute("continue")
@ -141,9 +141,9 @@ def test_heap_bins(start_binary):
)
assert not result.bins[largebin_size].is_corrupted
for addr in result.bins[largebin_size].fd_chain[:-1]:
assert pwndbg.gdblib.vmmap.find(addr)
assert pwndbg.aglib.vmmap.find(addr)
for addr in result.bins[largebin_size].bk_chain[:-1]:
assert pwndbg.gdblib.vmmap.find(addr)
assert pwndbg.aglib.vmmap.find(addr)
# check corrupted
gdb.execute("continue")

@ -6,7 +6,8 @@ import tempfile
import gdb
import pytest
import pwndbg
import pwndbg.aglib.arch
import pwndbg.aglib.heap
import tests
HEAP_BINARY = tests.binaries.get("heap_bugs.out")

@ -2,7 +2,9 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.aglib.arch
import pwndbg.aglib.memory
import pwndbg.aglib.vmmap
import tests
HEAP_VIS = tests.binaries.get("heap_vis.out")
@ -15,9 +17,9 @@ def test_vis_heap_chunk_command(start_binary):
# TODO/FIXME: Shall we have a standard method to do this kind of filtering?
# Note that we have `pages_filter` in pwndbg/pwndbg/commands/vmmap.py heh
heap_page = next(page for page in pwndbg.gdblib.vmmap.get() if page.objfile == "[heap]")
heap_page = next(page for page in pwndbg.aglib.vmmap.get() if page.objfile == "[heap]")
first_chunk_size = pwndbg.gdblib.memory.u64(heap_page.start + pwndbg.aglib.arch.ptrsize)
first_chunk_size = pwndbg.aglib.memory.u64(heap_page.start + pwndbg.aglib.arch.ptrsize)
# Just a sanity check...
assert (heap_page.start & 0xFFF) == 0
@ -50,7 +52,7 @@ def test_vis_heap_chunk_command(start_binary):
hexdump = hexdump_16B(addr)
nonlocal dq2
dq1, dq2 = map(pwndbg.gdblib.memory.u64, (addr, addr + 8))
dq1, dq2 = map(pwndbg.aglib.memory.u64, (addr, addr + 8))
formatted = f"{addr:#x}\t{dq1:#018x}\t{dq2:#018x}\t{hexdump}"
formatted += suffix

@ -2,8 +2,8 @@ from __future__ import annotations
import gdb
import pwndbg.gdblib.memory
import pwndbg.gdblib.stack
import pwndbg.aglib.memory
import pwndbg.aglib.stack
import tests
REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
@ -14,7 +14,7 @@ def test_callstack_readable(start_binary):
gdb.execute("b break_here")
gdb.execute("r")
addresses = pwndbg.gdblib.stack.callstack()
addresses = pwndbg.aglib.stack.callstack()
assert len(addresses) > 0
assert all(pwndbg.gdblib.memory.is_readable_address(address) for address in addresses)
assert all(pwndbg.aglib.memory.is_readable_address(address) for address in addresses)

@ -3,7 +3,7 @@ from __future__ import annotations
import gdb
import pytest
import pwndbg.gdblib
import pwndbg.aglib.regs
import tests
CONDBR_X64_BINARY = tests.binaries.get("conditional_branch_breakpoints_x64.out")
@ -35,4 +35,4 @@ def test_command_break_if_x64(start_binary, binary):
def continue_and_test_pc(stop_label):
gdb.execute("continue")
address = int(gdb.parse_and_eval(f"&{stop_label}"))
assert pwndbg.gdblib.regs.pc == address
assert pwndbg.aglib.regs.pc == address

@ -4,8 +4,8 @@ import gdb
from pwnlib.util.cyclic import cyclic
import pwndbg.aglib.arch
import pwndbg.gdblib.memory
import pwndbg.gdblib.regs
import pwndbg.aglib.memory
import pwndbg.aglib.regs
import tests
REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
@ -38,7 +38,7 @@ def test_command_cyclic_register(start_binary):
ptr_size = pwndbg.aglib.arch.ptrsize
test_offset = 45
pattern = cyclic(length=80, n=ptr_size)
pwndbg.gdblib.regs.rdi = int.from_bytes(
pwndbg.aglib.regs.rdi = int.from_bytes(
pattern[test_offset : test_offset + ptr_size], pwndbg.aglib.arch.endian
)
out = gdb.execute("cyclic -l $rdi", to_string=True)
@ -55,11 +55,11 @@ def test_command_cyclic_address(start_binary):
"""
start_binary(REFERENCE_BINARY)
addr = pwndbg.gdblib.regs.rsp
addr = pwndbg.aglib.regs.rsp
ptr_size = pwndbg.aglib.arch.ptrsize
test_offset = 48
pattern = cyclic(length=80, n=ptr_size)
pwndbg.gdblib.memory.write(addr, pattern)
pwndbg.aglib.memory.write(addr, pattern)
out = gdb.execute(f"cyclic -l '{{unsigned long}}{hex(addr + test_offset)}'", to_string=True)
assert out == (

@ -2,7 +2,7 @@ from __future__ import annotations
import gdb
import pwndbg.gdblib.regs
import pwndbg.aglib.regs
import tests
REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
@ -11,7 +11,7 @@ REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
def test_command_distance(start_binary):
start_binary(REFERENCE_BINARY)
rsp = pwndbg.gdblib.regs.rsp
rsp = pwndbg.aglib.regs.rsp
result = gdb.execute("distance $rsp $rsp+0x10", to_string=True)
assert result == f"{rsp:#x}->{rsp + 0x10:#x} is 0x10 bytes (0x2 words)\n"

@ -2,7 +2,7 @@ from __future__ import annotations
import gdb
import pwndbg.gdblib.regs
import pwndbg.aglib.regs
import tests
REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
@ -11,7 +11,7 @@ REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
def test_flags_command(start_binary):
start_binary(REFERENCE_BINARY)
old_eflags = pwndbg.gdblib.regs.eflags
old_eflags = pwndbg.aglib.regs.eflags
# Verify CF is not set
assert old_eflags & 0x1 == 0
@ -19,15 +19,15 @@ def test_flags_command(start_binary):
gdb.execute("setflag cf 1")
# Verify CF is set and no other flags have changed
assert (old_eflags | 1) == pwndbg.gdblib.regs.eflags
assert (old_eflags | 1) == pwndbg.aglib.regs.eflags
gdb.execute("setflag cf 0")
# Verify CF is not set and no other flags have changed
assert old_eflags == pwndbg.gdblib.regs.eflags
assert old_eflags == pwndbg.aglib.regs.eflags
# Test setting an invalid value
gdb.execute("setflag cf 2")
# Verify no flags have changed
assert old_eflags == pwndbg.gdblib.regs.eflags
assert old_eflags == pwndbg.aglib.regs.eflags

@ -2,7 +2,7 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.aglib.proc
import tests
REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
@ -37,10 +37,10 @@ def test_command_ignore_breakpoint_last_found_one():
assert out == "Will ignore next 1 crossings of breakpoint 1.\n"
gdb.execute("run")
assert not pwndbg.gdblib.proc.alive
assert not pwndbg.aglib.proc.alive
gdb.execute("run")
assert pwndbg.gdblib.proc.alive
assert pwndbg.aglib.proc.alive
def test_command_ignore_breakpoint_last_found_two():

@ -4,6 +4,7 @@ import time
import gdb
import pwndbg.dbg
import tests
REFERENCE_BINARY_THREADS = tests.binaries.get("multiple_threads.out")
@ -28,12 +29,12 @@ def test_command_killthreads_kills_all_threads_except_current(start_binary):
gdb.execute("break break_here")
gdb.execute("run")
wait_until(lambda: len(gdb.selected_inferior().threads()) == 3)
wait_until(lambda: len(pwndbg.dbg.selected_inferior().threads()) == 3)
gdb.execute("killthreads --all")
# check if only one thread is left
wait_until(lambda: len(gdb.selected_inferior().threads()) == 1)
wait_until(lambda: len(pwndbg.dbg.selected_inferior().threads()) == 1)
def test_command_killthreads_kills_specific_thread(start_binary):
@ -41,19 +42,23 @@ def test_command_killthreads_kills_specific_thread(start_binary):
gdb.execute("break break_here")
gdb.execute("run")
initial_thread_count = len(gdb.selected_inferior().threads())
initial_thread_count = len(pwndbg.dbg.selected_inferior().threads())
# check if thread with id 3 exists
wait_until(
lambda: len([thread for thread in gdb.selected_inferior().threads() if thread.num == 3])
lambda: len(
[thread for thread in pwndbg.dbg.selected_inferior().threads() if thread.index() == 3]
)
== 1
)
gdb.execute("killthreads 3")
# check if the thread was killed, and no other thread was killed
wait_until(
lambda: len([thread for thread in gdb.selected_inferior().threads() if thread.num == 3])
lambda: len(
[thread for thread in pwndbg.dbg.selected_inferior().threads() if thread.index() == 3]
)
== 0
)
assert len(gdb.selected_inferior().threads()) == initial_thread_count - 1
assert len(pwndbg.dbg.selected_inferior().threads()) == initial_thread_count - 1
gdb.execute("kill")
@ -64,7 +69,10 @@ def test_command_killthreads_produces_error_when_unknown_thread_passed(start_bin
gdb.execute("break break_here")
gdb.execute("run")
# check if thread with id 3 exists
assert len([thread for thread in gdb.selected_inferior().threads() if thread.num == 3]) == 1
assert (
len([thread for thread in pwndbg.dbg.selected_inferior().threads() if thread.index() == 3])
== 1
)
out = gdb.execute("killthreads 999", to_string=True)
assert "Thread ID 999 does not exist" in out

@ -5,7 +5,6 @@ from unittest.mock import patch
import gdb
import pytest
import pwndbg.gdblib.onegadget
import pwndbg.glibc
import tests

@ -7,6 +7,7 @@ import subprocess
import gdb
import pwndbg.aglib.proc
import tests
REFERENCE_BINARY_NET = tests.binaries.get("reference-binary-net.out")
@ -28,8 +29,8 @@ def test_command_procinfo(start_binary):
start_new_session=True,
)
bin_path = gdb.execute("pi pwndbg.gdblib.proc.exe", to_string=True).strip("\n")
pid = gdb.execute("pi pwndbg.gdblib.proc.pid", to_string=True).strip("\n")
bin_path = pwndbg.aglib.proc.exe
pid = str(pwndbg.aglib.proc.pid)
gdb.execute("break break_here")
gdb.execute("continue")

@ -2,7 +2,7 @@ from __future__ import annotations
import gdb
import pwndbg.gdblib
import pwndbg.aglib.regs
import tests
STEPUNTILASM_X64_BINARY = tests.binaries.get("stepuntilasm_x64.out")
@ -22,4 +22,4 @@ def test_command_untilasm_x64(start_binary):
def run_and_verify(stop_label, asm):
gdb.execute(f"stepuntilasm {asm}")
address = int(gdb.parse_and_eval(f"&{stop_label}"))
assert pwndbg.gdblib.regs.pc == address
assert pwndbg.aglib.regs.pc == address

@ -4,7 +4,10 @@ import re
import gdb
import pwndbg.gdblib
import pwndbg.aglib.memory
import pwndbg.aglib.proc
import pwndbg.aglib.regs
import pwndbg.aglib.vmmap
import tests
TELESCOPE_BINARY = tests.binaries.get("telescope_binary.out")
@ -68,12 +71,12 @@ def test_telescope_command_with_address_as_count(start_binary):
start_binary(TELESCOPE_BINARY)
out = gdb.execute("telescope 2", to_string=True).splitlines()
rsp = pwndbg.gdblib.regs.rsp
rsp = pwndbg.aglib.regs.rsp
assert len(out) == 2
assert out[0] == "00:0000│ rsp %#x ◂— 1" % rsp
expected = rf"01:0008│ {rsp + 8:#x} —▸ 0x[0-9a-f]+ ◂— '{pwndbg.gdblib.proc.exe}'"
expected = rf"01:0008│ {rsp + 8:#x} —▸ 0x[0-9a-f]+ ◂— '{pwndbg.aglib.proc.exe}'"
assert re.search(expected, out[1])
@ -81,7 +84,7 @@ def test_telescope_command_with_address_as_count_and_reversed_flag(start_binary)
start_binary(TELESCOPE_BINARY)
out = gdb.execute("telescope -r 2", to_string=True).splitlines()
rsp = pwndbg.gdblib.regs.rsp
rsp = pwndbg.aglib.regs.rsp
assert out == ["00:0000│ %#x ◂— 0" % (rsp - 8), "01:0008│ rsp %#x ◂— 1" % rsp]
@ -95,9 +98,9 @@ def test_command_telescope_reverse_skipped_records_shows_input_address(start_bin
gdb.execute("break break_here")
gdb.execute("run")
gdb.execute("up")
pwndbg.gdblib.memory.write(pwndbg.gdblib.regs.rsp - 8 * 3, b"\x00" * 8 * 4)
pwndbg.aglib.memory.write(pwndbg.aglib.regs.rsp - 8 * 3, b"\x00" * 8 * 4)
expected_value = hex(pwndbg.gdblib.regs.rsp)
expected_value = hex(pwndbg.aglib.regs.rsp)
result_str = gdb.execute("telescope -r $rsp", to_string=True)
result_lines = result_str.strip("\n").split("\n")
@ -113,8 +116,8 @@ def test_command_telescope_frame(start_binary):
gdb.execute("break break_here")
gdb.execute("run")
rsp = hex(pwndbg.gdblib.regs.sp)
rbp = hex(pwndbg.gdblib.regs[pwndbg.gdblib.regs.frame])
rsp = hex(pwndbg.aglib.regs.sp)
rbp = hex(pwndbg.aglib.regs[pwndbg.aglib.regs.frame])
result_str = gdb.execute("telescope --frame", to_string=True)
result_lines = result_str.strip().split("\n")
@ -133,7 +136,7 @@ def test_command_telescope_frame_bp_below_sp(start_binary):
gdb.execute("run")
gdb.execute("memoize") # turn off cache
pwndbg.gdblib.regs.sp = pwndbg.gdblib.regs[pwndbg.gdblib.regs.frame] + 1
pwndbg.aglib.regs.sp = pwndbg.aglib.regs[pwndbg.aglib.regs.frame] + 1
result_str = gdb.execute("telescope --frame", to_string=True)
@ -150,10 +153,10 @@ def test_command_telescope_frame_bp_sp_different_vmmaps(start_binary):
gdb.execute("run")
gdb.execute("memoize") # turn off cache
pages = pwndbg.gdblib.vmmap.get()
pages = pwndbg.aglib.vmmap.get()
pwndbg.gdblib.regs.sp = pages[0].start
pwndbg.gdblib.regs.bp = pages[1].start
pwndbg.aglib.regs.sp = pages[0].start
pwndbg.aglib.regs.bp = pages[1].start
result_str = gdb.execute("telescope --frame", to_string=True)

@ -3,8 +3,8 @@ from __future__ import annotations
import gdb
import pytest
import pwndbg.gdblib.tls
import pwndbg.gdblib.vmmap
import pwndbg.aglib.tls
import pwndbg.aglib.vmmap
import tests
TLS_X86_64_BINARY = tests.binaries.get("tls.x86-64.out")
@ -23,20 +23,20 @@ def test_tls_address_and_command(start_binary, binary):
expected_tls_address = int(gdb.parse_and_eval("(void *)tls_address"))
assert pwndbg.gdblib.tls.find_address_with_register() == expected_tls_address
assert pwndbg.aglib.tls.find_address_with_register() == expected_tls_address
assert pwndbg.gdblib.tls.find_address_with_pthread_self() == expected_tls_address
assert pwndbg.aglib.tls.find_address_with_pthread_self() == expected_tls_address
assert (
gdb.execute("tls", to_string=True)
== f"""Thread Local Storage (TLS) base: {expected_tls_address:#x}
TLS is located at:
{pwndbg.gdblib.vmmap.find(expected_tls_address)}\n"""
{pwndbg.aglib.vmmap.find(expected_tls_address)}\n"""
)
assert (
gdb.execute("tls --pthread-self", to_string=True)
== f"""Thread Local Storage (TLS) base: {expected_tls_address:#x}
TLS is located at:
{pwndbg.gdblib.vmmap.find(expected_tls_address)}\n"""
{pwndbg.aglib.vmmap.find(expected_tls_address)}\n"""
)

@ -5,7 +5,7 @@ import tempfile
import gdb
import pytest
import pwndbg
import pwndbg.aglib.proc
import tests
GAPS_MAP_BINARY = tests.binaries.get("mmap_gaps.out")
@ -32,7 +32,7 @@ def get_proc_maps():
# Note: info proc mappings may not have permissions information,
# so we get it here and fill from `perms`
with open("/proc/%d/maps" % pwndbg.gdblib.proc.pid) as f:
with open("/proc/%d/maps" % pwndbg.aglib.proc.pid) as f:
for line in f.read().splitlines():
addrs, perms, offset, _inode, size, objfile = line.split(maxsplit=6)
start, end = (int(v, 16) for v in addrs.split("-"))

@ -2,8 +2,8 @@ from __future__ import annotations
import gdb
import pwndbg.gdblib.memory
import pwndbg.gdblib.regs
import pwndbg.aglib.memory
import pwndbg.aglib.regs
import tests
from pwndbg.commands.xor import memfrob
@ -16,10 +16,10 @@ def test_command_xor_with_gdb_execute(start_binary):
"""
start_binary(REFERENCE_BINARY)
before = pwndbg.gdblib.regs.rsp
pwndbg.gdblib.memory.write(before, b"aaaaaaaa")
before = pwndbg.aglib.regs.rsp
pwndbg.aglib.memory.write(before, b"aaaaaaaa")
gdb.execute("xor $rsp ' ' 4")
after = pwndbg.gdblib.memory.read(before, 8)
after = pwndbg.aglib.memory.read(before, 8)
assert after == b"AAAAaaaa"
@ -29,11 +29,11 @@ def test_command_xor_with_int(start_binary):
"""
start_binary(REFERENCE_BINARY)
before = pwndbg.gdblib.regs.rsp
before = pwndbg.aglib.regs.rsp
assert isinstance(before, int)
pwndbg.gdblib.memory.write(before, b"aaaaaaaa")
pwndbg.aglib.memory.write(before, b"aaaaaaaa")
gdb.execute(f"xor {before} ' ' 4")
after = pwndbg.gdblib.memory.read(before, 8)
after = pwndbg.aglib.memory.read(before, 8)
assert after == b"AAAAaaaa"
@ -43,20 +43,20 @@ def test_command_xor_with_hex(start_binary):
"""
start_binary(REFERENCE_BINARY)
before = pwndbg.gdblib.regs.rsp
before = pwndbg.aglib.regs.rsp
before_hex = hex(before)
assert isinstance(before_hex, str)
pwndbg.gdblib.memory.write(before, b"aaaaaaaa")
pwndbg.aglib.memory.write(before, b"aaaaaaaa")
gdb.execute(f"xor {before_hex} ' ' 4")
after = pwndbg.gdblib.memory.read(before, 8)
after = pwndbg.aglib.memory.read(before, 8)
assert after == b"AAAAaaaa"
def test_command_memfrob(start_binary):
start_binary(REFERENCE_BINARY)
before = pwndbg.gdblib.regs.rsp
pwndbg.gdblib.memory.write(before, b"aaaaaaaa")
before = pwndbg.aglib.regs.rsp
pwndbg.aglib.memory.write(before, b"aaaaaaaa")
memfrob(before, 4)
after = pwndbg.gdblib.memory.read(before, 8)
after = pwndbg.aglib.memory.read(before, 8)
assert after == b"KKKKaaaa"

@ -50,8 +50,8 @@ if should_skip_test:
@pytest.mark.xfail(reason="flaky test")
def test_commands(start_binary, name):
print("Running command", name)
start_binary(BINARY)
try:
start_binary(BINARY)
gdb.execute(name)
except gdb.error as e:
ignore = False

@ -3,7 +3,9 @@ from __future__ import annotations
import gdb
import pytest
import pwndbg.gdblib.regs
import pwndbg.aglib.proc
import pwndbg.aglib.regs
import pwndbg.aglib.vmmap
import tests
REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
@ -26,20 +28,20 @@ def test_command_nextproginstr(start_binary):
# Sanity check
exec_bin_pages = [
p for p in pwndbg.gdblib.vmmap.get() if p.objfile == pwndbg.gdblib.proc.exe and p.execute
p for p in pwndbg.aglib.vmmap.get() if p.objfile == pwndbg.aglib.proc.exe and p.execute
]
assert any(pwndbg.gdblib.regs.pc in p for p in exec_bin_pages)
main_page = pwndbg.gdblib.vmmap.find(pwndbg.gdblib.regs.pc)
assert any(pwndbg.aglib.regs.pc in p for p in exec_bin_pages)
main_page = pwndbg.aglib.vmmap.find(pwndbg.aglib.regs.pc)
gdb.execute("break puts")
gdb.execute("continue")
# Sanity check that we are in libc
assert "libc" in pwndbg.gdblib.vmmap.find(pwndbg.gdblib.regs.rip).objfile
assert "libc" in pwndbg.aglib.vmmap.find(pwndbg.aglib.regs.rip).objfile
# Execute nextproginstr and see if we came back to the same vmmap page
gdb.execute("nextproginstr")
assert pwndbg.gdblib.regs.pc in main_page
assert pwndbg.aglib.regs.pc in main_page
# Ensure that nextproginstr won't jump now
out = gdb.execute("nextproginstr", to_string=True)
@ -56,7 +58,7 @@ def test_next_command_doesnt_freeze_crashed_binary(start_binary, command):
# The nextproginstr won't step if we are already on the binary address
# and interestingly, other commands won't step if the address can't be disassemblied
if command == "nextproginstr":
pwndbg.gdblib.regs.pc = 0x1234
pwndbg.aglib.regs.pc = 0x1234
# This should not halt/freeze the program
gdb.execute(command, to_string=True)

@ -5,6 +5,8 @@ import re
import gdb
import pytest
import pwndbg.aglib.memory
import pwndbg.aglib.regs
import pwndbg.commands
import pwndbg.commands.canary
import tests
@ -326,7 +328,7 @@ def test_context_disasm_proper_render_on_mem_change_issue_1818(start_binary, pat
gdb.execute("patch $rip nop;nop;nop;nop;nop", to_string=True)
else:
# Do the same, but through write API
pwndbg.gdblib.memory.write(pwndbg.gdblib.regs.rip, b"\x90" * 5)
pwndbg.aglib.memory.write(pwndbg.aglib.regs.rip, b"\x90" * 5)
# Actual test: we expect the read memory to be different now ;)
# (and not e.g. returned incorrectly from a not cleared cache)

@ -2,8 +2,12 @@ from __future__ import annotations
import os
import pwndbg.commands.cymbol
import pwndbg.gdblib.dt
import pwndbg.dbg
if pwndbg.dbg.is_gdblib_available():
import pwndbg.commands.cymbol
import pwndbg.gdblib.dt
import tests
REFERENCE_BINARY = tests.binaries.get("reference-binary.out")

@ -1,8 +1,8 @@
from __future__ import annotations
import pwndbg.gdblib.regs
import pwndbg.aglib.regs
import tests
from pwndbg.gdblib.nearpc import nearpc
from pwndbg.aglib.nearpc import nearpc
EMULATE_DISASM_BINARY = tests.binaries.get("emulate_disasm.out")
EMULATE_DISASM_LOOP_BINARY = tests.binaries.get("emulate_disasm_loop.out")
@ -53,7 +53,7 @@ def test_emulate_disasm_loop(start_binary):
disasm_with_emu_0x400080 = [
" ► 0x400080 <_start> movabs rsi, string RSI => 0x400094 (string) ◂— xor dword ptr [rdx], esi /* '12345' */",
f" 0x40008a <_start+10> mov rdi, rsp RDI => {hex(pwndbg.gdblib.regs.rsp)} ◂— 1",
f" 0x40008a <_start+10> mov rdi, rsp RDI => {hex(pwndbg.aglib.regs.rsp)} ◂— 1",
" 0x40008d <_start+13> mov ecx, 3 ECX => 3",
" 0x400092 <_start+18> rep movsb byte ptr [rdi], byte ptr [rsi]",
"",

@ -3,7 +3,7 @@ from __future__ import annotations
import gdb
import pytest
import pwndbg
import pwndbg.dbg
import pwndbg.lib.config
@ -73,7 +73,10 @@ def test_gdb_parameter_default_value_works(start_binary, params):
)
# Initialize and register param in GDB as if it would be done by gdblib.config.init_params
pwndbg.gdblib.config_mod.Parameter(param)
if pwndbg.dbg.is_gdblib_available():
from pwndbg.gdblib import config_mod as gdblib_config_mod
gdblib_config_mod.Parameter(param)
out = gdb.execute(f"show {param_name}", to_string=True)
assert (

@ -7,7 +7,6 @@ import tempfile
import gdb
import pytest
import pwndbg.gdblib.info
import pwndbg.glibc
import tests
@ -27,7 +26,7 @@ def test_parsing_info_sharedlibrary_to_find_libc_filename(start_binary, have_deb
gdb.execute("break break_here")
gdb.execute("continue")
if not have_debugging_information:
assert "(*)" in pwndbg.gdblib.info.sharedlibrary()
assert "(*)" in gdb.execute("info sharedlibrary", to_string=True)
libc_path = pwndbg.glibc.get_libc_filename_from_info_sharedlibrary()
assert libc_path is not None
@ -44,7 +43,7 @@ def test_parsing_info_sharedlibrary_to_find_libc_filename(start_binary, have_deb
gdb.execute("continue")
# Check if we can find the libc loaded by LD_PRELOAD
if not have_debugging_information:
assert "(*)" in pwndbg.gdblib.info.sharedlibrary()
assert "(*)" in gdb.execute("info sharedlibrary", to_string=True)
assert pwndbg.glibc.get_libc_filename_from_info_sharedlibrary() == test_libc_path
# Unfortunatly, if we used LD_PRELOAD to load libc, we might cannot find the libc's filename

@ -4,9 +4,9 @@ import gdb
from pwnlib.util.cyclic import cyclic
import pwndbg
import pwndbg.gdblib.memory
import pwndbg.gdblib.regs
import pwndbg.gdblib.vmmap
import pwndbg.aglib.memory
import pwndbg.aglib.regs
import pwndbg.aglib.vmmap
import tests
BINARY = tests.binaries.get("reference-binary.out")
@ -16,7 +16,7 @@ def run_tests(stack, use_big_endian, expected):
pwndbg.config.hexdump_group_use_big_endian = use_big_endian
# Put some data onto the stack
pwndbg.gdblib.memory.write(stack, cyclic(0x100))
pwndbg.aglib.memory.write(stack, cyclic(0x100))
# Test empty hexdump
result = gdb.execute("hexdump 0", to_string=True)
@ -39,7 +39,7 @@ def test_hexdump(start_binary):
# TODO: Setting theme options with Python isn't working
gdb.execute("set hexdump-byte-separator")
stack_addr = pwndbg.gdblib.regs.rsp - 0x100
stack_addr = pwndbg.aglib.regs.rsp - 0x100
expected = [
f"""+0000 0x{stack_addr:x} 6161616261616161 6161616461616163 │aaaabaaa│caaadaaa│
@ -62,9 +62,9 @@ def test_hexdump(start_binary):
def test_hexdump_collapse_lines(start_binary):
start_binary(BINARY)
sp = pwndbg.gdblib.regs.rsp
sp = pwndbg.aglib.regs.rsp
pwndbg.gdblib.memory.write(sp, b"abcdefgh\x01\x02\x03\x04\x05\x06\x07\x08" * 16)
pwndbg.aglib.memory.write(sp, b"abcdefgh\x01\x02\x03\x04\x05\x06\x07\x08" * 16)
def hexdump_lines(lines):
offset = (lines - 1) * 0x10 # last line offset
@ -88,11 +88,11 @@ def test_hexdump_saved_address_and_offset(start_binary):
# TODO There is no way to verify repetition: the last_address and offset are reset
# before each command
start_binary(BINARY)
sp = pwndbg.gdblib.regs.rsp
sp = pwndbg.aglib.regs.rsp
SIZE = 21
pwndbg.gdblib.memory.write(sp, b"abcdefgh\x01\x02\x03\x04\x05\x06\x07\x08" * 16)
pwndbg.aglib.memory.write(sp, b"abcdefgh\x01\x02\x03\x04\x05\x06\x07\x08" * 16)
out1 = gdb.execute(f"hexdump $rsp {SIZE}", to_string=True)
out2 = (

@ -2,8 +2,9 @@ from __future__ import annotations
import gdb
import pwndbg.gdblib.memory
import pwndbg.gdblib.stack
import pwndbg.aglib.memory
import pwndbg.aglib.stack
import pwndbg.dbg
import tests
REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
@ -15,24 +16,22 @@ def test_memory_read_write(start_binary):
Tests simple pwndbg's memory read/write operations with different argument types
"""
start_binary(REFERENCE_BINARY)
stack_addr = next(iter(pwndbg.gdblib.stack.get().values())).vaddr
stack_addr = next(iter(pwndbg.aglib.stack.get().values())).vaddr
# Testing write(addr, str)
val = "X" * 50
pwndbg.gdblib.memory.write(stack_addr, val)
assert pwndbg.gdblib.memory.read(stack_addr, len(val) + 1) == bytearray(b"X" * 50 + b"\x00")
pwndbg.aglib.memory.write(stack_addr, val)
assert pwndbg.aglib.memory.read(stack_addr, len(val) + 1) == bytearray(b"X" * 50 + b"\x00")
# Testing write(addr, bytearray)
val = bytearray("Y" * 10, "utf8")
pwndbg.gdblib.memory.write(stack_addr, val)
assert pwndbg.gdblib.memory.read(stack_addr, len(val) + 4) == val + bytearray(b"XXXX")
pwndbg.aglib.memory.write(stack_addr, val)
assert pwndbg.aglib.memory.read(stack_addr, len(val) + 4) == val + bytearray(b"XXXX")
# Testing write(addr, bytes)
val = bytes("Z" * 8, "utf8")
pwndbg.gdblib.memory.write(stack_addr, val)
assert pwndbg.gdblib.memory.read(stack_addr, len(val) + 4) == bytearray(
"Z" * 8 + "YYXX", "utf8"
)
pwndbg.aglib.memory.write(stack_addr, val)
assert pwndbg.aglib.memory.read(stack_addr, len(val) + 4) == bytearray("Z" * 8 + "YYXX", "utf8")
def test_memory_peek_poke(start_binary):
@ -46,23 +45,23 @@ def test_memory_peek_poke(start_binary):
# Address 0 is not mapped, so peek should return None
# and poke should fail
assert pwndbg.gdblib.memory.poke(0) is False
assert pwndbg.gdblib.memory.peek(0) is None
assert pwndbg.aglib.memory.poke(0) is False
assert pwndbg.aglib.memory.peek(0) is None
stack_addr = pwndbg.gdblib.regs.rsp
stack_addr = pwndbg.aglib.regs.rsp
for v in range(256):
data = bytearray([v, 0, 0, 0])
pwndbg.gdblib.memory.write(stack_addr, data)
pwndbg.aglib.memory.write(stack_addr, data)
# peek should return the first byte
assert pwndbg.gdblib.memory.peek(stack_addr) == bytearray([v])
assert pwndbg.aglib.memory.peek(stack_addr) == bytearray([v])
# Now poke it!
assert pwndbg.gdblib.memory.poke(stack_addr) is True
assert pwndbg.aglib.memory.poke(stack_addr) is True
# Now make sure poke did not change the underlying bytes
assert pwndbg.gdblib.memory.read(stack_addr, 4) == data
assert pwndbg.aglib.memory.read(stack_addr, 4) == data
# TODO/FIXME: Fix peek/poke exception handling and uncomment this!
"""
@ -70,23 +69,23 @@ def test_memory_peek_poke(start_binary):
# when incorrect argument type is passed
for not_parsable_as_int in (b"asdf", "asdf"):
with pytest.raises(ValueError):
pwndbg.gdblib.memory.peek(not_parsable_as_int)
pwndbg.aglib.memory.peek(not_parsable_as_int)
for not_parsable_as_int in (b"asdf", "asdf"):
with pytest.raises(ValueError):
pwndbg.gdblib.memory.poke(not_parsable_as_int)
pwndbg.aglib.memory.poke(not_parsable_as_int)
"""
# Acceptable inputs (not great; maybe we should ban them?)
# Note: they return 0 because the address 0 is not mapped
assert pwndbg.gdblib.memory.peek(0.0) is None
assert pwndbg.gdblib.memory.peek("0") is None
assert pwndbg.gdblib.memory.peek(b"0") is None
assert pwndbg.aglib.memory.peek(0.0) is None
assert pwndbg.aglib.memory.peek("0") is None
assert pwndbg.aglib.memory.peek(b"0") is None
def test_fetch_struct_as_dictionary(start_binary):
"""
Test pwndbg.gdblib.memory.fetch_struct_as_dictionary()
Test pwndbg.aglib.memory.fetch_struct_as_dictionary()
Ensure it can handle nested structs, anonymous structs & nested typedefs.
"""
start_binary(NESTED_STRUCTS_BINARY)
@ -103,17 +102,17 @@ def test_fetch_struct_as_dictionary(start_binary):
"outer_z": 5,
}
struct_address = pwndbg.gdblib.symbol.address("outer")
struct_address = pwndbg.dbg.selected_inferior().symbol_address_from_name("outer")
assert struct_address is not None
result = pwndbg.gdblib.memory.fetch_struct_as_dictionary("outer_struct", struct_address)
result = pwndbg.aglib.memory.fetch_struct_as_dictionary("outer_struct", struct_address)
assert result == expected_result
def test_fetch_struct_as_dictionary_include_filter(start_binary):
"""
Test pwndbg.gdblib.memory.fetch_struct_as_dictionary()
Test pwndbg.aglib.memory.fetch_struct_as_dictionary()
Ensure its include_only_fields filter works.
"""
start_binary(NESTED_STRUCTS_BINARY)
@ -127,10 +126,10 @@ def test_fetch_struct_as_dictionary_include_filter(start_binary):
"anonymous_nested": 100,
}
struct_address = pwndbg.gdblib.symbol.address("outer")
struct_address = pwndbg.dbg.selected_inferior().symbol_address_from_name("outer")
assert struct_address is not None
result = pwndbg.gdblib.memory.fetch_struct_as_dictionary(
result = pwndbg.aglib.memory.fetch_struct_as_dictionary(
"outer_struct",
struct_address,
include_only_fields={"outer_x", "inner", "anonymous_k", "anonymous_nested"},
@ -141,7 +140,7 @@ def test_fetch_struct_as_dictionary_include_filter(start_binary):
def test_fetch_struct_as_dictionary_exclude_filter(start_binary):
"""
Test pwndbg.gdblib.memory.fetch_struct_as_dictionary()
Test pwndbg.aglib.memory.fetch_struct_as_dictionary()
Ensure its exclude_fields filter works.
Note that the exclude filter cannot filter fields of anonymous structs.
"""
@ -156,10 +155,10 @@ def test_fetch_struct_as_dictionary_exclude_filter(start_binary):
"anonymous_nested": 100,
}
struct_address = pwndbg.gdblib.symbol.address("outer")
struct_address = pwndbg.dbg.selected_inferior().symbol_address_from_name("outer")
assert struct_address is not None
result = pwndbg.gdblib.memory.fetch_struct_as_dictionary(
result = pwndbg.aglib.memory.fetch_struct_as_dictionary(
"outer_struct",
struct_address,
exclude_fields={"outer_x", "inner", "outer_z"},

@ -2,7 +2,10 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.aglib.arch
import pwndbg.aglib.memory
import pwndbg.aglib.vmmap
import pwndbg.lib.memory
import tests
USE_FDS_BINARY = tests.binaries.get("use-fds.out")
@ -29,7 +32,7 @@ def test_mmap_executes_properly(start_binary):
# Checks whether permissions match.
def has_correct_perms(ptr, perm):
page = pwndbg.gdblib.vmmap.find(ptr)
page = pwndbg.aglib.vmmap.find(ptr)
return (
not (page.read ^ ("r" in perm))
and not (page.write ^ ("w" in perm))
@ -46,7 +49,7 @@ def test_mmap_executes_properly(start_binary):
# Check basic fixed mapping.
base_addr = 0xDEADBEEF & pwndbg.lib.memory.PAGE_MASK
while True:
page = pwndbg.gdblib.vmmap.find(base_addr)
page = pwndbg.aglib.vmmap.find(base_addr)
if page is None:
break
base_addr = page.end
@ -75,8 +78,8 @@ def test_mmap_executes_properly(start_binary):
# the first 16 bytes present in our newly created memory map, and compare
# them.
data_ptr = int(gdb.newest_frame().read_var("buf").address)
data_local = pwndbg.gdblib.memory.read(data_ptr, 16)
data_mapped = pwndbg.gdblib.memory.read(ptr, 16)
data_local = pwndbg.aglib.memory.read(data_ptr, 16)
data_mapped = pwndbg.aglib.memory.read(ptr, 16)
assert data_local == data_mapped

@ -2,7 +2,8 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.aglib.regs
import pwndbg.aglib.vmmap
import tests
SMALL_BINARY = tests.binaries.get("crash_simple.out.hardcoded")
@ -14,18 +15,18 @@ def test_mprotect_executes_properly(start_binary):
"""
start_binary(SMALL_BINARY)
pc = pwndbg.gdblib.regs.pc
pc = pwndbg.aglib.regs.pc
# Check if we can use mprotect with address provided as value
# and to set page permissions to RWX
gdb.execute("mprotect %d 4096 PROT_EXEC|PROT_READ|PROT_WRITE" % pc)
vm = pwndbg.gdblib.vmmap.find(pc)
vm = pwndbg.aglib.vmmap.find(pc)
assert vm.read and vm.write and vm.execute
# Check if we can use mprotect with address provided as register
# and to set page permissions to none
gdb.execute("mprotect $pc 0x1000 PROT_NONE")
vm = pwndbg.gdblib.vmmap.find(pc)
vm = pwndbg.aglib.vmmap.find(pc)
assert not (vm.read and vm.write and vm.execute)

@ -2,9 +2,7 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.color.message
import pwndbg.gdblib.proc
import tests
BINARY = tests.binaries.get("reference-binary.out")

@ -2,7 +2,7 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.dbg
import tests
MANGLING_BINARY = tests.binaries.get("symbol_1600_and_752.out")
@ -20,7 +20,7 @@ def test_symbol_get(start_binary):
# (we pass `to_string=True` to suppress the context output)
gdb.execute("nextret", to_string=True)
p = int(gdb.parse_and_eval("p"))
return pwndbg.gdblib.symbol.get(p)
return pwndbg.dbg.selected_inferior().symbol_name_at_address(p)
assert get_next_ptr() == "main"
@ -42,7 +42,7 @@ def test_symbol_duplicated_symbols_issue_1610():
# Sanity checks
assert gdb.execute("info symbol main", to_string=True) == "main in section .text\n"
assert pwndbg.gdblib.symbol.get(main_addr) == "main"
assert pwndbg.dbg.selected_inferior().symbol_name_at_address(main_addr) == "main"
# This causes some confusion, see below :)
# Note: {addr} argument is needed for old GDB (e.g. on Ubuntu 18.04)
@ -60,10 +60,13 @@ def test_symbol_duplicated_symbols_issue_1610():
assert out[2] == ""
# Make sure to clear cache!
pwndbg.gdblib.symbol.get.cache.clear()
if pwndbg.dbg.is_gdblib_available():
from pwndbg.gdblib.symbol import get as symbol_get
symbol_get.cache.clear()
# Real test assert - this should not crash!
assert pwndbg.gdblib.symbol.get(main_addr) == "main"
assert pwndbg.dbg.selected_inferior().symbol_name_at_address(main_addr) == "main"
def _get_section_addr(sect):

@ -2,7 +2,9 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.aglib.memory
import pwndbg.aglib.regs
import pwndbg.aglib.vmmap
import tests
MEMORY_BINARY = tests.binaries.get("memory.out")
@ -287,8 +289,8 @@ def test_windbg_eX_commands(start_binary):
### Test write & output on partial write
#########################################
# e.g. when we make a write to the last stack address
stack_ea = pwndbg.gdblib.regs[pwndbg.gdblib.regs.stack]
stack_page = pwndbg.gdblib.vmmap.find(stack_ea)
stack_ea = pwndbg.aglib.regs[pwndbg.aglib.regs.stack]
stack_page = pwndbg.aglib.vmmap.find(stack_ea)
# Last possible address on stack where we can perform an 8-byte write
stack_last_qword_ea = stack_page.end - 8
@ -300,7 +302,7 @@ def test_windbg_eX_commands(start_binary):
assert gdb_result[1] == "(Made 1 writes to memory; skipping further writes)"
# Check if the write actually occurred
assert pwndbg.gdblib.memory.read(stack_last_qword_ea, 8) == b"\xef\xbe\xad\xde\xbe\xba\xfe\xca"
assert pwndbg.aglib.memory.read(stack_last_qword_ea, 8) == b"\xef\xbe\xad\xde\xbe\xba\xfe\xca"
def test_windbg_commands_x86(start_binary):
@ -311,59 +313,57 @@ def test_windbg_commands_x86(start_binary):
start_binary(X86_BINARY)
# Prepare memory
pwndbg.gdblib.memory.write(pwndbg.gdblib.regs.esp, b"1234567890abcdef_")
pwndbg.gdblib.memory.write(pwndbg.gdblib.regs.esp + 16, b"\x00" * 16)
pwndbg.gdblib.memory.write(pwndbg.gdblib.regs.esp + 32, bytes(range(16)))
pwndbg.gdblib.memory.write(pwndbg.gdblib.regs.esp + 48, b"Z" * 16)
pwndbg.aglib.memory.write(pwndbg.aglib.regs.esp, b"1234567890abcdef_")
pwndbg.aglib.memory.write(pwndbg.aglib.regs.esp + 16, b"\x00" * 16)
pwndbg.aglib.memory.write(pwndbg.aglib.regs.esp + 32, bytes(range(16)))
pwndbg.aglib.memory.write(pwndbg.aglib.regs.esp + 48, b"Z" * 16)
#################################################
#### dX command tests
#################################################
db = gdb.execute("db $esp", to_string=True).splitlines()
assert db == [
"%x 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66" % pwndbg.gdblib.regs.esp,
"%x 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" % (pwndbg.gdblib.regs.esp + 16),
"%x 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" % (pwndbg.gdblib.regs.esp + 32),
"%x 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a" % (pwndbg.gdblib.regs.esp + 48),
"%x 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66" % pwndbg.aglib.regs.esp,
"%x 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" % (pwndbg.aglib.regs.esp + 16),
"%x 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" % (pwndbg.aglib.regs.esp + 32),
"%x 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a" % (pwndbg.aglib.regs.esp + 48),
]
dw = gdb.execute("dw $esp", to_string=True).splitlines()
assert dw == [
"%x 3231 3433 3635 3837 3039 6261 6463 6665" % pwndbg.gdblib.regs.esp,
"%x 0000 0000 0000 0000 0000 0000 0000 0000" % (pwndbg.gdblib.regs.esp + 16),
"%x 0100 0302 0504 0706 0908 0b0a 0d0c 0f0e" % (pwndbg.gdblib.regs.esp + 32),
"%x 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a" % (pwndbg.gdblib.regs.esp + 48),
"%x 3231 3433 3635 3837 3039 6261 6463 6665" % pwndbg.aglib.regs.esp,
"%x 0000 0000 0000 0000 0000 0000 0000 0000" % (pwndbg.aglib.regs.esp + 16),
"%x 0100 0302 0504 0706 0908 0b0a 0d0c 0f0e" % (pwndbg.aglib.regs.esp + 32),
"%x 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a" % (pwndbg.aglib.regs.esp + 48),
]
dd = gdb.execute("dd $esp", to_string=True).splitlines()
assert dd == [
"%x 34333231 38373635 62613039 66656463" % pwndbg.gdblib.regs.esp,
"%x 00000000 00000000 00000000 00000000" % (pwndbg.gdblib.regs.esp + 16),
"%x 03020100 07060504 0b0a0908 0f0e0d0c" % (pwndbg.gdblib.regs.esp + 32),
"%x 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a" % (pwndbg.gdblib.regs.esp + 48),
"%x 34333231 38373635 62613039 66656463" % pwndbg.aglib.regs.esp,
"%x 00000000 00000000 00000000 00000000" % (pwndbg.aglib.regs.esp + 16),
"%x 03020100 07060504 0b0a0908 0f0e0d0c" % (pwndbg.aglib.regs.esp + 32),
"%x 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a" % (pwndbg.aglib.regs.esp + 48),
]
dq = gdb.execute("dq $esp", to_string=True).splitlines()
assert dq == [
"%x 3837363534333231 6665646362613039" % pwndbg.gdblib.regs.esp,
"%x 0000000000000000 0000000000000000" % (pwndbg.gdblib.regs.esp + 16),
"%x 0706050403020100 0f0e0d0c0b0a0908" % (pwndbg.gdblib.regs.esp + 32),
"%x 5a5a5a5a5a5a5a5a 5a5a5a5a5a5a5a5a" % (pwndbg.gdblib.regs.esp + 48),
"%x 3837363534333231 6665646362613039" % pwndbg.aglib.regs.esp,
"%x 0000000000000000 0000000000000000" % (pwndbg.aglib.regs.esp + 16),
"%x 0706050403020100 0f0e0d0c0b0a0908" % (pwndbg.aglib.regs.esp + 32),
"%x 5a5a5a5a5a5a5a5a 5a5a5a5a5a5a5a5a" % (pwndbg.aglib.regs.esp + 48),
]
#################################################
#### eX command tests
#################################################
gdb.execute("eb $esp 00")
assert pwndbg.gdblib.memory.read(pwndbg.gdblib.regs.esp, 1) == b"\x00"
assert pwndbg.aglib.memory.read(pwndbg.aglib.regs.esp, 1) == b"\x00"
gdb.execute("ew $esp 4141")
assert pwndbg.gdblib.memory.read(pwndbg.gdblib.regs.esp, 2) == b"\x41\x41"
assert pwndbg.aglib.memory.read(pwndbg.aglib.regs.esp, 2) == b"\x41\x41"
gdb.execute("ed $esp 5252525252")
assert pwndbg.gdblib.memory.read(pwndbg.gdblib.regs.esp, 4) == b"\x52" * 4
assert pwndbg.aglib.memory.read(pwndbg.aglib.regs.esp, 4) == b"\x52" * 4
gdb.execute("eq $esp 1122334455667788")
assert (
pwndbg.gdblib.memory.read(pwndbg.gdblib.regs.esp, 8) == b"\x88\x77\x66\x55\x44\x33\x22\x11"
)
assert pwndbg.aglib.memory.read(pwndbg.aglib.regs.esp, 8) == b"\x88\x77\x66\x55\x44\x33\x22\x11"

@ -0,0 +1 @@
from __future__ import annotations

@ -1,11 +1,11 @@
from __future__ import annotations
import pwndbg.gdblib.memory
import pwndbg.gdblib.stack
import pwndbg.aglib.memory
import pwndbg.aglib.stack
def test_callstack_readable():
addresses = pwndbg.gdblib.stack.callstack()
addresses = pwndbg.aglib.stack.callstack()
assert len(addresses) > 0
assert all(pwndbg.gdblib.memory.is_readable_address(address) for address in addresses)
assert all(pwndbg.aglib.memory.is_readable_address(address) for address in addresses)

@ -2,7 +2,11 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.dbg
if pwndbg.dbg.is_gdblib_available():
import pwndbg.gdblib.kernel
import pwndbg.gdblib.kernel.slab
def test_command_kchecksec():

@ -4,21 +4,29 @@ import os
import pytest
import pwndbg
from pwndbg.gdblib import kernel
import pwndbg.dbg
is_gdblib = pwndbg.dbg.is_gdblib_available()
if is_gdblib:
import pwndbg.gdblib.kernel
import pwndbg.gdblib.kernel.kallsyms
import pwndbg.gdblib.symbol
ARCH = os.getenv("PWNDBG_ARCH")
KERNEL_TYPE = os.getenv("PWNDBG_KERNEL_TYPE")
KERNEL_VERSION = os.getenv("PWNDBG_KERNEL_VERSION")
@pytest.mark.skipif(not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols")
@pytest.mark.skipif(
is_gdblib and not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols"
)
def test_gdblib_kernel_archops_address_translation():
# test address translation functions for LowMem
min_low_pfn = int(pwndbg.gdblib.symbol.parse_and_eval("(long)min_low_pfn"))
max_low_pfn = int(pwndbg.gdblib.symbol.parse_and_eval("(long)max_low_pfn"))
pfns = [min_low_pfn, max_low_pfn]
kernel = pwndbg.gdblib.kernel
for pfn in pfns:
assert kernel.virt_to_pfn(kernel.pfn_to_virt(pfn)) == pfn
assert kernel.phys_to_pfn(kernel.pfn_to_phys(pfn)) == pfn
@ -30,7 +38,9 @@ def test_gdblib_kernel_archops_address_translation():
assert kernel.page_to_phys(kernel.phys_to_page(phys)) == phys
@pytest.mark.skipif(not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols")
@pytest.mark.skipif(
is_gdblib and not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols"
)
def test_gdblib_kernel_krelease():
release_ver = pwndbg.gdblib.kernel.krelease()
# release should be int tuple of form (major, minor, patch) or (major, minor)
@ -39,18 +49,24 @@ def test_gdblib_kernel_krelease():
assert release_str in pwndbg.gdblib.kernel.kversion()
@pytest.mark.skipif(not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols")
@pytest.mark.skipif(
is_gdblib and not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols"
)
def test_gdblib_kernel_is_kaslr_enabled():
pwndbg.gdblib.kernel.is_kaslr_enabled()
@pytest.mark.skipif(not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols")
@pytest.mark.skipif(
is_gdblib and not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols"
)
def test_gdblib_kernel_nproc():
# make sure no exception occurs
pwndbg.gdblib.kernel.nproc()
@pytest.mark.skipif(not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols")
@pytest.mark.skipif(
is_gdblib and not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols"
)
def test_gdblib_kernel_kbase():
# newer arm/arm64 kernels reserve (_stext, _end] and other kernels reserve [_text, _end)
# https://elixir.bootlin.com/linux/v6.8.4/source/arch/arm64/mm/init.c#L306
@ -60,7 +76,9 @@ def test_gdblib_kernel_kbase():
)
@pytest.mark.skipif(not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols")
@pytest.mark.skipif(
is_gdblib and not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols"
)
def test_gdblib_kernel_kallsyms():
ks = pwndbg.gdblib.kernel.kallsyms.get()
assert ks["commit_creds"][0] == pwndbg.gdblib.symbol.address("commit_creds")

@ -5,8 +5,8 @@ import user
from capstone.arm64_const import ARM64_INS_BL
import pwndbg.aglib.disasm
import pwndbg.gdblib.nearpc
import pwndbg.gdblib.symbol
import pwndbg.aglib.nearpc
import pwndbg.dbg
from pwndbg.aglib.disasm.instruction import InstructionCondition
AARCH64_GRACEFUL_EXIT = """
@ -89,7 +89,7 @@ def test_aarch64_syscall_annotation(qemu_assembly_run):
qemu_assembly_run(AARCH64_GRACEFUL_EXIT, "aarch64")
instructions = pwndbg.aglib.disasm.near(
address=pwndbg.gdblib.regs.pc, instructions=3, emulate=True
address=pwndbg.aglib.regs.pc, instructions=3, emulate=True
)[0]
future_syscall_ins = instructions[2]
@ -436,7 +436,7 @@ REFERENCE_BINARY = user.binaries.get("reference-binary.aarch64.out")
def test_aarch64_reference(qemu_start_binary):
qemu_start_binary(REFERENCE_BINARY, "aarch64")
gdb.execute("break break_here")
assert pwndbg.gdblib.symbol.address("main") is not None
assert pwndbg.dbg.selected_inferior().symbol_address_from_name("main") is not None
gdb.execute("continue")
gdb.execute("argv", to_string=True)

@ -2,7 +2,7 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.color
ARM_GRACEFUL_EXIT = """
mov r0, 0

@ -2,7 +2,7 @@ from __future__ import annotations
import gdb
import pwndbg
import pwndbg.color
MIPS_GRACEFUL_EXIT = """
li $v0, 0xfa1

@ -3,7 +3,8 @@ from __future__ import annotations
import gdb
import user
import pwndbg.gdblib.symbol
import pwndbg.color
import pwndbg.dbg
RISCV64_GRACEFUL_EXIT = """
li a2, 30
@ -296,7 +297,7 @@ REFERENCE_BINARY = user.binaries.get("reference-binary.riscv64.out")
def test_riscv64_reference(qemu_start_binary):
qemu_start_binary(REFERENCE_BINARY, "riscv64")
gdb.execute("break 4")
assert pwndbg.gdblib.symbol.address("main") is not None
assert pwndbg.dbg.selected_inferior().symbol_address_from_name("main") is not None
gdb.execute("continue")
gdb.execute("stepuntilasm jalr")

@ -3,11 +3,11 @@ from __future__ import annotations
import argparse
import concurrent.futures
import os
import random
import re
import subprocess
import sys
import time
from pathlib import Path
from subprocess import CompletedProcess
from typing import List
from typing import Tuple
@ -15,7 +15,56 @@ from typing import Tuple
root_dir = os.path.realpath("../")
def ensureZigPath():
def reserve_port(ip="127.0.0.1", port=0):
"""
https://github.com/Yelp/ephemeral-port-reserve/blob/master/ephemeral_port_reserve.py
Bind to an ephemeral port, force it into the TIME_WAIT state, and unbind it.
This means that further ephemeral port alloctions won't pick this "reserved" port,
but subprocesses can still bind to it explicitly, given that they use SO_REUSEADDR.
By default on linux you have a grace period of 60 seconds to reuse this port.
To check your own particular value:
$ cat /proc/sys/net/ipv4/tcp_fin_timeout
60
By default, the port will be reserved for localhost (aka 127.0.0.1).
To reserve a port for a different ip, provide the ip as the first argument.
Note that IP 0.0.0.0 is interpreted as localhost.
"""
import contextlib
import errno
from socket import SO_REUSEADDR
from socket import SOL_SOCKET
from socket import error as SocketError
from socket import socket
port = int(port)
with contextlib.closing(socket()) as s:
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
try:
s.bind((ip, port))
except SocketError as e:
# socket.error: EADDRINUSE Address already in use
if e.errno == errno.EADDRINUSE and port != 0:
s.bind((ip, 0))
else:
raise
# the connect below deadlocks on kernel >= 4.4.0 unless this arg is greater than zero
s.listen(1)
sockname = s.getsockname()
# these three are necessary just to get the port into a TIME_WAIT state
with contextlib.closing(socket()) as s2:
s2.connect(sockname)
sock, _ = s.accept()
with contextlib.closing(sock):
return sockname[1]
def ensure_zig_path():
if "ZIGPATH" not in os.environ:
# If ZIGPATH is not set, set it to $pwd/.zig
# In Docker environment this should by default be set to /opt/zig
@ -23,41 +72,17 @@ def ensureZigPath():
print(f'ZIGPATH set to {os.environ["ZIGPATH"]}')
def makeBinaries():
try:
subprocess.check_call(["make", "all"], cwd="./gdb-tests/tests/binaries")
except subprocess.CalledProcessError:
exit(1)
def make_binaries(test_dir: str):
dir_binaries = Path(test_dir) / "binaries"
if not dir_binaries.exists():
return
def makeCrossArchBinaries():
try:
subprocess.check_call(["make", "all"], cwd="./qemu-tests/tests/user/binaries")
subprocess.check_call(["make", "all"], cwd=str(dir_binaries))
except subprocess.CalledProcessError:
exit(1)
def open_ports(n: int) -> List[int]:
"""
Returns a list of `n` open ports
"""
try:
result = subprocess.run(
["netstat", "-tuln"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
if result.returncode != 0:
# If netstat not found, try ss
raise FileNotFoundError
except FileNotFoundError:
result = subprocess.run(["ss", "-tuln"], stdout=subprocess.PIPE)
used_ports = set(re.findall(r":(\d+)", result.stdout.decode()))
used_ports = set(map(int, used_ports))
available_ports = [port for port in range(1024, 65536) if port not in used_ports]
return random.sample(available_ports, n)
def run_gdb(
gdb_binary: str, gdb_args: List[str], env=None, capture_output=True
) -> CompletedProcess[str]:
@ -70,7 +95,7 @@ def run_gdb(
)
def getTestsList(
def get_tests_list(
collect_only: bool,
test_name_filter: str,
gdb_binary: str,
@ -174,12 +199,9 @@ def run_tests_and_print_stats(
test_results: List[Tuple[CompletedProcess[str], str]] = []
stats = TestStats()
port_iterator = iter(ports)
if args.serial:
test_results = [
run_test(test, args, gdb_binary, gdbinit_path, next(port_iterator, None))
for test in tests_list
run_test(test, args, gdb_binary, gdbinit_path, reserve_port()) for test in tests_list
]
else:
print("")
@ -187,7 +209,7 @@ def run_tests_and_print_stats(
with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
for test in tests_list:
executor.submit(
run_test, test, args, gdb_binary, gdbinit_path, next(port_iterator, None)
run_test, test, args, gdb_binary, gdbinit_path, reserve_port()
).add_done_callback(
lambda future: stats.handle_test_result(future.result(), args, test_dir_path)
)
@ -253,9 +275,13 @@ def parse_args():
return parser.parse_args()
TEST_FOLDER_NAME = {"gdb": "gdb-tests/tests", "cross-arch": "qemu-tests/tests/user"}
TEST_FOLDER_NAME = {
"gdb": "gdb-tests/tests",
"cross-arch": "qemu-tests/tests/user",
}
if __name__ == "__main__":
def main():
args = parse_args()
if args.cov:
print("Will run codecov")
@ -271,18 +297,23 @@ if __name__ == "__main__":
else:
gdbinit_path = os.path.join(root_dir, "gdbinit.py")
gdb_binary = "gdb"
test_dir_path = TEST_FOLDER_NAME[args.type]
if args.type == "gdb":
ensureZigPath()
makeBinaries()
else:
makeCrossArchBinaries()
gdb_binary = "gdb"
ensure_zig_path()
make_binaries(test_dir_path)
elif args.type == "cross-arch":
gdb_binary = "gdb-multiarch"
make_binaries(test_dir_path)
else:
raise NotImplementedError(args.type)
test_dir_path = TEST_FOLDER_NAME[args.type]
tests_list = getTestsList(
tests_list = get_tests_list(
args.collect_only, args.test_name_filter, gdb_binary, gdbinit_path, test_dir_path
)
ports = open_ports(len(tests_list))
run_tests_and_print_stats(tests_list, args, gdb_binary, gdbinit_path, test_dir_path, ports)
run_tests_and_print_stats(tests_list, args, gdb_binary, gdbinit_path, test_dir_path)
if __name__ == "__main__":
main()

Loading…
Cancel
Save