# [SECCON CTF 2021] dis-me writeup (Rev, 15 solves) @moratorium08
## Overview of the challenge
The concept of this challenge is to introduce an packer-like technique to pyc object.
The given pyc file contains a packed code in the middle of code bytes so python's dis cannot disassemble this program.
```
>>> dis.dis(x)
0 0 EXTENDED_ARG 3
2 JUMP_ABSOLUTE 898
4 ROT_THREE
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/vagrant/.pyenv/versions/3.6.9/lib/python3.6/dis.py", line 60, in dis
disassemble(x, file=file)
File "/home/vagrant/.pyenv/versions/3.6.9/lib/python3.6/dis.py", line 335, in disassemble
co.co_consts, cell_names, linestarts, file=file)
File "/home/vagrant/.pyenv/versions/3.6.9/lib/python3.6/dis.py", line 346, in _disassemble_bytes
line_offset=line_offset):
File "/home/vagrant/.pyenv/versions/3.6.9/lib/python3.6/dis.py", line 308, in _get_instructions_bytes
argval, argrepr = _get_name_info(arg, names)
File "/home/vagrant/.pyenv/versions/3.6.9/lib/python3.6/dis.py", line 272, in _get_name_info
argval = name_list[name_index]
IndexError: tuple index out of range
```
(In fact, pycdas can)
You may notice the first byte code is to just jump to addr `898`. Then you may want to extract the byte code from addr 898. So let's do that.
```
>>> dis.dis(c)
0 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (None)
4 IMPORT_NAME 0 (marshal)
6 STORE_NAME 0 (marshal)
8 LOAD_CONST 0 (0)
10 LOAD_CONST 1 (None)
12 IMPORT_NAME 1 (base64)
14 STORE_NAME 1 (base64)
16 LOAD_CONST 0 (0)
18 LOAD_CONST 1 (None)
20 IMPORT_NAME 2 (types)
22 STORE_NAME 2 (types)
24 LOAD_NAME 3 (open)
26 LOAD_NAME 4 (__file__)
28 LOAD_CONST 2 ('rb')
30 CALL_FUNCTION 2
32 STORE_NAME 5 (f)
... omitted ...
```
It looks good. By reading this file, you can see how the code can be unpacked:
```python=
f = open(__file__, "rb")
f.seek(12)
s = marshal.loads(f.read()).co_code[4:]
marshal.loads(base64.b64decode(
bytes([(x- 7 *i -45)%256 for i, x in
enumerate(s[2:2+s[0]*256+s[1]])])
))
```
So, let's do that.
```python=
import base64
f = open(F, "rb")
f.seek(12)
s = marshal.loads(f.read()).co_code[4:]
code_dump = base64.b64decode(bytes([(x - 7 * i - 45) % 256 for i, x in enumerate(s[2:2 +
s[0] * 256 + s[1]])]))
with open("fixed-func.pyc", "wb") as f:
f.write(header)
f.write(code_dump)
```
then
```
$ uncompyle6 fixed-func.pyc
# uncompyle6 version 3.8.0
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.9.6 (default, Sep 4 2021, 04:11:19)
# [GCC 9.3.0]
# Embedded file name: run.py
# Compiled at: 2021-11-21 18:11:20
# Size of source mod 2**32: 1169 bytes
f = lambda n: n if n <= 1 else (f(n - 1) + f(n - 2)) % 10
flag = input('Input the flag > ')
s = 1
if flag.startswith('SECCON{'):
if flag.endswith('}'):
if len(flag) == 40:
s = sum(abs(ord(c) - ord(str(f(i)))) for i, c in enumerate(flag[7:-1]))
s or print('Correct! The flag is', flag)
else:
print('Wrong :(')
# okay decompiling fixed-func.pyc
```
Now you can see what is the flag clearly :)