deffind(address):ifaddressisNone:returnNoneaddress=int(address)forpageinget():ifaddressinpage:returnpagereturnexplore(address)defget():ifnotpwndbg.proc.alive:returntuple()pages=[]pages.extend(proc_pid_maps())ifnotpagesandpwndbg.arch.currentin('i386','x86-64')andpwndbg.qemu.is_qemu():pages.extend(monitor_info_mem())ifnotpages:# If debugee is launched from a symlink the debugee memory maps will be# labeled with symlink path while in normal scenario the /proc/pid/maps# labels debugee memory maps with real path (after symlinks).# This is because the exe path in AUXV (and so `info auxv`) is before# following links.pages.extend(info_auxv())ifpages:pages.extend(info_sharedlibrary())else:pages.extend(info_files())pages.extend(pwndbg.stack.stacks.values())pages.extend(explored_pages)pages.extend(custom_pages)pages.sort()returntuple(pages)
在绝大多数情况下,程序直接从 proc_pid_maps 中获取全部的内存布局,不会执行后续的 if not pages 中的逻辑,而如果程序是在 qemu-user 下模拟的,那么插件也无法从 monitor_info_mem 中获取信息,那么就会调用 info_auxv(),并使用 AUXV(Auxiliary Vector) 的数据信息,来检测程序的内存布局,AUXV 是用来在动态链接器初始工作时没有完善的运行环境时,提供给动态链接器工作的一些提示性的信息,在 GDB 中输入 auxv 可以进行查看。
@pwndbg.memoize.reset_on_exitdefinfo_auxv(skip_exe=False):"""
Extracts the name of the executable from the output of the command
"info auxv". Note that if the executable path is a symlink,
it is not dereferenced by `info auxv` and we also don't dereference it.
Arguments:
skip_exe(bool): Do not return any mappings that belong to the exe.
Returns:
A list of pwndbg.memory.Page objects.
"""auxv=pwndbg.auxv.get()ifnotauxv:returntuple()pages=[]exe_name=auxv.AT_EXECFNor'main.exe'entry=auxv.AT_ENTRYbase=auxv.AT_BASEvdso=auxv.AT_SYSINFO_EHDRorauxv.AT_SYSINFOphdr=auxv.AT_PHDRifnotskip_exeand(entryorphdr):pages.extend(pwndbg.elf.map(entryorphdr,exe_name))ifbase:pages.extend(pwndbg.elf.map(base,'[linker]'))ifvdso:pages.extend(pwndbg.elf.map(vdso,'[vdso]'))returntuple(sorted(pages))
defmap(pointer,objfile=''):"""
Given a pointer into an ELF module, return a list of all loaded
sections in the ELF.
Returns:
A sorted list of pwndbg.memory.Page objects
Example:
>>> pwndbg.elf.load(pwndbg.regs.pc)
[Page('400000-4ef000 r-xp 0'),
Page('6ef000-6f0000 r--p ef000'),
Page('6f0000-6ff000 rw-p f0000')]
>>> pwndbg.elf.load(0x7ffff77a2000)
[Page('7ffff75e7000-7ffff77a2000 r-xp 0x1bb000 0'),
Page('7ffff77a2000-7ffff79a2000 ---p 0x200000 1bb000'),
Page('7ffff79a2000-7ffff79a6000 r--p 0x4000 1bb000'),
Page('7ffff79a6000-7ffff79ad000 rw-p 0x7000 1bf000')]
"""ei_class,ehdr=get_ehdr(pointer)returnmap_inner(ei_class,ehdr,objfile)
For the 3rd case, the problem is related to the QEMU user emulation’s gdbstub being underdeveloped. It lacks a few features related to getting files from the debugged target. I have been working some time ago on a patch to fix that in QEMU, which you can find here: https://lore.kernel.org/all/[email protected]/ and while it works, it need a few improvements and tests. I hope to find some time in the soonish future to work on that and finish that.
In theory, for 3rd we could parse the QEMU’s process memory maps and filter them by ourselves. However, this would only work if the QEMU process is run locally and would not work if it hosts its gdbstub from a remote server.
deffind_elf_magic(pointer,max_pages=1024,search_down=False,ret_addr_anyway=False):"""Search the nearest page which contains the ELF headers
by comparing the ELF magic with first 4 bytes.
Parameter:
search_down: change the search direction
to search over the lower address.
That is, decreasing the page pointer instead of increasing.
(default: False)
Returns:
An integer address of ELF page base
None if not found within the page limit
"""addr=pwndbg.lib.memory.page_align(pointer)step=pwndbg.lib.memory.PAGE_SIZEifsearch_down:step=-stepmax_addr=pwndbg.gdblib.arch.ptrmaskforiinrange(max_pages):# Make sure address within valid range or gdb will raise Overflow exceptionifaddr<0oraddr>max_addr:returnNonetry:data=pwndbg.gdblib.memory.read(addr,4)exceptgdb.MemoryError:returnaddrifret_addr_anywayelseNone# Return the address if found ELF headerifdata==b'\x7FELF':returnaddraddr+=stepreturnaddrifret_addr_anywayelseNonedefget_ehdr(pointer):"""Returns an ehdr object for the ELF pointer points into.
"""# Align down to a page boundary, and scan until we find# the ELF header.base=pwndbg.lib.memory.page_align(pointer)# For non linux ABI, the ELF header may not be found in memory.# This will hang the gdb when using the remote gdbserver to scan 1024 pagesbase=find_elf_magic(pointer,search_down=True)ifbaseisNone:ifpwndbg.abi.linux:print("ERROR: Could not find ELF base!")returnNone,None# Determine whether it's 32- or 64-bitei_class=pwndbg.gdblib.memory.byte(base+4)# Find out where the section headers startElfhdr=read(Ehdr,base)returnei_class,Elfhdr
替换 pwndbg\gdblib\vmmap.py 中 get 函数的以下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# TODO/FIXME: Do we still need it after coredump_maps()?# Add tests for other cases and see if this is needed e.g. for QEMU user# if not, remove the code below & cleanup other parts of Pwndbg codebaseifnotpages:# If debuggee is launched from a symlink the debuggee memory maps will be# labeled with symlink path while in normal scenario the /proc/pid/maps# labels debuggee memory maps with real path (after symlinks).# This is because the exe path in AUXV (and so `info auxv`) is before# following links.pages.extend(info_auxv())ifpages:pages.extend(info_sharedlibrary())else:ifpwndbg.gdblib.qemu.is_qemu():return(pwndbg.lib.memory.Page(0,pwndbg.gdblib.arch.ptrmask,7,0,"[qemu]"),)pages.extend(info_files())pages.extend(pwndbg.gdblib.stack.stacks.values())
变为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# TODO/FIXME: Do we still need it after coredump_maps()?# Add tests for other cases and see if this is needed e.g. for QEMU user# if not, remove the code below & cleanup other parts of Pwndbg codebaseifnotpages:# If debuggee is launched from a symlink the debuggee memory maps will be# labeled with symlink path while in normal scenario the /proc/pid/maps# labels debuggee memory maps with real path (after symlinks).# This is because the exe path in AUXV (and so `info auxv`) is before# following links.pages.extend(info_auxv())ifpages:pages.extend(info_sharedlibrary())else:#if pwndbg.gdblib.qemu.is_qemu():# return (pwndbg.lib.memory.Page(0, pwndbg.gdblib.arch.ptrmask, 7, 0, "[qemu]"),)pages.extend(info_files())pages.extend(pwndbg.gdblib.stack.stacks.values())