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.
Files changed (147) hide show
  1. pof/__init__.py +21 -0
  2. pof/__main__.py +22 -0
  3. pof/cli.py +187 -0
  4. pof/errors.py +2 -0
  5. pof/evasion/__init__.py +57 -0
  6. pof/evasion/argv.py +44 -0
  7. pof/evasion/base.py +48 -0
  8. pof/evasion/cpu/__init__.py +0 -0
  9. pof/evasion/cpu/cpu_count.py +27 -0
  10. pof/evasion/fs/__init__.py +0 -0
  11. pof/evasion/fs/directory_exist.py +29 -0
  12. pof/evasion/fs/directory_list_exist.py +46 -0
  13. pof/evasion/fs/directory_list_missing.py +45 -0
  14. pof/evasion/fs/directory_missing.py +28 -0
  15. pof/evasion/fs/exec_method.py +51 -0
  16. pof/evasion/fs/executable_path.py +66 -0
  17. pof/evasion/fs/file_exist.py +29 -0
  18. pof/evasion/fs/file_list_exist.py +46 -0
  19. pof/evasion/fs/file_list_missing.py +45 -0
  20. pof/evasion/fs/file_missing.py +31 -0
  21. pof/evasion/fs/tmp.py +112 -0
  22. pof/evasion/hardware/__init__.py +0 -0
  23. pof/evasion/hardware/ram_count.py +50 -0
  24. pof/evasion/hooks/__init__.py +0 -0
  25. pof/evasion/hooks/debugger.py +36 -0
  26. pof/evasion/hooks/tracemalloc.py +23 -0
  27. pof/evasion/human/__init__.py +0 -0
  28. pof/evasion/human/p.py +45 -0
  29. pof/evasion/human/prompt.py +69 -0
  30. pof/evasion/integrity.py +129 -0
  31. pof/evasion/multi.py +41 -0
  32. pof/evasion/os/__init__.py +0 -0
  33. pof/evasion/os/domain.py +27 -0
  34. pof/evasion/os/hostname.py +27 -0
  35. pof/evasion/os/uid.py +28 -0
  36. pof/evasion/os/username.py +27 -0
  37. pof/evasion/processes/__init__.py +0 -0
  38. pof/evasion/processes/proc_count.py +47 -0
  39. pof/evasion/time/__init__.py +0 -0
  40. pof/evasion/time/expire.py +75 -0
  41. pof/evasion/time/uptime.py +48 -0
  42. pof/evasion/time/utc.py +26 -0
  43. pof/evasion/utils.py +198 -0
  44. pof/main.py +369 -0
  45. pof/obfuscator/__init__.py +86 -0
  46. pof/obfuscator/builtins.py +482 -0
  47. pof/obfuscator/cipher/__init__.py +0 -0
  48. pof/obfuscator/cipher/deep_encryption.py +194 -0
  49. pof/obfuscator/cipher/rc4.py +22 -0
  50. pof/obfuscator/cipher/shift.py +19 -0
  51. pof/obfuscator/cipher/xor.py +121 -0
  52. pof/obfuscator/compression/__init__.py +0 -0
  53. pof/obfuscator/compression/bz2.py +22 -0
  54. pof/obfuscator/compression/gzip.py +22 -0
  55. pof/obfuscator/compression/lzma.py +22 -0
  56. pof/obfuscator/compression/zlib.py +22 -0
  57. pof/obfuscator/constants.py +294 -0
  58. pof/obfuscator/definitions.py +341 -0
  59. pof/obfuscator/encoding/__init__.py +0 -0
  60. pof/obfuscator/encoding/a85.py +21 -0
  61. pof/obfuscator/encoding/b16.py +21 -0
  62. pof/obfuscator/encoding/b32.py +21 -0
  63. pof/obfuscator/encoding/b32hex.py +21 -0
  64. pof/obfuscator/encoding/b64.py +21 -0
  65. pof/obfuscator/encoding/b85.py +25 -0
  66. pof/obfuscator/encoding/binascii.py +22 -0
  67. pof/obfuscator/encoding/snt.py +23 -0
  68. pof/obfuscator/esoteric/__init__.py +0 -0
  69. pof/obfuscator/esoteric/call.py +49 -0
  70. pof/obfuscator/esoteric/doc.py +237 -0
  71. pof/obfuscator/esoteric/globals.py +62 -0
  72. pof/obfuscator/esoteric/imports.py +55 -0
  73. pof/obfuscator/extract_variables.py +297 -0
  74. pof/obfuscator/junk/__init__.py +0 -0
  75. pof/obfuscator/junk/add_comments.py +102 -0
  76. pof/obfuscator/junk/add_newlines.py +36 -0
  77. pof/obfuscator/names.py +474 -0
  78. pof/obfuscator/names_rope.py +375 -0
  79. pof/obfuscator/numbers.py +271 -0
  80. pof/obfuscator/other/__init__.py +0 -0
  81. pof/obfuscator/other/tokens.py +47 -0
  82. pof/obfuscator/remove/__init__.py +0 -0
  83. pof/obfuscator/remove/comments.py +36 -0
  84. pof/obfuscator/remove/exceptions.py +75 -0
  85. pof/obfuscator/remove/indents.py +28 -0
  86. pof/obfuscator/remove/loggings.py +120 -0
  87. pof/obfuscator/remove/loggings_old.py +45 -0
  88. pof/obfuscator/remove/newline.py +27 -0
  89. pof/obfuscator/remove/print.py +40 -0
  90. pof/obfuscator/restructure.py +15 -0
  91. pof/obfuscator/stegano/__init__.py +0 -0
  92. pof/obfuscator/stegano/docstrings.py +111 -0
  93. pof/obfuscator/stegano/ipv6encoding.py +21 -0
  94. pof/obfuscator/stegano/macencoding.py +21 -0
  95. pof/obfuscator/stegano/uuidencoding.py +21 -0
  96. pof/obfuscator/strings.py +359 -0
  97. pof/stager/__init__.py +17 -0
  98. pof/stager/cipher/__init__.py +0 -0
  99. pof/stager/cipher/rc4.py +36 -0
  100. pof/stager/download.py +80 -0
  101. pof/stager/image.py +374 -0
  102. pof/stager/lots/__init__.py +1 -0
  103. pof/stager/lots/cl1pnet.py +51 -0
  104. pof/stager/lots/pastebin.py +35 -0
  105. pof/stager/lots/pasters.py +30 -0
  106. pof/stager/quine.py +135 -0
  107. pof/utils/__init__.py +0 -0
  108. pof/utils/cipher/__init__.py +7 -0
  109. pof/utils/cipher/rc4.py +407 -0
  110. pof/utils/cipher/shift.py +41 -0
  111. pof/utils/compression/__init__.py +11 -0
  112. pof/utils/compression/bz2.py +38 -0
  113. pof/utils/compression/gzip.py +38 -0
  114. pof/utils/compression/lzma.py +38 -0
  115. pof/utils/compression/zlib.py +38 -0
  116. pof/utils/encoding/__init__.py +19 -0
  117. pof/utils/encoding/a85.py +35 -0
  118. pof/utils/encoding/b16.py +30 -0
  119. pof/utils/encoding/b3.py +93 -0
  120. pof/utils/encoding/b32.py +30 -0
  121. pof/utils/encoding/b32hex.py +30 -0
  122. pof/utils/encoding/b64.py +30 -0
  123. pof/utils/encoding/b85.py +35 -0
  124. pof/utils/encoding/binascii.py +38 -0
  125. pof/utils/encoding/snt.py +97 -0
  126. pof/utils/entropy.py +24 -0
  127. pof/utils/extract_names.py +204 -0
  128. pof/utils/generator/__init__.py +17 -0
  129. pof/utils/generator/advanced.py +53 -0
  130. pof/utils/generator/base.py +178 -0
  131. pof/utils/generator/basic.py +107 -0
  132. pof/utils/generator/names.txt +37241 -0
  133. pof/utils/generator/unicode.py +171 -0
  134. pof/utils/se/__init__.py +3 -0
  135. pof/utils/se/homoglyphs.py +99 -0
  136. pof/utils/se/homoglyphs.txt +96 -0
  137. pof/utils/stegano/__init__.py +5 -0
  138. pof/utils/stegano/ipv6encoding.py +97 -0
  139. pof/utils/stegano/macencoding.py +96 -0
  140. pof/utils/stegano/uuidencoding.py +102 -0
  141. pof/utils/tokens.py +68 -0
  142. python_obfuscation_framework-1.4.1.dist-info/LICENSE +674 -0
  143. python_obfuscation_framework-1.4.1.dist-info/METADATA +851 -0
  144. python_obfuscation_framework-1.4.1.dist-info/RECORD +147 -0
  145. python_obfuscation_framework-1.4.1.dist-info/WHEEL +5 -0
  146. python_obfuscation_framework-1.4.1.dist-info/entry_points.txt +2 -0
  147. 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
@@ -0,0 +1,2 @@
1
+ class PofError(Exception):
2
+ pass
@@ -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
+ ]