Skip to content

Commit

Permalink
Fix 850 (#851)
Browse files Browse the repository at this point in the history
* Make sure GefSetting `on_write` hooks are called even on creation

* Removed `SettingHookType`, not useful

* in `GefSetting` replaced  `dict` with `super`

* Added loading counter
  • Loading branch information
hugsy committed Jun 26, 2022
1 parent a2a4bfa commit d594eb3
Showing 1 changed file with 47 additions and 19 deletions.
66 changes: 47 additions & 19 deletions gef.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import platform
import re
import shutil
import site
import socket
import string
import struct
Expand All @@ -80,7 +81,7 @@
from functools import lru_cache
from io import StringIO, TextIOWrapper
from types import ModuleType
from typing import (IO, Any, ByteString, Callable, Dict, Generator, Iterable,
from typing import (Any, ByteString, Callable, Dict, Generator, Iterable,
Iterator, List, NoReturn, Optional, Sequence, Tuple, Type,
Union)
from urllib.request import urlopen
Expand Down Expand Up @@ -9362,6 +9363,7 @@ def load_plugin(fpath: pathlib.Path) -> bool:
return True

nb_added = -1
start_time = time.perf_counter()
try:
nb_inital = len(__registered_commands__)
directories: List[str] = gef.config["gef.extra_plugins_dir"].split(";") or []
Expand All @@ -9383,8 +9385,10 @@ def load_plugin(fpath: pathlib.Path) -> bool:
nb_added = len(__registered_commands__) - nb_inital
if nb_added > 0:
self.load()
end_time = time.perf_counter()
load_time = end_time - start_time
ok(f"{Color.colorify(str(nb_added), 'bold green')} extra commands added from "
f"'{Color.colorify(', '.join(directories), 'bold blue')}'")
f"'{Color.colorify(', '.join(directories), 'bold blue')}' in {load_time:.2f} seconds")
except gdb.error as e:
err(f"failed: {e}")
return nb_added
Expand Down Expand Up @@ -9423,6 +9427,7 @@ def load(self) -> None:
current_functions = set( self.functions.keys() )
new_functions = set([x._function_ for x in __registered_functions__]) - current_functions
self.missing.clear()
self.__load_time_ms = time.perf_counter_ns()

# load all new functions
for name in sorted(new_functions):
Expand All @@ -9449,6 +9454,8 @@ def load(self) -> None:

except Exception as reason:
self.missing[name] = reason

self.__load_time_ms = (time.perf_counter_ns() - self.__load_time_ms) / 1000
return


Expand All @@ -9460,7 +9467,7 @@ def show_banner(self) -> None:
ver = f"{sys.version_info.major:d}.{sys.version_info.minor:d}"
gef_print(f"{Color.colorify(str(len(self.commands)), 'bold green')} commands loaded "
f"and {Color.colorify(str(len(self.functions)), 'bold blue')} functions added for "
f"GDB {Color.colorify(gdb.VERSION, 'bold yellow')} "
f"GDB {Color.colorify(gdb.VERSION, 'bold yellow')} in {self.__load_time_ms:.2f}ms "
f"using Python engine {Color.colorify(ver, 'bold red')}")

nb_missing = len(self.missing)
Expand Down Expand Up @@ -10302,60 +10309,78 @@ def malloc_align_address(self, address: int) -> int:

class GefSetting:
"""Basic class for storing gef settings as objects"""
READ_ACCESS = 0
WRITE_ACCESS = 1

def __init__(self, value: Any, cls: Optional[type] = None, description: Optional[str] = None, hooks: Optional[Dict[str, Callable]] = None) -> None:
self.value = value
self.type = cls or type(value)
self.description = description or ""
self.hooks: Tuple[List[Callable]] = ([], [])
self.hooks: Tuple[List[Callable], List[Callable]] = ([], [])
if hooks:
for access, func in hooks.items():
if access not in ("on_read", "on_write"):
raise ValueError(f"access not in (on_read, on_write)")
if access == "on_read":
idx = GefSetting.READ_ACCESS
elif access == "on_write":
idx = GefSetting.WRITE_ACCESS
else:
raise ValueError
if not callable(func):
raise ValueError(f"hook is not callable")
idx = 0 if (access == "on_read") else 1
self.hooks[idx].append(func)
return

def __str__(self) -> str:
return f"Setting(type={self.type.__name__}, value='{self.value}', desc='{self.description[:10]}...', "\
f"read_hooks={len(self.hooks[GefSetting.READ_ACCESS])}, write_hooks={len(self.hooks[GefSetting.READ_ACCESS])})"


class GefSettingsManager(dict):
"""
GefSettings acts as a dict where the global settings are stored and can be read, written or deleted as any other dict.
For instance, to read a specific command setting: `gef.config[mycommand.mysetting]`
"""
def __getitem__(self, name: str) -> Any:
setting : GefSetting = dict.__getitem__(self, name)
self.invoke_hooks(True, setting)
setting : GefSetting = super().__getitem__(name)
self.__invoke_read_hooks(setting)
return setting.value

def __setitem__(self, name: str, value: Any) -> None:
# check if the key exists
if dict.__contains__(self, name):
if super().__contains__(name):
# if so, update its value directly
setting = dict.__getitem__(self, name)
setting = super().__getitem__(name)
if not isinstance(setting, GefSetting): raise ValueError
setting.value = setting.type(value)
dict.__setitem__(self, name, setting)
self.invoke_hooks(False, setting)
else:
# if not, `value` must be a GefSetting
if not isinstance(value, GefSetting): raise Exception("Invalid argument")
if not value.type: raise Exception("Invalid type")
if not value.description: raise Exception("Invalid description")
dict.__setitem__(self, name, value)
setting = value
super().__setitem__(name, setting)
self.__invoke_write_hooks(setting)
return

def __delitem__(self, name: str) -> None:
dict.__delitem__(self, name)
super().__delitem__(name)
return

def raw_entry(self, name: str) -> GefSetting:
return dict.__getitem__(self, name)
return super().__getitem__(name)

def invoke_hooks(self, is_read: bool, setting: GefSetting) -> None:
def __invoke_read_hooks(self, setting: GefSetting) -> None:
self.__invoke_hooks(is_write=False, setting=setting)
return

def __invoke_write_hooks(self, setting: GefSetting) -> None:
self.__invoke_hooks(is_write=True, setting=setting)
return

def __invoke_hooks(self, is_write: bool, setting: GefSetting) -> None:
if not setting.hooks:
return
idx = 0 if is_read else 1
idx = int(is_write)
if setting.hooks[idx]:
for callback in setting.hooks[idx]:
callback()
Expand Down Expand Up @@ -10794,11 +10819,14 @@ def reset_caches(self) -> None:
except gdb.error:
pass

# load GEF
# load GEF, set up the managers and load the plugins, functions,
reset()
gef.gdb.load()
gef.gdb.show_banner()

# load config
gef.gdb.load_extra_plugins()

# gdb events configuration
gef_on_continue_hook(continue_handler)
gef_on_stop_hook(hook_stop_handler)
Expand Down

0 comments on commit d594eb3

Please sign in to comment.