跳转至

code object

在我们导入 python 脚本时在目录下会生成个一个相应的 pyc 文件,是pythoncodeobj的持久化储存形式,加速下一次的装载。

文件结构

pyc文件由三大部分组成

  • 最开始4个字节是一个Maigc int, 标识此pyc的版本信息

  • 接下来四个字节还是个int,是pyc产生的时间

  • 序列化的 PyCodeObject,结构参照include/code.h,序列化方法python/marshal

pyc完整的文件解析可以参照

关于co_code

一串二进制流,代表着指令序列,具体定义在include/opcode.h中,也可以参照python opcodes

python3.6 以上参数永远占1字节,如果指令不带参数的话则以0x00代替,在运行过程中被解释器忽略,也是Stegosaurus技术原理;而低于python3.5的版本中指令不带参数的话却没有0x00填充

例题

Hackover CTF 2016 : img-enc

首先尝试pycdc反编译失败

# Source Generated with Decompyle++
# File: imgenc.pyc (Python 2.7)

import sys
import numpy as np
from scipy.misc import imread, imsave

def doit(input_file, output_file, f):
Unsupported opcode: STOP_CODE
    img = imread(input_file, flatten = True)
    img /= 255
    size = img.shape[0]
# WARNING: Decompyle incomplete

注意到是python2.7,也就是说指令序列共占1字节或3字节(有参数无参数)

使用pcads得到

imgenc.pyc (Python 2.7)
...
                67      STOP_CODE               
                68      STOP_CODE               
                69      BINARY_DIVIDE           
                70      JUMP_IF_TRUE_OR_POP     5
                73      LOAD_CONST              3: 0
                76      LOAD_CONST              3: 0
                79      BINARY_DIVIDE       

定位到出错的地方,观察发现 LOAD_CONST LOAD_CONST BINARY_DIVIDE STORE_FAST opcodes (64 03 00 64 03 00 15 7d 05 00)被破坏了,根据上下文线索修复后

00000120  64 04 00 6b 00 00 72 ce  00 64 03 00 64 03 00 15  |d..k..r..d..d...|
00000130  7d 05 00 64 03 00 64 03  00 15 7d 05 00 64 03 00  |}..d..d...}..d..|
00000140  64 03 00 15 7d 05 00 64  03 00 64 03 00 15 7d 05  |d...}..d..d...}.|
00000150  00 64 03 00 64 03 00 15  7d 05 00 64 03 00 64 03  |.d..d...}..d..d.|
00000160  00 15 7d 05 00 64 03 00  64 03 00 15 7d 05 00 64  |..}..d..d...}..d|
00000170  03 00 64 03 00 15 7d 05  00 64 03 00 64 03 00 15  |..d...}..d..d...|
00000180  7d 05 00 64 03 00 64 03  00 15 7d 05 00 64 03 00  |}..d..d...}..d..|
00000190  64 03 00 15 7d 05 00 64  03 00 64 03 00 15 7d 05  |d...}..d..d...}.|
000001a0  00 64 03 00 64 03 00 15  7d 05 00 64 03 00 64 03  |.d..d...}..d..d.|
000001b0  00 15 7d 05 00 64 03 00  64 03 00 15 7d 05 00 6e  |..}..d..d...}..n|

接下来根据修复好的python源代码得到flag即可

延伸:

Tools

pycdc

将python字节码转换为可读的python 源代码,包含了反汇编(pycads)和反编译(pycdc)两种工具

Stegosaurus

允许我们在Python字节码文件(pyc或pyo)中嵌入任意Payload。由于编码密度较低,因此我们嵌入Payload的过程既不会改变源代码的运行行为,也不会改变源文件的文件大小

原理是在python的字节码文件中,利用冗余空间,将完整的payload代码分散隐藏到这些零零碎碎的空间中.

Ref: 一种用于在Python字节码中嵌入Payload的隐写工具 – Stegosaurus

Challenges: WHCTF-2017:Py-Py-Py