Skip to content

Commit 38e333c

Browse files
committed
Adds private auxiliary module configuration option
This adds a new `configure_from_data` method to analyzer.windows.lib.common.abstracts.Auxiliary. This optional method provides the ability to run private auxiliary-specific configuration code from `data.packages.<module_name>`. If an auxiliary module doesn't provide a `configure_from_data` method, it's logged but ignored.
1 parent 2283cfb commit 38e333c

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

analyzer/windows/analyzer.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,21 @@ def run(self):
509509
except ImportError as e:
510510
log.warning('Unable to import the auxiliary module "%s": %s', name, e)
511511

512+
def configure_aux_from_data(instance):
513+
# Do auxiliary module configuration stored in 'data/auxiliary/<package_name>'
514+
_class = type(instance)
515+
try:
516+
log.debug("attempting to configure '%s' from data", _class.__name__)
517+
instance.configure_from_data()
518+
except ModuleNotFoundError:
519+
# let it go, not every module is configurable from data
520+
log.debug("module %s does not support data configuration, ignoring", _class.__name__)
521+
except ImportError as iexc:
522+
# let it go but emit a warning; assume a dependency is missing
523+
log.warning("configuration error for module %s: %s", _class.__name__, iexc)
524+
except Exception as exc:
525+
log.error("error configuring module %s: %s", _class.__name__, exc)
526+
512527
# Walk through the available auxiliary modules.
513528
aux_modules = []
514529

@@ -517,6 +532,7 @@ def run(self):
517532
aux = module(self.options, self.config)
518533
log.debug('Initialized auxiliary module "%s"', module.__name__)
519534
aux_modules.append(aux)
535+
configure_aux_from_data(aux)
520536
log.debug('Trying to start auxiliary module "%s"...', module.__module__)
521537
aux.start()
522538
except (NotImplementedError, AttributeError) as e:

analyzer/windows/lib/common/abstracts.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,29 @@ def add_pid(self, pid):
322322

323323
def del_pid(self, pid):
324324
pass
325+
326+
def configure_from_data(self):
327+
"""Do private auxiliary module-specific configuration.
328+
329+
Auxiliary modules can implement this method to perform pre-analysis
330+
configuration based on runtime data contained in "data/auxiliary/<package_name>".
331+
332+
This method raises:
333+
- ImportError when any exception occurs during import
334+
- AttributeError if the module configure function is invalid
335+
- ModuleNotFoundError if the module does not support configuration from data
336+
"""
337+
package_module_name = self.__class__.__module__.split(".")[-1]
338+
module_name = f"data.auxiliary.{package_module_name}"
339+
try:
340+
mod = importlib.import_module(module_name)
341+
except ModuleNotFoundError as exc:
342+
raise exc
343+
except Exception as exc:
344+
raise ImportError(f"error importing {module_name}: {exc}") from exc
345+
346+
spec = inspect.getfullargspec(mod.configure)
347+
if len(spec.args) != 1:
348+
err_msg = f"{module_name}.configure: expected 1 arguments, got {len(spec.args)}"
349+
raise AttributeError(err_msg)
350+
mod.configure(self)

docs/book/src/customization/auxiliary.rst

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,46 @@ very end of the analysis process, before launching the processing and reporting
2727

2828
For example, an auxiliary module provided by default in CAPE is called *sniffer.py* and
2929
takes care of executing **tcpdump** in order to dump the generated network traffic.
30+
31+
32+
Private Auxiliary Configuration
33+
-----------------------------
34+
35+
Private auxiliary module configuration is stored outside the auxiliary class, in a module
36+
under the same name as the auxiliary module. This is useful when managing configuration
37+
of auxiliary modules separately if desired, for privacy reasons or otherwise.
38+
39+
Here is a configuration module example that installs some software prior to the auxiliary
40+
module starting:
41+
42+
.. code-block:: python
43+
:linenos:
44+
45+
# data/auxiliary/example.py
46+
import subprocess
47+
import logging
48+
from pathlib import Path
49+
50+
log = logging.getLogger(__name__)
51+
BIN_PATH = Path.cwd() / "bin"
52+
53+
54+
def configure(aux_instance):
55+
# here "example" refers to modules.auxiliary.example.Example
56+
if not aux_instance.enabled:
57+
return
58+
msi = aux_instance.options.get("example_msi")
59+
if not msi:
60+
return
61+
msi_path = BIN_PATH / msi
62+
if not msi_path.exists():
63+
log.warning("missing MSI %s", msi_path)
64+
return
65+
cmd = ["msiexec", "/i", msi_path, "/quiet"]
66+
try:
67+
log.info("Executing msi package...")
68+
subprocess.check_output(cmd)
69+
log.info("Installation succesful")
70+
except subprocess.CalledProcessError as exc:
71+
log.error("Installation failed: %s", exc)
72+
return

0 commit comments

Comments
 (0)