Python 3.12 pyc反编译全流程:字节码迷踪题解

二、字节码迷踪 —— Python pyc 跨版本逆向

 题目信息

题目名称

字节码迷踪

题目分类

REVERSE

题目难度

中级

题目分值

350

附件

py_obf_10.zip

Flag 格式

flag{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}(x 为小写字母或数字)

 题目展示

2-1  字节码迷踪 题目页面

 题目分析

题目描述说:你发现了一个可疑的 Python 编译文件集合,这些 .pyc 文件被认为是某个加密程序的组成部分,其中隐藏着重要的 flag 信息。由于原始源代码已经丢失,你只能通过分析编译后的字节码来还原程序逻辑并提取 flag。

解压 py_obf_10.zip 后只得到一个文件 py_obf_10.pyc(1179 bytes)。先看一下 pyc 文件头,确认 Python 版本:

Magic number: 0x0dcb (3531)

对应版本: Python 3.12.0

当前分析环境是 Python 3.10.2,没办法直接用内置的 marshal + dis 加载 3.12 的字节码。这里有两种思路:要么本地装 Python 3.12,要么用跨版本工具 xdis / uncompyle6 直接加载。考虑到只是要看常量和符号,用 xdis 更省事:

pip install uncompyle6

2-2  在线 pyc 反编译站点https:// tool.lu/pyc/

1. 提取常量与符号

用 xdis.load_module() 跨版本加载字节码,可以拿到完整的模块结构。模块级 co_consts 里有几个关键对象:

text

co_consts:

  [0] 0

  [1] None

  [2] <Code311: decrypt_flag>   <- 解密函数

  [3] <Code311: main>            <- 主函数

  [4] '__main__'

main 函数的常量列表里直接暴露了所有关键信息:

python

consts = (

    None,

    'aWNuaHRra3lgP2ZhaCJ3eTw3In19N2oiPGY9OCJ5dmdjfnxtPzdjY3ly',  # Base64 编码的 flag

    15,                                                             # XOR 密钥

    '请输入flag: ',                                                  # 提示语

    '正确!',                                                        # 成功消息

    '错误!',                                                        # 失败消息

)

varnames = ('encoded_flag', 'xor_key', 'user_input', 'correct_flag')

decrypt_flag 函数有两个参数(encoded_data, key),内部使用 base64.b64decode 解码后逐字节 XOR;它还有一个生成器 <genexpr>,闭包捕获了外层的 key。符号表里出现了 base64、b64decode、join、chr,函数逻辑基本就一目了然了。

2. 还原源代码

根据上面的常量、变量名、符号表,可以完整还原出原始 Python 代码:

import base64

 

def decrypt_flag(encoded_data, key):

    """Base64 解码后逐字节 XOR 解密"""

    decoded = base64.b64decode(encoded_data)

    return ''.join(chr(b ^ key) for b in decoded)

 

def main():

    encoded_flag = 'aWNuaHRra3lgP2ZhaCJ3eTw3In19N2oiPGY9OCJ5dmdjfnxtPzdjY3ly'

    xor_key = 15

 

    user_input = input('请输入flag: ').strip()

    correct_flag = decrypt_flag(encoded_flag, xor_key)

 

    if user_input == correct_flag:

        print('正确!')

    else:

        print('错误!')

 

if __name__ == '__main__':

    main()

3. 解密过程

加密逻辑非常清晰:base64_decode(data) -> XOR(0x0f) -> chr() -> join。直接照着写一个反向脚本即可:

import base64

 

encoded = "aWNuaHRra3lgP2ZhaCJ3eTw3In19N2oiPGY9OCJ5dmdjfnxtPzdjY3ly"

decoded = base64.b64decode(encoded)

xor_key = 15

 

flag = ''.join(chr(b ^ xor_key) for b in decoded)

print(flag)

逐字节看一下解密过程,前 5 个字符就能确认方向是对的:

text

Encoded (Base64): aWNuaHRra3lgP2ZhaCJ3eTw3In19N2oiPGY9OCJ5dmdjfnxtPzdjY3ly

Decoded (42 bytes): 69 63 6e 68 74 6b 6b 79 60 3f 66 61 68 22 77 79 ...

 

XOR 0x0f 逐字节:

  0x69 ^ 0x0f = 0x66 'f'

  0x63 ^ 0x0f = 0x6c 'l'

  0x6e ^ 0x0f = 0x61 'a'

  0x68 ^ 0x0f = 0x67 'g'

  0x74 ^ 0x0f = 0x7b '{'

  ... (共 42 字节)

如果手头没有 xor_key 这个常量,也可以对 0~255 所有密钥暴力搜索一遍,只有 key=15 能产生符合 flag{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} 格式约束的结果。

2-3  脚本运行得到 Flag

 Flag

FLAG

flag{ddvo0ing-xv38-rr8e-3i27-vyhlqsb08llv}

 技术总结

本题核心考点集中在 Python 字节码层面:

• 识别 .pyc 文件的 Magic Number,定位 Python 版本(这里是 3.12.0);

• 使用 xdis 等跨版本工具加载不同版本字节码,绕开版本不匹配的限制;

• 从 co_consts 和 co_names 还原程序逻辑,不需要完整反编译;

• Base64 + XOR 双重编码的识别与解密,遇到不知道密钥的情况可以暴力搜索。

整体难度不高,但要求选手对 Python 字节码结构、marshal 格式以及 pyc 文件头有一定了解,否则会被版本问题卡住。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0xBADCODE

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值