add strings.yield_in_page, refactor strings command (#2843)

* add strings.yield_in_page, refactor strings command

This commit adds a `pwndbg.aglib.strings.yield_in_page` function that yields all strings in a given memory page.

It also refactors the `pwndbg.commands.strings` command to use this feature.

* fixes

* fixes

* fix
pull/2829/head
Disconnect3d 8 months ago committed by GitHub
parent 00928004c4
commit 261fac8543
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -5,10 +5,13 @@ the debuggee's address space.
from __future__ import annotations from __future__ import annotations
import re
import string import string
from typing import Iterator
import pwndbg import pwndbg
import pwndbg.aglib.memory import pwndbg.aglib.memory
from pwndbg.lib.memory import Page
length = 15 length = 15
@ -51,3 +54,16 @@ def get(address: int, maxlen: int | None = None, maxread: int | None = None) ->
return sz return sz
return sz[:maxlen] + "..." return sz[:maxlen] + "..."
def yield_in_page(page: Page, n=4) -> Iterator[str]:
"""Yields strings of length >= n found in a given vmmap page"""
try:
data = pwndbg.aglib.memory.read(addr=page.vaddr, count=page.memsz, partial=True)
except pwndbg.dbg_mod.Error:
# E.g. we cannot read [vvar] page even though it has a READ permission
return
for match in re.finditer(rb"[ -~]{%d,}" % n, data):
decoded_str = match.group().decode("ascii", errors="ignore")
yield decoded_str

@ -1,14 +1,12 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import re
from typing import List from typing import List
import pwndbg import pwndbg
import pwndbg.aglib.memory import pwndbg.aglib.memory
import pwndbg.commands import pwndbg.commands
from pwndbg.commands import CommandCategory from pwndbg.commands import CommandCategory
from pwndbg.lib.memory import Page
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Extracts and displays ASCII strings from readable memory pages of the debugged process." description="Extracts and displays ASCII strings from readable memory pages of the debugged process."
@ -33,30 +31,18 @@ parser.add_argument(
@pwndbg.commands.ArgparsedCommand(parser, category=CommandCategory.LINUX) @pwndbg.commands.ArgparsedCommand(parser, category=CommandCategory.LINUX)
@pwndbg.commands.OnlyWhenRunning @pwndbg.commands.OnlyWhenRunning
def strings(n: int = 4, page_names: List[str] = [], save_as: str = None): def strings(n: int = 4, page_names: List[str] = [], save_as: str = None):
# Extract pages with PROT_READ permission # Get only readable pages and those that match the page_names filter
readable_pages: List[Page] = [page for page in pwndbg.aglib.vmmap.get() if page.read] pages = (
p
for p in pwndbg.aglib.vmmap.get()
if p.read and ((not page_names) or any(name in p.objfile for name in page_names))
)
for page in readable_pages: f = open(save_as, "w") if save_as else None
if page_names and not any(name in page.objfile for name in page_names):
continue # skip if page does not belong to any of the specified mappings
count = page.memsz for page in pages:
start_address = page.vaddr for string in pwndbg.aglib.strings.yield_in_page(page, n):
print(string, file=f)
try: if f:
data = pwndbg.aglib.memory.read(addr=start_address, count=count) f.close()
except pwndbg.dbg_mod.Error as e:
print(f"Skipping inaccessible page at {start_address:#x}: {e}")
continue # skip if access is denied
# all strings in the `data`
strings: List[bytes] = re.findall(rb"[ -~]{%d,}" % n, data)
decoded_strings: List[str] = [s.decode("ascii", errors="ignore") for s in strings]
if not save_as:
for string in decoded_strings:
print(string)
continue
with open(save_as, "w") as f:
f.writelines(string + "\n" for string in decoded_strings)

Loading…
Cancel
Save