You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python字节码逆向还原源码(CTF挑战):工具适配与opcode解析求助

手动解析Python字节码还原get_flag函数:参数计数与代码对应逻辑

你遇到的核心问题是字节码操作数(参数计数)与手写代码的栈/操作逻辑不匹配——Python字节码基于栈式虚拟机运行,每个opcode的操作数数量、栈操作行为是固定的,一旦手写代码的逻辑和目标字节码的栈操作顺序、操作数索引对应不上,dis输出就会出现差异。

关键分析与解决步骤

要对齐目标字节码和还原代码,你需要逐行拆解目标get_flag的字节码,重点关注以下几点:

1. 明确opcode的参数计数规则

每个Python opcode的操作数数量是固定的,举例:

  • LOAD_GLOBAL n:1个参数(n是全局名字表的索引,对应要加载的变量名)
  • CALL_FUNCTION n:1个参数(n是传入的位置参数数量)
  • BINARY_XOR:0个参数(直接弹出栈顶两个元素,计算异或后压栈)
  • LOAD_CONST n:1个参数(n是函数常量池的索引,对应常量值)

你可以对照Python官方dis库文档,或直接用dis.opname/dis.opmap查看opcode定义,确认每个指令的参数数量和栈行为。

2. 对比目标字节码与手写代码的dis输出差异

针对你当前的get_flag尝试代码,常见差异点可能有:

  • 变量来源的操作数索引:如果目标字节码中f/k是通过LOAD_CONST加载(函数内部常量),而你的代码用了全局变量(LOAD_GLOBAL),操作数索引(参数n)会完全不同;
  • 循环逻辑的栈操作:你写的for i in range(len(f))会生成len(f)、range()两次CALL_FUNCTION 1指令,但如果目标字节码是直接遍历f的元素(GET_ITER+FOR_ITER),就不会有这些调用,参数计数自然不匹配;
  • 字节拼接的指令序列:你的bytes([ord(f[i]) ^ ord(k[i % len(k)])])会生成BUILD_LIST 1+CALL_FUNCTION 1,如果目标字节码用BYTEARRAY_APPEND直接追加,指令和参数计数也会不一样。

3. 按栈操作逻辑逆向推导代码

比如假设目标get_flag的字节码片段如下(示例):

2           0 LOAD_CONST               1 (b'')
              2 STORE_FAST               0 (out)

  3           4 LOAD_GLOBAL              0 (f)
              6 GET_ITER
        >>    8 FOR_ITER                28 (to 38)
             10 STORE_FAST               1 (i)
             12 LOAD_FAST                0 (out)
             14 LOAD_GLOBAL              1 (f)
             16 LOAD_FAST                1 (i)
             18 BINARY_SUBSCR
             20 LOAD_GLOBAL              2 (ord)
             22 CALL_FUNCTION            1
             24 LOAD_GLOBAL              3 (k)
             26 LOAD_FAST                1 (i)
             28 LOAD_GLOBAL              2 (len)
             30 LOAD_GLOBAL              3 (k)
             32 CALL_FUNCTION            1
             34 BINARY_MODULO
             36 BINARY_SUBSCR
             38 LOAD_GLOBAL              2 (ord)
             40 CALL_FUNCTION            1
             42 BINARY_XOR
             44 BUILD_LIST               1
             46 CALL_FUNCTION            1
             48 BINARY_ADD
             50 STORE_FAST               0 (out)
             52 JUMP_ABSOLUTE            8
        >>   54 LOAD_FAST                0 (out)
             56 RETURN_VALUE

逐行拆解后可对应到代码:

def get_flag():
    out = b""
    for i in range(len(f)):
        out += bytes([ord(f[i]) ^ ord(k[i % len(k)])])
    return out

如果目标字节码没有range(len(f))的调用,而是直接遍历f的字符,那循环逻辑就要改成for char in f:,同时调整后续的索引逻辑。

核心总结

要解决参数计数不匹配的问题,你需要:

  1. 把目标字节码的每一条指令的操作数索引、栈操作行为拆解清楚;
  2. 对比手写代码的dis输出,找到指令序列、参数索引、栈深度的差异;
  3. 调整代码逻辑,让生成的字节码的操作数数量、栈行为完全对齐目标。

内容的提问来源于stack exchange,提问作者Ricardo Uqueio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 12:35:15