From 0e39240186daeff235f13343770c8aa22cac870c Mon Sep 17 00:00:00 2001 From: k4lizen <124312252+k4lizen@users.noreply.github.com> Date: Thu, 24 Jul 2025 23:04:57 +0200 Subject: [PATCH] Make ida integration work in shared libraries (#3172) * Make ida integration work in shared libraries * lint * edge case happens too often * make the check faster (and change the contract abit) --- pwndbg/aglib/vmmap.py | 41 +++++++++++++++++++++++++++++++++++++++ pwndbg/commands/xinfo.py | 14 +++++++------ pwndbg/integration/ida.py | 16 +++++++-------- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/pwndbg/aglib/vmmap.py b/pwndbg/aglib/vmmap.py index e607ccac1..de4ac7267 100644 --- a/pwndbg/aglib/vmmap.py +++ b/pwndbg/aglib/vmmap.py @@ -41,3 +41,44 @@ def find(address: int | pwndbg.dbg_mod.Value | None) -> pwndbg.lib.memory.Page | return page return pwndbg.aglib.vmmap_custom.explore(address) + + +def addr_region_start(address: int | pwndbg.dbg_mod.Value) -> int | None: + """ + Let's define a "region" as contiguous memory compromised of memory mappings + which all have the same object file name. Also referred to as "File (Base)" by + `xinfo`. + + Returns: + The start of the memory region this address belongs to, or None if the address + is not mapped. + """ + address = int(address) + if address < 0: + return None + + mappings = sorted(pwndbg.aglib.vmmap.get(), key=lambda p: p.vaddr) + idx = -1 + for i in range(len(mappings)): + if mappings[i].start <= address < mappings[i].end: + idx = i + break + + if idx == -1: + # Maybe we can find the page by exploring. + explored_page = pwndbg.aglib.vmmap_custom.explore(address) + if not explored_page: + return None + + # We know vmmap_custom.explore() can only find one page, it does + # not cascade a whole region so there is no need to look backwards. + return explored_page.start + + # Look backwards from i to find all the mappings with the same name. + objname = mappings[i].objfile + while i > 0 and objname == mappings[i - 1].objfile: + i -= 1 + + # There might be other mappings with the name "objname" in the address space + # but they are not contiguous with us, so we don't care. + return mappings[i].start diff --git a/pwndbg/commands/xinfo.py b/pwndbg/commands/xinfo.py index 92803aaab..034370199 100644 --- a/pwndbg/commands/xinfo.py +++ b/pwndbg/commands/xinfo.py @@ -61,17 +61,19 @@ def xinfo_mmap_file(page: Page, addr: int) -> None: file_name = page.objfile - objpages = filter(lambda p: p.objfile == file_name, pwndbg.aglib.vmmap.get()) - first = sorted(objpages, key=lambda p: p.vaddr)[0] + region_start = pwndbg.aglib.vmmap.addr_region_start(addr) + if region_start is None: + print("The file is not contiguous in memory.") + return # print offset from ELF base load address - rva = addr - first.vaddr - print_line("File (Base)", addr, first.vaddr, rva, "+") + rva = addr - region_start + print_line("File (Base)", addr, region_start, rva, "+") # find possible LOAD segments that designate memory and file backings containing_loads = [ seg - for seg in pwndbg.aglib.elf.get_containing_segments(file_name, first.vaddr, addr) + for seg in pwndbg.aglib.elf.get_containing_segments(file_name, region_start, addr) if seg["p_type"] == "PT_LOAD" ] @@ -89,7 +91,7 @@ def xinfo_mmap_file(page: Page, addr: int) -> None: else: print(f"{'File (Disk)'.rjust(20)} {M.get(addr)} = [not file backed]") - containing_sections = pwndbg.aglib.elf.get_containing_sections(file_name, first.vaddr, addr) + containing_sections = pwndbg.aglib.elf.get_containing_sections(file_name, region_start, addr) if len(containing_sections) > 0: print("\n Containing ELF sections:") for sec in containing_sections: diff --git a/pwndbg/integration/ida.py b/pwndbg/integration/ida.py index e7599333e..6e767c4d8 100644 --- a/pwndbg/integration/ida.py +++ b/pwndbg/integration/ida.py @@ -180,18 +180,18 @@ def can_connect() -> bool: def l2r(addr: int) -> int: - exe = pwndbg.aglib.elf.exe() - if not exe: - raise Exception("Can't find EXE base") - result = (addr - int(exe.address) + base()) & pwndbg.aglib.arch.ptrmask + region_start = pwndbg.aglib.vmmap.addr_region_start(addr) + if region_start is None: + return 0 + result = (addr - region_start + base()) & pwndbg.aglib.arch.ptrmask return result def r2l(addr: int) -> int: - exe = pwndbg.aglib.elf.exe() - if not exe: - raise Exception("Can't find EXE base") - result = (addr - base() + int(exe.address)) & pwndbg.aglib.arch.ptrmask + region_start = pwndbg.aglib.vmmap.addr_region_start(addr) + if region_start is None: + return 0 + result = (addr - base() + region_start) & pwndbg.aglib.arch.ptrmask return result