maltorch 0.1__tar.gz
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.
- maltorch-0.1/PKG-INFO +44 -0
- maltorch-0.1/README.md +7 -0
- maltorch-0.1/setup.cfg +4 -0
- maltorch-0.1/setup.py +63 -0
- maltorch-0.1/src/maltorch/VERSION +1 -0
- maltorch-0.1/src/maltorch/__init__.py +0 -0
- maltorch-0.1/src/maltorch/adv/__init__.py +0 -0
- maltorch-0.1/src/maltorch/adv/evasion/__init__.py +1 -0
- maltorch-0.1/src/maltorch/adv/evasion/backend_attack.py +122 -0
- maltorch-0.1/src/maltorch/adv/evasion/base_optim_attack_creator.py +114 -0
- maltorch-0.1/src/maltorch/adv/evasion/content_shift.py +121 -0
- maltorch-0.1/src/maltorch/adv/evasion/gamma_section_injection.py +94 -0
- maltorch-0.1/src/maltorch/adv/evasion/gradfree_attack.py +70 -0
- maltorch-0.1/src/maltorch/adv/evasion/gradient_attack.py +39 -0
- maltorch-0.1/src/maltorch/adv/evasion/padding.py +111 -0
- maltorch-0.1/src/maltorch/adv/evasion/partialdos.py +107 -0
- maltorch-0.1/src/maltorch/adv/evasion/section_injection.py +128 -0
- maltorch-0.1/src/maltorch/data/__init__.py +0 -0
- maltorch-0.1/src/maltorch/data/loader.py +41 -0
- maltorch-0.1/src/maltorch/data_processing/__init__.py +0 -0
- maltorch-0.1/src/maltorch/data_processing/drs_preprocessing.py +41 -0
- maltorch-0.1/src/maltorch/data_processing/dynamic/__init__.py +0 -0
- maltorch-0.1/src/maltorch/data_processing/dynamic/constants.py +68 -0
- maltorch-0.1/src/maltorch/data_processing/dynamic/emulation.py +162 -0
- maltorch-0.1/src/maltorch/data_processing/dynamic/normalization.py +101 -0
- maltorch-0.1/src/maltorch/data_processing/dynamic/tokenization.py +459 -0
- maltorch-0.1/src/maltorch/data_processing/grayscale_preprocessing.py +64 -0
- maltorch-0.1/src/maltorch/data_processing/majority_voting_postprocessing.py +31 -0
- maltorch-0.1/src/maltorch/data_processing/random_drs_preprocessing.py +50 -0
- maltorch-0.1/src/maltorch/data_processing/rs_preprocessing.py +44 -0
- maltorch-0.1/src/maltorch/data_processing/rsdel_preprocessing.py +43 -0
- maltorch-0.1/src/maltorch/data_processing/sequential_drs_preprocessing.py +59 -0
- maltorch-0.1/src/maltorch/datasets/__init__.py +0 -0
- maltorch-0.1/src/maltorch/datasets/binary_dataset.py +75 -0
- maltorch-0.1/src/maltorch/datasets/drs_dataset.py +65 -0
- maltorch-0.1/src/maltorch/datasets/dynamic_drs_dataset.py +92 -0
- maltorch-0.1/src/maltorch/datasets/grayscale_dataset.py +80 -0
- maltorch-0.1/src/maltorch/datasets/random_chunk_sampler.py +33 -0
- maltorch-0.1/src/maltorch/datasets/random_drs_dataset.py +62 -0
- maltorch-0.1/src/maltorch/datasets/rs_dataset.py +69 -0
- maltorch-0.1/src/maltorch/datasets/rsdel_dataset.py +65 -0
- maltorch-0.1/src/maltorch/datasets/sequential_drs_dataset.py +69 -0
- maltorch-0.1/src/maltorch/initializers/__init__.py +0 -0
- maltorch-0.1/src/maltorch/initializers/content_shift_initializer.py +27 -0
- maltorch-0.1/src/maltorch/initializers/initializers.py +40 -0
- maltorch-0.1/src/maltorch/initializers/padding_initializer.py +27 -0
- maltorch-0.1/src/maltorch/initializers/partial_dos_initializer.py +17 -0
- maltorch-0.1/src/maltorch/initializers/section_injection_initializer.py +35 -0
- maltorch-0.1/src/maltorch/manipulations/__init__.py +0 -0
- maltorch-0.1/src/maltorch/manipulations/byte_manipulation.py +22 -0
- maltorch-0.1/src/maltorch/manipulations/gamma_section_injection_manipulation.py +63 -0
- maltorch-0.1/src/maltorch/manipulations/replacement_manipulation.py +32 -0
- maltorch-0.1/src/maltorch/optim/__init__.py +0 -0
- maltorch-0.1/src/maltorch/optim/base.py +39 -0
- maltorch-0.1/src/maltorch/optim/bgd.py +118 -0
- maltorch-0.1/src/maltorch/optim/byte_gradient_processing.py +9 -0
- maltorch-0.1/src/maltorch/optim/optimizer_factory.py +34 -0
- maltorch-0.1/src/maltorch/trainers/__init__.py +0 -0
- maltorch-0.1/src/maltorch/trainers/early_stopping_pytorch_trainer.py +158 -0
- maltorch-0.1/src/maltorch/trainers/weighted_bce_pytorch_trainer.py +164 -0
- maltorch-0.1/src/maltorch/utils/__init__.py +0 -0
- maltorch-0.1/src/maltorch/utils/config.py +5 -0
- maltorch-0.1/src/maltorch/utils/drs_certification.py +71 -0
- maltorch-0.1/src/maltorch/utils/embedding.py +74 -0
- maltorch-0.1/src/maltorch/utils/pe_operations.py +232 -0
- maltorch-0.1/src/maltorch/utils/strings.py +4 -0
- maltorch-0.1/src/maltorch/utils/utils.py +49 -0
- maltorch-0.1/src/maltorch/zoo/__init__.py +0 -0
- maltorch-0.1/src/maltorch/zoo/avaststyleconv.py +111 -0
- maltorch-0.1/src/maltorch/zoo/bbdnn.py +152 -0
- maltorch-0.1/src/maltorch/zoo/ember_gbdt.py +70 -0
- maltorch-0.1/src/maltorch/zoo/malconv.py +79 -0
- maltorch-0.1/src/maltorch/zoo/model.py +185 -0
- maltorch-0.1/src/maltorch/zoo/nebula.py +351 -0
- maltorch-0.1/src/maltorch/zoo/ngramconv.py +81 -0
- maltorch-0.1/src/maltorch/zoo/quovadis.py +278 -0
- maltorch-0.1/src/maltorch/zoo/resnet18.py +17 -0
- maltorch-0.1/src/maltorch/zoo/shallowconv.py +121 -0
- maltorch-0.1/src/maltorch.egg-info/PKG-INFO +44 -0
- maltorch-0.1/src/maltorch.egg-info/SOURCES.txt +81 -0
- maltorch-0.1/src/maltorch.egg-info/dependency_links.txt +1 -0
- maltorch-0.1/src/maltorch.egg-info/requires.txt +7 -0
- maltorch-0.1/src/maltorch.egg-info/top_level.txt +1 -0
maltorch-0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: maltorch
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Pytorch-based library for creating Adversarial EXEmples against Windows Malware detectors.
|
|
5
|
+
Home-page:
|
|
6
|
+
Author: Luca Demetrio, Daniel Gibert, Andrea Ponte, Maura Pintor
|
|
7
|
+
Author-email: luca.demetrio@unige.it
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: torch
|
|
23
|
+
Requires-Dist: torchvision
|
|
24
|
+
Requires-Dist: secml-torch
|
|
25
|
+
Requires-Dist: lightgbm==3.3.5
|
|
26
|
+
Requires-Dist: lief
|
|
27
|
+
Requires-Dist: nevergrad
|
|
28
|
+
Requires-Dist: joblib>=1.3.2
|
|
29
|
+
Dynamic: author
|
|
30
|
+
Dynamic: author-email
|
|
31
|
+
Dynamic: classifier
|
|
32
|
+
Dynamic: description
|
|
33
|
+
Dynamic: description-content-type
|
|
34
|
+
Dynamic: license
|
|
35
|
+
Dynamic: requires-dist
|
|
36
|
+
Dynamic: summary
|
|
37
|
+
|
|
38
|
+
# Maltorch: Pentesting of AI-based Windows Malware Detectors
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
```bash
|
|
42
|
+
pip install maltoch
|
|
43
|
+
pip install git+https://github.com/zangobot/ember.git
|
|
44
|
+
```
|
maltorch-0.1/README.md
ADDED
maltorch-0.1/setup.cfg
ADDED
maltorch-0.1/setup.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
3
|
+
from setuptools import find_packages, setup
|
|
4
|
+
|
|
5
|
+
here = pathlib.Path.cwd()
|
|
6
|
+
readme_path = here / "README.md"
|
|
7
|
+
version_path = here / "src" / "maltorch" / "VERSION"
|
|
8
|
+
|
|
9
|
+
CLASSIFIERS = """\
|
|
10
|
+
Development Status :: 3 - Alpha
|
|
11
|
+
Intended Audience :: Science/Research
|
|
12
|
+
Intended Audience :: Developers
|
|
13
|
+
License :: OSI Approved
|
|
14
|
+
Programming Language :: Python
|
|
15
|
+
Programming Language :: Python :: 3
|
|
16
|
+
Programming Language :: Python :: 3.8
|
|
17
|
+
Programming Language :: Python :: 3.9
|
|
18
|
+
Programming Language :: Python :: 3.10
|
|
19
|
+
Programming Language :: Python :: Implementation :: PyPy
|
|
20
|
+
Topic :: Software Development
|
|
21
|
+
Topic :: Scientific/Engineering
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# Get the long description from the README file
|
|
25
|
+
with readme_path.open() as f:
|
|
26
|
+
long_description = f.read()
|
|
27
|
+
|
|
28
|
+
# Get the version from VERSION file
|
|
29
|
+
with version_path.open() as f:
|
|
30
|
+
version = f.read()
|
|
31
|
+
|
|
32
|
+
setup(
|
|
33
|
+
name='maltorch',
|
|
34
|
+
version=version,
|
|
35
|
+
packages=find_packages(
|
|
36
|
+
where="src",
|
|
37
|
+
exclude=[
|
|
38
|
+
"*.tests",
|
|
39
|
+
"*.tests.*",
|
|
40
|
+
"tests.*",
|
|
41
|
+
"tests",
|
|
42
|
+
],
|
|
43
|
+
),
|
|
44
|
+
package_dir={'': 'src'},
|
|
45
|
+
classifiers=[_f for _f in CLASSIFIERS.split("\n") if _f],
|
|
46
|
+
data_files=[("src/maltorch/VERSION", ["src/maltorch/VERSION"])],
|
|
47
|
+
url='',
|
|
48
|
+
license='MIT',
|
|
49
|
+
author='Luca Demetrio, Daniel Gibert, Andrea Ponte, Maura Pintor',
|
|
50
|
+
author_email='luca.demetrio@unige.it',
|
|
51
|
+
description='Pytorch-based library for creating Adversarial EXEmples against Windows Malware detectors.',
|
|
52
|
+
long_description=long_description,
|
|
53
|
+
long_description_content_type="text/markdown",
|
|
54
|
+
install_requires=[
|
|
55
|
+
'torch',
|
|
56
|
+
'torchvision',
|
|
57
|
+
'secml-torch',
|
|
58
|
+
'lightgbm==3.3.5',
|
|
59
|
+
'lief',
|
|
60
|
+
'nevergrad',
|
|
61
|
+
'joblib>=1.3.2'
|
|
62
|
+
],
|
|
63
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from typing import Union, List, Callable
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from secmlt.adv.evasion.base_evasion_attack import BaseEvasionAttack
|
|
5
|
+
from secmlt.models.base_model import BaseModel
|
|
6
|
+
from secmlt.optimization.initializer import Initializer
|
|
7
|
+
from secmlt.trackers import Tracker
|
|
8
|
+
|
|
9
|
+
from maltorch.manipulations.byte_manipulation import ByteManipulation
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BackendAttack(BaseEvasionAttack):
|
|
13
|
+
@classmethod
|
|
14
|
+
def _trackers_allowed(cls):
|
|
15
|
+
return True
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def get_perturbation_models():
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
y_target: Union[int, None],
|
|
24
|
+
query_budget: int,
|
|
25
|
+
loss_function: Union[str, torch.nn.Module],
|
|
26
|
+
optimizer_cls: Callable,
|
|
27
|
+
manipulation_function: ByteManipulation,
|
|
28
|
+
initializer: Initializer,
|
|
29
|
+
trackers: Union[List[Tracker], Tracker] = None,
|
|
30
|
+
**kwargs
|
|
31
|
+
):
|
|
32
|
+
self.y_target = y_target
|
|
33
|
+
self.query_budget = query_budget
|
|
34
|
+
self.loss_function = loss_function
|
|
35
|
+
self.manipulation_function = manipulation_function
|
|
36
|
+
self.initializer = initializer
|
|
37
|
+
self.trackers = trackers
|
|
38
|
+
self.optimizer = None
|
|
39
|
+
self.optimizer_cls = optimizer_cls
|
|
40
|
+
self._best_loss = None
|
|
41
|
+
self._best_delta = None
|
|
42
|
+
|
|
43
|
+
def _init_attack_manipulation(
|
|
44
|
+
self, samples: torch.Tensor
|
|
45
|
+
) -> (torch.Tensor, torch.Tensor):
|
|
46
|
+
return self.manipulation_function.initialize(samples.data)
|
|
47
|
+
|
|
48
|
+
def _apply_manipulation(
|
|
49
|
+
self, x: torch.Tensor, delta: torch.Tensor
|
|
50
|
+
) -> (torch.Tensor, torch.Tensor):
|
|
51
|
+
raise NotImplementedError()
|
|
52
|
+
|
|
53
|
+
def _optimizer_step(self, delta: torch.Tensor, loss: torch.Tensor) -> torch.Tensor:
|
|
54
|
+
raise NotImplementedError()
|
|
55
|
+
|
|
56
|
+
def _init_optimizer(self, model: BaseModel, delta: torch.Tensor) -> Callable:
|
|
57
|
+
raise NotImplementedError()
|
|
58
|
+
|
|
59
|
+
def _consumed_budget(self):
|
|
60
|
+
raise NotImplementedError()
|
|
61
|
+
|
|
62
|
+
def _init_best_tracking(self, delta: torch.Tensor):
|
|
63
|
+
self._best_delta = torch.zeros_like(delta.detach().cpu())
|
|
64
|
+
self._best_loss = torch.zeros((delta.shape[0], 1)).fill_(torch.inf)
|
|
65
|
+
|
|
66
|
+
def _track_best(self, loss: torch.Tensor, delta: torch.Tensor):
|
|
67
|
+
where_best = (loss < self._best_loss).squeeze(1)
|
|
68
|
+
self._best_delta[where_best] = delta[where_best]
|
|
69
|
+
self._best_loss[where_best] = loss[where_best]
|
|
70
|
+
|
|
71
|
+
def _get_best_delta(self):
|
|
72
|
+
return self._best_delta
|
|
73
|
+
|
|
74
|
+
def _track(
|
|
75
|
+
self,
|
|
76
|
+
iteration: int,
|
|
77
|
+
loss: torch.Tensor,
|
|
78
|
+
scores: torch.Tensor,
|
|
79
|
+
x_adv: torch.Tensor,
|
|
80
|
+
delta: torch.Tensor,
|
|
81
|
+
):
|
|
82
|
+
if self._trackers_allowed():
|
|
83
|
+
if self.trackers:
|
|
84
|
+
for tracker in self.trackers:
|
|
85
|
+
tracker.track(
|
|
86
|
+
iteration=iteration,
|
|
87
|
+
loss=loss.data,
|
|
88
|
+
scores=scores.data,
|
|
89
|
+
x_adv=x_adv.data,
|
|
90
|
+
delta=delta,
|
|
91
|
+
grad=None,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def _run(
|
|
95
|
+
self,
|
|
96
|
+
model: BaseModel,
|
|
97
|
+
samples: torch.Tensor,
|
|
98
|
+
labels: torch.Tensor,
|
|
99
|
+
**optim_kwargs,
|
|
100
|
+
) -> (torch.Tensor, torch.Tensor):
|
|
101
|
+
multiplier = 1 if self.y_target is not None else -1
|
|
102
|
+
target = (
|
|
103
|
+
torch.zeros_like(labels) + self.y_target
|
|
104
|
+
if self.y_target is not None
|
|
105
|
+
else labels
|
|
106
|
+
).type(labels.dtype)
|
|
107
|
+
target = target.to(labels.device)
|
|
108
|
+
x_adv, delta = self._init_attack_manipulation(samples)
|
|
109
|
+
self.optimizer = self._init_optimizer(model, delta)
|
|
110
|
+
budget = 0
|
|
111
|
+
self._init_best_tracking(delta)
|
|
112
|
+
while budget < self.query_budget:
|
|
113
|
+
x_adv, _ = self._apply_manipulation(samples, delta)
|
|
114
|
+
scores = model.decision_function(x_adv)
|
|
115
|
+
loss = self.loss_function(scores, target) * multiplier
|
|
116
|
+
delta = self._optimizer_step(delta, loss)
|
|
117
|
+
budget += self._consumed_budget()
|
|
118
|
+
self._track(budget, loss, scores, x_adv, delta)
|
|
119
|
+
self._track_best(loss, delta)
|
|
120
|
+
best_delta = self._get_best_delta()
|
|
121
|
+
best_x, _ = self._apply_manipulation(samples, best_delta)
|
|
122
|
+
return best_x, self._best_delta
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
from typing import Type
|
|
4
|
+
|
|
5
|
+
from maltorch.adv.evasion.backend_attack import BackendAttack
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OptimizerBackends:
|
|
9
|
+
"""Available backends."""
|
|
10
|
+
|
|
11
|
+
NG = "nevergrad"
|
|
12
|
+
GRADIENT = "gradient"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BaseOptimAttackCreator:
|
|
16
|
+
"""Generic creator for attacks."""
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def get_implementation(cls, backend: str) -> Type[BackendAttack]:
|
|
20
|
+
"""
|
|
21
|
+
Get the implementation of the attack with the given backend.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
backend : str
|
|
26
|
+
The backend for the attack. See secmlt.adv.backends for
|
|
27
|
+
available backends.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
BaseEvasionAttack
|
|
32
|
+
Attack implementation.
|
|
33
|
+
"""
|
|
34
|
+
implementations = {
|
|
35
|
+
OptimizerBackends.NG: cls.get_nevergrad_implementation,
|
|
36
|
+
OptimizerBackends.GRADIENT: cls._get_native_implementation,
|
|
37
|
+
}
|
|
38
|
+
cls.check_backend_available(backend)
|
|
39
|
+
return implementations[backend]()
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def check_backend_available(cls, backend: str) -> bool:
|
|
43
|
+
"""
|
|
44
|
+
Check if a given backend is available for the attack.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
backend : str
|
|
49
|
+
Backend string.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
bool
|
|
54
|
+
True if the given backend is implemented.
|
|
55
|
+
|
|
56
|
+
Raises
|
|
57
|
+
------
|
|
58
|
+
NotImplementedError
|
|
59
|
+
Raises NotImplementedError if the requested backend is not in
|
|
60
|
+
the list of the possible backends (check secmlt.adv.backends).
|
|
61
|
+
"""
|
|
62
|
+
if backend in cls.get_backends():
|
|
63
|
+
return True
|
|
64
|
+
msg = "Unsupported or not-implemented backend."
|
|
65
|
+
raise NotImplementedError(msg)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def get_nevergrad_implementation(cls) -> BackendAttack:
|
|
69
|
+
"""
|
|
70
|
+
Get the Nevergrad implementation of the attack.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
BaseEvasionAttack
|
|
75
|
+
Nevergrad implementation of the attack.
|
|
76
|
+
|
|
77
|
+
Raises
|
|
78
|
+
------
|
|
79
|
+
ImportError
|
|
80
|
+
Raises ImportError if Nevergrad extra is not installed.
|
|
81
|
+
"""
|
|
82
|
+
if importlib.util.find_spec("nevergrad", None) is not None:
|
|
83
|
+
return cls._get_nevergrad_implementation()
|
|
84
|
+
msg = "Nevergrad extra not installed."
|
|
85
|
+
raise ImportError(msg)
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _get_nevergrad_implementation() -> BackendAttack:
|
|
89
|
+
msg = "Nevergrad implementation not available."
|
|
90
|
+
raise NotImplementedError(msg)
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def _get_native_implementation() -> BackendAttack:
|
|
94
|
+
msg = "Native implementation not available."
|
|
95
|
+
raise NotImplementedError(msg)
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def get_backends() -> set[str]:
|
|
100
|
+
"""
|
|
101
|
+
Get the available backends for the given attack.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
set[str]
|
|
106
|
+
Set of implemented backends available for the attack.
|
|
107
|
+
|
|
108
|
+
Raises
|
|
109
|
+
------
|
|
110
|
+
NotImplementedError
|
|
111
|
+
Raises NotImplementedError if not implemented in the inherited class.
|
|
112
|
+
"""
|
|
113
|
+
msg = "Backends should be specified in inherited class."
|
|
114
|
+
raise NotImplementedError(msg)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from typing import Type, Union, List, Callable
|
|
2
|
+
|
|
3
|
+
from secmlt.trackers import Tracker
|
|
4
|
+
from torch.nn import BCEWithLogitsLoss
|
|
5
|
+
|
|
6
|
+
from maltorch.adv.evasion.base_optim_attack_creator import (
|
|
7
|
+
BaseOptimAttackCreator,
|
|
8
|
+
OptimizerBackends,
|
|
9
|
+
)
|
|
10
|
+
from maltorch.adv.evasion.gradfree_attack import GradientFreeBackendAttack
|
|
11
|
+
from maltorch.adv.evasion.gradient_attack import GradientBackendAttack
|
|
12
|
+
from maltorch.initializers.content_shift_initializer import ContentShiftInitializer
|
|
13
|
+
from maltorch.manipulations.replacement_manipulation import ReplacementManipulation
|
|
14
|
+
from maltorch.optim.optimizer_factory import MalwareOptimizerFactory
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ContentShiftGradFree(GradientFreeBackendAttack):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
query_budget: int,
|
|
21
|
+
manipulation_size: int,
|
|
22
|
+
y_target: Union[int, None] = None,
|
|
23
|
+
population_size: int = 10,
|
|
24
|
+
random_init: bool = False,
|
|
25
|
+
trackers: Union[List[Tracker], Tracker] = None,
|
|
26
|
+
):
|
|
27
|
+
initializer = ContentShiftInitializer(
|
|
28
|
+
random_init=random_init, preferred_manipulation_size=manipulation_size
|
|
29
|
+
)
|
|
30
|
+
optimizer_cls = MalwareOptimizerFactory.create_ga(
|
|
31
|
+
population_size=population_size
|
|
32
|
+
)
|
|
33
|
+
loss_function = BCEWithLogitsLoss(reduction="none")
|
|
34
|
+
manipulation_function = ReplacementManipulation(initializer=initializer)
|
|
35
|
+
super().__init__(
|
|
36
|
+
y_target=y_target,
|
|
37
|
+
query_budget=query_budget,
|
|
38
|
+
loss_function=loss_function,
|
|
39
|
+
initializer=initializer,
|
|
40
|
+
manipulation_function=manipulation_function,
|
|
41
|
+
optimizer_cls=optimizer_cls,
|
|
42
|
+
trackers=trackers,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ContentShiftGrad(GradientBackendAttack):
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
query_budget: int,
|
|
50
|
+
manipulation_size: int,
|
|
51
|
+
y_target: Union[int, None] = None,
|
|
52
|
+
random_init: bool = False,
|
|
53
|
+
step_size: int = 58,
|
|
54
|
+
device: str = "cpu",
|
|
55
|
+
trackers: Union[List[Tracker], Tracker] = None,
|
|
56
|
+
):
|
|
57
|
+
initializer = ContentShiftInitializer(
|
|
58
|
+
random_init=random_init, preferred_manipulation_size=manipulation_size
|
|
59
|
+
)
|
|
60
|
+
optimizer_cls = MalwareOptimizerFactory.create_bgd(lr=step_size, device=device)
|
|
61
|
+
loss_function = BCEWithLogitsLoss(reduction="none")
|
|
62
|
+
manipulation_function = ReplacementManipulation(initializer=initializer)
|
|
63
|
+
super().__init__(
|
|
64
|
+
y_target=y_target,
|
|
65
|
+
query_budget=query_budget,
|
|
66
|
+
loss_function=loss_function,
|
|
67
|
+
optimizer_cls=optimizer_cls,
|
|
68
|
+
manipulation_function=manipulation_function,
|
|
69
|
+
initializer=initializer,
|
|
70
|
+
trackers=trackers,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ContentShift(BaseOptimAttackCreator):
|
|
75
|
+
"""
|
|
76
|
+
Content Shift attack
|
|
77
|
+
|
|
78
|
+
Demetrio, L., Coull, S. E., Biggio, B., Lagorio, G., Armando, A., & Roli, F. (2021).
|
|
79
|
+
Adversarial EXEmples: A survey and experimental evaluation of practical attacks on machine learning for windows malware detection.
|
|
80
|
+
ACM Transactions on Privacy and Security (TOPS), 24(4), 1-31.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def get_backends() -> set[str]:
|
|
85
|
+
return {OptimizerBackends.GRADIENT, OptimizerBackends.NG}
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _get_nevergrad_implementation() -> Type[ContentShiftGradFree]:
|
|
89
|
+
return ContentShiftGradFree
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def _get_native_implementation() -> Type[ContentShiftGrad]:
|
|
93
|
+
return ContentShiftGrad
|
|
94
|
+
|
|
95
|
+
def __new__(
|
|
96
|
+
cls,
|
|
97
|
+
query_budget: int,
|
|
98
|
+
y_target: Union[int, None] = None,
|
|
99
|
+
perturbation_size: int = 512,
|
|
100
|
+
random_init: bool = False,
|
|
101
|
+
step_size: int = 16,
|
|
102
|
+
population_size: int = 10,
|
|
103
|
+
device: str = "cpu",
|
|
104
|
+
trackers: Union[List[Tracker], Tracker] = None,
|
|
105
|
+
backend: str = OptimizerBackends.GRADIENT,
|
|
106
|
+
) -> Callable:
|
|
107
|
+
implementation: Callable = cls.get_implementation(backend)
|
|
108
|
+
if backend == OptimizerBackends.GRADIENT:
|
|
109
|
+
kwargs = {"step_size": step_size, "device": device}
|
|
110
|
+
else:
|
|
111
|
+
kwargs = {
|
|
112
|
+
"population_size": population_size,
|
|
113
|
+
}
|
|
114
|
+
return implementation(
|
|
115
|
+
query_budget=query_budget,
|
|
116
|
+
perturbation_size=perturbation_size,
|
|
117
|
+
y_target=y_target,
|
|
118
|
+
trackers=trackers,
|
|
119
|
+
random_init=random_init,
|
|
120
|
+
**kwargs,
|
|
121
|
+
)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Type, Union, List, Callable
|
|
3
|
+
|
|
4
|
+
from secmlt.trackers import Tracker
|
|
5
|
+
from torch.nn import BCEWithLogitsLoss
|
|
6
|
+
|
|
7
|
+
from maltorch.adv.evasion.base_optim_attack_creator import (
|
|
8
|
+
BaseOptimAttackCreator,
|
|
9
|
+
OptimizerBackends,
|
|
10
|
+
)
|
|
11
|
+
from maltorch.adv.evasion.gradfree_attack import GradientFreeBackendAttack
|
|
12
|
+
from maltorch.initializers.initializers import IdentityInitializer
|
|
13
|
+
from maltorch.manipulations.gamma_section_injection_manipulation import GAMMASectionInjectionManipulation
|
|
14
|
+
from maltorch.optim.optimizer_factory import MalwareOptimizerFactory
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GAMMASectionInjectionGradFree(GradientFreeBackendAttack):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
query_budget: int,
|
|
21
|
+
benignware_folder: Path,
|
|
22
|
+
how_many_sections: int,
|
|
23
|
+
which_sections: list = None,
|
|
24
|
+
y_target: Union[int, None] = None,
|
|
25
|
+
population_size: int = 10,
|
|
26
|
+
random_init: bool = False,
|
|
27
|
+
trackers: Union[List[Tracker], Tracker] = None,
|
|
28
|
+
):
|
|
29
|
+
if which_sections is None:
|
|
30
|
+
which_sections = ['rodata']
|
|
31
|
+
initializer = IdentityInitializer(
|
|
32
|
+
random_init=random_init
|
|
33
|
+
)
|
|
34
|
+
optimizer_cls = MalwareOptimizerFactory.create_ga(
|
|
35
|
+
population_size=population_size
|
|
36
|
+
)
|
|
37
|
+
loss_function = BCEWithLogitsLoss(reduction="none")
|
|
38
|
+
manipulation_function = GAMMASectionInjectionManipulation(benignware_folder=benignware_folder,
|
|
39
|
+
which_sections=which_sections,
|
|
40
|
+
how_many_sections=how_many_sections)
|
|
41
|
+
super().__init__(
|
|
42
|
+
y_target=y_target,
|
|
43
|
+
query_budget=query_budget,
|
|
44
|
+
loss_function=loss_function,
|
|
45
|
+
initializer=initializer,
|
|
46
|
+
manipulation_function=manipulation_function,
|
|
47
|
+
optimizer_cls=optimizer_cls,
|
|
48
|
+
trackers=trackers,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class GAMMASectionInjection(BaseOptimAttackCreator):
|
|
53
|
+
"""
|
|
54
|
+
GAMMA Section Injection attack
|
|
55
|
+
|
|
56
|
+
Demetrio, L., Biggio, B., Lagorio, G., Roli, F., & Armando, A. (2021).
|
|
57
|
+
Functionality-preserving black-box optimization of adversarial windows malware.
|
|
58
|
+
IEEE Transactions on Information Forensics and Security, 16, 3469-3478.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def get_backends() -> set[str]:
|
|
63
|
+
return {OptimizerBackends.NG}
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def _get_nevergrad_implementation() -> Type[GAMMASectionInjectionGradFree]:
|
|
67
|
+
return GAMMASectionInjectionGradFree
|
|
68
|
+
|
|
69
|
+
def __new__(
|
|
70
|
+
cls,
|
|
71
|
+
query_budget: int,
|
|
72
|
+
benignware_folder: Path,
|
|
73
|
+
which_sections: list = None,
|
|
74
|
+
y_target: Union[int, None] = None,
|
|
75
|
+
how_many_sections: int = 75,
|
|
76
|
+
random_init: bool = False,
|
|
77
|
+
step_size: int = 16,
|
|
78
|
+
population_size: int = 10,
|
|
79
|
+
device: str = "cpu",
|
|
80
|
+
trackers: Union[List[Tracker], Tracker] = None,
|
|
81
|
+
backend: str = OptimizerBackends.NG,
|
|
82
|
+
) -> Callable:
|
|
83
|
+
if which_sections is None:
|
|
84
|
+
which_sections = ['rodata']
|
|
85
|
+
return GAMMASectionInjectionGradFree(
|
|
86
|
+
query_budget=query_budget,
|
|
87
|
+
benignware_folder=benignware_folder,
|
|
88
|
+
how_many_sections=how_many_sections,
|
|
89
|
+
which_sections=which_sections,
|
|
90
|
+
population_size=population_size,
|
|
91
|
+
y_target=y_target,
|
|
92
|
+
trackers=trackers,
|
|
93
|
+
random_init=random_init,
|
|
94
|
+
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import nevergrad
|
|
2
|
+
import torch
|
|
3
|
+
from nevergrad.optimization import Optimizer
|
|
4
|
+
from secmlt.models.base_model import BaseModel
|
|
5
|
+
from torch.utils.data import DataLoader, TensorDataset
|
|
6
|
+
|
|
7
|
+
from maltorch.adv.evasion.backend_attack import BackendAttack
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GradientFreeBackendAttack(BackendAttack):
|
|
11
|
+
def _optimizer_step(
|
|
12
|
+
self, delta: nevergrad.p.Array, loss: torch.Tensor
|
|
13
|
+
) -> nevergrad.p.Array:
|
|
14
|
+
self.optimizer.tell(delta, loss.item())
|
|
15
|
+
delta = self.optimizer.ask()
|
|
16
|
+
return delta
|
|
17
|
+
|
|
18
|
+
def _apply_manipulation(
|
|
19
|
+
self, x: torch.Tensor, delta: nevergrad.p.Array
|
|
20
|
+
) -> (torch.Tensor, torch.Tensor):
|
|
21
|
+
p_delta = torch.from_numpy(delta.value)
|
|
22
|
+
return self.manipulation_function(x.data, p_delta)
|
|
23
|
+
|
|
24
|
+
def _consumed_budget(self):
|
|
25
|
+
return 1
|
|
26
|
+
|
|
27
|
+
def _init_attack_manipulation(
|
|
28
|
+
self, samples: torch.Tensor
|
|
29
|
+
) -> (torch.Tensor, nevergrad.p.Array):
|
|
30
|
+
x_adv, delta = super()._init_attack_manipulation(samples)
|
|
31
|
+
optim_delta = nevergrad.p.Array(shape=delta.shape, lower=0.0, upper=255.0)
|
|
32
|
+
optim_delta.value = delta.numpy()
|
|
33
|
+
return x_adv, optim_delta
|
|
34
|
+
|
|
35
|
+
def _init_optimizer(self, model: BaseModel, delta: nevergrad.p.Array) -> Optimizer:
|
|
36
|
+
self.optimizer = self.optimizer_cls(
|
|
37
|
+
parametrization=nevergrad.p.Array(
|
|
38
|
+
shape=delta.value.shape, lower=0.0, upper=255.0
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
return self.optimizer
|
|
42
|
+
|
|
43
|
+
def __call__(self, model: BaseModel, data_loader: DataLoader) -> DataLoader:
|
|
44
|
+
adversarials = []
|
|
45
|
+
original_labels = []
|
|
46
|
+
for samples, labels in data_loader:
|
|
47
|
+
for sample, label in zip(samples, labels):
|
|
48
|
+
sample = sample.unsqueeze(0)
|
|
49
|
+
label = label.unsqueeze(0)
|
|
50
|
+
x_adv, _ = self._run(model, sample, label)
|
|
51
|
+
adversarials.append(x_adv)
|
|
52
|
+
original_labels.append(label)
|
|
53
|
+
adversarials = (
|
|
54
|
+
torch.nn.utils.rnn.pad_sequence(adversarials, padding_value=256)
|
|
55
|
+
.squeeze()
|
|
56
|
+
.long()
|
|
57
|
+
)
|
|
58
|
+
original_labels = torch.vstack(original_labels)
|
|
59
|
+
adversarial_dataset = TensorDataset(adversarials, original_labels)
|
|
60
|
+
return DataLoader(
|
|
61
|
+
adversarial_dataset,
|
|
62
|
+
batch_size=data_loader.batch_size,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def _init_best_tracking(self, delta: torch.Tensor): ...
|
|
66
|
+
|
|
67
|
+
def _track_best(self, loss: torch.Tensor, delta: torch.Tensor): ...
|
|
68
|
+
|
|
69
|
+
def _get_best_delta(self):
|
|
70
|
+
return self.optimizer.provide_recommendation()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from secmlt.models.base_model import BaseModel
|
|
3
|
+
|
|
4
|
+
from maltorch.adv.evasion.backend_attack import BackendAttack
|
|
5
|
+
from maltorch.optim.base import BaseByteOptimizer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GradientBackendAttack(BackendAttack):
|
|
9
|
+
def _init_attack_manipulation(
|
|
10
|
+
self, samples: torch.Tensor
|
|
11
|
+
) -> (torch.Tensor, torch.Tensor):
|
|
12
|
+
x_adv, delta = self.manipulation_function.initialize(samples.data)
|
|
13
|
+
delta = delta.to(samples.device)
|
|
14
|
+
delta.requires_grad = True
|
|
15
|
+
return x_adv, delta
|
|
16
|
+
|
|
17
|
+
def _apply_manipulation(
|
|
18
|
+
self, x: torch.Tensor, delta: torch.Tensor
|
|
19
|
+
) -> (torch.Tensor, torch.Tensor):
|
|
20
|
+
x.data, delta.data = self.manipulation_function(x.data, delta.data)
|
|
21
|
+
return x, delta
|
|
22
|
+
|
|
23
|
+
def _init_optimizer(
|
|
24
|
+
self, model: BaseModel, delta: torch.Tensor
|
|
25
|
+
) -> BaseByteOptimizer:
|
|
26
|
+
self.optimizer = self.optimizer_cls(
|
|
27
|
+
[delta],
|
|
28
|
+
indexes_to_perturb=self.manipulation_function.indexes_to_perturb,
|
|
29
|
+
model=model,
|
|
30
|
+
)
|
|
31
|
+
return self.optimizer
|
|
32
|
+
|
|
33
|
+
def _optimizer_step(self, delta: torch.Tensor, loss: torch.Tensor) -> torch.Tensor:
|
|
34
|
+
loss.sum().backward()
|
|
35
|
+
self.optimizer.step()
|
|
36
|
+
return delta
|
|
37
|
+
|
|
38
|
+
def _consumed_budget(self):
|
|
39
|
+
return 2
|