Created
December 12, 2024 23:53
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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