maco 1.2.16__py3-none-any.whl → 1.2.18__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.
@@ -0,0 +1,129 @@
1
+ """yara-python facade that uses yara-x."""
2
+
3
+ import re
4
+ from collections import namedtuple
5
+ from itertools import cycle
6
+ from typing import Dict, List, Union
7
+
8
+ import yara_x
9
+
10
+ from maco.exceptions import SyntaxError
11
+
12
+ RULE_ID_RE = re.compile("(\w+)? ?rule (\w+)")
13
+
14
+
15
+ # Create interfaces that resembles yara-python (but is running yara-x under the hood)
16
+ class StringMatchInstance:
17
+ """Instance of a string match."""
18
+
19
+ def __init__(self, match: yara_x.Match, file_content: bytes):
20
+ """Initializes StringMatchInstance."""
21
+ self.matched_data = file_content[match.offset : match.offset + match.length]
22
+ self.matched_length = match.length
23
+ self.offset = match.offset
24
+ self.xor_key = match.xor_key
25
+
26
+ def plaintext(self) -> bytes:
27
+ """Plaintext of the matched data.
28
+
29
+ Returns:
30
+ (bytes): Plaintext of the matched cipher text
31
+ """
32
+ if not self.xor_key:
33
+ # No need to XOR the matched data
34
+ return self.matched_data
35
+ else:
36
+ return bytes(c ^ k for c, k in zip(self.matched_data, cycle(self.xor_key)))
37
+
38
+
39
+ class StringMatch:
40
+ """String match."""
41
+
42
+ def __init__(self, pattern: yara_x.Pattern, file_content: bytes):
43
+ """Initializes StringMatch."""
44
+ self.identifier = pattern.identifier
45
+ self.instances = [StringMatchInstance(match, file_content) for match in pattern.matches]
46
+ self._is_xor = any([match.xor_key for match in pattern.matches])
47
+
48
+ def is_xor(self):
49
+ """Checks if string match is xor'd.
50
+
51
+ Returns:
52
+ (bool): True if match is xor'd
53
+ """
54
+ return self._is_xor
55
+
56
+
57
+ class Match:
58
+ """Match."""
59
+
60
+ def __init__(self, rule: yara_x.Rule, file_content: bytes):
61
+ """Initializes Match."""
62
+ self.rule = rule.identifier
63
+ self.namespace = rule.namespace
64
+ self.tags = list(rule.tags) or []
65
+ self.meta = dict()
66
+ # Ensure metadata doesn't get overwritten
67
+ for k, v in rule.metadata:
68
+ self.meta.setdefault(k, []).append(v)
69
+ self.strings = [StringMatch(pattern, file_content) for pattern in rule.patterns]
70
+
71
+
72
+ class Rules:
73
+ """Rules."""
74
+
75
+ def __init__(self, source: str = None, sources: Dict[str, str] = None):
76
+ """Initializes Rules.
77
+
78
+ Raises:
79
+ SyntaxError: Raised when there's a syntax error in the YARA rule.
80
+ """
81
+ Rule = namedtuple("Rule", "identifier namespace is_global")
82
+ if source:
83
+ sources = {"default": source}
84
+
85
+ try:
86
+ self._rules = []
87
+ compiler = yara_x.Compiler(relaxed_re_syntax=True)
88
+ for namespace, source in sources.items():
89
+ compiler.new_namespace(namespace)
90
+ for rule_type, id in RULE_ID_RE.findall(source):
91
+ is_global = True if rule_type == "global" else False
92
+ self._rules.append(Rule(namespace=namespace, identifier=id, is_global=is_global))
93
+ compiler.add_source(source)
94
+ self.scanner = yara_x.Scanner(compiler.build())
95
+ except yara_x.CompileError as e:
96
+ raise SyntaxError(e)
97
+
98
+ def __iter__(self):
99
+ """Iterate over rules.
100
+
101
+ Yields:
102
+ YARA rules
103
+ """
104
+ for rule in self._rules:
105
+ yield rule
106
+
107
+ def match(self, filepath: str = None, data: Union[bytes, bytearray] = None) -> List[Match]:
108
+ """Performs a scan to check for YARA rules matches based on the file, either given by path or buffer.
109
+
110
+ Returns:
111
+ (List[Match]): A list of YARA matches.
112
+ """
113
+ if filepath:
114
+ with open(filepath, "rb") as fp:
115
+ data = fp.read()
116
+
117
+ if isinstance(data, bytearray):
118
+ data = bytes(data)
119
+
120
+ return [Match(m, data) for m in self.scanner.scan(data).matching_rules]
121
+
122
+
123
+ def compile(source: str = None, sources: Dict[str, str] = None) -> Rules:
124
+ """Compiles YARA rules from source or from sources.
125
+
126
+ Returns:
127
+ (Rules): a Rules object
128
+ """
129
+ return Rules(source, sources)
maco/utils.py CHANGED
@@ -259,15 +259,15 @@ def _install_required_packages(create_venv: bool, directories: List[str], python
259
259
  # This prevents issues during maco development and building extractors against local libraries.
260
260
  if create_venv:
261
261
  # when running in custom virtual environment, always upgrade packages.
262
- install_command.append("-U")
262
+ install_command.extend(["--upgrade", "--no-cache"])
263
263
 
264
264
  # Update the pip install command depending on where the dependencies are coming from
265
265
  if "requirements.txt" in req_files:
266
266
  # Perform a pip install using the requirements flag
267
- install_command.extend(["-r", "requirements.txt"])
267
+ install_command.extend(["--requirements", "requirements.txt"])
268
268
  elif "pyproject.toml" in req_files:
269
269
  # Assume we're dealing with a project directory
270
- pyproject_command = ["-e", "."]
270
+ pyproject_command = ["--editable", "."]
271
271
 
272
272
  # Check to see if there are optional dependencies required
273
273
  with open(os.path.join(dir, "pyproject.toml"), "rb") as f:
@@ -280,8 +280,8 @@ def _install_required_packages(create_venv: bool, directories: List[str], python
280
280
 
281
281
  install_command.extend(pyproject_command)
282
282
 
283
- # always require maco to be installed
284
- install_command.append("maco")
283
+ # Always require maco-extractor to be installed
284
+ install_command.append("maco-extractor")
285
285
  logger.debug(f"Install command: {' '.join(install_command)} [{dir}]")
286
286
  # this uses VIRTUAL_ENV to control usage of a virtual environment
287
287
  p = subprocess.run(
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,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maco
3
- Version: 1.2.16
3
+ Version: 1.2.18
4
+ Summary: Maco is a framework for creating and using malware configuration extractors.
4
5
  Author: sl-govau
5
6
  Maintainer: cccs-rs
6
7
  License: MIT License
@@ -33,7 +34,7 @@ Requires-Dist: cart
33
34
  Requires-Dist: pydantic>=2.0.0
34
35
  Requires-Dist: tomli>=1.1.0; python_version < "3.11"
35
36
  Requires-Dist: uv
36
- Requires-Dist: yara-x==0.11.0
37
+ Requires-Dist: yara-x
37
38
  Requires-Dist: multiprocess>=0.70.17
38
39
  Dynamic: license-file
39
40
 
@@ -8,28 +8,38 @@ demo_extractors/terminator.py,sha256=nxoZYRteYDQS7wp-aAsCaxCSJ9FSE54jPrW3fJpRVho
8
8
  demo_extractors/complex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  demo_extractors/complex/complex.py,sha256=GYKmPOD8-fyVHxwjZb-3t1IghKVMuLtdUvCs5C5yPe0,2625
10
10
  demo_extractors/complex/complex_utils.py,sha256=5kdMl-niSH9d-d3ChuItpmlPT4U-S9g-iyBZlR4tfmQ,296
11
+ extractor_setup/maco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ extractor_setup/maco/base_test.py,sha256=DrVE7vOazeLQpOQeIDwBYK1WtlmdJrRe50JOqP5t4Y0,3198
13
+ extractor_setup/maco/cli.py,sha256=nrSukAJAthbstZT3-lQNPz4zOOMcBhvfYQqLh_B5Jdk,9457
14
+ extractor_setup/maco/collector.py,sha256=R3zw-fUJBlwmcSqvkQ-PnoJdHfRm2V0JAOl7N8MTAbY,8240
15
+ extractor_setup/maco/exceptions.py,sha256=XBHUrs1kr1ZayPI9B_W-WejKgVmC8sWL_o4RL0b4DQE,745
16
+ extractor_setup/maco/extractor.py,sha256=s36aGcsXSc-9iCik6iihVt5G1a1DZUA7TquvWYQNwdE,2912
17
+ extractor_setup/maco/utils.py,sha256=yNm5CiHc9033ONX_gFwg9Lng5IYFujLDtw6FfiJkAao,23425
18
+ extractor_setup/maco/yara.py,sha256=y141t8NqDDXHY23uE1d6BDPeNmSuUuohR6Yr_LKa7GI,4067
19
+ extractor_setup/maco/model/__init__.py,sha256=ULdyHx8R5D2ICHZo3VoCk1YTlewTok36TYIpwx__pNY,45
20
+ extractor_setup/maco/model/model.py,sha256=DBHTmZXMzjpVq0s2mzZv3VCzPhwPnv7sH6u_QZCTcA4,24484
11
21
  maco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
22
  maco/base_test.py,sha256=DrVE7vOazeLQpOQeIDwBYK1WtlmdJrRe50JOqP5t4Y0,3198
13
23
  maco/cli.py,sha256=nrSukAJAthbstZT3-lQNPz4zOOMcBhvfYQqLh_B5Jdk,9457
14
24
  maco/collector.py,sha256=R3zw-fUJBlwmcSqvkQ-PnoJdHfRm2V0JAOl7N8MTAbY,8240
15
25
  maco/exceptions.py,sha256=XBHUrs1kr1ZayPI9B_W-WejKgVmC8sWL_o4RL0b4DQE,745
16
26
  maco/extractor.py,sha256=s36aGcsXSc-9iCik6iihVt5G1a1DZUA7TquvWYQNwdE,2912
17
- maco/utils.py,sha256=wzecqu1W_BbhDHSs07sweOKNeFrmC4CDYmAN_qyYJS4,23362
18
- maco/yara.py,sha256=gkHHxwZNxzZV7nHZM3HNUmhHXB7VW82voCHK5mHpt2Q,3970
27
+ maco/utils.py,sha256=yNm5CiHc9033ONX_gFwg9Lng5IYFujLDtw6FfiJkAao,23425
28
+ maco/yara.py,sha256=y141t8NqDDXHY23uE1d6BDPeNmSuUuohR6Yr_LKa7GI,4067
19
29
  maco/model/__init__.py,sha256=ULdyHx8R5D2ICHZo3VoCk1YTlewTok36TYIpwx__pNY,45
20
30
  maco/model/model.py,sha256=DBHTmZXMzjpVq0s2mzZv3VCzPhwPnv7sH6u_QZCTcA4,24484
21
- maco-1.2.16.dist-info/licenses/LICENSE.md,sha256=gMSjshPhXvV_F1qxmeNkKdBqGWkd__fEJf4glS504bM,1478
31
+ maco-1.2.18.dist-info/licenses/LICENSE.md,sha256=gMSjshPhXvV_F1qxmeNkKdBqGWkd__fEJf4glS504bM,1478
22
32
  model_setup/maco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
33
  model_setup/maco/base_test.py,sha256=DrVE7vOazeLQpOQeIDwBYK1WtlmdJrRe50JOqP5t4Y0,3198
24
34
  model_setup/maco/cli.py,sha256=nrSukAJAthbstZT3-lQNPz4zOOMcBhvfYQqLh_B5Jdk,9457
25
35
  model_setup/maco/collector.py,sha256=R3zw-fUJBlwmcSqvkQ-PnoJdHfRm2V0JAOl7N8MTAbY,8240
26
36
  model_setup/maco/exceptions.py,sha256=XBHUrs1kr1ZayPI9B_W-WejKgVmC8sWL_o4RL0b4DQE,745
27
37
  model_setup/maco/extractor.py,sha256=s36aGcsXSc-9iCik6iihVt5G1a1DZUA7TquvWYQNwdE,2912
28
- model_setup/maco/utils.py,sha256=wzecqu1W_BbhDHSs07sweOKNeFrmC4CDYmAN_qyYJS4,23362
29
- model_setup/maco/yara.py,sha256=gkHHxwZNxzZV7nHZM3HNUmhHXB7VW82voCHK5mHpt2Q,3970
38
+ model_setup/maco/utils.py,sha256=yNm5CiHc9033ONX_gFwg9Lng5IYFujLDtw6FfiJkAao,23425
39
+ model_setup/maco/yara.py,sha256=y141t8NqDDXHY23uE1d6BDPeNmSuUuohR6Yr_LKa7GI,4067
30
40
  model_setup/maco/model/__init__.py,sha256=ULdyHx8R5D2ICHZo3VoCk1YTlewTok36TYIpwx__pNY,45
31
41
  model_setup/maco/model/model.py,sha256=DBHTmZXMzjpVq0s2mzZv3VCzPhwPnv7sH6u_QZCTcA4,24484
32
- pipelines/publish.yaml,sha256=xt3WNU-5kIICJgKIiiE94M3dWjS3uEiun-n4OmIssK8,1471
42
+ pipelines/publish.yaml,sha256=yjc3eqrI-LHLSfZ0DPtxwdfPDT0NI6LUA_zy61UxN_8,1654
33
43
  pipelines/test.yaml,sha256=btJVI-R39UBeYosGu7TOpU6V9ogFW3FT3ROtWygQGQ0,1472
34
44
  tests/data/example.txt.cart,sha256=j4ZdDnFNVq7lb-Qi4pY4evOXKQPKG-GSg-n-uEqPhV0,289
35
45
  tests/data/trigger_complex.txt,sha256=uqnLSrnyDGCmXwuPmZ2s8vdhH0hJs8DxvyaW_tuYY24,64
@@ -42,8 +52,8 @@ tests/extractors/bob/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
42
52
  tests/extractors/bob/bob.py,sha256=4fpqy_O6NDinJImghyW5OwYgnaB05aY4kgoIS_C3c_U,253
43
53
  tests/extractors/import_rewriting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
54
  tests/extractors/import_rewriting/importer.py,sha256=wqF1AG2zXXuj9EMt9qlDorab-UD0GYuFggtrCuz4sf0,289735
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,,
55
+ maco-1.2.18.dist-info/METADATA,sha256=citbYasnfKhc-PAxK7tLQt_Dc2LZRbhKn26ChD0PC3g,15310
56
+ maco-1.2.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ maco-1.2.18.dist-info/entry_points.txt,sha256=TpcwG1gedIg8Y7a9ZOv8aQpuwEUftCefDrAjzeP-o6U,39
58
+ maco-1.2.18.dist-info/top_level.txt,sha256=xiVS11ZoyN8ChHJQGpOzTH4ZyQ3YJe1qT3Yt4gcKGUk,65
59
+ maco-1.2.18.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  demo_extractors
2
+ extractor_setup
2
3
  maco
3
4
  model_setup
4
5
  pipelines
model_setup/maco/utils.py CHANGED
@@ -259,15 +259,15 @@ def _install_required_packages(create_venv: bool, directories: List[str], python
259
259
  # This prevents issues during maco development and building extractors against local libraries.
260
260
  if create_venv:
261
261
  # when running in custom virtual environment, always upgrade packages.
262
- install_command.append("-U")
262
+ install_command.extend(["--upgrade", "--no-cache"])
263
263
 
264
264
  # Update the pip install command depending on where the dependencies are coming from
265
265
  if "requirements.txt" in req_files:
266
266
  # Perform a pip install using the requirements flag
267
- install_command.extend(["-r", "requirements.txt"])
267
+ install_command.extend(["--requirements", "requirements.txt"])
268
268
  elif "pyproject.toml" in req_files:
269
269
  # Assume we're dealing with a project directory
270
- pyproject_command = ["-e", "."]
270
+ pyproject_command = ["--editable", "."]
271
271
 
272
272
  # Check to see if there are optional dependencies required
273
273
  with open(os.path.join(dir, "pyproject.toml"), "rb") as f:
@@ -280,8 +280,8 @@ def _install_required_packages(create_venv: bool, directories: List[str], python
280
280
 
281
281
  install_command.extend(pyproject_command)
282
282
 
283
- # always require maco to be installed
284
- install_command.append("maco")
283
+ # Always require maco-extractor to be installed
284
+ install_command.append("maco-extractor")
285
285
  logger.debug(f"Install command: {' '.join(install_command)} [{dir}]")
286
286
  # this uses VIRTUAL_ENV to control usage of a virtual environment
287
287
  p = subprocess.run(
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
 
pipelines/publish.yaml CHANGED
@@ -12,34 +12,33 @@ pool:
12
12
  vmImage: "ubuntu-22.04"
13
13
 
14
14
  jobs:
15
- - job: test
16
- displayName: Test
17
- strategy:
18
- matrix:
19
- Python38:
20
- python.version: '3.8'
21
- Python39:
22
- python.version: '3.9'
23
- Python310:
24
- python.version: '3.10'
25
- Python311:
26
- python.version: '3.11'
27
- Python312:
28
- python.version: '3.12'
29
- steps:
30
- - task: UsePythonVersion@0
31
- displayName: 'Use Python $(python.version)'
32
- inputs:
33
- versionSpec: '$(python.version)'
15
+ # - job: test
16
+ # displayName: Test
17
+ # strategy:
18
+ # matrix:
19
+ # Python38:
20
+ # python.version: '3.8'
21
+ # Python39:
22
+ # python.version: '3.9'
23
+ # Python310:
24
+ # python.version: '3.10'
25
+ # Python311:
26
+ # python.version: '3.11'
27
+ # Python312:
28
+ # python.version: '3.12'
29
+ # steps:
30
+ # - task: UsePythonVersion@0
31
+ # displayName: 'Use Python $(python.version)'
32
+ # inputs:
33
+ # versionSpec: '$(python.version)'
34
34
 
35
- - script: |
36
- set -x
35
+ # - script: |
36
+ # set -x
37
37
 
38
- python -m pip install -U tox
39
- python -m tox -e py
38
+ # python -m pip install -U tox
39
+ # python -m tox -e py
40
40
 
41
41
  - job: build_and_deploy
42
- dependsOn: test
43
42
  displayName: Build and Deploy
44
43
  variables:
45
44
  - group: deployment-information
@@ -64,6 +63,13 @@ jobs:
64
63
  ls ../dist
65
64
  displayName: Build (Model Only)
66
65
 
66
+ - script: |
67
+ set -x
68
+ cd extractor_setup
69
+ python -m build --outdir ../dist
70
+ ls ../dist
71
+ displayName: Build (Extractor Essentials)
72
+
67
73
  - script: |
68
74
  set -xv # Echo commands before they are run
69
75
  sudo env "PATH=$PATH" python -m pip install --no-cache-dir twine
File without changes