Skip to content

Commit

Permalink
Determine the actual canary location (#918)
Browse files Browse the repository at this point in the history
* Determine the actual canary location when possible

* Fix canary test

* Test changes and small refactoring
  • Loading branch information
clubby789 authored Nov 17, 2022
1 parent 75c76fe commit 63ac481
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 5 deletions.
27 changes: 23 additions & 4 deletions gef.py
Original file line number Diff line number Diff line change
Expand Up @@ -2265,6 +2265,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
Expand Down Expand Up @@ -2925,6 +2928,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")
Expand Down Expand Up @@ -10445,7 +10450,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
Expand Down Expand Up @@ -10519,15 +10523,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]:
Expand Down
15 changes: 14 additions & 1 deletion tests/commands/canary.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand All @@ -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)

0 comments on commit 63ac481

Please sign in to comment.