maco 1.2.15__py3-none-any.whl → 1.2.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- maco/model/model.py +12 -0
- maco/utils.py +49 -71
- maco/yara.py +5 -2
- {maco-1.2.15.dist-info → maco-1.2.17.dist-info}/METADATA +2 -2
- {maco-1.2.15.dist-info → maco-1.2.17.dist-info}/RECORD +12 -12
- model_setup/maco/model/model.py +12 -0
- model_setup/maco/utils.py +49 -71
- model_setup/maco/yara.py +5 -2
- {maco-1.2.15.dist-info → maco-1.2.17.dist-info}/WHEEL +0 -0
- {maco-1.2.15.dist-info → maco-1.2.17.dist-info}/entry_points.txt +0 -0
- {maco-1.2.15.dist-info → maco-1.2.17.dist-info}/licenses/LICENSE.md +0 -0
- {maco-1.2.15.dist-info → maco-1.2.17.dist-info}/top_level.txt +0 -0
maco/model/model.py
CHANGED
|
@@ -391,6 +391,18 @@ class ExtractorModel(ForbidModel):
|
|
|
391
391
|
|
|
392
392
|
proxy: List[Proxy] = []
|
|
393
393
|
|
|
394
|
+
class ICMP(ForbidModel):
|
|
395
|
+
"""Usage of ICMP."""
|
|
396
|
+
|
|
397
|
+
type: Optional[int] = None
|
|
398
|
+
code: Optional[int] = None
|
|
399
|
+
header: Optional[str] = None # Some malware uses non-standard header fields
|
|
400
|
+
hostname: Optional[str] = None
|
|
401
|
+
|
|
402
|
+
usage: Optional[ConnUsageEnum] = None
|
|
403
|
+
|
|
404
|
+
icmp: List[ICMP] = []
|
|
405
|
+
|
|
394
406
|
#
|
|
395
407
|
# inter process communication (IPC)
|
|
396
408
|
#
|
maco/utils.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"""Common utilities shared between the MACO collector and configextractor-py."""
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
|
-
import importlib.machinery
|
|
5
|
-
import importlib.util
|
|
6
4
|
import inspect
|
|
7
5
|
import json
|
|
8
6
|
import logging
|
|
@@ -13,8 +11,9 @@ import shutil
|
|
|
13
11
|
import subprocess
|
|
14
12
|
import sys
|
|
15
13
|
import tempfile
|
|
14
|
+
from importlib.machinery import SourceFileLoader
|
|
16
15
|
|
|
17
|
-
from multiprocess import Queue
|
|
16
|
+
from multiprocess import Process, Queue
|
|
18
17
|
|
|
19
18
|
from maco import yara
|
|
20
19
|
|
|
@@ -27,9 +26,8 @@ from base64 import b64decode
|
|
|
27
26
|
from copy import deepcopy
|
|
28
27
|
from glob import glob
|
|
29
28
|
from logging import Logger
|
|
30
|
-
from pkgutil import walk_packages
|
|
31
29
|
from types import ModuleType
|
|
32
|
-
from typing import Callable, Dict, List,
|
|
30
|
+
from typing import Callable, Dict, List, Tuple, Union
|
|
33
31
|
|
|
34
32
|
from uv import find_uv_bin
|
|
35
33
|
|
|
@@ -261,15 +259,15 @@ def _install_required_packages(create_venv: bool, directories: List[str], python
|
|
|
261
259
|
# This prevents issues during maco development and building extractors against local libraries.
|
|
262
260
|
if create_venv:
|
|
263
261
|
# when running in custom virtual environment, always upgrade packages.
|
|
264
|
-
install_command.
|
|
262
|
+
install_command.extend(["--upgrade", "--no-cache"])
|
|
265
263
|
|
|
266
264
|
# Update the pip install command depending on where the dependencies are coming from
|
|
267
265
|
if "requirements.txt" in req_files:
|
|
268
266
|
# Perform a pip install using the requirements flag
|
|
269
|
-
install_command.extend(["
|
|
267
|
+
install_command.extend(["--requirements", "requirements.txt"])
|
|
270
268
|
elif "pyproject.toml" in req_files:
|
|
271
269
|
# Assume we're dealing with a project directory
|
|
272
|
-
pyproject_command = ["
|
|
270
|
+
pyproject_command = ["--editable", "."]
|
|
273
271
|
|
|
274
272
|
# Check to see if there are optional dependencies required
|
|
275
273
|
with open(os.path.join(dir, "pyproject.toml"), "rb") as f:
|
|
@@ -343,13 +341,43 @@ def find_and_insert_venv(path: str, venvs: List[str]) -> Tuple[str, str]:
|
|
|
343
341
|
return venv, site_package
|
|
344
342
|
|
|
345
343
|
|
|
344
|
+
def register_extractor_module(
|
|
345
|
+
extractor_source_file: str,
|
|
346
|
+
module_name: str,
|
|
347
|
+
venvs: List[str],
|
|
348
|
+
extractor_module_callback: Callable[[ModuleType, str], None],
|
|
349
|
+
logger: Logger,
|
|
350
|
+
):
|
|
351
|
+
"""Register the extractor module in isolation.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
extractor_source_file (str): Path to source file of extractor
|
|
355
|
+
module_name (str): The name of the module relative to the package directory
|
|
356
|
+
venvs (List[str]): List of virtual environments
|
|
357
|
+
extractor_module_callback (Callable[[ModuleType, str], None]): Callback used to register extractors
|
|
358
|
+
logger (Logger): Logger to use
|
|
359
|
+
|
|
360
|
+
"""
|
|
361
|
+
try:
|
|
362
|
+
logger.info(f"Inspecting '{extractor_source_file}' for extractors..")
|
|
363
|
+
venv, site_packages = find_and_insert_venv(extractor_source_file, venvs)
|
|
364
|
+
loader = SourceFileLoader(
|
|
365
|
+
module_name,
|
|
366
|
+
extractor_source_file,
|
|
367
|
+
)
|
|
368
|
+
extractor_module_callback(loader.load_module(), venv)
|
|
369
|
+
finally:
|
|
370
|
+
# Cleanup virtual environment that was loaded into PATH
|
|
371
|
+
if venv and site_packages in sys.path:
|
|
372
|
+
sys.path.remove(site_packages)
|
|
373
|
+
|
|
374
|
+
|
|
346
375
|
def register_extractors(
|
|
347
376
|
current_directory: str,
|
|
348
377
|
venvs: List[str],
|
|
349
378
|
extractor_files: List[str],
|
|
350
379
|
extractor_module_callback: Callable[[ModuleType, str], None],
|
|
351
380
|
logger: Logger,
|
|
352
|
-
default_loaded_modules: Set[str] = set(sys.modules.keys()),
|
|
353
381
|
):
|
|
354
382
|
"""Register extractors with in the current directory.
|
|
355
383
|
|
|
@@ -359,7 +387,6 @@ def register_extractors(
|
|
|
359
387
|
extractor_files (List[str]): List of extractor files found
|
|
360
388
|
extractor_module_callback (Callable[[ModuleType, str], None]): Callback used to register extractors
|
|
361
389
|
logger (Logger): Logger to use
|
|
362
|
-
default_loaded_modules (Set[str]): Set of default loaded modules
|
|
363
390
|
"""
|
|
364
391
|
package_name = os.path.basename(current_directory)
|
|
365
392
|
parent_directory = os.path.dirname(current_directory)
|
|
@@ -375,74 +402,25 @@ def register_extractors(
|
|
|
375
402
|
sys.path.insert(1, current_directory)
|
|
376
403
|
sys.path.insert(1, parent_directory)
|
|
377
404
|
|
|
378
|
-
#
|
|
379
|
-
|
|
380
|
-
|
|
405
|
+
# Load the potential extractors directly from the source file
|
|
406
|
+
registration_processes = []
|
|
407
|
+
for extractor_source_file in extractor_files:
|
|
408
|
+
module_name = extractor_source_file.replace(f"{parent_directory}/", "").replace("/", ".")[:-3]
|
|
409
|
+
p = Process(
|
|
410
|
+
target=register_extractor_module,
|
|
411
|
+
args=(extractor_source_file, module_name, venvs, extractor_module_callback, logger),
|
|
412
|
+
)
|
|
413
|
+
p.start()
|
|
414
|
+
registration_processes.append(p)
|
|
381
415
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if ispkg:
|
|
385
|
-
# Skip packages
|
|
386
|
-
continue
|
|
416
|
+
for p in registration_processes:
|
|
417
|
+
p.join()
|
|
387
418
|
|
|
388
|
-
module_path = os.path.realpath(os.path.join(module_path.path, module_name.rsplit(".", 1)[1]) + ".py")
|
|
389
|
-
if module_path in extractor_files:
|
|
390
|
-
# Cross this extractor off the list of extractors to find
|
|
391
|
-
logger.debug(f"Inspecting '{module_name}' for extractors..")
|
|
392
|
-
extractor_files.remove(module_path)
|
|
393
|
-
try:
|
|
394
|
-
# This is an extractor we've been looking for, load the module and invoke callback
|
|
395
|
-
venv, site_packages = find_and_insert_venv(module_path, venvs)
|
|
396
|
-
module = importlib.import_module(module_name)
|
|
397
|
-
module.__file__ = os.path.realpath(module.__file__)
|
|
398
|
-
|
|
399
|
-
# Patch the original directory information into the module
|
|
400
|
-
original_package_name = os.path.basename(current_directory)
|
|
401
|
-
module.__name__ = module.__name__.replace(package_name, original_package_name)
|
|
402
|
-
module.__package__ = module.__package__.replace(package_name, original_package_name)
|
|
403
|
-
extractor_module_callback(module, venv)
|
|
404
|
-
finally:
|
|
405
|
-
# Cleanup virtual environment that was loaded into PATH
|
|
406
|
-
if venv and site_packages in sys.path:
|
|
407
|
-
sys.path.remove(site_packages)
|
|
408
|
-
|
|
409
|
-
if not extractor_files:
|
|
410
|
-
return
|
|
411
419
|
finally:
|
|
412
420
|
# Cleanup changes made to PATH
|
|
413
421
|
sys.path.remove(parent_directory)
|
|
414
422
|
sys.path.remove(current_directory)
|
|
415
423
|
|
|
416
|
-
if package_venv and package_site_packages in sys.path:
|
|
417
|
-
sys.path.remove(package_site_packages)
|
|
418
|
-
|
|
419
|
-
# Remove any modules that were loaded to deconflict with later modules loads
|
|
420
|
-
[sys.modules.pop(k) for k in set(sys.modules.keys()) - default_loaded_modules]
|
|
421
|
-
|
|
422
|
-
# If there still exists extractor files we haven't found yet, try searching in the available subdirectories
|
|
423
|
-
if extractor_files:
|
|
424
|
-
for dir in os.listdir(current_directory):
|
|
425
|
-
path = os.path.join(current_directory, dir)
|
|
426
|
-
if dir == "__pycache__":
|
|
427
|
-
# Ignore the cache created
|
|
428
|
-
continue
|
|
429
|
-
elif dir.endswith(".egg-info"):
|
|
430
|
-
# Ignore these directories
|
|
431
|
-
continue
|
|
432
|
-
elif dir.startswith("."):
|
|
433
|
-
# Ignore hidden directories
|
|
434
|
-
continue
|
|
435
|
-
|
|
436
|
-
if os.path.isdir(path):
|
|
437
|
-
# Check subdirectory to find the rest of the detected extractors
|
|
438
|
-
register_extractors(
|
|
439
|
-
path, venvs, extractor_files, extractor_module_callback, logger, default_loaded_modules
|
|
440
|
-
)
|
|
441
|
-
|
|
442
|
-
if not extractor_files:
|
|
443
|
-
# We were able to find all the extractor files
|
|
444
|
-
break
|
|
445
|
-
|
|
446
424
|
|
|
447
425
|
def proxy_logging(queue: Queue, callback: Callable[[ModuleType, str], None], *args, **kwargs):
|
|
448
426
|
"""Ensures logging is set up correctly for a child process and then executes the callback."""
|
maco/yara.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import re
|
|
4
4
|
from collections import namedtuple
|
|
5
5
|
from itertools import cycle
|
|
6
|
-
from typing import Dict, List
|
|
6
|
+
from typing import Dict, List, Union
|
|
7
7
|
|
|
8
8
|
import yara_x
|
|
9
9
|
|
|
@@ -104,7 +104,7 @@ class Rules:
|
|
|
104
104
|
for rule in self._rules:
|
|
105
105
|
yield rule
|
|
106
106
|
|
|
107
|
-
def match(self, filepath: str = None, data: bytes = None) -> List[Match]:
|
|
107
|
+
def match(self, filepath: str = None, data: Union[bytes, bytearray] = None) -> List[Match]:
|
|
108
108
|
"""Performs a scan to check for YARA rules matches based on the file, either given by path or buffer.
|
|
109
109
|
|
|
110
110
|
Returns:
|
|
@@ -114,6 +114,9 @@ class Rules:
|
|
|
114
114
|
with open(filepath, "rb") as fp:
|
|
115
115
|
data = fp.read()
|
|
116
116
|
|
|
117
|
+
if isinstance(data, bytearray):
|
|
118
|
+
data = bytes(data)
|
|
119
|
+
|
|
117
120
|
return [Match(m, data) for m in self.scanner.scan(data).matching_rules]
|
|
118
121
|
|
|
119
122
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maco
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.17
|
|
4
4
|
Author: sl-govau
|
|
5
5
|
Maintainer: cccs-rs
|
|
6
6
|
License: MIT License
|
|
@@ -33,7 +33,7 @@ Requires-Dist: cart
|
|
|
33
33
|
Requires-Dist: pydantic>=2.0.0
|
|
34
34
|
Requires-Dist: tomli>=1.1.0; python_version < "3.11"
|
|
35
35
|
Requires-Dist: uv
|
|
36
|
-
Requires-Dist: yara-x
|
|
36
|
+
Requires-Dist: yara-x
|
|
37
37
|
Requires-Dist: multiprocess>=0.70.17
|
|
38
38
|
Dynamic: license-file
|
|
39
39
|
|
|
@@ -14,21 +14,21 @@ maco/cli.py,sha256=nrSukAJAthbstZT3-lQNPz4zOOMcBhvfYQqLh_B5Jdk,9457
|
|
|
14
14
|
maco/collector.py,sha256=R3zw-fUJBlwmcSqvkQ-PnoJdHfRm2V0JAOl7N8MTAbY,8240
|
|
15
15
|
maco/exceptions.py,sha256=XBHUrs1kr1ZayPI9B_W-WejKgVmC8sWL_o4RL0b4DQE,745
|
|
16
16
|
maco/extractor.py,sha256=s36aGcsXSc-9iCik6iihVt5G1a1DZUA7TquvWYQNwdE,2912
|
|
17
|
-
maco/utils.py,sha256=
|
|
18
|
-
maco/yara.py,sha256=
|
|
17
|
+
maco/utils.py,sha256=7Xf-kWCDm1DdpBCGOvecEb_hqKoRgNJi0M_OYsJeSrM,23405
|
|
18
|
+
maco/yara.py,sha256=y141t8NqDDXHY23uE1d6BDPeNmSuUuohR6Yr_LKa7GI,4067
|
|
19
19
|
maco/model/__init__.py,sha256=ULdyHx8R5D2ICHZo3VoCk1YTlewTok36TYIpwx__pNY,45
|
|
20
|
-
maco/model/model.py,sha256=
|
|
21
|
-
maco-1.2.
|
|
20
|
+
maco/model/model.py,sha256=DBHTmZXMzjpVq0s2mzZv3VCzPhwPnv7sH6u_QZCTcA4,24484
|
|
21
|
+
maco-1.2.17.dist-info/licenses/LICENSE.md,sha256=gMSjshPhXvV_F1qxmeNkKdBqGWkd__fEJf4glS504bM,1478
|
|
22
22
|
model_setup/maco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
model_setup/maco/base_test.py,sha256=DrVE7vOazeLQpOQeIDwBYK1WtlmdJrRe50JOqP5t4Y0,3198
|
|
24
24
|
model_setup/maco/cli.py,sha256=nrSukAJAthbstZT3-lQNPz4zOOMcBhvfYQqLh_B5Jdk,9457
|
|
25
25
|
model_setup/maco/collector.py,sha256=R3zw-fUJBlwmcSqvkQ-PnoJdHfRm2V0JAOl7N8MTAbY,8240
|
|
26
26
|
model_setup/maco/exceptions.py,sha256=XBHUrs1kr1ZayPI9B_W-WejKgVmC8sWL_o4RL0b4DQE,745
|
|
27
27
|
model_setup/maco/extractor.py,sha256=s36aGcsXSc-9iCik6iihVt5G1a1DZUA7TquvWYQNwdE,2912
|
|
28
|
-
model_setup/maco/utils.py,sha256=
|
|
29
|
-
model_setup/maco/yara.py,sha256=
|
|
28
|
+
model_setup/maco/utils.py,sha256=7Xf-kWCDm1DdpBCGOvecEb_hqKoRgNJi0M_OYsJeSrM,23405
|
|
29
|
+
model_setup/maco/yara.py,sha256=y141t8NqDDXHY23uE1d6BDPeNmSuUuohR6Yr_LKa7GI,4067
|
|
30
30
|
model_setup/maco/model/__init__.py,sha256=ULdyHx8R5D2ICHZo3VoCk1YTlewTok36TYIpwx__pNY,45
|
|
31
|
-
model_setup/maco/model/model.py,sha256=
|
|
31
|
+
model_setup/maco/model/model.py,sha256=DBHTmZXMzjpVq0s2mzZv3VCzPhwPnv7sH6u_QZCTcA4,24484
|
|
32
32
|
pipelines/publish.yaml,sha256=xt3WNU-5kIICJgKIiiE94M3dWjS3uEiun-n4OmIssK8,1471
|
|
33
33
|
pipelines/test.yaml,sha256=btJVI-R39UBeYosGu7TOpU6V9ogFW3FT3ROtWygQGQ0,1472
|
|
34
34
|
tests/data/example.txt.cart,sha256=j4ZdDnFNVq7lb-Qi4pY4evOXKQPKG-GSg-n-uEqPhV0,289
|
|
@@ -42,8 +42,8 @@ tests/extractors/bob/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
42
42
|
tests/extractors/bob/bob.py,sha256=4fpqy_O6NDinJImghyW5OwYgnaB05aY4kgoIS_C3c_U,253
|
|
43
43
|
tests/extractors/import_rewriting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
44
|
tests/extractors/import_rewriting/importer.py,sha256=wqF1AG2zXXuj9EMt9qlDorab-UD0GYuFggtrCuz4sf0,289735
|
|
45
|
-
maco-1.2.
|
|
46
|
-
maco-1.2.
|
|
47
|
-
maco-1.2.
|
|
48
|
-
maco-1.2.
|
|
49
|
-
maco-1.2.
|
|
45
|
+
maco-1.2.17.dist-info/METADATA,sha256=7NtcX0tJ94BxrswwvqTQO9Ou-UVURL5-j0nBOSGoWQY,15224
|
|
46
|
+
maco-1.2.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
47
|
+
maco-1.2.17.dist-info/entry_points.txt,sha256=TpcwG1gedIg8Y7a9ZOv8aQpuwEUftCefDrAjzeP-o6U,39
|
|
48
|
+
maco-1.2.17.dist-info/top_level.txt,sha256=iMRwuzmrHA3zSwiSeMIl6FWhzRpn_st-I4fAv-kw5_o,49
|
|
49
|
+
maco-1.2.17.dist-info/RECORD,,
|
model_setup/maco/model/model.py
CHANGED
|
@@ -391,6 +391,18 @@ class ExtractorModel(ForbidModel):
|
|
|
391
391
|
|
|
392
392
|
proxy: List[Proxy] = []
|
|
393
393
|
|
|
394
|
+
class ICMP(ForbidModel):
|
|
395
|
+
"""Usage of ICMP."""
|
|
396
|
+
|
|
397
|
+
type: Optional[int] = None
|
|
398
|
+
code: Optional[int] = None
|
|
399
|
+
header: Optional[str] = None # Some malware uses non-standard header fields
|
|
400
|
+
hostname: Optional[str] = None
|
|
401
|
+
|
|
402
|
+
usage: Optional[ConnUsageEnum] = None
|
|
403
|
+
|
|
404
|
+
icmp: List[ICMP] = []
|
|
405
|
+
|
|
394
406
|
#
|
|
395
407
|
# inter process communication (IPC)
|
|
396
408
|
#
|
model_setup/maco/utils.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"""Common utilities shared between the MACO collector and configextractor-py."""
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
|
-
import importlib.machinery
|
|
5
|
-
import importlib.util
|
|
6
4
|
import inspect
|
|
7
5
|
import json
|
|
8
6
|
import logging
|
|
@@ -13,8 +11,9 @@ import shutil
|
|
|
13
11
|
import subprocess
|
|
14
12
|
import sys
|
|
15
13
|
import tempfile
|
|
14
|
+
from importlib.machinery import SourceFileLoader
|
|
16
15
|
|
|
17
|
-
from multiprocess import Queue
|
|
16
|
+
from multiprocess import Process, Queue
|
|
18
17
|
|
|
19
18
|
from maco import yara
|
|
20
19
|
|
|
@@ -27,9 +26,8 @@ from base64 import b64decode
|
|
|
27
26
|
from copy import deepcopy
|
|
28
27
|
from glob import glob
|
|
29
28
|
from logging import Logger
|
|
30
|
-
from pkgutil import walk_packages
|
|
31
29
|
from types import ModuleType
|
|
32
|
-
from typing import Callable, Dict, List,
|
|
30
|
+
from typing import Callable, Dict, List, Tuple, Union
|
|
33
31
|
|
|
34
32
|
from uv import find_uv_bin
|
|
35
33
|
|
|
@@ -261,15 +259,15 @@ def _install_required_packages(create_venv: bool, directories: List[str], python
|
|
|
261
259
|
# This prevents issues during maco development and building extractors against local libraries.
|
|
262
260
|
if create_venv:
|
|
263
261
|
# when running in custom virtual environment, always upgrade packages.
|
|
264
|
-
install_command.
|
|
262
|
+
install_command.extend(["--upgrade", "--no-cache"])
|
|
265
263
|
|
|
266
264
|
# Update the pip install command depending on where the dependencies are coming from
|
|
267
265
|
if "requirements.txt" in req_files:
|
|
268
266
|
# Perform a pip install using the requirements flag
|
|
269
|
-
install_command.extend(["
|
|
267
|
+
install_command.extend(["--requirements", "requirements.txt"])
|
|
270
268
|
elif "pyproject.toml" in req_files:
|
|
271
269
|
# Assume we're dealing with a project directory
|
|
272
|
-
pyproject_command = ["
|
|
270
|
+
pyproject_command = ["--editable", "."]
|
|
273
271
|
|
|
274
272
|
# Check to see if there are optional dependencies required
|
|
275
273
|
with open(os.path.join(dir, "pyproject.toml"), "rb") as f:
|
|
@@ -343,13 +341,43 @@ def find_and_insert_venv(path: str, venvs: List[str]) -> Tuple[str, str]:
|
|
|
343
341
|
return venv, site_package
|
|
344
342
|
|
|
345
343
|
|
|
344
|
+
def register_extractor_module(
|
|
345
|
+
extractor_source_file: str,
|
|
346
|
+
module_name: str,
|
|
347
|
+
venvs: List[str],
|
|
348
|
+
extractor_module_callback: Callable[[ModuleType, str], None],
|
|
349
|
+
logger: Logger,
|
|
350
|
+
):
|
|
351
|
+
"""Register the extractor module in isolation.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
extractor_source_file (str): Path to source file of extractor
|
|
355
|
+
module_name (str): The name of the module relative to the package directory
|
|
356
|
+
venvs (List[str]): List of virtual environments
|
|
357
|
+
extractor_module_callback (Callable[[ModuleType, str], None]): Callback used to register extractors
|
|
358
|
+
logger (Logger): Logger to use
|
|
359
|
+
|
|
360
|
+
"""
|
|
361
|
+
try:
|
|
362
|
+
logger.info(f"Inspecting '{extractor_source_file}' for extractors..")
|
|
363
|
+
venv, site_packages = find_and_insert_venv(extractor_source_file, venvs)
|
|
364
|
+
loader = SourceFileLoader(
|
|
365
|
+
module_name,
|
|
366
|
+
extractor_source_file,
|
|
367
|
+
)
|
|
368
|
+
extractor_module_callback(loader.load_module(), venv)
|
|
369
|
+
finally:
|
|
370
|
+
# Cleanup virtual environment that was loaded into PATH
|
|
371
|
+
if venv and site_packages in sys.path:
|
|
372
|
+
sys.path.remove(site_packages)
|
|
373
|
+
|
|
374
|
+
|
|
346
375
|
def register_extractors(
|
|
347
376
|
current_directory: str,
|
|
348
377
|
venvs: List[str],
|
|
349
378
|
extractor_files: List[str],
|
|
350
379
|
extractor_module_callback: Callable[[ModuleType, str], None],
|
|
351
380
|
logger: Logger,
|
|
352
|
-
default_loaded_modules: Set[str] = set(sys.modules.keys()),
|
|
353
381
|
):
|
|
354
382
|
"""Register extractors with in the current directory.
|
|
355
383
|
|
|
@@ -359,7 +387,6 @@ def register_extractors(
|
|
|
359
387
|
extractor_files (List[str]): List of extractor files found
|
|
360
388
|
extractor_module_callback (Callable[[ModuleType, str], None]): Callback used to register extractors
|
|
361
389
|
logger (Logger): Logger to use
|
|
362
|
-
default_loaded_modules (Set[str]): Set of default loaded modules
|
|
363
390
|
"""
|
|
364
391
|
package_name = os.path.basename(current_directory)
|
|
365
392
|
parent_directory = os.path.dirname(current_directory)
|
|
@@ -375,74 +402,25 @@ def register_extractors(
|
|
|
375
402
|
sys.path.insert(1, current_directory)
|
|
376
403
|
sys.path.insert(1, parent_directory)
|
|
377
404
|
|
|
378
|
-
#
|
|
379
|
-
|
|
380
|
-
|
|
405
|
+
# Load the potential extractors directly from the source file
|
|
406
|
+
registration_processes = []
|
|
407
|
+
for extractor_source_file in extractor_files:
|
|
408
|
+
module_name = extractor_source_file.replace(f"{parent_directory}/", "").replace("/", ".")[:-3]
|
|
409
|
+
p = Process(
|
|
410
|
+
target=register_extractor_module,
|
|
411
|
+
args=(extractor_source_file, module_name, venvs, extractor_module_callback, logger),
|
|
412
|
+
)
|
|
413
|
+
p.start()
|
|
414
|
+
registration_processes.append(p)
|
|
381
415
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if ispkg:
|
|
385
|
-
# Skip packages
|
|
386
|
-
continue
|
|
416
|
+
for p in registration_processes:
|
|
417
|
+
p.join()
|
|
387
418
|
|
|
388
|
-
module_path = os.path.realpath(os.path.join(module_path.path, module_name.rsplit(".", 1)[1]) + ".py")
|
|
389
|
-
if module_path in extractor_files:
|
|
390
|
-
# Cross this extractor off the list of extractors to find
|
|
391
|
-
logger.debug(f"Inspecting '{module_name}' for extractors..")
|
|
392
|
-
extractor_files.remove(module_path)
|
|
393
|
-
try:
|
|
394
|
-
# This is an extractor we've been looking for, load the module and invoke callback
|
|
395
|
-
venv, site_packages = find_and_insert_venv(module_path, venvs)
|
|
396
|
-
module = importlib.import_module(module_name)
|
|
397
|
-
module.__file__ = os.path.realpath(module.__file__)
|
|
398
|
-
|
|
399
|
-
# Patch the original directory information into the module
|
|
400
|
-
original_package_name = os.path.basename(current_directory)
|
|
401
|
-
module.__name__ = module.__name__.replace(package_name, original_package_name)
|
|
402
|
-
module.__package__ = module.__package__.replace(package_name, original_package_name)
|
|
403
|
-
extractor_module_callback(module, venv)
|
|
404
|
-
finally:
|
|
405
|
-
# Cleanup virtual environment that was loaded into PATH
|
|
406
|
-
if venv and site_packages in sys.path:
|
|
407
|
-
sys.path.remove(site_packages)
|
|
408
|
-
|
|
409
|
-
if not extractor_files:
|
|
410
|
-
return
|
|
411
419
|
finally:
|
|
412
420
|
# Cleanup changes made to PATH
|
|
413
421
|
sys.path.remove(parent_directory)
|
|
414
422
|
sys.path.remove(current_directory)
|
|
415
423
|
|
|
416
|
-
if package_venv and package_site_packages in sys.path:
|
|
417
|
-
sys.path.remove(package_site_packages)
|
|
418
|
-
|
|
419
|
-
# Remove any modules that were loaded to deconflict with later modules loads
|
|
420
|
-
[sys.modules.pop(k) for k in set(sys.modules.keys()) - default_loaded_modules]
|
|
421
|
-
|
|
422
|
-
# If there still exists extractor files we haven't found yet, try searching in the available subdirectories
|
|
423
|
-
if extractor_files:
|
|
424
|
-
for dir in os.listdir(current_directory):
|
|
425
|
-
path = os.path.join(current_directory, dir)
|
|
426
|
-
if dir == "__pycache__":
|
|
427
|
-
# Ignore the cache created
|
|
428
|
-
continue
|
|
429
|
-
elif dir.endswith(".egg-info"):
|
|
430
|
-
# Ignore these directories
|
|
431
|
-
continue
|
|
432
|
-
elif dir.startswith("."):
|
|
433
|
-
# Ignore hidden directories
|
|
434
|
-
continue
|
|
435
|
-
|
|
436
|
-
if os.path.isdir(path):
|
|
437
|
-
# Check subdirectory to find the rest of the detected extractors
|
|
438
|
-
register_extractors(
|
|
439
|
-
path, venvs, extractor_files, extractor_module_callback, logger, default_loaded_modules
|
|
440
|
-
)
|
|
441
|
-
|
|
442
|
-
if not extractor_files:
|
|
443
|
-
# We were able to find all the extractor files
|
|
444
|
-
break
|
|
445
|
-
|
|
446
424
|
|
|
447
425
|
def proxy_logging(queue: Queue, callback: Callable[[ModuleType, str], None], *args, **kwargs):
|
|
448
426
|
"""Ensures logging is set up correctly for a child process and then executes the callback."""
|
model_setup/maco/yara.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import re
|
|
4
4
|
from collections import namedtuple
|
|
5
5
|
from itertools import cycle
|
|
6
|
-
from typing import Dict, List
|
|
6
|
+
from typing import Dict, List, Union
|
|
7
7
|
|
|
8
8
|
import yara_x
|
|
9
9
|
|
|
@@ -104,7 +104,7 @@ class Rules:
|
|
|
104
104
|
for rule in self._rules:
|
|
105
105
|
yield rule
|
|
106
106
|
|
|
107
|
-
def match(self, filepath: str = None, data: bytes = None) -> List[Match]:
|
|
107
|
+
def match(self, filepath: str = None, data: Union[bytes, bytearray] = None) -> List[Match]:
|
|
108
108
|
"""Performs a scan to check for YARA rules matches based on the file, either given by path or buffer.
|
|
109
109
|
|
|
110
110
|
Returns:
|
|
@@ -114,6 +114,9 @@ class Rules:
|
|
|
114
114
|
with open(filepath, "rb") as fp:
|
|
115
115
|
data = fp.read()
|
|
116
116
|
|
|
117
|
+
if isinstance(data, bytearray):
|
|
118
|
+
data = bytes(data)
|
|
119
|
+
|
|
117
120
|
return [Match(m, data) for m in self.scanner.scan(data).matching_rules]
|
|
118
121
|
|
|
119
122
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|