├── .gitattributes
├── crc32_unobfuscated.py
├── test_unobfuscated.py
├── bitwise_obfuscation.py
├── .gitignore
├── README.md
├── dirac_point_function.py
├── crc32_obfuscated.py
├── astobfuscate.py
├── test_obfuscated.py
└── LICENSE
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/crc32_unobfuscated.py:
--------------------------------------------------------------------------------
1 | # Make an example function that checks to see
2 | # if the flag's CRC is equal to a specified value
3 | # Flag is b'example_ctf{this_is_a_flag_A9uIus}'
4 |
5 | # Uses obfuscation techniques from dirac_point_function.py
6 | # And bitwise_obfuscation.py
7 |
8 |
9 | def flag_check(buf, *, ast_no_obfuscate=None):
10 | if not buf.startswith(bytes('example_ctf{', encoding='utf-8')) and buf[-1] == '}':
11 | return False
12 |
13 | init = 0xffffffff
14 |
15 | arr = []
16 |
17 | for entry in range(256):
18 | l = entry
19 | for i in range(8):
20 | if l & 1:
21 | l = ~(l & 0x1db710640) & ~(~l & ~0x1db710640)
22 | l = l // 2
23 | arr.append(l)
24 |
25 | for char in buf:
26 | init = (init // 256) ^ arr[(~(~(~(init & 0xff) & ~(init & 0xff))) & char) | ((~(~(init & 0xff) & ~(init & 0xff))) & ~char)]
27 |
28 | init = init ^ 0xffffffff
29 |
30 | return ((lambda a, b: (lambda f, a, b: f(f, a, b))(lambda f, a, b: f(f, a ^ b, (a & b) << 1) if b else a, a, b))((((init ^ 2706522384) + 1) & (~(init ^ 2706522384)) | 1081813890), (
31 | ~(((init ^ 2706522384) + 1) & (~(init ^ 2706522384))) & 1065669757)) % (1 << 32)) ^ 4293732728 == 1234567
32 |
--------------------------------------------------------------------------------
/test_unobfuscated.py:
--------------------------------------------------------------------------------
1 | import getpass
2 | from os import urandom
3 |
4 | name = getpass.win_getpass('What is your name?')
5 |
6 |
7 | def random_bytes(num_bytes, *, ast_no_obfuscate=None):
8 | byte_data = urandom(num_bytes)
9 | return byte_data
10 |
11 |
12 | def single_random_byte(unused_arg):
13 | return urandom(1)
14 |
15 |
16 | def add_2_nested(a, b):
17 | def x(b):
18 | return b + 2
19 | return x(a)
20 |
21 |
22 | class Data:
23 | internal = 2
24 |
25 | def add_2_instance(self, a, *, ast_no_obfuscate=None):
26 | return a + self.internal
27 |
28 | @classmethod
29 | def add_2_class(cls, a):
30 | return a + cls.internal
31 |
32 | @staticmethod
33 | def add_2_static(a):
34 | return a + 2
35 |
36 | if name:
37 | print('Hello, %s!' % name)
38 | else:
39 | print('I see how it is!')
40 |
41 | x = 57
42 | print(x, Data.add_2_class(x), Data().add_2_instance(x), Data().add_2_static(x), add_2_nested(x, None), x // 2, 0.1 + 0.2, ~2)
43 | print('Expected: 57 59 59 59 59 28 0.30000000000000004 -3\n')
44 |
45 | x += 15
46 | print(x)
47 | print('Expected: 72\n')
48 |
49 | Data.internal += 1
50 |
51 | print(x, Data.add_2_class(x), Data().add_2_instance(x), Data().add_2_static(x), add_2_nested(x, None))
52 | print('Expected: 72 75 75 74 74\n')
53 |
54 | print(True, True, True, True, True)
55 | print('Expected: True True True True True\n')
56 |
57 | print(False, False, False, False, False)
58 | print('Expected: False False False False False\n')
59 |
60 | print('Random byte is: %r' % single_random_byte(None))
--------------------------------------------------------------------------------
/bitwise_obfuscation.py:
--------------------------------------------------------------------------------
1 | from z3 import *
2 | """
3 | Proves the equivalence of certain operators to highly obfuscated bitwise alternatives
4 |
5 | Uses the Z3 theorem prover:
6 |
7 | https://github.com/Z3Prover/z3
8 | """
9 |
10 | BITS = 64
11 |
12 | def prove(equality):
13 | s = Solver()
14 | s.add(Not(equality))
15 | return s.check() == unsat
16 |
17 |
18 | x, y = BitVecs('x y', BITS)
19 |
20 | proofs = [
21 | x + y == 2 * (x | y) - (x ^ y),
22 | x + y == (x | y) + (x & y),
23 |
24 | x - y == 2 * (x | ~y + 1) - (x ^ ~y + 1),
25 | x - y == (x | ~y + 1) + (x & ~y + 1),
26 |
27 | x | y == (x ^ y) | (x & y),
28 | x | y == ~(~(x & x) & ~(y & y)),
29 |
30 | x & y == (x | y) - (x ^ y),
31 | x & y == ~(~(x & y) & ~(x & y)),
32 |
33 | x ^ y == x - y + 2 * (~x & y),
34 | x ^ y == (x | y) & ~(x & y)
35 | x ^ y == ~(x & y) & ~(~x & ~y),
36 | x ^ y == (~x & y) | (x & ~y),
37 |
38 | ~x == ~(x & x),
39 | ~x == ~(x & (x ^ ~x)) & ~(~x & 0),
40 | ~x == x ^ (x ^ ~x),
41 |
42 | -x == ~(x & x) + 1,
43 | -x == (~(x & (x ^ ~x)) & ~(~x & 0)) + 1,
44 | -x == (x ^ (x ^ ~x)) + 1,
45 | ]
46 |
47 | assert(all([prove(proof) for proof in proofs]))
48 |
49 | print('Proved all equalities!')
50 |
51 | # Addition through bitwise and recursion
52 | # XOR finds places that add to 1, AND and bitshift is the carry.
53 | # Y combinator is used to recurively move the carry until the addition is done.
54 |
55 | add = lambda a, b: (lambda f, a, b: f(f, a, b))(lambda f, a, b: f(f, a ^ b, (a & b) << 1) if b else a, a, b)
56 |
57 | for i in range(129):
58 | for j in range(129):
59 | assert add(i, j) == i + j
60 |
61 | print('Proved obfuscated recursive addition function!')
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .nox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # IPython
77 | profile_default/
78 | ipython_config.py
79 |
80 | # pyenv
81 | .python-version
82 |
83 | # celery beat schedule file
84 | celerybeat-schedule
85 |
86 | # SageMath parsed files
87 | *.sage.py
88 |
89 | # Environments
90 | .env
91 | .venv
92 | env/
93 | venv/
94 | ENV/
95 | env.bak/
96 | venv.bak/
97 |
98 | # Spyder project settings
99 | .spyderproject
100 | .spyproject
101 |
102 | # Rope project settings
103 | .ropeproject
104 |
105 | # mkdocs documentation
106 | /site
107 |
108 | # mypy
109 | .mypy_cache/
110 | .dmypy.json
111 | dmypy.json
112 |
113 | # Pyre type checker
114 | .pyre/
115 | .idea/
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ASTObfuscate
2 |
3 | ASTObfuscate is a little project designed to parse Python syntax, and turn that Python into an obfuscated version for compilation into `.pyc` file.
4 |
5 | ASTObfuscate is not meant for pure obfuscation of the source code, as it is relatively easy to make the source code much more readable by folding in constants
6 |
7 | ### Use Cases
8 | ASTObfuscate was originally based on [Jurrian Bremer's AST obfuscator he made for a CTF event](http://jbremer.org/python-source-obfuscation-using-asts/).
9 | ASTObfuscate may not hold up to expert eyes, but combining it with compilation into a `.pyc` file will probably make for a reasonably difficult CTF, but manual obfuscation
10 | of the source code is recommended before running it through ASTObfuscate.
11 |
12 | See the crc32 files for an example of simple CTF flag verifier obfuscated with 5 passes of ASTObfuscate
13 |
14 | ### Examples
15 | ```python
16 | print('Hello, World!')
17 | ```
18 | becomes
19 | ```python
20 | OO11lOO01O00lllOlllOl = (lambda l00110100O00lO11111lO,
21 | lO10111llOlO1011100ll: l00110100O00lO11111lO % lO10111llOlO1011100ll)
22 | O00011OOO10OlOOl01O11 = (lambda Ol01lOlOO0100OlOlO0O0,
23 | O0lO101lO0Ol1O1lO0lOl: Ol01lOlOO0100OlOlO0O0 ^ O0lO101lO0Ol1O1lO0lOl)
24 | print(''.join(filter(lambda llO1lOOll111O0100OllO: ord(
25 | llO1lOOll111O0100OllO) % O00011OOO10OlOOl01O11(2 ^ 4, 0 * 3 + 0) != 0 *
26 | 5 + 0, '*ZZ\x12\x0cx\x00\x0660\x12xZ\x18\x0c')).join(chr(
27 | O00011OOO10OlOOl01O11(ll0O1O110lOO0OlOl10OO, OO11OOl1lO0O0011OOlll)) for
28 | OO11OOl1lO0O0011OOlll, ll0O1O110lOO0OlOl10OO in zip(
29 | b'\\rUc\xc0KgW\xf8\xfc?\x12\x0b', b'}\x169\x11\xaf\x1cG{\x97\x90SwC'))[
30 | ::-1 * 6 + 5 ^ 0 * 6 + 0])
31 | ```
32 |
33 | ### Usage
34 | ```$ astobfuscate.py