From f9830fa810b4c4f31bd0d97d4c5fac77a64525e0 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Thu, 3 Jul 2025 11:30:33 -0400 Subject: [PATCH 1/4] plugin_manager: individual verbose options --- src/penguin/plugin_manager.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/penguin/plugin_manager.py b/src/penguin/plugin_manager.py index 84b61fa29..f4f7ff2a2 100644 --- a/src/penguin/plugin_manager.py +++ b/src/penguin/plugin_manager.py @@ -67,6 +67,14 @@ def __preinit__(self, plugins: 'IGLOOPluginManager', args: Dict) -> None: logname = camel_to_snake(self.name) self.logger = getColoredLogger(f"plugins.{logname}") + # The verbose argument is used to set the logger level + verbose_arg = self.args.get("verbose", False) + if verbose_arg is True: + self.logger.setLevel("DEBUG") + elif isinstance(verbose_arg, int) or isinstance(verbose_arg, str): + # If verbose is an int, set the logger level to that value + self.logger.setLevel(verbose_arg) + @property def name(self) -> str: """ @@ -263,6 +271,12 @@ def initialize(self, panda: Panda, args: Dict[str, Any]) -> None: self.args = args self.logger = getColoredLogger("penguin.plugin_manger") + # Set the logger level based on the 'verbose' argument + # Do not pass verbose as an argument to the plugins + # as they set those individually + if self.args.pop("verbose", None) is not None: + self.logger.setLevel("DEBUG") + self.plugin_cbs: Dict[Plugin, Dict[str, List[Callable]]] = {} self.registered_cbs: Dict[Tuple[Plugin, str], Callable] = {} self.aliases: Dict[str, str] = {} From fcfcf47e7ee3a25916d0b7ae41e3cca9bdce031f Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Thu, 3 Jul 2025 11:46:55 -0400 Subject: [PATCH 2/4] use penguin_verbose for overall verbose argument --- src/penguin/penguin_run.py | 2 +- src/penguin/plugin_manager.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/penguin/penguin_run.py b/src/penguin/penguin_run.py index f8ef682ba..b9c2657da 100755 --- a/src/penguin/penguin_run.py +++ b/src/penguin/penguin_run.py @@ -542,7 +542,7 @@ def run_config( "fs": config_fs, "fw": config_image, "outdir": out_dir, - "verbose": verbose, + "penguin_verbose": verbose, "telnet_port": telnet_port, } args.update(vpn_args) diff --git a/src/penguin/plugin_manager.py b/src/penguin/plugin_manager.py index f4f7ff2a2..17f51b5e5 100644 --- a/src/penguin/plugin_manager.py +++ b/src/penguin/plugin_manager.py @@ -271,10 +271,8 @@ def initialize(self, panda: Panda, args: Dict[str, Any]) -> None: self.args = args self.logger = getColoredLogger("penguin.plugin_manger") - # Set the logger level based on the 'verbose' argument - # Do not pass verbose as an argument to the plugins - # as they set those individually - if self.args.pop("verbose", None) is not None: + # Set the logger level based on the 'penguin_verbose' argument + if self.args.get("penguin_verbose", False): self.logger.setLevel("DEBUG") self.plugin_cbs: Dict[Plugin, Dict[str, List[Callable]]] = {} From 7390c7e70afaf3c5c662350b22ae8b5cf8ab409b Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Thu, 3 Jul 2025 11:52:36 -0400 Subject: [PATCH 3/4] plugin: initialize logger with multiple options --- src/penguin/plugin_manager.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/penguin/plugin_manager.py b/src/penguin/plugin_manager.py index 17f51b5e5..6de469022 100644 --- a/src/penguin/plugin_manager.py +++ b/src/penguin/plugin_manager.py @@ -64,16 +64,30 @@ def __preinit__(self, plugins: 'IGLOOPluginManager', args: Dict) -> None: """ self.plugins = plugins self.args = args - logname = camel_to_snake(self.name) - self.logger = getColoredLogger(f"plugins.{logname}") + self._initialize_logger() + def _set_level_arg(self, level: Union[str, bool, int]) -> None: # The verbose argument is used to set the logger level verbose_arg = self.args.get("verbose", False) - if verbose_arg is True: - self.logger.setLevel("DEBUG") + if isinstance(verbose_arg, bool): + if verbose_arg is True: + self.logger.setLevel("DEBUG") elif isinstance(verbose_arg, int) or isinstance(verbose_arg, str): # If verbose is an int, set the logger level to that value self.logger.setLevel(verbose_arg) + else: + raise ValueError( + f"Unsupported verbose argument type: {type(verbose_arg)}. Expected bool, int, or str.") + + def _initialize_logger(self): + logname = camel_to_snake(self.name) + self.logger = getColoredLogger(f"plugins.{logname}") + if self.args.get("verbose", None) is not None: + # If verbose is set, set the logger level to DEBUG + self._set_level_arg(self.args["verbose"]) + elif self.args.get("loglevel", None) is not None: + # If loglevel is set, set the logger level to that value + self._set_level_arg(self.args["loglevel"]) @property def name(self) -> str: From a5d6e0bd63c94bceb5c501d36d26cc5c34ca8e04 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Thu, 3 Jul 2025 12:00:24 -0400 Subject: [PATCH 4/4] update plugins to penguin_verbose --- pyplugins/actuation/vpn.py | 2 +- pyplugins/analysis/env.py | 4 ++-- pyplugins/analysis/interfaces.py | 2 +- pyplugins/apis/send_hypercall.py | 2 +- pyplugins/apis/uprobes.py | 2 +- pyplugins/hyper/bash_command.py | 2 +- pyplugins/hyper/canary.py | 2 +- pyplugins/hyper/portal.py | 2 -- pyplugins/hyper/shell.py | 2 +- pyplugins/hyper/uboot.py | 2 +- pyplugins/interventions/lifeguard.py | 2 +- pyplugins/interventions/mount.py | 2 +- pyplugins/interventions/nvram2.py | 2 +- pyplugins/interventions/pseudofiles.py | 2 +- pyplugins/loggers/db.py | 2 +- pyplugins/testing/ioctl_interaction_test.py | 2 +- pyplugins/testing/kffi_test.py | 2 +- pyplugins/testing/portal_test.py | 2 -- 18 files changed, 17 insertions(+), 21 deletions(-) diff --git a/pyplugins/actuation/vpn.py b/pyplugins/actuation/vpn.py index 5065f23e2..fc9daf65c 100644 --- a/pyplugins/actuation/vpn.py +++ b/pyplugins/actuation/vpn.py @@ -105,7 +105,7 @@ def __init__(self, panda) -> None: self.outdir = self.get_arg("outdir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") # TODO: add option on whether or not to pass -o to host vpn diff --git a/pyplugins/analysis/env.py b/pyplugins/analysis/env.py index e89202260..00c1136e3 100644 --- a/pyplugins/analysis/env.py +++ b/pyplugins/analysis/env.py @@ -49,7 +49,7 @@ def __init__(self): self.env_vars = set() # set of env vars that were read through libc getenv self.uboot_vars = set() # set of env vars that were read through libc getenv self.mtd_vars = set() # set of mtd partitions read out of /proc/mtd - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") self.default_env_vars = DEFAULT_ENV_VARS @@ -214,7 +214,7 @@ def __init__(self): # this hurts performance, but without it we definitely miss some comparisons # in targetcmp/callwitharg/callstack_instr. panda.disable_tb_chaining() - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") self.outdir = self.get_arg("outdir") diff --git a/pyplugins/analysis/interfaces.py b/pyplugins/analysis/interfaces.py index bef5a3dc4..305eccea0 100644 --- a/pyplugins/analysis/interfaces.py +++ b/pyplugins/analysis/interfaces.py @@ -15,7 +15,7 @@ class Interfaces(Plugin): def __init__(self): self.outdir = self.get_arg("outdir") self.conf = self.get_arg("conf") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") open(f"{self.outdir}/{iface_log}", "w").close() diff --git a/pyplugins/apis/send_hypercall.py b/pyplugins/apis/send_hypercall.py index 8dc2960dd..769a46bd4 100644 --- a/pyplugins/apis/send_hypercall.py +++ b/pyplugins/apis/send_hypercall.py @@ -60,7 +60,7 @@ def __init__(self) -> None: Sets up logging, event registration, and subscribes to the igloo_send_hypercall event. """ self.outdir = self.get_arg("outdir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") self.registered_events: Dict[str, Callable[..., Tuple[int, Union[str, bytes]]]] = {} diff --git a/pyplugins/apis/uprobes.py b/pyplugins/apis/uprobes.py index 1a14e14f9..f00b89af2 100644 --- a/pyplugins/apis/uprobes.py +++ b/pyplugins/apis/uprobes.py @@ -35,7 +35,7 @@ class Uprobes(Plugin): def __init__(self): self.outdir = self.get_arg("outdir") self.projdir = self.get_arg("proj_dir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") self.libsymbols = os.path.join( self.projdir, "static", "LibrarySymbols.json.xz") diff --git a/pyplugins/hyper/bash_command.py b/pyplugins/hyper/bash_command.py index 4c1d907a2..8776e0d11 100644 --- a/pyplugins/hyper/bash_command.py +++ b/pyplugins/hyper/bash_command.py @@ -54,7 +54,7 @@ def __init__(self) -> None: **Returns:** None """ self.outdir = self.get_arg("outdir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") # Bash diff --git a/pyplugins/hyper/canary.py b/pyplugins/hyper/canary.py index 569fc430e..6b54e4f82 100644 --- a/pyplugins/hyper/canary.py +++ b/pyplugins/hyper/canary.py @@ -54,7 +54,7 @@ def __init__(self) -> None: """ self.outdir = self.get_arg("outdir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") @plugins.SendHypercall.subscribe("canary") diff --git a/pyplugins/hyper/portal.py b/pyplugins/hyper/portal.py index 41fbfa882..3de5eb415 100644 --- a/pyplugins/hyper/portal.py +++ b/pyplugins/hyper/portal.py @@ -134,8 +134,6 @@ def __init__(self) -> None: **Returns:** None """ self.outdir = self.get_arg("outdir") - # if self.get_arg_bool("verbose"): - # self.logger.setLevel("DEBUG") # Set endianness format character for struct operations self.endian_format = '<' if self.panda.endianness == 'little' else '>' self.portal_interrupt = None diff --git a/pyplugins/hyper/shell.py b/pyplugins/hyper/shell.py index 3ba2fcc3f..c19c80ac0 100644 --- a/pyplugins/hyper/shell.py +++ b/pyplugins/hyper/shell.py @@ -74,7 +74,7 @@ def __init__(self, panda: Any) -> None: self.read_scripts = {} # filename -> contents self.last_line = None - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") # initialize outfiles: diff --git a/pyplugins/hyper/uboot.py b/pyplugins/hyper/uboot.py index 7083b596d..2fb6e6d33 100644 --- a/pyplugins/hyper/uboot.py +++ b/pyplugins/hyper/uboot.py @@ -59,7 +59,7 @@ def __init__(self) -> None: open(os.path.join(self.outdir, UBOOT_LOG), "w").close() self.uboot_log = set() - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") # U-Boot diff --git a/pyplugins/interventions/lifeguard.py b/pyplugins/interventions/lifeguard.py index 3477b4e3a..9e7dccb3a 100644 --- a/pyplugins/interventions/lifeguard.py +++ b/pyplugins/interventions/lifeguard.py @@ -95,7 +95,7 @@ def __init__(self, panda: object) -> None: """ self.panda = panda self.outdir = self.get_arg("outdir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") self.blocked_signals = [] diff --git a/pyplugins/interventions/mount.py b/pyplugins/interventions/mount.py index 204d28963..9c0493931 100644 --- a/pyplugins/interventions/mount.py +++ b/pyplugins/interventions/mount.py @@ -66,7 +66,7 @@ def __init__(self): self.mounts = set() self.fake_mounts = self.get_arg("fake_mounts") or [] self.all_succeed = self.get_arg("all_succeed") or False - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") plugins.syscalls.syscall("on_sys_mount_return")(self.post_mount) diff --git a/pyplugins/interventions/nvram2.py b/pyplugins/interventions/nvram2.py index ef7b1cf53..594f7158a 100644 --- a/pyplugins/interventions/nvram2.py +++ b/pyplugins/interventions/nvram2.py @@ -61,7 +61,7 @@ def __init__(self): - None """ self.outdir = self.get_arg("outdir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") # Even at debug level, logging every nvram get/clear can be very verbose. # As such, we only debug log nvram sets diff --git a/pyplugins/interventions/pseudofiles.py b/pyplugins/interventions/pseudofiles.py index dbbc7ee51..5b39ba8f4 100644 --- a/pyplugins/interventions/pseudofiles.py +++ b/pyplugins/interventions/pseudofiles.py @@ -140,7 +140,7 @@ def __init__(self): self.outdir = self.get_arg("outdir") self.proj_dir = self.get_arg("proj_dir") self.written_data = {} # filename -> data that was written to it - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel(logging.DEBUG) self.did_mtd_warn = False # Set if we've warned about misconfigured MTD devices # XXX: It has seemed like this should be 1 for some architectures, but diff --git a/pyplugins/loggers/db.py b/pyplugins/loggers/db.py index 91899c3ca..3b17fd602 100644 --- a/pyplugins/loggers/db.py +++ b/pyplugins/loggers/db.py @@ -67,7 +67,7 @@ def __init__(self) -> None: self.finished_worker = Event() self.initialized_db = False - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") # Start the background flush thread diff --git a/pyplugins/testing/ioctl_interaction_test.py b/pyplugins/testing/ioctl_interaction_test.py index 75df764f2..8ebbbb0ac 100644 --- a/pyplugins/testing/ioctl_interaction_test.py +++ b/pyplugins/testing/ioctl_interaction_test.py @@ -9,7 +9,7 @@ class TestIoctlInteraction(Plugin): def __init__(self): self.outdir = self.get_arg("outdir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") @plugins.syscalls.syscall("on_sys_ioctl_return", arg_filters=[None, SIOCDEVPRIVATE]) diff --git a/pyplugins/testing/kffi_test.py b/pyplugins/testing/kffi_test.py index 0595b9303..b1d1fd063 100644 --- a/pyplugins/testing/kffi_test.py +++ b/pyplugins/testing/kffi_test.py @@ -9,7 +9,7 @@ class KFFITest(Plugin): def __init__(self): self.outdir = self.get_arg("outdir") - if self.get_arg_bool("verbose"): + if self.get_arg_bool("penguin_verbose"): self.logger.setLevel("DEBUG") @syscalls.syscall("on_sys_ioctl_return", arg_filters=[0x14, 0x15, 0x16]) diff --git a/pyplugins/testing/portal_test.py b/pyplugins/testing/portal_test.py index 74b0d7a29..608a812ee 100644 --- a/pyplugins/testing/portal_test.py +++ b/pyplugins/testing/portal_test.py @@ -10,8 +10,6 @@ class PortalTest(Plugin): def __init__(self, panda): self.panda = panda self.outdir = self.get_arg("outdir") - # if self.get_arg_bool("verbose"): - # self.logger.setLevel("DEBUG") ''' This test checks that we can get information from our program, its arguments,