maco 1.2.15__py3-none-any.whl → 1.2.16__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 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, Set, Tuple, Union
30
+ from typing import Callable, Dict, List, Tuple, Union
33
31
 
34
32
  from uv import find_uv_bin
35
33
 
@@ -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
- # Insert any virtual environment necessary to load directory as package
379
- package_venv, package_site_packages = find_and_insert_venv(current_directory, venvs)
380
- package = importlib.import_module(package_name)
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
- # Walk through our new package and find the extractors that YARA identified
383
- for module_path, module_name, ispkg in walk_packages(package.__path__, package.__name__ + "."):
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."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maco
3
- Version: 1.2.15
3
+ Version: 1.2.16
4
4
  Author: sl-govau
5
5
  Maintainer: cccs-rs
6
6
  License: MIT License
@@ -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=rXKrrKTNi7DEC5SZUnUQcxnRRmJXRp0y4DuVaDkBYvY,24977
17
+ maco/utils.py,sha256=wzecqu1W_BbhDHSs07sweOKNeFrmC4CDYmAN_qyYJS4,23362
18
18
  maco/yara.py,sha256=gkHHxwZNxzZV7nHZM3HNUmhHXB7VW82voCHK5mHpt2Q,3970
19
19
  maco/model/__init__.py,sha256=ULdyHx8R5D2ICHZo3VoCk1YTlewTok36TYIpwx__pNY,45
20
- maco/model/model.py,sha256=whdeqwphReHpgQ5f6kODB7pQI3UEylTTiVqNq_FHNBg,24156
21
- maco-1.2.15.dist-info/licenses/LICENSE.md,sha256=gMSjshPhXvV_F1qxmeNkKdBqGWkd__fEJf4glS504bM,1478
20
+ maco/model/model.py,sha256=DBHTmZXMzjpVq0s2mzZv3VCzPhwPnv7sH6u_QZCTcA4,24484
21
+ maco-1.2.16.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=rXKrrKTNi7DEC5SZUnUQcxnRRmJXRp0y4DuVaDkBYvY,24977
28
+ model_setup/maco/utils.py,sha256=wzecqu1W_BbhDHSs07sweOKNeFrmC4CDYmAN_qyYJS4,23362
29
29
  model_setup/maco/yara.py,sha256=gkHHxwZNxzZV7nHZM3HNUmhHXB7VW82voCHK5mHpt2Q,3970
30
30
  model_setup/maco/model/__init__.py,sha256=ULdyHx8R5D2ICHZo3VoCk1YTlewTok36TYIpwx__pNY,45
31
- model_setup/maco/model/model.py,sha256=whdeqwphReHpgQ5f6kODB7pQI3UEylTTiVqNq_FHNBg,24156
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.15.dist-info/METADATA,sha256=BBjPNqDyPQPPwFHL0G0LqOrM2zYURFENydH2K63J6aU,15232
46
- maco-1.2.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
- maco-1.2.15.dist-info/entry_points.txt,sha256=TpcwG1gedIg8Y7a9ZOv8aQpuwEUftCefDrAjzeP-o6U,39
48
- maco-1.2.15.dist-info/top_level.txt,sha256=iMRwuzmrHA3zSwiSeMIl6FWhzRpn_st-I4fAv-kw5_o,49
49
- maco-1.2.15.dist-info/RECORD,,
45
+ maco-1.2.16.dist-info/METADATA,sha256=v5ZV9RoifsSn6im1tO0wpUJeKPGW3Wf-_C_BjF7qNmM,15232
46
+ maco-1.2.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
+ maco-1.2.16.dist-info/entry_points.txt,sha256=TpcwG1gedIg8Y7a9ZOv8aQpuwEUftCefDrAjzeP-o6U,39
48
+ maco-1.2.16.dist-info/top_level.txt,sha256=iMRwuzmrHA3zSwiSeMIl6FWhzRpn_st-I4fAv-kw5_o,49
49
+ maco-1.2.16.dist-info/RECORD,,
@@ -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, Set, Tuple, Union
30
+ from typing import Callable, Dict, List, Tuple, Union
33
31
 
34
32
  from uv import find_uv_bin
35
33
 
@@ -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
- # Insert any virtual environment necessary to load directory as package
379
- package_venv, package_site_packages = find_and_insert_venv(current_directory, venvs)
380
- package = importlib.import_module(package_name)
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
- # Walk through our new package and find the extractors that YARA identified
383
- for module_path, module_name, ispkg in walk_packages(package.__path__, package.__name__ + "."):
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."""
File without changes