Skip to content

Instantly share code, notes, and snippets.

@LizzyFleckenstein03
Created December 12, 2024 23:53
Show Gist options
  • Save LizzyFleckenstein03/a521edd2236076890d7bd64bac1f508a to your computer and use it in GitHub Desktop.
Save LizzyFleckenstein03/a521edd2236076890d7bd64bac1f508a to your computer and use it in GitHub Desktop.
gdb stack scanner. search stack for return addresses even when a program doesn't use base pointers
import re
import gdb
def get_executable_sections(pid):
return [
(int(mat.group(1), 16), int(mat.group(2), 16)) for mat in [
re.search("^([0-9a-f]*)-([0-9a-f]*) ..x", l)
for l in open(f"/proc/{pid}/maps", "r").read().splitlines()
] if mat != None
]
def is_executable(addr, sects):
for s in sects:
if addr >= s[0] and addr < s[1]:
return True
return False
def decode_uint(arr, size):
n = 0
for i in range(size):
n = (n << 8) | ord(arr[size-1-i])
return (n, arr[size:])
def scan_stack(depth):
inf = gdb.selected_inferior()
(arch_sp, arch_wsize) = ({
"i386": ("esp", 4),
"i386:x86-64": ("rsp", 8),
})[inf.architecture().name()]
sects = get_executable_sections(inf.pid)
sp = gdb.selected_frame().read_register(arch_sp)
mem = inf.read_memory(sp, depth * arch_wsize)
results = []
for i in range(depth):
(addr, mem) = decode_uint(mem, arch_wsize)
if is_executable(addr, sects):
results.append(addr)
return results
# this is a hack
def disas_instr_before(addr):
insts = gdb.execute(f"disas {addr}-0x20,+0x40", to_string=True).splitlines()
last = None
for inst in insts:
mat = re.search("^ 0x([0-9a-f]*)[ \t]*(<[^>]*>)?:[ \t]*(.*)$", inst)
if not mat:
continue
if int(mat.group(1), 16) == addr:
return last
last = (mat.group(2), mat.group(3))
raise Exception("addr not found in disassembly")
def print_table(table):
dim = len(table[0])
sizes = [0] * dim
for row in table:
sizes = [max(len(row[i]), sizes[i]) for i in range(dim)]
for row in table:
print(" ".join([row[i].ljust(sizes[i]) for i in range(dim)]))
class StackScanCmd(gdb.Command):
def __init__(self):
super(StackScanCmd, self).__init__("stscan", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
depth = 1024
try:
depth = int(arg)
except:
pass
prog = gdb.current_progspace()
results = scan_stack(depth)
table = []
for addr in results:
sym = None
disas = None
try:
(sym, disas) = disas_instr_before(addr)
except:
pass
obj = prog.objfile_for_address(addr)
table.append([
f"0x{addr:x}",
f"from {obj.filename}" if obj else "",
sym or "",
disas or "",
])
print_table(table)
StackScanCmd()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment