🦙

AlpacaHack Round 4 - pytecode

2024/10/07に公開

お仕事のためリアタイできなかったですが、pytecode面白そうだったのでupsolveしました。

(アセンブリではないバイトコードやpickleを変換することを「ディスアセンブル」「デコンパイル」と呼ぶことが正確かどうかはよく知らないですが、この記事では可読性のためにラベル付けを行うことを「ディスアセンブル」、実行可能なpythonコードに変換することを「デコンパイル」と呼ぶことにします。)

Step 1: Pickleの解析

Pickleのデコンパイラといえば、fickling。とりあえず一度デコンパイルしてみよう。

disasm.py
from fickling import fickle, tracing
from astunparse import unparse

hex_code = "..."
bts = bytes.fromhex(hex_code)
stacked_pickled = fickle.StackedPickle.load(bts)
interpreter = fickle.Interpreter(stacked_pickled[0])
trace = tracing.Trace(interpreter)
code = unparse(trace.run())
open("disasmed.py", "w").write(code)

実行結果

Traceback (most recent call last):
  File "/workspaces/pytecode/disasm.py", line 6, in <module>
    stacked_pickled = fickle.StackedPickle.load(bts)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pytecode/venv/lib/python3.11/site-packages/fickling/fickle.py", line 1607, in load
    p = Pickled.load(pickled)
        ^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pytecode/venv/lib/python3.11/site-packages/fickling/fickle.py", line 668, in load
    opcodes.append(Opcode(info=info, argument=arg, data=data, position=pos))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pytecode/venv/lib/python3.11/site-packages/fickling/fickle.py", line 137, in __new__
    raise NotImplementedError(f"TODO: Add support for Opcode {info.name}")
NotImplementedError: TODO: Add support for Opcode INST

どうやら、INSTという命令が実装されていないようだ。pickle.pyを読むと、クラスのimportとインスタンス化を同時に行う命令のようだ。STACK_GLOBALの実装やOBJの実装を参考にしながら、ficklingのクラスを実装してみる。

fickling/fickle.py
1616a1617,1625
> class Inst(StackSliceOpcode):
>     name = "INST"
> 
>     def run(self, interpreter: Interpreter, stack_slice: List[ast.expr]):
>         module, name = self.arg.split(" ")
>         alias = ast.alias(name)
>         interpreter.module_body.append(ast.ImportFrom(module=module, names=[alias], level=0))
>         interpreter.stack.append(ast.Call(ast.Name(name, ast.Load()), stack_slice, []))
> 
追記

https://github.com/trailofbits/fickling/commit/10a3ab071b35e3b9b9d7bbf00787cfc36dde7356

なんと二週間前にINSTのサポートのコミットが作成されていました。バージョンのアップデートが楽しみです。

これでficklingはコードを吐き出すが、次のようなエラーが出てしまう。

Traceback (most recent call last):
  File "/workspaces/pytecode/disasmed.py", line 12, in <module>
    _var4.update({0: 3, 6: b'\x97\x00|\x00|\x01x\x02x\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x02z\x19\x00\x00c\x03c\x02<\x00\x00\x00|\x00S\x00'})
    ^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'update'

これは、ficklingのSETITEMSの実装が間違っていて、SETITEMSされる先が辞書型しか想定されておらず、dict.updateが利用されているのが原因である。(参考: pickle.py)

それを修正したのが以下であるが、正直生成コードを直接いじってもよかったかな、と思った。

