Adding custom address markers (#3123)

* added new bitflags to aarch64

* added custom address markers

* optimization

* supporting buddydump on aarch64

* supporting access to paging level

* cleaned up marker handling

* cleaned up marker handling further

* fixed doc

* supporting access to ptr_siz

* improved aarch64 vmmap

* fixed stuff based on comments

* cleaned up

* refactored

* cleaned up

* ldt remap

* improved paging commands

* improved physmap handling for aarch64

* improved physmap handling for aarch64 when ptrace scope is enabled

* improved physmap handling for aarch64 when ptrace scope is enabled

* linting

* improved caching

* cleaned up

* cleaned up kernel-vmmap=monitor handling

* cleaned up kernel-vmmap=monitor handling

* cleaned up vmemmap size calc for aarch64
pull/3145/head
jxuanli 6 months ago committed by GitHub
parent e6dacb29df
commit 4ee3ce2e4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -581,17 +581,6 @@ The soft line width for go-dump pretty printing.
---------- ----------
## **guess-physmap**
Should guess physmap base address when debug symbols are not present.
**Default:** off
----------
## **hexdump-bytes** ## **hexdump-bytes**

@ -26,6 +26,8 @@ import pwndbg.lib.kernel.kconfig
import pwndbg.lib.kernel.structs import pwndbg.lib.kernel.structs
import pwndbg.lib.memory import pwndbg.lib.memory
import pwndbg.search import pwndbg.search
from pwndbg.aglib.kernel.paging import ArchPagingInfo
from pwndbg.lib.regs import BitFlags
_kconfig: pwndbg.lib.kernel.kconfig.Kconfig | None = None _kconfig: pwndbg.lib.kernel.kconfig.Kconfig | None = None
@ -116,7 +118,7 @@ def get_first_kernel_ro() -> pwndbg.lib.memory.Page | None:
if base is None: if base is None:
return None return None
for mapping in pwndbg.aglib.vmmap.get(): for mapping in pwndbg.aglib.kernel.paging.get_memory_map_raw():
if mapping.vaddr < base: if mapping.vaddr < base:
continue continue
@ -200,11 +202,6 @@ def is_kaslr_enabled() -> bool:
return "nokaslr" not in kcmdline() return "nokaslr" not in kcmdline()
@pwndbg.lib.cache.cache_until("start")
def kbase() -> int | None:
return pwndbg.aglib.kernel.paging.kbase()
def get_idt_entries() -> List[pwndbg.lib.kernel.structs.IDTEntry]: def get_idt_entries() -> List[pwndbg.lib.kernel.structs.IDTEntry]:
""" """
Retrieves the IDT entries from memory. Retrieves the IDT entries from memory.
@ -234,10 +231,6 @@ class ArchOps(ABC):
# use through kernel configuration, enabling support for additional models # use through kernel configuration, enabling support for additional models
# in the page_to_pfn() and pfn_to_page() methods in the future. # in the page_to_pfn() and pfn_to_page() methods in the future.
@abstractmethod
def page_size(self) -> int:
raise NotImplementedError()
@abstractmethod @abstractmethod
def per_cpu(self, addr: pwndbg.dbg_mod.Value, cpu=None) -> pwndbg.dbg_mod.Value: def per_cpu(self, addr: pwndbg.dbg_mod.Value, cpu=None) -> pwndbg.dbg_mod.Value:
raise NotImplementedError() raise NotImplementedError()
@ -266,10 +259,40 @@ class ArchOps(ABC):
def page_to_pfn(self, page: int) -> int: def page_to_pfn(self, page: int) -> int:
raise NotImplementedError() raise NotImplementedError()
@property
@pwndbg.lib.cache.cache_until("start")
def STRUCT_PAGE_SIZE(self):
return arch_paginginfo().STRUCT_PAGE_SIZE
@property
@pwndbg.lib.cache.cache_until("start")
def STRUCT_PAGE_SHIFT(self):
return arch_paginginfo().STRUCT_PAGE_SHIFT
@property @property
def page_offset(self) -> int: def page_offset(self) -> int:
return arch_paginginfo().physmap
@property
def page_shift(self) -> int:
return arch_paginginfo().page_shift
@property
def vmemmap(self) -> int:
return arch_paginginfo().vmemmap
@property
def kbase(self) -> int:
return arch_paginginfo().kbase
@property
def ptr_size(self) -> int:
raise NotImplementedError() raise NotImplementedError()
@property
def page_size(self) -> int:
return 1 << self.page_shift
def virt_to_pfn(self, virt: int) -> int: def virt_to_pfn(self, virt: int) -> int:
return phys_to_pfn(virt_to_phys(virt)) return phys_to_pfn(virt_to_phys(virt))
@ -293,9 +316,6 @@ class ArchOps(ABC):
class x86Ops(ArchOps): class x86Ops(ArchOps):
def page_size(self) -> int:
return 1 << self.page_shift
def phys_to_virt(self, phys: int) -> int: def phys_to_virt(self, phys: int) -> int:
return (phys + self.page_offset) % (1 << self.ptr_size) return (phys + self.page_offset) % (1 << self.ptr_size)
@ -310,41 +330,16 @@ class x86Ops(ArchOps):
def ptr_size(self) -> int: def ptr_size(self) -> int:
raise NotImplementedError() raise NotImplementedError()
@property
@abstractmethod
def page_shift(self) -> int:
raise NotImplementedError()
@property
@abstractmethod
def page_offset(self) -> int:
raise NotImplementedError()
@staticmethod @staticmethod
def paging_enabled() -> bool: def paging_enabled() -> bool:
return int(pwndbg.aglib.regs.cr0) & BIT(31) != 0 return int(pwndbg.aglib.regs.cr0) & BIT(31) != 0
class i386Ops(x86Ops): class i386Ops(x86Ops):
@requires_kconfig()
def __init__(self) -> None:
# https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/page_32_types.h#L18
self._PAGE_OFFSET = int(kconfig()["CONFIG_PAGE_OFFSET"], 16)
self.START_KERNEL_map = self._PAGE_OFFSET
@property @property
def ptr_size(self) -> int: def ptr_size(self) -> int:
return 32 return 32
@property
def page_offset(self) -> int:
return self._PAGE_OFFSET
@property
def page_shift(self) -> int:
# https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/page_types.h#L10
return 12
def virt_to_phys(self, virt: int) -> int: def virt_to_phys(self, virt: int) -> int:
return (virt - self.page_offset) % (1 << 32) return (virt - self.page_offset) % (1 << 32)
@ -360,51 +355,12 @@ class i386Ops(x86Ops):
class x86_64Ops(x86Ops): class x86_64Ops(x86Ops):
def __init__(self) -> None: def __init__(self) -> None:
self.STRUCT_PAGE_SIZE = pwndbg.aglib.typeinfo.load("struct page").sizeof
self.STRUCT_PAGE_SHIFT = int(math.log2(self.STRUCT_PAGE_SIZE))
self.phys_base = 0x1000000 self.phys_base = 0x1000000
try:
self.START_KERNEL_map = pwndbg.aglib.kernel.kbase()
except Exception:
print("WARNING: an error ocurred when retrieving kbase")
self.START_KERNEL_map = None
if self.START_KERNEL_map is None:
# put this here in case kbase also returns None
self.START_KERNEL_map = 0xFFFFFFFF80000000
if pwndbg.aglib.kernel.has_debug_syms():
# if there are debug symbols
self._PAGE_OFFSET = pwndbg.aglib.kernel.paging.physmap_base()
self.VMEMMAP_START = pwndbg.aglib.symbol.lookup_symbol_value("vmemmap_base")
if self._PAGE_OFFSET is not None and self.VMEMMAP_START is not None:
return
if self.uses_5lvl_paging():
# https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/page_64_types.h#L41
self._PAGE_OFFSET = 0xFF11000000000000
# https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/pgtable_64_types.h#L131
self.VMEMMAP_START = 0xFFD4000000000000
else:
# https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/page_64_types.h#L42
self._PAGE_OFFSET = 0xFFFF888000000000
# https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/pgtable_64_types.h#L130
self.VMEMMAP_START = 0xFFFFEA0000000000
@property @property
def ptr_size(self) -> int: def ptr_size(self) -> int:
return 64 return 64
@property
def page_offset(self) -> int:
return self._PAGE_OFFSET
@property
def page_shift(self) -> int:
# https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/page_64_types.h#L50
return 12
@requires_debug_syms() @requires_debug_syms()
def per_cpu(self, addr: pwndbg.dbg_mod.Value, cpu: int | None = None) -> pwndbg.dbg_mod.Value: def per_cpu(self, addr: pwndbg.dbg_mod.Value, cpu: int | None = None) -> pwndbg.dbg_mod.Value:
if cpu is None: if cpu is None:
@ -418,78 +374,25 @@ class x86_64Ops(x86Ops):
return pwndbg.dbg.selected_inferior().create_value(per_cpu_addr, addr.type) return pwndbg.dbg.selected_inferior().create_value(per_cpu_addr, addr.type)
def virt_to_phys(self, virt: int) -> int: def virt_to_phys(self, virt: int) -> int:
if virt < self.START_KERNEL_map: if virt < self.kbase:
return (virt - self.page_offset) % (1 << 64) return (virt - self.page_offset) % (1 << 64)
return ((virt - self.START_KERNEL_map) + self.phys_base) % (1 << 64) return ((virt - self.kbase) + self.phys_base) % (1 << 64)
def pfn_to_page(self, pfn: int) -> int: def pfn_to_page(self, pfn: int) -> int:
# assumption: SPARSEMEM_VMEMMAP memory model used # assumption: SPARSEMEM_VMEMMAP memory model used
# FLATMEM or SPARSEMEM not (yet) implemented # FLATMEM or SPARSEMEM not (yet) implemented
return (pfn << self.STRUCT_PAGE_SHIFT) + self.VMEMMAP_START return (pfn << self.STRUCT_PAGE_SHIFT) + self.vmemmap
def page_to_pfn(self, page: int) -> int: def page_to_pfn(self, page: int) -> int:
# assumption: SPARSEMEM_VMEMMAP memory model used # assumption: SPARSEMEM_VMEMMAP memory model used
# FLATMEM or SPARSEMEM not (yet) implemented # FLATMEM or SPARSEMEM not (yet) implemented
return (page - self.VMEMMAP_START) >> self.STRUCT_PAGE_SHIFT return (page - self.vmemmap) >> self.STRUCT_PAGE_SHIFT
@staticmethod
@requires_debug_syms()
def cpu_feature_capability(feature: int) -> bool:
boot_cpu_data = pwndbg.aglib.symbol.lookup_symbol("boot_cpu_data")
assert boot_cpu_data is not None, "Symbol boot_cpu_data not exists"
boot_cpu_data = boot_cpu_data.dereference()
capabilities = boot_cpu_data["x86_capability"]
return (int(capabilities[feature // 32]) >> (feature % 32)) & 1 == 1
@staticmethod
@requires_debug_syms()
def uses_5lvl_paging() -> bool:
# https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/cpufeatures.h#L381
X86_FEATURE_LA57 = 16 * 32 + 16
# Separate to avoid using kconfig if possible
if not x86_64Ops.cpu_feature_capability(X86_FEATURE_LA57) or "no5lvl" in kcmdline():
return False
return x86_64Ops._kconfig_5lvl_paging()
@staticmethod
@requires_kconfig()
def _kconfig_5lvl_paging() -> bool:
return kconfig().get("CONFIG_X86_5LEVEL") == "y"
class Aarch64Ops(ArchOps): class Aarch64Ops(ArchOps):
@requires_kconfig(default={}) @property
def __init__(self) -> None: def ptr_size(self):
page_type = pwndbg.aglib.typeinfo.load("struct page") return 64
assert page_type is not None, "Type 'struct page' not exists"
self.STRUCT_PAGE_SIZE = page_type.sizeof
self.STRUCT_PAGE_SHIFT = int(math.log2(self.STRUCT_PAGE_SIZE))
self.VA_BITS = int(kconfig()["ARM64_VA_BITS"])
self.PAGE_SHIFT = int(kconfig()["CONFIG_ARM64_PAGE_SHIFT"])
addr = pwndbg.aglib.symbol.lookup_symbol_addr("memstart_addr")
assert addr is not None, "Symbol memstart_addr not exists"
self.PHYS_OFFSET = pwndbg.aglib.memory.u(addr)
self.PAGE_OFFSET = (-1 << self.VA_BITS) + 2**64
VA_BITS_MIN = 48 if self.VA_BITS > 48 else self.VA_BITS
PAGE_END = (-1 << (VA_BITS_MIN - 1)) + 2**64
VMEMMAP_SIZE = (PAGE_END - self.PAGE_OFFSET) >> (self.PAGE_SHIFT - self.STRUCT_PAGE_SHIFT)
if pwndbg.aglib.kernel.krelease() >= (5, 11):
# Linux 5.11 changed the calculation for VMEMMAP_START
# https://elixir.bootlin.com/linux/v5.11/source/arch/arm64/include/asm/memory.h#L53
self.VMEMMAP_SHIFT = self.PAGE_SHIFT - self.STRUCT_PAGE_SHIFT
self.VMEMMAP_START = -(1 << (self.VA_BITS - self.VMEMMAP_SHIFT)) % (1 << 64)
else:
self.VMEMMAP_START = (-VMEMMAP_SIZE - 2 * 1024 * 1024) + 2**64
def page_size(self) -> int:
return 1 << self.PAGE_SHIFT
@requires_debug_syms() @requires_debug_syms()
def per_cpu(self, addr: pwndbg.dbg_mod.Value, cpu: int | None = None) -> pwndbg.dbg_mod.Value: def per_cpu(self, addr: pwndbg.dbg_mod.Value, cpu: int | None = None) -> pwndbg.dbg_mod.Value:
@ -504,32 +407,46 @@ class Aarch64Ops(ArchOps):
return pwndbg.dbg.selected_inferior().create_value(per_cpu_addr, addr.type) return pwndbg.dbg.selected_inferior().create_value(per_cpu_addr, addr.type)
def virt_to_phys(self, virt: int) -> int: def virt_to_phys(self, virt: int) -> int:
return virt - self.PAGE_OFFSET return virt - self.page_offset
def phys_to_virt(self, phys: int) -> int: def phys_to_virt(self, phys: int) -> int:
return phys + self.PAGE_OFFSET return phys + self.page_offset
def phys_to_pfn(self, phys: int) -> int: def phys_to_pfn(self, phys: int) -> int:
return phys >> self.PAGE_SHIFT return phys >> self.page_shift
def pfn_to_phys(self, pfn: int) -> int: def pfn_to_phys(self, pfn: int) -> int:
return pfn << self.PAGE_SHIFT return pfn << self.page_shift
def pfn_to_page(self, pfn: int) -> int: def pfn_to_page(self, pfn: int) -> int:
# assumption: SPARSEMEM_VMEMMAP memory model used # assumption: SPARSEMEM_VMEMMAP memory model used
# FLATMEM or SPARSEMEM not (yet) implemented # FLATMEM or SPARSEMEM not (yet) implemented
return (pfn << self.STRUCT_PAGE_SHIFT) + self.VMEMMAP_START return (pfn << self.STRUCT_PAGE_SHIFT) + self.vmemmap
def page_to_pfn(self, page: int) -> int: def page_to_pfn(self, page: int) -> int:
# assumption: SPARSEMEM_VMEMMAP memory model used # assumption: SPARSEMEM_VMEMMAP memory model used
# FLATMEM or SPARSEMEM not (yet) implemented # FLATMEM or SPARSEMEM not (yet) implemented
return (page - self.VMEMMAP_START) >> self.STRUCT_PAGE_SHIFT return (page - self.vmemmap) >> self.STRUCT_PAGE_SHIFT
@staticmethod @staticmethod
def paging_enabled() -> bool: def paging_enabled() -> bool:
return int(pwndbg.aglib.regs.SCTLR) & BIT(0) != 0 return int(pwndbg.aglib.regs.SCTLR) & BIT(0) != 0
_arch_paginginfo: ArchPagingInfo = None
@pwndbg.lib.cache.cache_until("start")
def arch_paginginfo() -> ArchPagingInfo:
global _arch_paginginfo
if _arch_paginginfo is None:
if pwndbg.aglib.arch.name == "aarch64":
_arch_paginginfo = pwndbg.aglib.kernel.paging.Aarch64PagingInfo()
elif pwndbg.aglib.arch.name == "x86-64":
_arch_paginginfo = pwndbg.aglib.kernel.paging.x86_64PagingInfo()
return _arch_paginginfo
_arch_ops: ArchOps = None _arch_ops: ArchOps = None
@ -547,10 +464,18 @@ def arch_ops() -> ArchOps:
return _arch_ops return _arch_ops
def ptr_size() -> int:
ops = arch_ops()
if ops:
return ops.ptr_size
else:
raise NotImplementedError()
def page_size() -> int: def page_size() -> int:
ops = arch_ops() ops = arch_ops()
if ops: if ops:
return ops.page_size() return ops.page_size
else: else:
raise NotImplementedError() raise NotImplementedError()
@ -667,6 +592,15 @@ def virt_to_pfn(virt: int) -> int:
raise NotImplementedError() raise NotImplementedError()
@pwndbg.lib.cache.cache_until("stop")
def kbase() -> int | None:
ops = arch_ops()
if ops:
return ops.kbase
else:
raise NotImplementedError()
def paging_enabled() -> bool: def paging_enabled() -> bool:
arch_name = pwndbg.aglib.arch.name arch_name = pwndbg.aglib.arch.name
if arch_name == "i386": if arch_name == "i386":

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import math
from typing import List from typing import List
from typing import Tuple from typing import Tuple
@ -8,97 +9,415 @@ import pwndbg.aglib.vmmap_custom
import pwndbg.color.message as M import pwndbg.color.message as M
import pwndbg.lib.cache import pwndbg.lib.cache
import pwndbg.lib.memory import pwndbg.lib.memory
from pwndbg import config
ENTRYMASK = ~((1 << 12) - 1) & ((1 << 51) - 1) ENTRYMASK = ~((1 << 12) - 1) & ((1 << 51) - 1)
# don't return None but rather an invalid value for address markers
# this way arithmetic ops do not panic if physmap is not found
INVALID_ADDR = 1 << 64
@pwndbg.lib.cache.cache_until("start", "stop") @pwndbg.lib.cache.cache_until("stop")
def get_memory_map_raw() -> Tuple[pwndbg.lib.memory.Page, ...]: def get_memory_map_raw() -> Tuple[pwndbg.lib.memory.Page, ...]:
return pwndbg.aglib.kernel.vmmap.kernel_vmmap(False) return pwndbg.aglib.kernel.vmmap.kernel_vmmap(False)
def find_kbase(pages) -> int | None: def guess_physmap():
arch_name = pwndbg.aglib.arch.name # this is mostly true
# https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt
for page in get_memory_map_raw():
if page.start and pwndbg.aglib.memory.is_kernel(page.start):
return page.start
return INVALID_ADDR
address = 0
if arch_name == "x86-64": class ArchPagingInfo:
address = pwndbg.aglib.kernel.get_idt_entries()[0].offset USERLAND = "userland"
elif arch_name == "aarch64": KERNELLAND = "kernel [.text]"
address = pwndbg.aglib.regs.vbar KERNELRO = "kernel [.rodata]"
else: KERNELBSS = "kernel [.bss]"
KERNELDRIVER = "kernel [.driver .bpf]"
ESPSTACK = "espfix"
PHYSMAP = "physmap"
VMALLOC = "vmalloc"
VMEMMAP = "vmemmap"
physmap: int
vmalloc: int
vmemmap: int
kbase: int
addr_marker_sz: int
@property
@pwndbg.lib.cache.cache_until("start")
def STRUCT_PAGE_SIZE(self):
a = pwndbg.aglib.typeinfo.load("struct page")
if a is None:
# this has been the case for all v5 and v6 releases
return 0x40
return a.sizeof
@property
@pwndbg.lib.cache.cache_until("start")
def STRUCT_PAGE_SHIFT(self):
return int(math.log2(self.STRUCT_PAGE_SIZE))
@property
def page_shift(self) -> int:
raise NotImplementedError()
@property
def paging_level(self) -> int:
raise NotImplementedError()
def adjust(self, name: str) -> str:
raise NotImplementedError()
def markers(self) -> Tuple[Tuple[str, int], ...]:
raise NotImplementedError()
def handle_kernel_pages(self, pages):
# this is arch dependent
raise NotImplementedError()
def kbase_helper(self, address):
for mapping in get_memory_map_raw():
# should be page aligned -- either from pt-dump or info mem
# only search in kernel mappings:
# https://www.kernel.org/doc/html/v5.3/arm64/memory.html
if not pwndbg.aglib.memory.is_kernel(mapping.vaddr):
continue
if address in mapping:
return mapping.vaddr
return None return None
mappings = pages
for mapping in mappings:
# should be page aligned -- either from pt-dump or info mem
# only search in kernel mappings: class x86_64PagingInfo(ArchPagingInfo):
# https://www.kernel.org/doc/html/v5.3/arm64/memory.html @property
if mapping.vaddr & (0xFFFF << 48) == 0: @pwndbg.lib.cache.cache_until("stop")
continue def physmap(self):
pob = pwndbg.aglib.symbol.lookup_symbol_addr("page_offset_base")
result = None
if pob is not None:
if pwndbg.aglib.memory.peek(pob):
result = pwndbg.aglib.memory.u64(pob)
if result is None:
return guess_physmap()
return result
if not mapping.execute: @property
continue @pwndbg.lib.cache.cache_until("stop")
def kbase(self):
return self.kbase_helper(pwndbg.aglib.kernel.get_idt_entries()[0].offset)
if address in mapping: @property
return mapping.vaddr def page_shift(self) -> int:
return 12
return None @property
@pwndbg.lib.cache.cache_until("stop")
def vmalloc(self):
addr = pwndbg.aglib.symbol.lookup_symbol_addr("vmalloc_base")
if addr:
return pwndbg.aglib.memory.u64(addr)
return None
@property
@pwndbg.lib.cache.cache_until("stop")
def vmemmap(self):
addr = pwndbg.aglib.symbol.lookup_symbol_addr("vmemmap_base")
if addr:
return pwndbg.aglib.memory.u64(addr)
# resort to default
if self.paging_level == 5:
return 0xFFD4000000000000
return 0xFFFFEA0000000000
@pwndbg.aglib.proc.OnlyWithArch(["x86-64"]) @property
def uses_5lvl_paging() -> bool: def paging_level(self) -> int:
if pwndbg.aglib.kernel.has_debug_syms(): if pwndbg.aglib.kernel.has_debug_syms():
ops: pwndbg.aglib.kernel.x86_64Ops = pwndbg.aglib.kernel.arch_ops() # https://elixir.bootlin.com/linux/v6.2/source/arch/x86/include/asm/cpufeatures.h#L381
return ops.uses_5lvl_paging() X86_FEATURE_LA57 = 16 * 32 + 16
pages = get_memory_map_raw() feature = X86_FEATURE_LA57
for page in pages: # Separate to avoid using kconfig if possible
if page.start & (1 << 63) > 0: boot_cpu_data = pwndbg.aglib.symbol.lookup_symbol("boot_cpu_data")
return page.start < (0xFFF << (4 * 13)) assert boot_cpu_data is not None, "Symbol boot_cpu_data not exists"
return False boot_cpu_data = boot_cpu_data.dereference()
capabilities = boot_cpu_data["x86_capability"]
guess_physmap = config.add_param( cpu_feature_capability = (int(capabilities[feature // 32]) >> (feature % 32)) & 1 == 1
"guess-physmap", if not cpu_feature_capability or "no5lvl" in pwndbg.aglib.kernel.kcmdline():
False, return 4
"Should guess physmap base address when debug symbols are not present", return 5
) # CONFIG_X86_5LEVEL is only a hint -- whether 5lvl paging is used depends on the hardware
# see also: https://www.kernel.org/doc/html/next/x86/x86_64/mm.html
pages = get_memory_map_raw()
def physmap_base() -> int: for page in pages:
if pwndbg.aglib.kernel.has_debug_syms() and pwndbg.aglib.arch.name == "x86-64": if pwndbg.aglib.memory.is_kernel(page.start):
result = pwndbg.aglib.symbol.lookup_symbol_addr("page_offset_base") if page.start < (0xFFF << (4 * 13)):
if pwndbg.aglib.memory.peek(result): return 5
result = pwndbg.aglib.memory.u64(result) return 4
else:
@pwndbg.lib.cache.cache_until("stop")
def markers(self) -> Tuple[Tuple[str, int], ...]:
return (
(self.USERLAND, 0),
(None, 0x8000000000000000),
("ldt remap", 0xFFFF880000000000 if self.paging_level == 4 else 0xFF10000000000000),
(self.PHYSMAP, self.physmap),
(self.VMALLOC, self.vmalloc),
(self.VMEMMAP, self.vmemmap),
# TODO: find better ways to handle the following constants
# I cound not find kernel symbols that reference their values
# the actual region base may differ but the region always falls within the below range
# even if KASLR is enabled
("cpu entry", 0xFFFFFE0000000000),
(self.ESPSTACK, 0xFFFFFF0000000000),
("EFI", 0xFFFFFFEF00000000),
(self.KERNELLAND, self.kbase),
("fixmap", 0xFFFFFFFFFF000000),
("legacy abi", 0xFFFFFFFFFF600000),
(None, 0xFFFFFFFFFFFFFFFF),
)
def adjust(self, name):
name = name.lower()
if "low kernel" in name:
return self.PHYSMAP
if "high kernel" in name:
return self.KERNELLAND
if self.VMALLOC in name:
return self.VMALLOC
if self.VMEMMAP in name:
return self.VMEMMAP
if " area" in name:
return name[:-5]
return name
def handle_kernel_pages(self, pages):
kernel_idx = None
for i, page in enumerate(pages):
if kernel_idx is None and self.kbase in page:
kernel_idx = i
kbase = self.kbase
if kernel_idx is None:
return
has_loadable_driver = False
for i in range(kernel_idx, len(pages)):
page = pages[i]
if page.objfile != self.KERNELLAND:
break
if not page.execute:
if page.write:
page.objfile = self.KERNELBSS
else:
page.objfile = self.KERNELRO
if has_loadable_driver:
page.objfile = self.KERNELDRIVER
if page.execute and page.start != kbase:
page.objfile = self.KERNELDRIVER
has_loadable_driver = True
if pwndbg.aglib.regs[pwndbg.aglib.regs.stack] in page:
page.objfile = "kernel [stack]"
class Aarch64PagingInfo(ArchPagingInfo):
def __init__(self):
self.tcr_el1 = pwndbg.lib.regs.aarch64_tcr_flags
self.tcr_el1.value = pwndbg.aglib.regs.TCR_EL1
# TODO: this is probably not entirely correct
# https://elixir.bootlin.com/linux/v6.16-rc2/source/arch/arm64/include/asm/memory.h#L56
self.va_bits = 64 - self.tcr_el1["T1SZ"] # this is prob only `vabits_actual`
self.va_bits_min = 48 if self.va_bits > 48 else self.va_bits
# https://elixir.bootlin.com/linux/v6.13.12/source/arch/arm64/include/asm/memory.h#L47
module_start_wo_kaslr = (-1 << (self.va_bits_min - 1)) + 2**64
self.vmalloc = module_start_wo_kaslr + 0x80000000
shift = self.page_shift - self.STRUCT_PAGE_SHIFT
self.VMEMMAP_SIZE = (module_start_wo_kaslr - ((-1 << self.va_bits) + 2**64)) >> shift
@property
@pwndbg.lib.cache.cache_until("stop")
def physmap(self):
# addr = pwndbg.aglib.symbol.lookup_symbol_addr("memstart_addr")
# if addr is None:
# return guess_physmap()
# return pwndbg.aglib.memory.u(addr)
return guess_physmap()
@property
@pwndbg.lib.cache.cache_until("stop")
def kbase(self):
return self.kbase_helper(pwndbg.aglib.regs.vbar)
@property
@pwndbg.lib.cache.cache_until("stop")
def kversion(self):
try:
return pwndbg.aglib.kernel.krelease()
except Exception:
return None return None
if result is not None:
return result @property
if guess_physmap or pwndbg.aglib.arch.name == "aarch64": @pwndbg.lib.cache.cache_until("stop")
# this is mostly true def physmap_end(self):
# https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt res = None
for page in get_memory_map_raw():
if page.end >= self.vmalloc:
break
res = page.end
if res is None:
return INVALID_ADDR
return res
@property
@pwndbg.lib.cache.cache_until("stop")
def module_start(self):
# this is only used for marking the end of module_start
self.module_end = -1
res = None
for page in get_memory_map_raw(): for page in get_memory_map_raw():
if page.start & (1 << 63) > 0: if page.start >= self.kbase:
return page.start break
print(M.warn("physmap base cannot be determined, resort to default")) if page.execute:
if uses_5lvl_paging(): res = page.start
return 0xFF11000000000000 if res is None:
return 0xFFFF888000000000 return INVALID_ADDR
prev = None
for page in get_memory_map_raw():
if page.start >= res:
if prev is not None and page.start > prev + 0x1000:
break
prev = self.module_end = page.end
return res
@property
@pwndbg.lib.cache.cache_until("stop")
def vmemmap(self):
if self.kversion is None:
return None
if self.kversion >= (6, 9):
# https://elixir.bootlin.com/linux/v6.16-rc2/source/arch/arm64/include/asm/memory.h#L33
return (-0x40000000 % INVALID_ADDR) - self.VMEMMAP_SIZE
if self.kversion >= (5, 11):
# Linux 5.11 changed the calculation for VMEMMAP_START
# https://elixir.bootlin.com/linux/v5.11/source/arch/arm64/include/asm/memory.h#L53
VMEMMAP_SHIFT = self.page_shift - self.STRUCT_PAGE_SHIFT
return -(1 << (self.va_bits - VMEMMAP_SHIFT)) % INVALID_ADDR
return (-self.VMEMMAP_SIZE - 2 * 1024 * 1024) + 2**64
@property
@pwndbg.lib.cache.cache_until("stop")
def pci(self):
if self.kversion is None:
return None
self.pci_end = INVALID_ADDR
if self.kversion >= (6, 9):
pci = self.vmemmap + self.VMEMMAP_SIZE + 0x00800000
self.pci_end = pci + 0x01000000
return pci
if self.kversion >= (5, 11):
self.pci_end = self.vmemmap - 0x00800000
return self.pci_end - 0x01000000
self.pci_end = self.vmemmap - 0x00200000
return self.pci_end - 0x01000000
@property
@pwndbg.lib.cache.cache_until("stop")
def ksize(self):
start = pwndbg.aglib.symbol.lookup_symbol_addr("_text")
end = pwndbg.aglib.symbol.lookup_symbol_addr("_end")
if start is not None and end is not None:
return end - start
# fallback
return 100 << 21 # 100M
@property
@pwndbg.lib.cache.cache_until("stop")
def page_shift(self) -> int:
# TODO: this might be arm version dependent
if self.tcr_el1["TG1"] == 1:
return 14
elif self.tcr_el1["TG1"] == 0:
return 12
else:
return 16
@pwndbg.lib.cache.cache_until("stop")
def markers(self) -> Tuple[Tuple[str, int], ...]:
address_markers = pwndbg.aglib.symbol.lookup_symbol_addr("address_markers")
if address_markers is not None:
sections = [(self.USERLAND, 0)]
value = 0
name = None
for i in range(20):
value = pwndbg.aglib.memory.u64(address_markers + i * 0x10)
name_ptr = pwndbg.aglib.memory.u64(address_markers + i * 0x10 + 8)
name = None
if name_ptr > 0:
name = pwndbg.aglib.memory.string(name_ptr).decode()
name = self.adjust(name)
if value > 0:
sections.append((name, value))
if value == 0xFFFFFFFFFFFFFFFF:
break
return tuple(sections)
if self.kversion is None:
return ()
return (
(self.USERLAND, 0),
(None, 0x8000000000000000),
(self.PHYSMAP, self.physmap),
(None, self.physmap_end),
(self.VMALLOC, self.vmalloc),
(self.VMEMMAP, self.vmemmap),
(None, self.vmemmap + self.VMEMMAP_SIZE),
("pci", self.pci),
(None, self.pci_end),
# TODO: prob not entirely correct but the computation is too complicated
("fixmap", self.pci_end),
(None, 0xFFFFFFFFFFFFFFFF),
)
def adjust(self, name):
name = name.lower()
if "end" in name:
return None
if "linear" in name:
return self.PHYSMAP
if "modules" in name:
return self.KERNELDRIVER
if self.VMEMMAP in name:
return self.VMEMMAP
if self.VMALLOC in name:
return self.VMALLOC
return " ".join(name.strip().split()[:-1])
@pwndbg.lib.cache.cache_until("start") def handle_kernel_pages(self, pages):
def kbase(): for i in range(len(pages)):
return find_kbase(get_memory_map_raw()) page = pages[i]
if page.start < self.module_start or page.start > self.kbase + self.ksize:
continue
if self.module_start <= page.start < self.module_end:
page.objfile = self.KERNELDRIVER
continue
if page.start < self.kbase:
continue
page.objfile = self.KERNELLAND
if not page.execute:
if page.write:
page.objfile = self.KERNELBSS
else:
page.objfile = self.KERNELRO
if pwndbg.aglib.regs[pwndbg.aglib.regs.stack] in page:
page.objfile = "kernel [stack]"
@pwndbg.aglib.proc.OnlyWithArch(["x86-64"]) @pwndbg.aglib.proc.OnlyWithArch(["x86-64"])
def pagewalk(target, entry=None) -> List[Tuple[int | None, int | None]]: def pagewalk(target, entry=None) -> List[Tuple[int | None, int | None]]:
level = 4 level = pwndbg.aglib.kernel.arch_paginginfo().paging_level
if uses_5lvl_paging(): base = pwndbg.aglib.kernel.arch_paginginfo().physmap
level = 5
base = physmap_base()
if entry is None: if entry is None:
entry = pwndbg.aglib.regs["cr3"] entry = pwndbg.aglib.regs["cr3"]
else: else:
@ -124,5 +443,5 @@ def pagewalk(target, entry=None) -> List[Tuple[int | None, int | None]]:
if entry == 0: if entry == 0:
return result return result
result[i] = (entry, vaddr) result[i] = (entry, vaddr)
result[0] = (None, (entry & ENTRYMASK) + base + offset) result[0] = (entry, (entry & ENTRYMASK) + base + offset)
return result return result

@ -28,73 +28,13 @@ import pwndbg.lib.memory
class KernelVmmap: class KernelVmmap:
USERLAND = "userland"
KERNELLAND = "kernel [.text]"
KERNELRO = "kernel [.rodata]"
KERNELBSS = "kernel [.bss]"
KERNELDRIVER = "kernel [.driver .bpf]"
ESPSTACK = "%esp fixup"
def __init__(self, pages: Tuple[pwndbg.lib.memory.Page, ...]): def __init__(self, pages: Tuple[pwndbg.lib.memory.Page, ...]):
self.pages = pages self.pages = pages
self.sections = None self.sections = None
self.pi = pwndbg.aglib.kernel.arch_paginginfo()
if not pwndbg.aglib.kernel.has_debug_syms(): if not pwndbg.aglib.kernel.has_debug_syms():
return return
self.kbase = kbase = pwndbg.aglib.kernel.paging.find_kbase(pages) self.sections = self.pi.markers()
if pwndbg.aglib.arch.name == "x86-64":
# https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt
# works for v5.x and v6.x
physmap = pwndbg.aglib.kernel.paging.physmap_base()
if physmap is None: # what??
return
vmalloc = pwndbg.aglib.symbol.lookup_symbol_value("vmalloc_base")
vmemmap = pwndbg.aglib.symbol.lookup_symbol_value("vmemmap_base")
self.sections = (
(self.USERLAND, 0),
(None, 0x8000000000000000),
("physmap", physmap),
("vmalloc", vmalloc),
("vmemmap", vmemmap),
# TODO: find better ways to handle the following constants
# I cound not find kernel symbols that reference their values
# the actual region base may differ but the region always falls within the below range
# even if KASLR is enabled
("cpu entry", 0xFFFFFE0000000000),
("rand cpu entry", 0xFFFFFE0000001000),
(self.ESPSTACK, 0xFFFFFF0000000000),
("EFI", 0xFFFFFFEF00000000),
(self.KERNELLAND, kbase),
("fixmap", 0xFFFFFFFFFF000000),
("legacy abi", 0xFFFFFFFFFF600000),
(None, 0xFFFFFFFFFFFFFFFF),
)
if pwndbg.aglib.arch.name == "aarch64":
# https://www.kernel.org/doc/html/v5.3/arm64/memory.html
# https://elixir.bootlin.com/linux/v6.15/source/arch/arm64/mm/ptdump.c#L351
# TODO: I don't think those are necessarily accurate when KASLR is enabled
# but I'm not familiar with ARM enough quite yet to find better ways
address_markers = pwndbg.aglib.symbol.lookup_symbol_addr("address_markers")
if address_markers is None:
return
sections = [(self.USERLAND, 0)]
value = 0
name = None
for i in range(20):
value = pwndbg.aglib.memory.u64(address_markers + i * 0x10)
if value > 0:
name_ptr = pwndbg.aglib.memory.u64(address_markers + i * 0x10 + 8)
name = None
if name_ptr > 0:
name = pwndbg.aglib.memory.string(name_ptr).decode()
name = name.split(" ")[0].lower()
if "end" in name:
name = None
if name == "linear":
name = "physmap"
sections.append((name, value))
if value == 0xFFFFFFFFFFFFFFFF:
break
self.sections = tuple(sections)
def get_name(self, addr: int) -> str: def get_name(self, addr: int) -> str:
if addr is None or self.sections is None: if addr is None or self.sections is None:
@ -109,27 +49,21 @@ class KernelVmmap:
return None return None
def adjust(self): def adjust(self):
if self.pages is None or len(self.pages) == 0:
return
for i, page in enumerate(self.pages): for i, page in enumerate(self.pages):
name = self.get_name(page.start) name = self.get_name(page.start)
if name is not None: if name is not None:
page.objfile = name page.objfile = name
user_idx, kernel_idx = None, None self.handle_user_pages()
for i, page in enumerate(self.pages): self.pi.handle_kernel_pages(self.pages)
if user_idx is None and page.objfile == self.USERLAND:
user_idx = i
if kernel_idx is None and page.objfile == self.KERNELLAND:
kernel_idx = i
self.handle_user_pages(user_idx)
self.handle_kernel_pages(kernel_idx)
self.handle_offsets() self.handle_offsets()
def handle_user_pages(self, user_idx): def handle_user_pages(self):
if user_idx is None: base_offset = self.pages[0].start
return for i in range(len(self.pages)):
base_offset = self.pages[user_idx].start
for i in range(user_idx, len(self.pages)):
page = self.pages[i] page = self.pages[i]
if page.objfile != self.USERLAND: if page.objfile != self.pi.USERLAND:
break break
diff = page.start - base_offset diff = page.start - base_offset
if diff > 0x100000: if diff > 0x100000:
@ -144,32 +78,11 @@ class KernelVmmap:
# page.objfile += f"_{hex(i)[2:]}" # page.objfile += f"_{hex(i)[2:]}"
base_offset = page.start base_offset = page.start
def handle_kernel_pages(self, kernel_idx):
if kernel_idx is None:
return
has_loadable_driver = False
for i in range(kernel_idx, len(self.pages)):
page = self.pages[i]
if page.objfile != self.KERNELLAND:
break
if not page.execute:
if page.write:
page.objfile = self.KERNELBSS
else:
page.objfile = self.KERNELRO
if has_loadable_driver:
page.objfile = self.KERNELDRIVER
if page.execute and page.start != self.kbase:
page.objfile = self.KERNELDRIVER
has_loadable_driver = True
if pwndbg.aglib.regs[pwndbg.aglib.regs.stack] in page:
page.objfile = "kernel [stack]"
def handle_offsets(self): def handle_offsets(self):
prev_objfile, base = "", 0 prev_objfile, base = "", 0
for page in self.pages: for page in self.pages:
# the check on KERNELRO is to make getting offsets for symbols such as `init_creds` more convinient # the check on KERNELRO is to make getting offsets for symbols such as `init_creds` more convinient
if page.objfile != self.KERNELRO and prev_objfile != page.objfile: if page.objfile != self.pi.KERNELRO and prev_objfile != page.objfile:
prev_objfile = page.objfile prev_objfile = page.objfile
base = page.start base = page.start
page.offset = page.start - base page.offset = page.start - base
@ -391,13 +304,17 @@ def kernel_vmmap_via_monitor_info_mem() -> Tuple[pwndbg.lib.memory.Page, ...]:
global monitor_info_mem_not_warned global monitor_info_mem_not_warned
pages: List[pwndbg.lib.memory.Page] = [] pages: List[pwndbg.lib.memory.Page] = []
for line in lines: for line in lines:
dash_idx = line.index("-") try:
space_idx = line.index(" ") dash_idx = line.index("-")
rspace_idx = line.rindex(" ") space_idx = line.index(" ")
rspace_idx = line.rindex(" ")
start = int(line[:dash_idx], 16)
end = int(line[dash_idx + 1 : space_idx], 16) start = int(line[:dash_idx], 16)
size = int(line[space_idx + 1 : rspace_idx], 16) end = int(line[dash_idx + 1 : space_idx], 16)
size = int(line[space_idx + 1 : rspace_idx], 16)
except Exception:
# invalid format
continue
if end - start != size and monitor_info_mem_not_warned: if end - start != size and monitor_info_mem_not_warned:
print( print(
M.warn( M.warn(
@ -418,9 +335,8 @@ def kernel_vmmap_via_monitor_info_mem() -> Tuple[pwndbg.lib.memory.Page, ...]:
flags |= 4 flags |= 4
if "w" in perm: if "w" in perm:
flags |= 2 flags |= 2
if len(perm) == 4: # if the qemu version displays if the page is executable if "x" in perm:
if "x" in perm: flags |= 1
flags |= 1
pages.append(pwndbg.lib.memory.Page(start, size, flags, 0, "<qemu>")) pages.append(pwndbg.lib.memory.Page(start, size, flags, 0, "<qemu>"))
return tuple(pages) return tuple(pages)
@ -462,17 +378,19 @@ def kernel_vmmap(process_pages=True) -> Tuple[pwndbg.lib.memory.Page, ...]:
pages = kernel_vmmap_via_page_tables() pages = kernel_vmmap_via_page_tables()
elif kernel_vmmap_mode == "monitor": elif kernel_vmmap_mode == "monitor":
pages = kernel_vmmap_via_monitor_info_mem() pages = kernel_vmmap_via_monitor_info_mem()
if process_pages and pwndbg.aglib.arch.name == "x86-64":
# TODO: check version here when QEMU displays the x bit for x64
for page in pages:
pgwalk_res = pwndbg.aglib.kernel.paging.pagewalk(page.start)
entry, vaddr = pgwalk_res[0]
if entry and entry >> 63 == 0:
page.flags |= 1
if pages is None: if pages is None:
return () return ()
if process_pages: if process_pages:
kv = KernelVmmap(pages) kv = KernelVmmap(pages)
kv.adjust() kv.adjust()
if kernel_vmmap_mode == "monitor" and pwndbg.aglib.arch.name == "x86-64":
# TODO: check version here when QEMU displays the x bit for x64
for page in pages:
if page.objfile == kv.pi.ESPSTACK:
continue
pgwalk_res = pwndbg.aglib.kernel.paging.pagewalk(page.start)
entry, _ = pgwalk_res[0]
if entry and entry >> 63 == 0:
page.flags |= 1
return tuple(pages) return tuple(pages)

@ -441,3 +441,7 @@ def is_pagefault_supported() -> bool:
# TODO: use a better detection method # TODO: use a better detection method
return pwndbg.dbg.selected_inferior().is_linux() return pwndbg.dbg.selected_inferior().is_linux()
def is_kernel(addr: int):
return addr >> 63 == 1

@ -26,7 +26,7 @@ def kbase(rebase=False) -> None:
print(M.error("kbase does not work when kernel-vmmap is set to none")) print(M.error("kbase does not work when kernel-vmmap is set to none"))
return return
base = pwndbg.aglib.kernel.kbase() base = pwndbg.aglib.kernel.arch_paginginfo().kbase
if base is None: if base is None:
print(M.error("Unable to locate the kernel base")) print(M.error("Unable to locate the kernel base"))

@ -42,7 +42,7 @@ def pg_indices(vaddr, nr_level):
def pagewalk(vaddr, entry=None): def pagewalk(vaddr, entry=None):
vaddr = int(pwndbg.dbg.selected_frame().evaluate_expression(vaddr)) vaddr = int(pwndbg.dbg.selected_frame().evaluate_expression(vaddr))
# https://blog.zolutal.io/understanding-paging/ # https://blog.zolutal.io/understanding-paging/
level = 4 level = pwndbg.aglib.kernel.arch_paginginfo().paging_level
names = ( names = (
"Page", "Page",
"PT", "PT",
@ -50,8 +50,7 @@ def pagewalk(vaddr, entry=None):
"PUD", "PUD",
"PGD", "PGD",
) )
if pwndbg.aglib.kernel.paging.uses_5lvl_paging(): if level == 5:
level = 5
names = ( names = (
"Page", "Page",
"PT", "PT",
@ -70,10 +69,14 @@ def pagewalk(vaddr, entry=None):
if vaddr is None: if vaddr is None:
print(M.warn("address is not mapped")) print(M.warn("address is not mapped"))
return return
phys = vaddr - pwndbg.aglib.kernel.paging.physmap_base() phys = vaddr - pwndbg.aglib.kernel.arch_paginginfo().physmap
print(f"pagewalk result: {C.green(hex(vaddr))} [phys: {C.yellow(hex(phys))}]") print(f"pagewalk result: {C.green(hex(vaddr))} [phys: {C.yellow(hex(phys))}]")
def paging_print_helper(name, addr):
print(f"{C.green(name)}: {C.yellow(hex(pwndbg.aglib.kernel.phys_to_virt(int(addr))))}")
p2v_parser = argparse.ArgumentParser( p2v_parser = argparse.ArgumentParser(
description="Translate physical address to its corresponding virtual address." description="Translate physical address to its corresponding virtual address."
) )
@ -86,7 +89,8 @@ p2v_parser.add_argument("paddr", type=str, help="")
@pwndbg.commands.OnlyWhenPagingEnabled @pwndbg.commands.OnlyWhenPagingEnabled
def p2v(paddr): def p2v(paddr):
paddr = pwndbg.dbg.selected_frame().evaluate_expression(paddr) paddr = pwndbg.dbg.selected_frame().evaluate_expression(paddr)
return pwndbg.aglib.kernel.phys_to_virt(int(paddr)) vaddr = pwndbg.aglib.kernel.phys_to_virt(int(paddr))
paging_print_helper("Virtual address", vaddr)
v2p_parser = argparse.ArgumentParser( v2p_parser = argparse.ArgumentParser(
@ -101,4 +105,5 @@ v2p_parser.add_argument("vaddr", type=str, help="")
@pwndbg.commands.OnlyWhenPagingEnabled @pwndbg.commands.OnlyWhenPagingEnabled
def v2p(vaddr): def v2p(vaddr):
vaddr = pwndbg.dbg.selected_frame().evaluate_expression(vaddr) vaddr = pwndbg.dbg.selected_frame().evaluate_expression(vaddr)
return pwndbg.aglib.kernel.virt_to_phys(int(vaddr)) paddr = pwndbg.aglib.kernel.virt_to_phys(int(vaddr))
paging_print_helper("Physical address", paddr)

@ -27,12 +27,14 @@ class BitFlags:
# - aarch64_sctlr_flags is used for "sctlr", "sctlr_el2", "sctlr_el3" # - aarch64_sctlr_flags is used for "sctlr", "sctlr_el2", "sctlr_el3"
regname: str regname: str
flags: OrderedDict[str, Union[int, Tuple[int, int]]] flags: OrderedDict[str, Union[int, Tuple[int, int]]]
value: int
def __init__(self, flags: List[Tuple[str, Union[int, Tuple[int, int]]]] = []): def __init__(self, flags: List[Tuple[str, Union[int, Tuple[int, int]]]] = [], value=None):
self.regname = "" self.regname = ""
self.flags = {} self.flags = {}
for name, bits in flags: for name, bits in flags:
self.flags[name] = bits self.flags[name] = bits
self.value = value
def __getattr__(self, name): def __getattr__(self, name):
if name in {"regname"}: if name in {"regname"}:
@ -40,7 +42,11 @@ class BitFlags:
return getattr(self.flags, name) return getattr(self.flags, name)
def __getitem__(self, key): def __getitem__(self, key):
return self.flags[key] r = self.flags[key]
if isinstance(r, int):
return (self.value >> r) & 1
s, e = r
return ((~((1 << s) - 1) & ((1 << e) - 1)) & self.value) >> s
def __setitem__(self, key, value): def __setitem__(self, key, value):
self.flags[key] = value self.flags[key] = value
@ -525,6 +531,14 @@ aarch64_sctlr_flags = BitFlags(
] ]
) )
aarch64_tcr_flags = BitFlags(
[
("TG1", (30, 31)),
("T1SZ", (16, 21)),
("T0SZ", (0, 5)),
]
)
aarch64_scr_flags = BitFlags( aarch64_scr_flags = BitFlags(
[ [
("HCE", 8), ("HCE", 8),
@ -593,6 +607,7 @@ aarch64 = RegisterSet(
"spsr_el1": aarch64_cpsr_flags, "spsr_el1": aarch64_cpsr_flags,
"spsr_el2": aarch64_cpsr_flags, "spsr_el2": aarch64_cpsr_flags,
"spsr_el3": aarch64_cpsr_flags, "spsr_el3": aarch64_cpsr_flags,
"tcr_el1": aarch64_tcr_flags,
}, },
# X29 is the frame pointer register (FP) but setting it # X29 is the frame pointer register (FP) but setting it
# as frame here messes up the register order to the point # as frame here messes up the register order to the point

@ -193,10 +193,6 @@ def test_command_kernel_vmmap():
@pytest.mark.skipif(not pwndbg.aglib.kernel.has_debug_syms(), reason="test requires debug symbols") @pytest.mark.skipif(not pwndbg.aglib.kernel.has_debug_syms(), reason="test requires debug symbols")
@pytest.mark.skipif(
pwndbg.aglib.arch.name not in ["i386", "x86-64"],
reason="function page_offset is only implemented for x86",
)
def test_command_buddydump(): def test_command_buddydump():
res = gdb.execute("buddydump", to_string=True) res = gdb.execute("buddydump", to_string=True)
NOFREEPAGE = "No free pages with specified filters found.\n" NOFREEPAGE = "No free pages with specified filters found.\n"
@ -250,11 +246,6 @@ def test_command_pagewalk():
# no kbase? fine # no kbase? fine
pages = pwndbg.aglib.vmmap.get() pages = pwndbg.aglib.vmmap.get()
address = pages[0].start address = pages[0].start
if not pwndbg.aglib.kernel.has_debug_syms():
# even if no debug symbols, still gracefully handle it
res = gdb.execute(f"pagewalk {hex(address)}")
# now let's guess the phymap base and should work as intended
res = gdb.execute("set guess-physmap on")
res = gdb.execute(f"pagewalk {hex(address)}", to_string=True) res = gdb.execute(f"pagewalk {hex(address)}", to_string=True)
assert "PMD" in res # Page Size is only set for PMDe or PTe assert "PMD" in res # Page Size is only set for PMDe or PTe
res = res.splitlines()[-1] res = res.splitlines()[-1]

Loading…
Cancel
Save