diff --git a/pwndbg/aglib/__init__.py b/pwndbg/aglib/__init__.py index e0678cb89..c24ed318e 100644 --- a/pwndbg/aglib/__init__.py +++ b/pwndbg/aglib/__init__.py @@ -23,6 +23,7 @@ def load_aglib(): import pwndbg.aglib.memory import pwndbg.aglib.nearpc import pwndbg.aglib.next + import pwndbg.aglib.onegadget import pwndbg.aglib.proc import pwndbg.aglib.qemu import pwndbg.aglib.regs as regs_mod diff --git a/pwndbg/gdblib/onegadget.py b/pwndbg/aglib/onegadget.py similarity index 88% rename from pwndbg/gdblib/onegadget.py rename to pwndbg/aglib/onegadget.py index dd3872bda..882e8d69d 100644 --- a/pwndbg/gdblib/onegadget.py +++ b/pwndbg/aglib/onegadget.py @@ -10,14 +10,14 @@ from typing import Any from typing import Dict from typing import Tuple -import gdb from tabulate import tabulate import pwndbg.aglib.arch +import pwndbg.aglib.file +import pwndbg.aglib.memory +import pwndbg.aglib.vmmap import pwndbg.color.message as M -import pwndbg.gdblib.file -import pwndbg.gdblib.memory -import pwndbg.gdblib.vmmap +import pwndbg.dbg import pwndbg.glibc import pwndbg.lib.cache import pwndbg.lib.tempfile @@ -36,12 +36,12 @@ CAST_PATTERN = re.compile(r"^\([s|u]\d+\)") XMM_SHIFT = " >> " CONSTRAINT_SEPARATOR = " || " CAST_DEREF_MAPPING = { - "(u16)": pwndbg.gdblib.memory.u16, - "(s16)": pwndbg.gdblib.memory.s16, - "(u32)": pwndbg.gdblib.memory.u32, - "(s32)": pwndbg.gdblib.memory.s32, - "(u64)": pwndbg.gdblib.memory.u64, - "(s64)": pwndbg.gdblib.memory.s64, + "(u16)": pwndbg.aglib.memory.u16, + "(s16)": pwndbg.aglib.memory.s16, + "(u32)": pwndbg.aglib.memory.u32, + "(s32)": pwndbg.aglib.memory.s32, + "(u64)": pwndbg.aglib.memory.u64, + "(s64)": pwndbg.aglib.memory.s64, } CAST_MAPPING = { "(u16)": lambda x: ctypes.c_uint16(x).value, @@ -128,7 +128,7 @@ class Lambda: @property def gdb_expr(self) -> str: - # TODO: Don't use gdb.parse_and_eval here, directly fetching the value with `pwndbg.gdblib.memory` would be better(?) + # TODO: Don't use gdb.parse_and_eval here, directly fetching the value with `pwndbg.aglib.memory` would be better(?) obj = self.obj if isinstance(obj, str): if obj.startswith("xmm"): @@ -136,6 +136,13 @@ class Lambda: # https://github.com/david942j/one_gadget/blob/65ce1dade70bf89e7496346ccf452ce5b2d139b3/lib/one_gadget/emulators/x86.rb#L242-L248 # So we can hardcode the shifting :p # TODO: Handle xmm register in a better way + + # TODO: In LLDB this syntax is invalid: + # error: :1:23: illegal vector component name 'v' + # 1 | ((unsigned long)($xmm0.v2_int64[1])) + # | ^~~~~~~~~ + # while parsing (u64)xmm0 >> 64 for argv[1] + bits = pwndbg.aglib.arch.ptrsize * 8 if XMM_SHIFT in obj: obj = obj.replace(XMM_SHIFT + str(bits), f".v{128 // bits}_int{bits}[1]") @@ -269,9 +276,7 @@ def run_onegadget() -> str: """ Run onegadget and return the output """ - libc_path = pwndbg.gdblib.file.get_file( - pwndbg.glibc.get_libc_filename_from_info_sharedlibrary() - ) + libc_path = pwndbg.aglib.file.get_file(pwndbg.glibc.get_libc_filename_from_info_sharedlibrary()) # We need cache because onegadget might be slow cache_file = os.path.join(ONEGADGET_CACHEDIR, compute_file_hash(libc_path)) if os.path.exists(cache_file): @@ -300,6 +305,7 @@ def parse_expression(expr: str) -> Tuple[int | None, str, str | None]: lambda_expr = Lambda.parse(expr) if not isinstance(lambda_expr, Lambda): return lambda_expr, expr, None + gdb_expr = lambda_expr.gdb_expr try: if cast: @@ -307,13 +313,23 @@ def parse_expression(expr: str) -> Tuple[int | None, str, str | None]: # Remove the first *, we use cast to handle it instead of unsigned long gdb_expr = gdb_expr.lstrip("*") # Now gdb_expr is a pointer, we need dereference it with cast - result = CAST_DEREF_MAPPING[cast](int(gdb.parse_and_eval(gdb_expr))) + addr = int(pwndbg.dbg.selected_inferior().evaluate_expression(gdb_expr)) + result = CAST_DEREF_MAPPING[cast](addr) else: - result = CAST_MAPPING[cast](int(gdb.parse_and_eval(gdb_expr))) + addr = int(pwndbg.dbg.selected_inferior().evaluate_expression(gdb_expr)) + result = CAST_MAPPING[cast](addr) else: - result = int(gdb.parse_and_eval(gdb_expr)) + addr = int(pwndbg.dbg.selected_inferior().evaluate_expression(gdb_expr)) + result = addr + return result, f"{cast}{lambda_expr.color_str}", None - except gdb.error as e: + except pwndbg.dbg_mod.Error as e: + error_message = ( + f"Pwndbg encountered an issue while evaluating the expression: {cast}{lambda_expr.color_str}\n" + f"Error details: {str(e)}\n" + f"Consider creating an issue in the pwndbg repository." + ) + print(M.warn(error_message)) return None, f"{cast}{lambda_expr.color_str}", str(e) @@ -352,14 +368,14 @@ def check_stack_argv(expr: str) -> Tuple[CheckSatResult, str]: if n > 1: return UNKNOWN, output_msg return SAT, output_msg - page = pwndbg.gdblib.vmmap.find(result) + page = pwndbg.aglib.vmmap.find(result) if page is None or not page.read: output_msg += ( f"argv[{n}] = {color_str} = {result:#x}, {color_str} is not a valid address\n" ) return UNSAT, output_msg if n > 0: - output_msg += f"argv[{n}] = {color_str} = {result:#x} -> {bytes(pwndbg.gdblib.memory.string(result))!r}\n" + output_msg += f"argv[{n}] = {color_str} = {result:#x} -> {bytes(pwndbg.aglib.memory.string(result))!r}\n" else: output_msg += ( f"argv[{n}] = {color_str} = {result:#x}, {color_str} is a readable address\n" @@ -377,15 +393,15 @@ def check_non_stack_argv(expr: str) -> Tuple[CheckSatResult, str]: argv, color_str, err = parse_expression(expr) if err is not None: # We don't have to print the error message here, it should be printed already - return UNSAT, output_msg + return UNSAT, f"{err} while parsing {color_str}\n" output_msg += f"Assume argv = {color_str} = {argv:#x}, checking the content of argv\n" n = 0 while True: try: - argv_n = pwndbg.gdblib.memory.pvoid(argv + n * pwndbg.aglib.arch.ptrsize) - except gdb.MemoryError: + argv_n = pwndbg.aglib.memory.pvoid(argv + n * pwndbg.aglib.arch.ptrsize) + except pwndbg.dbg_mod.Error: output_msg += f"&argv[{n}] = {argv + n * pwndbg.aglib.arch.ptrsize:#x}, {argv + n * pwndbg.aglib.arch.ptrsize:#x} is a invalid address\n" return UNSAT, output_msg if argv_n == 0: @@ -396,11 +412,11 @@ def check_non_stack_argv(expr: str) -> Tuple[CheckSatResult, str]: # {whatever_but_readable, NULL} is always a valid argv output_msg += f"argv[{n}] is NULL, {color_str} is a valid argv\n" return SAT, output_msg - page = pwndbg.gdblib.vmmap.find(argv_n) + page = pwndbg.aglib.vmmap.find(argv_n) if page is None or not page.read: output_msg += f"argv[{n}] = {argv_n:#x}, {argv_n:#x} is a invalid address\n" return UNSAT, output_msg - output_msg += f"argv[{n}] = {argv_n:#x} -> {bytes(pwndbg.gdblib.memory.string(argv_n))!r}\n" + output_msg += f"argv[{n}] = {argv_n:#x} -> {bytes(pwndbg.aglib.memory.string(argv_n))!r}\n" n += 1 @@ -421,10 +437,11 @@ def check_envp(expr: str) -> Tuple[bool, str]: if expr.startswith("{"): # Note: we don't have to handle this case for now, but might need to implement it in the future return False, output_msg + envp, color_str, err = parse_expression(expr) if err is not None: # we don't have to print the error message here, it should be printed already - return False, output_msg + return False, f"{err} while parsing {color_str}\n" output_msg += f"Assume envp = {color_str} = {envp:#x}, checking the content of envp\n" @@ -433,14 +450,14 @@ def check_envp(expr: str) -> Tuple[bool, str]: n = 0 while True: try: - envp_n = pwndbg.gdblib.memory.pvoid(envp + n * pwndbg.aglib.arch.ptrsize) - except gdb.MemoryError: + envp_n = pwndbg.aglib.memory.pvoid(envp + n * pwndbg.aglib.arch.ptrsize) + except pwndbg.dbg_mod.Error: output_msg += f"&envp[{n}] = {envp + n * pwndbg.aglib.arch.ptrsize:#x}, {envp + n * pwndbg.aglib.arch.ptrsize:#x} is a invalid address\n" return False, output_msg if envp_n == 0: output_msg += f"envp[{n}] is NULL, {color_str} is a valid envp\n" return True, output_msg - page = pwndbg.gdblib.vmmap.find(envp_n) + page = pwndbg.aglib.vmmap.find(envp_n) if page is None or not page.read: output_msg += f"envp[{n}] = {envp_n:#x}, {envp_n:#x} is a invalid address\n" return False, output_msg @@ -481,7 +498,7 @@ def check_constraint(constraint: str) -> Tuple[CheckSatResult, str]: for expr in exprs.split(", "): result, color_str, err = parse_expression(expr) if err is None: - page = pwndbg.gdblib.vmmap.find(result) + page = pwndbg.aglib.vmmap.find(result) passed = page is not None and page.write output_msg += f"{color_str} = {result:#x}, {color_str} is {'' if passed else 'not '}writable\n" else: @@ -493,7 +510,7 @@ def check_constraint(constraint: str) -> Tuple[CheckSatResult, str]: expr = WRITABLE_COLON_PATTERN.match(constraint).group(1) result, color_str, err = parse_expression(expr) if err is None: - page = pwndbg.gdblib.vmmap.find(result) + page = pwndbg.aglib.vmmap.find(result) passed = page is not None and page.write output_msg += ( f"{color_str} = {result:#x}, {color_str} is {'' if passed else 'not '}writable\n" @@ -530,9 +547,12 @@ def check_constraint(constraint: str) -> Tuple[CheckSatResult, str]: elif IS_GOT_ADDRESS_PATTERN.match(constraint): expr = IS_GOT_ADDRESS_PATTERN.match(constraint).group(1) result, color_str, err = parse_expression(expr) - got_plt_address = pwndbg.glibc.get_section_address_by_name(".got.plt") - passed = result == got_plt_address - output_msg += f"{color_str} = {result:#x}, {color_str} is {'' if passed else 'not '}the GOT address ({got_plt_address:#x}) of libc\n" + if err is None: + got_plt_address = pwndbg.glibc.get_section_address_by_name(".got.plt") + passed = result == got_plt_address + output_msg += f"{color_str} = {result:#x}, {color_str} is {'' if passed else 'not '}the GOT address ({got_plt_address:#x}) of libc\n" + else: + output_msg += f"{err} while parsing {color_str}\n" else: raise ValueError(f"Unsupported constraint: {constraint}") diff --git a/pwndbg/commands/__init__.py b/pwndbg/commands/__init__.py index ab41c7bc3..056ae1cd6 100644 --- a/pwndbg/commands/__init__.py +++ b/pwndbg/commands/__init__.py @@ -706,7 +706,6 @@ def load_commands() -> None: import pwndbg.commands.killthreads import pwndbg.commands.klookup import pwndbg.commands.kversion - import pwndbg.commands.onegadget import pwndbg.commands.pcplist import pwndbg.commands.peda import pwndbg.commands.reload @@ -749,6 +748,7 @@ def load_commands() -> None: import pwndbg.commands.mprotect import pwndbg.commands.nearpc import pwndbg.commands.next + import pwndbg.commands.onegadget import pwndbg.commands.p2p import pwndbg.commands.patch import pwndbg.commands.pie diff --git a/pwndbg/commands/onegadget.py b/pwndbg/commands/onegadget.py index 8e472cebe..0428df678 100644 --- a/pwndbg/commands/onegadget.py +++ b/pwndbg/commands/onegadget.py @@ -3,9 +3,9 @@ from __future__ import annotations import argparse import shutil +import pwndbg.aglib.onegadget import pwndbg.color.message as M import pwndbg.commands -import pwndbg.gdblib.onegadget import pwndbg.glibc from pwndbg.commands import CommandCategory @@ -38,10 +38,10 @@ def onegadget(show_unsat: bool = False, no_unknown: bool = False, verbose: bool print(f"Using libc: {M.hint(path)}") print() - gadgets_count = pwndbg.gdblib.onegadget.find_gadgets(show_unsat, no_unknown, verbose) + gadgets_count = pwndbg.aglib.onegadget.find_gadgets(show_unsat, no_unknown, verbose) for result, count in gadgets_count.items(): print(f"Found {M.hint(count)} {result} gadgets.") - if not gadgets_count[pwndbg.gdblib.onegadget.SAT] and not show_unsat: + if not gadgets_count[pwndbg.aglib.onegadget.SAT] and not show_unsat: print( M.warn( "No valid gadgets found, you might want to run with --show-unsat again to check unsatisfiable gadgets.\n" diff --git a/pwndbg/gdblib/__init__.py b/pwndbg/gdblib/__init__.py index 24922998a..392236a8b 100644 --- a/pwndbg/gdblib/__init__.py +++ b/pwndbg/gdblib/__init__.py @@ -32,7 +32,6 @@ def load_gdblib() -> None: import pwndbg.gdblib.hooks import pwndbg.gdblib.kernel import pwndbg.gdblib.memory - import pwndbg.gdblib.onegadget import pwndbg.gdblib.prompt import pwndbg.gdblib.regs as regs_mod import pwndbg.gdblib.symbol