diff --git a/gef.py b/gef.py index a00386ea0..b9a7f724f 100644 --- a/gef.py +++ b/gef.py @@ -2260,6 +2260,9 @@ def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: raise NotImplementedError + def canary_address(self) -> int: + raise NotImplementedError + @classmethod def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: raise NotImplementedError @@ -2920,6 +2923,8 @@ def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: ] return "; ".join(insns) + def canary_address(self) -> int: + return self.register("fs_base") + 0x28 class PowerPC(Architecture): aliases = ("PowerPC", Elf.Abi.POWERPC, "PPC") @@ -10439,7 +10444,6 @@ def reset_caches(self) -> None: self._os = None self._pid = None self._file = None - self._canary = None self._maps: Optional[pathlib.Path] = None self._root: Optional[pathlib.Path] = None return @@ -10513,15 +10517,30 @@ def pagesize(self) -> int: @property def canary(self) -> Optional[Tuple[int, int]]: - """Returns a tuple of the canary address and value, read from the auxiliary vector.""" + """Return a tuple of the canary address and value, read from the canonical + location if supported by the architecture. Otherwise, read from the auxiliary + vector.""" + try: + canary_location = gef.arch.canary_address() + canary = gef.memory.read_integer(canary_location) + except NotImplementedError: + # Fall back to `AT_RANDOM`, which is the original source + # of the canary value but not the canonical location + return self.original_canary + return canary, canary_location + + @property + def original_canary(self) -> Optional[Tuple[int, int]]: + """Return a tuple of the initial canary address and value, read from the + auxiliary vector.""" auxval = self.auxiliary_vector if not auxval: return None canary_location = auxval["AT_RANDOM"] canary = gef.memory.read_integer(canary_location) canary &= ~0xFF - self._canary = (canary, canary_location) - return self._canary + return canary, canary_location + @property def maps(self) -> Optional[pathlib.Path]: diff --git a/tests/commands/canary.py b/tests/commands/canary.py index c3f23f719..67c84101a 100644 --- a/tests/commands/canary.py +++ b/tests/commands/canary.py @@ -3,9 +3,12 @@ """ -from tests.utils import gdb_start_silent_cmd, gdb_run_cmd, _target +from tests.utils import gdb_start_silent_cmd, gdb_run_cmd, _target, gdb_test_python_method from tests.utils import GefUnitTestGeneric +import pytest +import platform +ARCH = platform.machine() class CanaryCommand(GefUnitTestGeneric): """`canary` command test module""" @@ -16,3 +19,13 @@ def test_cmd_canary(self): res = gdb_start_silent_cmd("canary", target=_target("canary")) self.assertNoException(res) self.assertIn("The canary of process", res) + res = gdb_test_python_method("gef.session.canary[0] == gef.session.original_canary[0]") + self.assertNoException(res) + self.assertIn("True", res) + + @pytest.mark.skipif(ARCH != "x86_64", reason=f"Not implemented for {ARCH}") + def test_overwrite_canary(self): + patch = r"pi gef.memory.write(gef.arch.canary_address(), p64(0xdeadbeef))" + res = gdb_start_silent_cmd(patch, target=_target("canary"), after=["canary"]) + self.assertNoException(res) + self.assertIn("0xdeadbeef", res)