Port to aglib: onegadget (#2564)

* Port to aglib: onegadget

* onegadget: fix error handling
pull/2508/head^2
patryk4815 1 year ago committed by GitHub
parent 0076f108ab
commit 921ba71cf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -23,6 +23,7 @@ def load_aglib():
import pwndbg.aglib.memory import pwndbg.aglib.memory
import pwndbg.aglib.nearpc import pwndbg.aglib.nearpc
import pwndbg.aglib.next import pwndbg.aglib.next
import pwndbg.aglib.onegadget
import pwndbg.aglib.proc import pwndbg.aglib.proc
import pwndbg.aglib.qemu import pwndbg.aglib.qemu
import pwndbg.aglib.regs as regs_mod import pwndbg.aglib.regs as regs_mod

@ -10,14 +10,14 @@ from typing import Any
from typing import Dict from typing import Dict
from typing import Tuple from typing import Tuple
import gdb
from tabulate import tabulate from tabulate import tabulate
import pwndbg.aglib.arch import pwndbg.aglib.arch
import pwndbg.aglib.file
import pwndbg.aglib.memory
import pwndbg.aglib.vmmap
import pwndbg.color.message as M import pwndbg.color.message as M
import pwndbg.gdblib.file import pwndbg.dbg
import pwndbg.gdblib.memory
import pwndbg.gdblib.vmmap
import pwndbg.glibc import pwndbg.glibc
import pwndbg.lib.cache import pwndbg.lib.cache
import pwndbg.lib.tempfile import pwndbg.lib.tempfile
@ -36,12 +36,12 @@ CAST_PATTERN = re.compile(r"^\([s|u]\d+\)")
XMM_SHIFT = " >> " XMM_SHIFT = " >> "
CONSTRAINT_SEPARATOR = " || " CONSTRAINT_SEPARATOR = " || "
CAST_DEREF_MAPPING = { CAST_DEREF_MAPPING = {
"(u16)": pwndbg.gdblib.memory.u16, "(u16)": pwndbg.aglib.memory.u16,
"(s16)": pwndbg.gdblib.memory.s16, "(s16)": pwndbg.aglib.memory.s16,
"(u32)": pwndbg.gdblib.memory.u32, "(u32)": pwndbg.aglib.memory.u32,
"(s32)": pwndbg.gdblib.memory.s32, "(s32)": pwndbg.aglib.memory.s32,
"(u64)": pwndbg.gdblib.memory.u64, "(u64)": pwndbg.aglib.memory.u64,
"(s64)": pwndbg.gdblib.memory.s64, "(s64)": pwndbg.aglib.memory.s64,
} }
CAST_MAPPING = { CAST_MAPPING = {
"(u16)": lambda x: ctypes.c_uint16(x).value, "(u16)": lambda x: ctypes.c_uint16(x).value,
@ -128,7 +128,7 @@ class Lambda:
@property @property
def gdb_expr(self) -> str: 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 obj = self.obj
if isinstance(obj, str): if isinstance(obj, str):
if obj.startswith("xmm"): 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 # https://github.com/david942j/one_gadget/blob/65ce1dade70bf89e7496346ccf452ce5b2d139b3/lib/one_gadget/emulators/x86.rb#L242-L248
# So we can hardcode the shifting :p # So we can hardcode the shifting :p
# TODO: Handle xmm register in a better way # TODO: Handle xmm register in a better way
# TODO: In LLDB this syntax is invalid:
# error: <user expression 62>: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 bits = pwndbg.aglib.arch.ptrsize * 8
if XMM_SHIFT in obj: if XMM_SHIFT in obj:
obj = obj.replace(XMM_SHIFT + str(bits), f".v{128 // bits}_int{bits}[1]") 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 Run onegadget and return the output
""" """
libc_path = pwndbg.gdblib.file.get_file( libc_path = pwndbg.aglib.file.get_file(pwndbg.glibc.get_libc_filename_from_info_sharedlibrary())
pwndbg.glibc.get_libc_filename_from_info_sharedlibrary()
)
# We need cache because onegadget might be slow # We need cache because onegadget might be slow
cache_file = os.path.join(ONEGADGET_CACHEDIR, compute_file_hash(libc_path)) cache_file = os.path.join(ONEGADGET_CACHEDIR, compute_file_hash(libc_path))
if os.path.exists(cache_file): 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) lambda_expr = Lambda.parse(expr)
if not isinstance(lambda_expr, Lambda): if not isinstance(lambda_expr, Lambda):
return lambda_expr, expr, None return lambda_expr, expr, None
gdb_expr = lambda_expr.gdb_expr gdb_expr = lambda_expr.gdb_expr
try: try:
if cast: 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 # Remove the first *, we use cast to handle it instead of unsigned long
gdb_expr = gdb_expr.lstrip("*") gdb_expr = gdb_expr.lstrip("*")
# Now gdb_expr is a pointer, we need dereference it with cast # 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: 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: 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 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) 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: if n > 1:
return UNKNOWN, output_msg return UNKNOWN, output_msg
return SAT, 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: if page is None or not page.read:
output_msg += ( output_msg += (
f"argv[{n}] = {color_str} = {result:#x}, {color_str} is not a valid address\n" f"argv[{n}] = {color_str} = {result:#x}, {color_str} is not a valid address\n"
) )
return UNSAT, output_msg return UNSAT, output_msg
if n > 0: 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: else:
output_msg += ( output_msg += (
f"argv[{n}] = {color_str} = {result:#x}, {color_str} is a readable address\n" 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) argv, color_str, err = parse_expression(expr)
if err is not None: if err is not None:
# We don't have to print the error message here, it should be printed already # 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" output_msg += f"Assume argv = {color_str} = {argv:#x}, checking the content of argv\n"
n = 0 n = 0
while True: while True:
try: try:
argv_n = pwndbg.gdblib.memory.pvoid(argv + n * pwndbg.aglib.arch.ptrsize) argv_n = pwndbg.aglib.memory.pvoid(argv + n * pwndbg.aglib.arch.ptrsize)
except gdb.MemoryError: 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" 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 return UNSAT, output_msg
if argv_n == 0: 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 # {whatever_but_readable, NULL} is always a valid argv
output_msg += f"argv[{n}] is NULL, {color_str} is a valid argv\n" output_msg += f"argv[{n}] is NULL, {color_str} is a valid argv\n"
return SAT, output_msg 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: if page is None or not page.read:
output_msg += f"argv[{n}] = {argv_n:#x}, {argv_n:#x} is a invalid address\n" output_msg += f"argv[{n}] = {argv_n:#x}, {argv_n:#x} is a invalid address\n"
return UNSAT, output_msg 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 n += 1
@ -421,10 +437,11 @@ def check_envp(expr: str) -> Tuple[bool, str]:
if expr.startswith("{"): if expr.startswith("{"):
# Note: we don't have to handle this case for now, but might need to implement it in the future # Note: we don't have to handle this case for now, but might need to implement it in the future
return False, output_msg return False, output_msg
envp, color_str, err = parse_expression(expr) envp, color_str, err = parse_expression(expr)
if err is not None: if err is not None:
# we don't have to print the error message here, it should be printed already # 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" 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 n = 0
while True: while True:
try: try:
envp_n = pwndbg.gdblib.memory.pvoid(envp + n * pwndbg.aglib.arch.ptrsize) envp_n = pwndbg.aglib.memory.pvoid(envp + n * pwndbg.aglib.arch.ptrsize)
except gdb.MemoryError: 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" 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 return False, output_msg
if envp_n == 0: if envp_n == 0:
output_msg += f"envp[{n}] is NULL, {color_str} is a valid envp\n" output_msg += f"envp[{n}] is NULL, {color_str} is a valid envp\n"
return True, output_msg 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: if page is None or not page.read:
output_msg += f"envp[{n}] = {envp_n:#x}, {envp_n:#x} is a invalid address\n" output_msg += f"envp[{n}] = {envp_n:#x}, {envp_n:#x} is a invalid address\n"
return False, output_msg return False, output_msg
@ -481,7 +498,7 @@ def check_constraint(constraint: str) -> Tuple[CheckSatResult, str]:
for expr in exprs.split(", "): for expr in exprs.split(", "):
result, color_str, err = parse_expression(expr) result, color_str, err = parse_expression(expr)
if err is None: if err is None:
page = pwndbg.gdblib.vmmap.find(result) page = pwndbg.aglib.vmmap.find(result)
passed = page is not None and page.write passed = page is not None and page.write
output_msg += f"{color_str} = {result:#x}, {color_str} is {'' if passed else 'not '}writable\n" output_msg += f"{color_str} = {result:#x}, {color_str} is {'' if passed else 'not '}writable\n"
else: else:
@ -493,7 +510,7 @@ def check_constraint(constraint: str) -> Tuple[CheckSatResult, str]:
expr = WRITABLE_COLON_PATTERN.match(constraint).group(1) expr = WRITABLE_COLON_PATTERN.match(constraint).group(1)
result, color_str, err = parse_expression(expr) result, color_str, err = parse_expression(expr)
if err is None: if err is None:
page = pwndbg.gdblib.vmmap.find(result) page = pwndbg.aglib.vmmap.find(result)
passed = page is not None and page.write passed = page is not None and page.write
output_msg += ( output_msg += (
f"{color_str} = {result:#x}, {color_str} is {'' if passed else 'not '}writable\n" 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): elif IS_GOT_ADDRESS_PATTERN.match(constraint):
expr = IS_GOT_ADDRESS_PATTERN.match(constraint).group(1) expr = IS_GOT_ADDRESS_PATTERN.match(constraint).group(1)
result, color_str, err = parse_expression(expr) result, color_str, err = parse_expression(expr)
got_plt_address = pwndbg.glibc.get_section_address_by_name(".got.plt") if err is None:
passed = result == got_plt_address got_plt_address = pwndbg.glibc.get_section_address_by_name(".got.plt")
output_msg += f"{color_str} = {result:#x}, {color_str} is {'' if passed else 'not '}the GOT address ({got_plt_address:#x}) of libc\n" 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: else:
raise ValueError(f"Unsupported constraint: {constraint}") raise ValueError(f"Unsupported constraint: {constraint}")

@ -706,7 +706,6 @@ def load_commands() -> None:
import pwndbg.commands.killthreads import pwndbg.commands.killthreads
import pwndbg.commands.klookup import pwndbg.commands.klookup
import pwndbg.commands.kversion import pwndbg.commands.kversion
import pwndbg.commands.onegadget
import pwndbg.commands.pcplist import pwndbg.commands.pcplist
import pwndbg.commands.peda import pwndbg.commands.peda
import pwndbg.commands.reload import pwndbg.commands.reload
@ -749,6 +748,7 @@ def load_commands() -> None:
import pwndbg.commands.mprotect import pwndbg.commands.mprotect
import pwndbg.commands.nearpc import pwndbg.commands.nearpc
import pwndbg.commands.next import pwndbg.commands.next
import pwndbg.commands.onegadget
import pwndbg.commands.p2p import pwndbg.commands.p2p
import pwndbg.commands.patch import pwndbg.commands.patch
import pwndbg.commands.pie import pwndbg.commands.pie

@ -3,9 +3,9 @@ from __future__ import annotations
import argparse import argparse
import shutil import shutil
import pwndbg.aglib.onegadget
import pwndbg.color.message as M import pwndbg.color.message as M
import pwndbg.commands import pwndbg.commands
import pwndbg.gdblib.onegadget
import pwndbg.glibc import pwndbg.glibc
from pwndbg.commands import CommandCategory 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(f"Using libc: {M.hint(path)}")
print() 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(): for result, count in gadgets_count.items():
print(f"Found {M.hint(count)} {result} gadgets.") 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( print(
M.warn( M.warn(
"No valid gadgets found, you might want to run with --show-unsat again to check unsatisfiable gadgets.\n" "No valid gadgets found, you might want to run with --show-unsat again to check unsatisfiable gadgets.\n"

@ -32,7 +32,6 @@ def load_gdblib() -> None:
import pwndbg.gdblib.hooks import pwndbg.gdblib.hooks
import pwndbg.gdblib.kernel import pwndbg.gdblib.kernel
import pwndbg.gdblib.memory import pwndbg.gdblib.memory
import pwndbg.gdblib.onegadget
import pwndbg.gdblib.prompt import pwndbg.gdblib.prompt
import pwndbg.gdblib.regs as regs_mod import pwndbg.gdblib.regs as regs_mod
import pwndbg.gdblib.symbol import pwndbg.gdblib.symbol

Loading…
Cancel
Save