fickling/fickle.py
1338,1344c1338,1342
<             update_dict = ast.Dict(keys=update_dict_keys, values=update_dict_values)
<             interpreter.module_body.append(
<                 ast.Expr(
<                     ast.Call(
<                         ast.Attribute(ast.Name(dict_name, ast.Load()), "update"),
<                         [update_dict],
<                         [],
---
>             for key, value in zip(update_dict_keys, update_dict_values):
>                 interpreter.module_body.append(
>                     ast.Assign(
>                         [ast.Subscript(ast.Name(dict_name, ast.Load()), key, ast.Store())],
>                         value

これでinpの値を書き換えて実行すると、正しく動いていることが確認できた。

実際にデコンパイルされるコード
disasmed.py
from types import FunctionType
from types import CodeType
_var0 = getattr('?', '__mul__')
_var1 = _var0(1337)
from builtins import tuple
from builtins import tuple
from __main__ import inp
_var2 = getattr(int, 'from_bytes')
_var3 = FunctionType(CodeType(*tuple([2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie'])), {})(inp, _var2)
_var4 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var4[0] = 3
_var4[6] = b'\x97\x00|\x00|\x01x\x02x\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x02z\x19\x00\x00c\x03c\x02<\x00\x00\x00|\x00S\x00'
from builtins import tuple
_var5 = FunctionType(CodeType(*tuple(_var4)), {})(_var3, 0, 1384820248286204727)
_var6 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var6[0] = 3
_var6[6] = b'\x97\x00|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x02|\x00|\x01<\x00\x00\x00|\x00|\x02<\x00\x00\x00|\x00S\x00'
from builtins import tuple
_var7 = FunctionType(CodeType(*tuple(_var6)), {})(_var3, 2, 7)
_var8 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var8[0] = 3
_var8[6] = b'\x97\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x02z\n\x00\x00}\x04|\x04|\x04z\n\x00\x00}\x05|\x04|\x05k\x00\x00\x00\x00\x00r\x03|\x04\x0b\x00n\x01|\x04|\x00|\x01<\x00\x00\x00|\x00S\x00'
from builtins import tuple
_var9 = FunctionType(CodeType(*tuple(_var8)), {})(_var3, 4, 9259542123273814144)
_var10 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var10[0] = 3
_var10[6] = b'\x97\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\x02\x00\x00}\x04|\x04|\x02z\x03\x00\x00|\x04z\n\x00\x00}\x05|\x03|\x05z\x01\x00\x00}\x06|\x03|\x02z\x16\x00\x00}\x03|\x04|\x04z\x00\x00\x00}\x07|\x07|\x07z\x05\x00\x00|\x07z\x00\x00\x00}\x08|\x04|\x08z\x03\x00\x00}\t|\t|\x02z\n\x00\x00}\n|\x03|\x06|\nz\x03\x00\x00z\x14\x00\x00}\x03|\x03|\x00|\x01<\x00\x00\x00|\x00S\x00'
from builtins import tuple
_var11 = FunctionType(CodeType(*tuple(_var10)), {})(_var3, 1, 13)
_var12 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var12[0] = 4
_var12[6] = b'\x97\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00z\x0c\x00\x00|\x00|\x03<\x00\x00\x00|\x00S\x00'
from builtins import tuple
_var13 = FunctionType(CodeType(*tuple(_var12)), {})(_var3, 1, 6, 1)
_var14 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var14[0] = 3
_var14[6] = b'\x97\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x02z\t\x00\x00z\x0c\x00\x00|\x00|\x01<\x00\x00\x00|\x00S\x00'
from builtins import tuple
_var15 = FunctionType(CodeType(*tuple(_var14)), {})(_var3, 5, 12)
_var16 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var16[0] = 3
_var16[6] = b'\x97\x00|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x02|\x00|\x01<\x00\x00\x00|\x00|\x02<\x00\x00\x00|\x00S\x00'
from builtins import tuple
_var17 = FunctionType(CodeType(*tuple(_var16)), {})(_var3, 3, 6)
_var18 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var18[0] = 2
_var18[6] = b'\x97\x00|\x00|\x00z\x06\x00\x00}\x02|\x00|\x00z\x02\x00\x00}\x03|\x03|\x03z\x00\x00\x00}\x04|\x03|\x04z\x00\x00\x00}\x05|\x04|\x04z\x08\x00\x00}\x06|\x04|\x05z\x00\x00\x00}\x07|\x05|\x04z\x05\x00\x00}\x08|\x06|\x04z\x05\x00\x00|\x03z\n\x00\x00}\t|\x03|\x08z\x03\x00\x00}\n|\n|\x04z\x05\x00\x00}\x0b|\x03|\nz\x03\x00\x00|\x03z\n\x00\x00}\x0c|\t|\x08z\x05\x00\x00}\r|\t|\x0b|\nz\x00\x00\x00|\x03z\n\x00\x00z\x05\x00\x00}\x0e|\x02|\x03|\x04|\x05|\x06|\x07|\x08|\tg\x08}\x0f|\x0fD\x00]\x1b}\x10|\x01|\x10\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00z\x0c\x00\x00|\x01|\x10<\x00\x00\x00|\r|\x00z\x05\x00\x00|\x0ez\x00\x00\x00|\x0cz\x01\x00\x00}\x00\x8c\x1c|\x01S\x00'
from builtins import tuple
_var19 = FunctionType(CodeType(*tuple(_var18)), {})(1384596539316466481, _var3)
_var20 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
_var20[0] = 2
_var20[6] = b'\x97\x00|\x00|\x01k\x02\x00\x00\x00\x00S\x00'
from builtins import tuple
_var21 = FunctionType(CodeType(*tuple(_var20)), {})(_var3, [4714351799183481704, 9920649113211881281, 1577726041120304458, 11479115687019652446, 3159180328194279316, 7893169807958830647, 9845433486767146114, 7349253534905816330])
result = _var21

Step 2: pythonバイトコードの解読

デコンパイルされたコードを軽くまとめると次のようになる。

disasmed.py
from types import FunctionType, CodeType
_var1 = '?' * 1337
inp = b"Alpaca{abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123}" # 制約より64文字
_var3 = FunctionType(CodeType(*tuple([2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie'])), {})(inp, int.from_bytes)
_var4 = [3, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x01x\x02x\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x02z\x19\x00\x00c\x03c\x02<\x00\x00\x00|\x00S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
FunctionType(CodeType(*tuple(_var4)), {})(_var3, 0, 1384820248286204727)
_var6 = [3, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x02|\x00|\x01<\x00\x00\x00|\x00|\x02<\x00\x00\x00|\x00S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
FunctionType(CodeType(*tuple(_var6)), {})(_var3, 2, 7)
_var8 = [3, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x02z\n\x00\x00}\x04|\x04|\x04z\n\x00\x00}\x05|\x04|\x05k\x00\x00\x00\x00\x00r\x03|\x04\x0b\x00n\x01|\x04|\x00|\x01<\x00\x00\x00|\x00S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
FunctionType(CodeType(*tuple(_var8)), {})(_var3, 4, 9259542123273814144)
_var10 = [3, 0, 0, 1337, 1337, 0,  b'\x97\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\x02\x00\x00}\x04|\x04|\x02z\x03\x00\x00|\x04z\n\x00\x00}\x05|\x03|\x05z\x01\x00\x00}\x06|\x03|\x02z\x16\x00\x00}\x03|\x04|\x04z\x00\x00\x00}\x07|\x07|\x07z\x05\x00\x00|\x07z\x00\x00\x00}\x08|\x04|\x08z\x03\x00\x00}\t|\t|\x02z\n\x00\x00}\n|\x03|\x06|\nz\x03\x00\x00z\x14\x00\x00}\x03|\x03|\x00|\x01<\x00\x00\x00|\x00S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
FunctionType(CodeType(*tuple(_var10)), {})(_var3, 1, 13)
_var12 = [4, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00z\x0c\x00\x00|\x00|\x03<\x00\x00\x00|\x00S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
FunctionType(CodeType(*tuple(_var12)), {})(_var3, 1, 6, 1)
_var14 = [3, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x02z\t\x00\x00z\x0c\x00\x00|\x00|\x01<\x00\x00\x00|\x00S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
FunctionType(CodeType(*tuple(_var14)), {})(_var3, 5, 12)
_var16 = [3, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00|\x01\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x02|\x00|\x01<\x00\x00\x00|\x00|\x02<\x00\x00\x00|\x00S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
FunctionType(CodeType(*tuple(_var16)), {})(_var3, 3, 6)
_var18 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00z\x06\x00\x00}\x02|\x00|\x00z\x02\x00\x00}\x03|\x03|\x03z\x00\x00\x00}\x04|\x03|\x04z\x00\x00\x00}\x05|\x04|\x04z\x08\x00\x00}\x06|\x04|\x05z\x00\x00\x00}\x07|\x05|\x04z\x05\x00\x00}\x08|\x06|\x04z\x05\x00\x00|\x03z\n\x00\x00}\t|\x03|\x08z\x03\x00\x00}\n|\n|\x04z\x05\x00\x00}\x0b|\x03|\nz\x03\x00\x00|\x03z\n\x00\x00}\x0c|\t|\x08z\x05\x00\x00}\r|\t|\x0b|\nz\x00\x00\x00|\x03z\n\x00\x00z\x05\x00\x00}\x0e|\x02|\x03|\x04|\x05|\x06|\x07|\x08|\tg\x08}\x0f|\x0fD\x00]\x1b}\x10|\x01|\x10\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00z\x0c\x00\x00|\x01|\x10<\x00\x00\x00|\r|\x00z\x05\x00\x00|\x0ez\x00\x00\x00|\x0cz\x01\x00\x00}\x00\x8c\x1c|\x01S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
FunctionType(CodeType(*tuple(_var18)), {})(1384596539316466481, _var3)
_var20 = [2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x01k\x02\x00\x00\x00\x00S\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie']
result = FunctionType(CodeType(*tuple(_var20)), {})(_var3, [4714351799183481704, 9920649113211881281, 1577726041120304458, 11479115687019652446, 3159180328194279316, 7893169807958830647, 9845433486767146114, 7349253534905816330])

このCodeTypeとかFunctionTypeは何なのか調べてみると、どうやら通常はcompile関数の成果物として想定されているクラスのようだ。ドキュメントにはどの引数が何を表しているか詳細は載っていないが、見るからに第六引数がpythonのバイトコードなのだろう。

バイトコードのデコンパイルはpycdcを使ってみる。

_var3の生成で利用されるコードであれば、次のように記述することでmarshalファイルを生成することができる。

marshal.dump(CodeType(*tuple([2, 0, 0, 1337, 1337, 0, b'\x97\x00|\x00|\x00k\x03\x00\x00\x00\x00}\x02|\x00|\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x03|\x03|\x03z\n\x00\x00}\x04|\x04|\x04z\x08\x00\x00}\x05|\x05|\x05z\x03\x00\x00}\x06|\x05|\x06z\x03\x00\x00}\x07|\x07|\x05z\x03\x00\x00}\x08|\x07|\x05z\n\x00\x00}\t|\x08|\tz\n\x00\x00}\n|\x08|\x06z\n\x00\x00}\x0b|\x08|\x05z\n\x00\x00}\x0c|\x04|\x05|\x06|\t|\x07|\n|\x0b|\x0cg\x08}\r|\x04g\x01|\x08z\x05\x00\x00}\x0e|\rD\x00]"}\x0f|\x0f|\x08z\x05\x00\x00}\x10|\x10|\x08z\x00\x00\x00}\x11\x02\x00|\x01|\x00|\x10|\x11\x85\x02\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x0e|\x0f<\x00\x00\x00\x8c#|\x0eS\x00', (), (), tuple(_var1), 'the', 'cake', 'is', 1337, b'a', b'lie'])), open("var3.mshl", "wb"))

これをデコンパイルすると、実行結果は以下のようになった。

$ ./pycdc/pycdc -c -v 3.11 var3.mshl 
# Source Generated with Decompyle++
# File: var3.mshl (Python 3.11)

Unsupported opcode: JUMP_BACKWARD
? = ? != ?
? = ?[?]
? = ? - ?
? = ? ** ?
? = ? << ?
? = ? << ?
? = ? << ?
? = ? - ?
? = ? - ?
? = ? - ?
? = ? - ?
? = [
    ?,
    ?,
    ?,
    ?,
    ?,
    ?,
    ?,
    ?]
? = [
    ?] * ?
# WARNING: Decompyle incomplete

"?"ばかり表示されているのは、第九引数の_var1は変数名のリストなのだが、これの要素が全て"?"となっているからである。つまり、_var1

_var1 = [f"v{i}" for i in range(1337)]

のように書き換えれば

$ ./pycdc/pycdc -c -v 3.11 var3.mshl 
# Source Generated with Decompyle++
# File: var3.mshl (Python 3.11)

Unsupported opcode: JUMP_BACKWARD
v2 = v0 != v0
v3 = v0[v2]
v4 = v3 - v3
v5 = v4 ** v4
v6 = v5 << v5
v7 = v5 << v6
v8 = v7 << v5
v9 = v7 - v5
v10 = v8 - v9
v11 = v8 - v6
v12 = v8 - v5
v13 = [
    v4,
    v5,
    v6,
    v9,
    v7,
    v10,
    v11,
    v12]
v14 = [
    v4] * v8
# WARNING: Decompyle incomplete

のように変数名がわかりやすくなる。

また、Unsupported opcode: JUMP_BACKWARDと出力されていることからもわかる通り、対応しているopcodeまでしか出力されない(_var3、_var4、var18以外はデコンパイル可能)。代わりに

$ ./pycdc/pycdas -c -v 3.11 var3.mshl

を実行することで、ディスアセンブルすることができるので、ドキュメントを読みながら地道にコード化していく。

ポイントとしては、_var3_var18には

v2 = v0 % v0
v3 = v0 // v0
v4 = v3 + v3
v5 = v3 + v4
v6 = v4 ** v4
v7 = v4 + v5
v8 = v5 * v4
v9 = v6 * v4 - v3
v10 = v3 << v8
v11 = v10 * v4
v12 = (v3 << v10) - v3
v13 = v9 * v8
v14 = v9 * (v11 + v10 - v3)
v15 = [
    v2,
    v3,
    v4,
    v5,
    v6,
    v7,
    v8,
    v9]

のようなコードが出現するが、よく見るとv0 % v0は必ず0だし、v0//v0は必ず1、のように入力依存であるように見せかけて実はそうではない変数が出現することに注意が必要。上記の例だと、v15[0,1,2,3,4,5,6,7]と等しくなる。

inp = b"Alpaca{abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123}" # 制約より64文字
_var3 = [0] * 8
for i in range(8):
    _var3[i] = int.from_bytes(inp[i * 8: i * 8 + 8])
_var3[0] ^= 1384820248286204727
_var3[2], _var3[7] = _var3[7], _var3[2]
_var3[4] = abs(_var3[4] - 9259542123273814144)
_var3[1] = (_var3[1] >> 13) | (_var3[1] & 0x1fff) << 51 # 右に13ビット分ローテートシフト
_var3[1] = _var3[1] ^ _var3[6]
_var3[5] = _var3[5] ^ _var3[5] >> 12
_var3[3], _var3[6] = _var3[6], _var3[3]
v0 = 1384596539316466481
for i in range(8):
    _var3[i] ^= v0
    v0 = (42 * v0 + 1337) & 0xffffffffffffffff
result = _var3 == [4714351799183481704, 9920649113211881281, 1577726041120304458, 11479115687019652446, 3159180328194279316, 7893169807958830647, 9845433486767146114, 7349253534905816330]
print(result)

あとはコードを逆算するだけである。大体はシンプルに逆算できるが、以下のコードは少し考える必要がある。

_var3[5] = _var3[5] ^ _var3[5] >> 12

右に12ビットシフトした値とXORしているので、上位12ビットは変化していない。つまり、上記と同じ演算を行うと、上位24ビットのみが逆算できる。これを5回繰り返すことによって、上位36、48、60、64ビットと順に正しいビットが計算できる。

それを踏まえて以下のように逆算するコードを書いた。

ans = [4714351799183481704, 9920649113211881281, 1577726041120304458, 11479115687019652446, 3159180328194279316, 7893169807958830647, 9845433486767146114, 7349253534905816330]

v0 = 1384596539316466481
for i in range(8):
    ans[i] = ans[i] ^ v0
    v0 = (42 * v0 + 1337) & 0xffffffffffffffff
ans[3], ans[6] = ans[6], ans[3]
orig = ans[5]
for _ in range(5):
    ans[5] = orig ^ (ans[5] >> 12)
ans[1] = ans[1] ^ ans[6]
ans[1] = (ans[1] >> 51) | (ans[1] & 0x7ffffffffffff) << 13
if ans[4] + 9259542123273814144 > 64:
  ans[4] = 9259542123273814144 - ans[4]
else:
  ans[4] = ans[4] + 9259542123273814144
ans[2], ans[7] = ans[7], ans[2]
ans[0] ^= 1384820248286204727
  
res = b""
for i in range(8):
    res += ans[i].to_bytes(8)
print(res)

まとめ

pickle最高!

Discussion