python-obfuscation-framework 1.4.1__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.
- pof/__init__.py +21 -0
- pof/__main__.py +22 -0
- pof/cli.py +187 -0
- pof/errors.py +2 -0
- pof/evasion/__init__.py +57 -0
- pof/evasion/argv.py +44 -0
- pof/evasion/base.py +48 -0
- pof/evasion/cpu/__init__.py +0 -0
- pof/evasion/cpu/cpu_count.py +27 -0
- pof/evasion/fs/__init__.py +0 -0
- pof/evasion/fs/directory_exist.py +29 -0
- pof/evasion/fs/directory_list_exist.py +46 -0
- pof/evasion/fs/directory_list_missing.py +45 -0
- pof/evasion/fs/directory_missing.py +28 -0
- pof/evasion/fs/exec_method.py +51 -0
- pof/evasion/fs/executable_path.py +66 -0
- pof/evasion/fs/file_exist.py +29 -0
- pof/evasion/fs/file_list_exist.py +46 -0
- pof/evasion/fs/file_list_missing.py +45 -0
- pof/evasion/fs/file_missing.py +31 -0
- pof/evasion/fs/tmp.py +112 -0
- pof/evasion/hardware/__init__.py +0 -0
- pof/evasion/hardware/ram_count.py +50 -0
- pof/evasion/hooks/__init__.py +0 -0
- pof/evasion/hooks/debugger.py +36 -0
- pof/evasion/hooks/tracemalloc.py +23 -0
- pof/evasion/human/__init__.py +0 -0
- pof/evasion/human/p.py +45 -0
- pof/evasion/human/prompt.py +69 -0
- pof/evasion/integrity.py +129 -0
- pof/evasion/multi.py +41 -0
- pof/evasion/os/__init__.py +0 -0
- pof/evasion/os/domain.py +27 -0
- pof/evasion/os/hostname.py +27 -0
- pof/evasion/os/uid.py +28 -0
- pof/evasion/os/username.py +27 -0
- pof/evasion/processes/__init__.py +0 -0
- pof/evasion/processes/proc_count.py +47 -0
- pof/evasion/time/__init__.py +0 -0
- pof/evasion/time/expire.py +75 -0
- pof/evasion/time/uptime.py +48 -0
- pof/evasion/time/utc.py +26 -0
- pof/evasion/utils.py +198 -0
- pof/main.py +369 -0
- pof/obfuscator/__init__.py +86 -0
- pof/obfuscator/builtins.py +482 -0
- pof/obfuscator/cipher/__init__.py +0 -0
- pof/obfuscator/cipher/deep_encryption.py +194 -0
- pof/obfuscator/cipher/rc4.py +22 -0
- pof/obfuscator/cipher/shift.py +19 -0
- pof/obfuscator/cipher/xor.py +121 -0
- pof/obfuscator/compression/__init__.py +0 -0
- pof/obfuscator/compression/bz2.py +22 -0
- pof/obfuscator/compression/gzip.py +22 -0
- pof/obfuscator/compression/lzma.py +22 -0
- pof/obfuscator/compression/zlib.py +22 -0
- pof/obfuscator/constants.py +294 -0
- pof/obfuscator/definitions.py +341 -0
- pof/obfuscator/encoding/__init__.py +0 -0
- pof/obfuscator/encoding/a85.py +21 -0
- pof/obfuscator/encoding/b16.py +21 -0
- pof/obfuscator/encoding/b32.py +21 -0
- pof/obfuscator/encoding/b32hex.py +21 -0
- pof/obfuscator/encoding/b64.py +21 -0
- pof/obfuscator/encoding/b85.py +25 -0
- pof/obfuscator/encoding/binascii.py +22 -0
- pof/obfuscator/encoding/snt.py +23 -0
- pof/obfuscator/esoteric/__init__.py +0 -0
- pof/obfuscator/esoteric/call.py +49 -0
- pof/obfuscator/esoteric/doc.py +237 -0
- pof/obfuscator/esoteric/globals.py +62 -0
- pof/obfuscator/esoteric/imports.py +55 -0
- pof/obfuscator/extract_variables.py +297 -0
- pof/obfuscator/junk/__init__.py +0 -0
- pof/obfuscator/junk/add_comments.py +102 -0
- pof/obfuscator/junk/add_newlines.py +36 -0
- pof/obfuscator/names.py +474 -0
- pof/obfuscator/names_rope.py +375 -0
- pof/obfuscator/numbers.py +271 -0
- pof/obfuscator/other/__init__.py +0 -0
- pof/obfuscator/other/tokens.py +47 -0
- pof/obfuscator/remove/__init__.py +0 -0
- pof/obfuscator/remove/comments.py +36 -0
- pof/obfuscator/remove/exceptions.py +75 -0
- pof/obfuscator/remove/indents.py +28 -0
- pof/obfuscator/remove/loggings.py +120 -0
- pof/obfuscator/remove/loggings_old.py +45 -0
- pof/obfuscator/remove/newline.py +27 -0
- pof/obfuscator/remove/print.py +40 -0
- pof/obfuscator/restructure.py +15 -0
- pof/obfuscator/stegano/__init__.py +0 -0
- pof/obfuscator/stegano/docstrings.py +111 -0
- pof/obfuscator/stegano/ipv6encoding.py +21 -0
- pof/obfuscator/stegano/macencoding.py +21 -0
- pof/obfuscator/stegano/uuidencoding.py +21 -0
- pof/obfuscator/strings.py +359 -0
- pof/stager/__init__.py +17 -0
- pof/stager/cipher/__init__.py +0 -0
- pof/stager/cipher/rc4.py +36 -0
- pof/stager/download.py +80 -0
- pof/stager/image.py +374 -0
- pof/stager/lots/__init__.py +1 -0
- pof/stager/lots/cl1pnet.py +51 -0
- pof/stager/lots/pastebin.py +35 -0
- pof/stager/lots/pasters.py +30 -0
- pof/stager/quine.py +135 -0
- pof/utils/__init__.py +0 -0
- pof/utils/cipher/__init__.py +7 -0
- pof/utils/cipher/rc4.py +407 -0
- pof/utils/cipher/shift.py +41 -0
- pof/utils/compression/__init__.py +11 -0
- pof/utils/compression/bz2.py +38 -0
- pof/utils/compression/gzip.py +38 -0
- pof/utils/compression/lzma.py +38 -0
- pof/utils/compression/zlib.py +38 -0
- pof/utils/encoding/__init__.py +19 -0
- pof/utils/encoding/a85.py +35 -0
- pof/utils/encoding/b16.py +30 -0
- pof/utils/encoding/b3.py +93 -0
- pof/utils/encoding/b32.py +30 -0
- pof/utils/encoding/b32hex.py +30 -0
- pof/utils/encoding/b64.py +30 -0
- pof/utils/encoding/b85.py +35 -0
- pof/utils/encoding/binascii.py +38 -0
- pof/utils/encoding/snt.py +97 -0
- pof/utils/entropy.py +24 -0
- pof/utils/extract_names.py +204 -0
- pof/utils/generator/__init__.py +17 -0
- pof/utils/generator/advanced.py +53 -0
- pof/utils/generator/base.py +178 -0
- pof/utils/generator/basic.py +107 -0
- pof/utils/generator/names.txt +37241 -0
- pof/utils/generator/unicode.py +171 -0
- pof/utils/se/__init__.py +3 -0
- pof/utils/se/homoglyphs.py +99 -0
- pof/utils/se/homoglyphs.txt +96 -0
- pof/utils/stegano/__init__.py +5 -0
- pof/utils/stegano/ipv6encoding.py +97 -0
- pof/utils/stegano/macencoding.py +96 -0
- pof/utils/stegano/uuidencoding.py +102 -0
- pof/utils/tokens.py +68 -0
- python_obfuscation_framework-1.4.1.dist-info/LICENSE +674 -0
- python_obfuscation_framework-1.4.1.dist-info/METADATA +851 -0
- python_obfuscation_framework-1.4.1.dist-info/RECORD +147 -0
- python_obfuscation_framework-1.4.1.dist-info/WHEEL +5 -0
- python_obfuscation_framework-1.4.1.dist-info/entry_points.txt +2 -0
- python_obfuscation_framework-1.4.1.dist-info/top_level.txt +1 -0
pof/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# POF, a free and open source Python obfuscation framework.
|
|
2
|
+
# Copyright (C) 2022-2024 POF Team
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
from pof.main import BaseObfuscator, Obfuscator
|
|
18
|
+
|
|
19
|
+
__version__ = "1.4.1"
|
|
20
|
+
|
|
21
|
+
__all__ = ("BaseObfuscator", "Obfuscator", "__version__")
|
pof/__main__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# POF, a free and open source Python obfuscation framework.
|
|
2
|
+
# Copyright (C) 2022-2024 POF Team
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
from pof.cli import _cli
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
sys.exit(_cli()) # pragma: no cover
|
pof/cli.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from pof import Obfuscator, __version__
|
|
7
|
+
from pof.errors import PofError
|
|
8
|
+
from pof.evasion import * # noqa: F403
|
|
9
|
+
from pof.evasion import __all__ as all_evasion
|
|
10
|
+
from pof.obfuscator import * # noqa: F403
|
|
11
|
+
from pof.obfuscator import __all__ as all_obfuscator
|
|
12
|
+
from pof.stager import * # noqa: F403
|
|
13
|
+
from pof.stager import __all__ as all_stager
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PofCliError(PofError):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CLIObfuscator(Obfuscator):
|
|
21
|
+
def obfuscator(self, source, obfuscator, *args, **kwargs):
|
|
22
|
+
"""Execute a single obfuscator."""
|
|
23
|
+
tokens = self._get_tokens(source)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
if "Obfuscator" not in obfuscator:
|
|
27
|
+
msg = "this is not an obfuscator"
|
|
28
|
+
raise KeyError(msg) # noqa: TRY301
|
|
29
|
+
obf = globals()[obfuscator]
|
|
30
|
+
except KeyError as err:
|
|
31
|
+
lobf = ", ".join([e for e in all_obfuscator if "Obfuscator" in e])
|
|
32
|
+
msg = f"obfuscator {obfuscator} not found in: {lobf}"
|
|
33
|
+
raise PofCliError(msg) from err
|
|
34
|
+
|
|
35
|
+
logging.info(f"using '{obf.__name__}' obfuscator")
|
|
36
|
+
logging.debug(f"args={args}")
|
|
37
|
+
logging.debug(f"kwargs={kwargs}")
|
|
38
|
+
tokens = globals()[obfuscator](*args, **kwargs).obfuscate_tokens(tokens)
|
|
39
|
+
return self._untokenize(tokens)
|
|
40
|
+
|
|
41
|
+
def stager(self, source, stager, *args, **kwargs):
|
|
42
|
+
"""Execute a single stager."""
|
|
43
|
+
tokens = self._get_tokens(source)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
if "Stager" not in stager:
|
|
47
|
+
msg = "this is not a stager"
|
|
48
|
+
raise KeyError(msg) # noqa: TRY301
|
|
49
|
+
obf = globals()[stager]
|
|
50
|
+
except KeyError as err:
|
|
51
|
+
lpl = ", ".join([e for e in all_stager if "Stager" in e])
|
|
52
|
+
msg = f"stager '{stager}' not found in: {lpl}"
|
|
53
|
+
raise PofCliError(msg) from err
|
|
54
|
+
|
|
55
|
+
logging.info(f"using '{obf.__name__}' stager")
|
|
56
|
+
logging.debug(f"{args=}")
|
|
57
|
+
logging.debug(f"{kwargs=}")
|
|
58
|
+
tokens = globals()[stager](*args, **kwargs).generate_stager(tokens)
|
|
59
|
+
return self._untokenize(tokens)
|
|
60
|
+
|
|
61
|
+
def evasion(self, source, evasion, *args, **kwargs):
|
|
62
|
+
"""Execute a single evasion method."""
|
|
63
|
+
tokens = self._get_tokens(source)
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
if "Evasion" not in evasion:
|
|
67
|
+
msg = "this is not an evasion method"
|
|
68
|
+
raise KeyError(msg) # noqa: TRY301
|
|
69
|
+
obf = globals()[evasion]
|
|
70
|
+
except KeyError as err:
|
|
71
|
+
lobf = ", ".join([e for e in all_evasion if "Evasion" in e])
|
|
72
|
+
msg = f"evasion {evasion} not found in: {lobf}"
|
|
73
|
+
raise PofCliError(msg) from err
|
|
74
|
+
|
|
75
|
+
logging.info(f"using '{obf.__name__}' evasion")
|
|
76
|
+
logging.debug(f"{args=}")
|
|
77
|
+
logging.debug(f"{kwargs=}")
|
|
78
|
+
tokens = globals()[evasion](*args, **kwargs).add_evasion(tokens)
|
|
79
|
+
return self._untokenize(tokens)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
handler = logging.StreamHandler()
|
|
83
|
+
formatter = logging.Formatter("%(levelname)s %(message)s\x1b[39m")
|
|
84
|
+
|
|
85
|
+
# colored logging
|
|
86
|
+
logging.addLevelName(logging.DEBUG, "\x1b[36m[*]")
|
|
87
|
+
logging.addLevelName(logging.INFO, "\x1b[32m[+]")
|
|
88
|
+
logging.addLevelName(logging.WARNING, "\x1b[33m[?]")
|
|
89
|
+
logging.addLevelName(logging.ERROR, "\x1b[31m[!]")
|
|
90
|
+
logging.addLevelName(logging.CRITICAL, "\x1b[31m[!!!]")
|
|
91
|
+
|
|
92
|
+
handler.setFormatter(formatter)
|
|
93
|
+
|
|
94
|
+
logging.basicConfig(level=logging.INFO, handlers=[handler])
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _handle(args):
|
|
98
|
+
if args.version:
|
|
99
|
+
print(__version__) # noqa: T201
|
|
100
|
+
return 0
|
|
101
|
+
|
|
102
|
+
level = getattr(logging, args.logging)
|
|
103
|
+
logger = logging.getLogger()
|
|
104
|
+
logger.setLevel(level)
|
|
105
|
+
|
|
106
|
+
a = []
|
|
107
|
+
k = {}
|
|
108
|
+
if args.kwargs is not None:
|
|
109
|
+
for e in args.kwargs:
|
|
110
|
+
if "=" in e:
|
|
111
|
+
key, value = e.split("=")
|
|
112
|
+
k.update({key: value})
|
|
113
|
+
else:
|
|
114
|
+
a.append(e)
|
|
115
|
+
|
|
116
|
+
logging.info(f"starting obfuscation of {args.input.name}")
|
|
117
|
+
source = args.input.read()
|
|
118
|
+
start = time.time()
|
|
119
|
+
|
|
120
|
+
out = CLIObfuscator().__getattribute__(args.function)(source, *a, **k)
|
|
121
|
+
|
|
122
|
+
end = time.time()
|
|
123
|
+
time_diff = round(end - start, 4)
|
|
124
|
+
logging.info(f"took: {time_diff}s")
|
|
125
|
+
args.output.write(out)
|
|
126
|
+
logging.info(f"successfully obfuscated {args.input.name} to {args.output.name}")
|
|
127
|
+
# no errors
|
|
128
|
+
return 0
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _cli():
|
|
132
|
+
parser = argparse.ArgumentParser(
|
|
133
|
+
prog="obfuscate",
|
|
134
|
+
description="%(prog)s CLI tool to obfuscate Python source code.",
|
|
135
|
+
)
|
|
136
|
+
parser.add_argument("-v", "--version", action="store_true")
|
|
137
|
+
parser.add_argument(
|
|
138
|
+
"--raise-exceptions",
|
|
139
|
+
action="store_true",
|
|
140
|
+
help="Raise exceptions instead of just printing them.",
|
|
141
|
+
)
|
|
142
|
+
parser.add_argument(
|
|
143
|
+
"input",
|
|
144
|
+
nargs="?", # required to be able to pipe to stdin
|
|
145
|
+
help="input file",
|
|
146
|
+
type=argparse.FileType("r"),
|
|
147
|
+
default=sys.stdin,
|
|
148
|
+
)
|
|
149
|
+
parser.add_argument(
|
|
150
|
+
"-o",
|
|
151
|
+
"--output",
|
|
152
|
+
help="output file",
|
|
153
|
+
type=argparse.FileType("w"),
|
|
154
|
+
default=sys.stdout,
|
|
155
|
+
)
|
|
156
|
+
parser.add_argument(
|
|
157
|
+
"-f",
|
|
158
|
+
"--function",
|
|
159
|
+
help="obfuscation function",
|
|
160
|
+
default="obfuscate",
|
|
161
|
+
)
|
|
162
|
+
parser.add_argument(
|
|
163
|
+
"--logging",
|
|
164
|
+
help="logging level, INFO, DEBUG, ERROR, CRITICAL",
|
|
165
|
+
default="INFO",
|
|
166
|
+
)
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
"-k",
|
|
169
|
+
"--kwargs",
|
|
170
|
+
help="variables for obfuscator",
|
|
171
|
+
nargs="*",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
args = parser.parse_args()
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
return _handle(args)
|
|
178
|
+
except Exception as e:
|
|
179
|
+
logging.error(str(e)) # noqa: TRY400
|
|
180
|
+
if args.raise_exceptions:
|
|
181
|
+
raise
|
|
182
|
+
logging.debug("use `--raise-exceptions` to see full trace back.")
|
|
183
|
+
return 1
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
if __name__ == "__main__":
|
|
187
|
+
sys.exit(_cli()) # pragma: no cover
|
pof/errors.py
ADDED
pof/evasion/__init__.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from .argv import ArgvEvasion
|
|
2
|
+
from .cpu.cpu_count import CPUCountEvasion
|
|
3
|
+
from .fs.directory_exist import DirectoryExistEvasion
|
|
4
|
+
from .fs.directory_list_exist import DirectoryListExistEvasion
|
|
5
|
+
from .fs.directory_list_missing import DirectoryListMissingEvasion
|
|
6
|
+
from .fs.directory_missing import DirectoryMissingEvasion
|
|
7
|
+
from .fs.exec_method import ExecMethodEvasion
|
|
8
|
+
from .fs.executable_path import ExecPathEvasion
|
|
9
|
+
from .fs.file_exist import FileExistEvasion
|
|
10
|
+
from .fs.file_list_exist import FileListExistEvasion
|
|
11
|
+
from .fs.file_list_missing import FileListMissingEvasion
|
|
12
|
+
from .fs.file_missing import FileMissingEvasion
|
|
13
|
+
from .fs.tmp import LinuxTmpCountEvasion, TmpCountEvasion, WinTmpCountEvasion
|
|
14
|
+
from .hardware.ram_count import LinuxRAMCountEvasion
|
|
15
|
+
from .hooks.debugger import DebuggerEvasion
|
|
16
|
+
from .hooks.tracemalloc import TracemallocEvasion
|
|
17
|
+
from .human.prompt import WinPromptEvasion
|
|
18
|
+
from .multi import MultiEvasion
|
|
19
|
+
from .os.domain import DomainEvasion
|
|
20
|
+
from .os.hostname import HostnameEvasion
|
|
21
|
+
from .os.uid import LinuxUIDEvasion
|
|
22
|
+
from .os.username import UsernameEvasion
|
|
23
|
+
from .processes.proc_count import LinuxProcCountEvasion
|
|
24
|
+
from .time.expire import ExpireEvasion
|
|
25
|
+
from .time.uptime import LinuxUptimeEvasion
|
|
26
|
+
from .time.utc import UTCEvasion
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"ArgvEvasion",
|
|
30
|
+
"CPUCountEvasion",
|
|
31
|
+
"DebuggerEvasion",
|
|
32
|
+
"DirectoryExistEvasion",
|
|
33
|
+
"DirectoryListExistEvasion",
|
|
34
|
+
"DirectoryListMissingEvasion",
|
|
35
|
+
"DirectoryMissingEvasion",
|
|
36
|
+
"DomainEvasion",
|
|
37
|
+
"ExecMethodEvasion",
|
|
38
|
+
"ExecPathEvasion",
|
|
39
|
+
"ExpireEvasion",
|
|
40
|
+
"FileExistEvasion",
|
|
41
|
+
"FileListExistEvasion",
|
|
42
|
+
"FileListMissingEvasion",
|
|
43
|
+
"FileMissingEvasion",
|
|
44
|
+
"HostnameEvasion",
|
|
45
|
+
"LinuxProcCountEvasion",
|
|
46
|
+
"LinuxRAMCountEvasion",
|
|
47
|
+
"LinuxTmpCountEvasion",
|
|
48
|
+
"LinuxUIDEvasion",
|
|
49
|
+
"LinuxUptimeEvasion",
|
|
50
|
+
"MultiEvasion",
|
|
51
|
+
"TmpCountEvasion",
|
|
52
|
+
"TracemallocEvasion",
|
|
53
|
+
"UTCEvasion",
|
|
54
|
+
"UsernameEvasion",
|
|
55
|
+
"WinPromptEvasion",
|
|
56
|
+
"WinTmpCountEvasion",
|
|
57
|
+
]
|
pof/evasion/argv.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from tokenize import NAME, NUMBER, OP, STRING
|
|
2
|
+
|
|
3
|
+
from pof.evasion.base import BaseEvasion
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ArgvEvasion(BaseEvasion):
|
|
7
|
+
"""Ensure that the correct argument is passed."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, argv="argv", position=1) -> None:
|
|
10
|
+
self.argv = argv
|
|
11
|
+
# can put -1 in position to select the last argument
|
|
12
|
+
self.position = position
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def import_tokens():
|
|
16
|
+
return [
|
|
17
|
+
(NAME, "import"),
|
|
18
|
+
(NAME, "sys"),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def check_tokens(self):
|
|
22
|
+
"""Validates system does not use UTC timezone.
|
|
23
|
+
|
|
24
|
+
`len(sys.argv)==1 or sys.argv[1]!="123"`
|
|
25
|
+
"""
|
|
26
|
+
return [
|
|
27
|
+
(NAME, "len"),
|
|
28
|
+
(OP, "("),
|
|
29
|
+
(NAME, "sys"),
|
|
30
|
+
(OP, "."),
|
|
31
|
+
(NAME, "argv"),
|
|
32
|
+
(OP, ")"),
|
|
33
|
+
(OP, "=="),
|
|
34
|
+
(NUMBER, str(self.position)),
|
|
35
|
+
(NAME, "or"),
|
|
36
|
+
(NAME, "sys"),
|
|
37
|
+
(OP, "."),
|
|
38
|
+
(NAME, "argv"),
|
|
39
|
+
(OP, "["),
|
|
40
|
+
(NUMBER, str(self.position)),
|
|
41
|
+
(OP, "]"),
|
|
42
|
+
(OP, "!="),
|
|
43
|
+
(STRING, repr(self.argv)),
|
|
44
|
+
]
|
pof/evasion/base.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from tokenize import DEDENT, INDENT, LPAR, NAME, NEWLINE, OP, RPAR, STRING
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BaseEvasion:
|
|
5
|
+
# add evasion class name inside the exception
|
|
6
|
+
ADD_CLASS_NAME = True
|
|
7
|
+
|
|
8
|
+
@classmethod
|
|
9
|
+
def fail_call_tokens(cls):
|
|
10
|
+
return [
|
|
11
|
+
(NAME, "raise"),
|
|
12
|
+
(NAME, "Exception"),
|
|
13
|
+
(LPAR, "("),
|
|
14
|
+
(
|
|
15
|
+
STRING,
|
|
16
|
+
(
|
|
17
|
+
repr(cls.__class__.__name__)
|
|
18
|
+
if cls.ADD_CLASS_NAME
|
|
19
|
+
else repr("evasion check triggered")
|
|
20
|
+
),
|
|
21
|
+
),
|
|
22
|
+
(RPAR, ")"),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def import_tokens():
|
|
27
|
+
return []
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def check_tokens():
|
|
31
|
+
return []
|
|
32
|
+
|
|
33
|
+
def add_evasion(self, tokens):
|
|
34
|
+
return [
|
|
35
|
+
*self.import_tokens(),
|
|
36
|
+
(NEWLINE, "\n"),
|
|
37
|
+
(NAME, "if"),
|
|
38
|
+
(LPAR, "("),
|
|
39
|
+
*self.check_tokens(),
|
|
40
|
+
(RPAR, ")"),
|
|
41
|
+
(OP, ":"),
|
|
42
|
+
(NEWLINE, "\n"),
|
|
43
|
+
(INDENT, " "),
|
|
44
|
+
*self.fail_call_tokens(),
|
|
45
|
+
(NEWLINE, "\n"),
|
|
46
|
+
(DEDENT, ""),
|
|
47
|
+
*tokens,
|
|
48
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from tokenize import LPAR, NAME, NUMBER, OP, RPAR
|
|
2
|
+
|
|
3
|
+
from pof.evasion.base import BaseEvasion
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CPUCountEvasion(BaseEvasion):
|
|
7
|
+
def __init__(self, min_cpu_count: int = 2) -> None:
|
|
8
|
+
self.min_cpu_count = min_cpu_count
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def import_tokens() -> list[tuple[int, str]]:
|
|
12
|
+
return [
|
|
13
|
+
(NAME, "import"),
|
|
14
|
+
(NAME, "multiprocessing"),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def check_tokens(self) -> list[tuple[int, str]]:
|
|
18
|
+
"""`multiprocessing.cpu_count() < 2`."""
|
|
19
|
+
return [
|
|
20
|
+
(NAME, "multiprocessing"),
|
|
21
|
+
(OP, "."),
|
|
22
|
+
(NAME, "cpu_count"),
|
|
23
|
+
(LPAR, "("),
|
|
24
|
+
(RPAR, ")"),
|
|
25
|
+
(OP, "<"),
|
|
26
|
+
(NUMBER, str(self.min_cpu_count)),
|
|
27
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from tokenize import LPAR, NAME, OP, RPAR, STRING
|
|
2
|
+
|
|
3
|
+
from pof.evasion.base import BaseEvasion
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DirectoryExistEvasion(BaseEvasion):
|
|
7
|
+
def __init__(self, directory) -> None:
|
|
8
|
+
self.directory = directory
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def import_tokens():
|
|
12
|
+
return [
|
|
13
|
+
(NAME, "import"),
|
|
14
|
+
(NAME, "os"),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def check_tokens(self):
|
|
18
|
+
"""`not os.path.isdir('/tmp/a')`."""
|
|
19
|
+
return [
|
|
20
|
+
(NAME, "not"),
|
|
21
|
+
(NAME, "os"),
|
|
22
|
+
(OP, "."),
|
|
23
|
+
(NAME, "path"),
|
|
24
|
+
(OP, "."),
|
|
25
|
+
(NAME, "isdir"),
|
|
26
|
+
(LPAR, "("),
|
|
27
|
+
(STRING, repr(self.directory)),
|
|
28
|
+
(RPAR, ")"),
|
|
29
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import io
|
|
2
|
+
from tokenize import LPAR, NAME, OP, RPAR, generate_tokens
|
|
3
|
+
|
|
4
|
+
from pof.evasion.base import BaseEvasion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DirectoryListExistEvasion(BaseEvasion):
|
|
8
|
+
def __init__(self, directory_list) -> None:
|
|
9
|
+
self.directory_list = directory_list
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def import_tokens():
|
|
13
|
+
return [
|
|
14
|
+
(NAME, "import"),
|
|
15
|
+
(NAME, "os"),
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
def check_tokens(self):
|
|
19
|
+
"""Trigger evasion if any directory from the list is absent on the system.
|
|
20
|
+
|
|
21
|
+
`not all([os.path.isdir(p)for p in['/tmp/a','/tmp/b',...]])`
|
|
22
|
+
"""
|
|
23
|
+
# TODO (deoktr): change
|
|
24
|
+
io_obj = io.StringIO(repr(self.directory_list))
|
|
25
|
+
directory_list_tokens = list(generate_tokens(io_obj.readline))
|
|
26
|
+
|
|
27
|
+
return [
|
|
28
|
+
(NAME, "not"),
|
|
29
|
+
(NAME, "all"),
|
|
30
|
+
(LPAR, "("),
|
|
31
|
+
(OP, "["),
|
|
32
|
+
(NAME, "os"),
|
|
33
|
+
(OP, "."),
|
|
34
|
+
(NAME, "path"),
|
|
35
|
+
(OP, "."),
|
|
36
|
+
(NAME, "isdir"),
|
|
37
|
+
(LPAR, "("),
|
|
38
|
+
(NAME, "p"),
|
|
39
|
+
(RPAR, ")"),
|
|
40
|
+
(NAME, "for"),
|
|
41
|
+
(NAME, "p"),
|
|
42
|
+
(NAME, "in"),
|
|
43
|
+
*directory_list_tokens,
|
|
44
|
+
(OP, "]"),
|
|
45
|
+
(RPAR, ")"),
|
|
46
|
+
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import io
|
|
2
|
+
from tokenize import LPAR, NAME, OP, RPAR, generate_tokens
|
|
3
|
+
|
|
4
|
+
from pof.evasion.base import BaseEvasion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DirectoryListMissingEvasion(BaseEvasion):
|
|
8
|
+
def __init__(self, directory_list) -> None:
|
|
9
|
+
self.directory_list = directory_list
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def import_tokens():
|
|
13
|
+
return [
|
|
14
|
+
(NAME, "import"),
|
|
15
|
+
(NAME, "os"),
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
def check_tokens(self):
|
|
19
|
+
"""Trigger evasion if any directory from the list is present on the system.
|
|
20
|
+
|
|
21
|
+
`any([os.path.isdir(p) for p in ['/tmp/a', '/tmp/b', ...]])`
|
|
22
|
+
"""
|
|
23
|
+
# TODO (deoktr): change
|
|
24
|
+
io_obj = io.StringIO(repr(self.directory_list))
|
|
25
|
+
directory_list_tokens = list(generate_tokens(io_obj.readline))
|
|
26
|
+
|
|
27
|
+
return [
|
|
28
|
+
(NAME, "any"),
|
|
29
|
+
(LPAR, "("),
|
|
30
|
+
(OP, "["),
|
|
31
|
+
(NAME, "os"),
|
|
32
|
+
(OP, "."),
|
|
33
|
+
(NAME, "path"),
|
|
34
|
+
(OP, "."),
|
|
35
|
+
(NAME, "isdir"),
|
|
36
|
+
(LPAR, "("),
|
|
37
|
+
(NAME, "p"),
|
|
38
|
+
(RPAR, ")"),
|
|
39
|
+
(NAME, "for"),
|
|
40
|
+
(NAME, "p"),
|
|
41
|
+
(NAME, "in"),
|
|
42
|
+
*directory_list_tokens,
|
|
43
|
+
(OP, "]"),
|
|
44
|
+
(RPAR, ")"),
|
|
45
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from tokenize import LPAR, NAME, OP, RPAR, STRING
|
|
2
|
+
|
|
3
|
+
from pof.evasion.base import BaseEvasion
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DirectoryMissingEvasion(BaseEvasion):
|
|
7
|
+
def __init__(self, directory) -> None:
|
|
8
|
+
self.directory = directory
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def import_tokens():
|
|
12
|
+
return [
|
|
13
|
+
(NAME, "import"),
|
|
14
|
+
(NAME, "os"),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def check_tokens(self):
|
|
18
|
+
"""`os.path.isdir('/tmp/a')`."""
|
|
19
|
+
return [
|
|
20
|
+
(NAME, "os"),
|
|
21
|
+
(OP, "."),
|
|
22
|
+
(NAME, "path"),
|
|
23
|
+
(OP, "."),
|
|
24
|
+
(NAME, "isdir"),
|
|
25
|
+
(LPAR, "("),
|
|
26
|
+
(STRING, repr(self.directory)),
|
|
27
|
+
(RPAR, ")"),
|
|
28
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from tokenize import NAME, OP
|
|
2
|
+
|
|
3
|
+
from pof.evasion.base import BaseEvasion
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ExecMethodEvasion(BaseEvasion):
|
|
7
|
+
def __init__(self, method="file") -> None:
|
|
8
|
+
# "file" or "memory"
|
|
9
|
+
self.exec_method = method
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def import_tokens():
|
|
13
|
+
return [
|
|
14
|
+
(NAME, "import"),
|
|
15
|
+
(NAME, "time"),
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
def check_tokens(self):
|
|
19
|
+
"""Verify the execution method.
|
|
20
|
+
|
|
21
|
+
When Python code is executed from the console or from the memory
|
|
22
|
+
directly the `__file__` object will be equal to `<stdin>`, if a script
|
|
23
|
+
is meant to be launched with a specific method the evasion can be added.
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
|
|
27
|
+
This doesn't work (trigger evasion when file is selected)
|
|
28
|
+
```bash
|
|
29
|
+
echo "print(__file__)" | ./pof.py ... | python
|
|
30
|
+
Exception: evasion check triggered
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This works (doesn't trigger evasion when file is selected)
|
|
34
|
+
```bash
|
|
35
|
+
echo "print(__file__)" | ./pof.py ... > bin/a.py & python bin/a.py
|
|
36
|
+
/path/to/file/bin/a.py
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Todo:
|
|
40
|
+
If running from the console `__file__` doesn't exist, this needs to be
|
|
41
|
+
taken into account !
|
|
42
|
+
"""
|
|
43
|
+
equal = "=!"
|
|
44
|
+
if self.exec_method == "file":
|
|
45
|
+
equal = "=="
|
|
46
|
+
|
|
47
|
+
return [
|
|
48
|
+
(NAME, "__file__"),
|
|
49
|
+
(OP, equal),
|
|
50
|
+
(NAME, repr("<stdin>")),
|
|
51
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import io
|
|
2
|
+
from tokenize import LPAR, NAME, OP, RPAR, generate_tokens
|
|
3
|
+
|
|
4
|
+
from pof.evasion.base import BaseEvasion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExecPathEvasion(BaseEvasion):
|
|
8
|
+
DEFAULT_CONTAIN_LIST = (
|
|
9
|
+
"virus",
|
|
10
|
+
"VIRUS",
|
|
11
|
+
"sample",
|
|
12
|
+
"SAMPLE",
|
|
13
|
+
"sandbox",
|
|
14
|
+
"SANDBOX",
|
|
15
|
+
"malware",
|
|
16
|
+
"Malware",
|
|
17
|
+
# Anubis sandbox
|
|
18
|
+
"InsideTm",
|
|
19
|
+
"insidetm",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def __init__(self, contain_list=DEFAULT_CONTAIN_LIST) -> None:
|
|
23
|
+
self.contain_list = contain_list
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def import_tokens():
|
|
27
|
+
return [
|
|
28
|
+
(NAME, "import"),
|
|
29
|
+
(NAME, "pathlib"),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
def check_tokens(self):
|
|
33
|
+
"""Check if full exec path contains one of the specific strings.
|
|
34
|
+
|
|
35
|
+
`any([s in str(pathlib.Path(__file__).absolute()) for s in["virus",...]])`
|
|
36
|
+
"""
|
|
37
|
+
# TODO (deoktr): generate tokens differently
|
|
38
|
+
io_obj = io.StringIO(repr(self.contain_list))
|
|
39
|
+
contain_list_tokens = list(generate_tokens(io_obj.readline))
|
|
40
|
+
|
|
41
|
+
return [
|
|
42
|
+
(NAME, "any"),
|
|
43
|
+
(LPAR, "("),
|
|
44
|
+
(OP, "["),
|
|
45
|
+
(NAME, "s"),
|
|
46
|
+
(NAME, "in"),
|
|
47
|
+
(NAME, "str"),
|
|
48
|
+
(LPAR, "("),
|
|
49
|
+
(NAME, "pathlib"),
|
|
50
|
+
(OP, "."),
|
|
51
|
+
(NAME, "Path"),
|
|
52
|
+
(LPAR, "("),
|
|
53
|
+
(NAME, "__file__"),
|
|
54
|
+
(RPAR, ")"),
|
|
55
|
+
(OP, "."),
|
|
56
|
+
(NAME, "absolute"),
|
|
57
|
+
(LPAR, "("),
|
|
58
|
+
(RPAR, ")"),
|
|
59
|
+
(RPAR, ")"),
|
|
60
|
+
(NAME, "for"),
|
|
61
|
+
(NAME, "s"),
|
|
62
|
+
(NAME, "in"),
|
|
63
|
+
*contain_list_tokens,
|
|
64
|
+
(OP, "]"),
|
|
65
|
+
(RPAR, ")"),
|
|
66
|
+
]
|