pyc文件python版本为3.13.8,而且似乎有混淆,无法反编译,先进行模糊测试:
1 | import sys, os, marshal, threading |
发现文件错误时指令数量为610,含有文件但长度错误时指令数量为768
爆破长度,发现直到10KB都处于768,尝试反汇编前768条指令
1 | import sys, os, marshal, dis, threading, builtins, io, weakref, argparse, types |
发现前面加载了一个351项的常量,这个常量可以在pyc文件的尾部看到,实际上是一个00FF的int类型整数表和一个32126的可打印字符表,不确定其是同时加载到一个组中还是分开加载的,按顺序的351项如下:
1 | 6c77e381e668654641a02c6776351a72bc7dcf6109994773549058aa085005454404373671229f5c003376fbb95aea8d98efbf69ffad4406bd6b4cc5787c3c8a52d225474ea32a589160394dacce4b49437b9cf4573807a52eda2785cc6ebb422d3af333378fc4a9f651a17b1618790b0caeb65974606de9a25b95272f83fef11924873b452425d1657020e55c9a3deed84166d93f53a65975359754fd0a4c787e4a02f77a4363b1dd932667361e035eb74fec1bc9c3f2665d5e522c79467aab6fdfb30149172b2e3dd6c76228343c61e8562f88dc947c89234877e4b5b27312af6d103bc12ac0641f1557cbf9b89beb5d749672d0510ee7804f1d6353267132502d690d5ffcba3e40dee2281cc223b43a5b84d7f86a406ecd5f308e62f0fa4e8bca9d3f2b8211d570315aa829f54d8c7d7f4a0f6f306814be568621c632346a216c9e6b644b927520c8d348e1383ea429e042dbedd439a7b055225531137e |
可打印字符:
1 | lwFgv5r}aGTE7q"\D|%N*X`MKIC{Wn3Y'$e =A?LxJ&6f^R,yzo+.b(4</Hsm;d]tQOcS2P-i_#:[j@0p1ZhV!ku8>)B9U~ |
int:
1 | e381e6686541a02c1abccf0999739058aa0850054404369f003376fbb95aea8d98efbf69ffad06bd6b4cc5783c8a52d247a39139acce9cf43807a52eda2785ccbb422d3af3378fc4a9f651a17b1618790b0caeb674606de9a25b952f83fef11924873b4525d170e55c9aeed866d953a65975359754fd0a7e02f77a4363b1dd93671e035eb74fec1bc9c3f25d46abdfb30149173dd6c761e85688dc947c892377e4b5b212af10c12ac01f1557cbf9b89beb9672d00ee7801d26710dfcba3e40dee2281cc2b484d7f86ecd5f8e62f0fa4e8bca9d3f2b8211d5a829f54d8c7d7f4a0f6f3014be8621c632346a6c9e644b9220c8d348e1a4e0dbedd4a7b022553113 |
然后接下来的逻辑:
1 | 00489 T46924 check.py:False debug_func off=0896 LOAD_CONST None |
在这个call处,如果文件不存在,则后续进入报错并退出,否则继续执行:
1 | 00565 T46924 check.py:? debug_func off=1028 PUSH_NULL |
在这个call后程序退出,在此之前有大量加载指令,尝试把这些加载指令的参数也hook出来,发现hook不到,该思路不通.
回归到反编译问题中,注意到dis.dis迅速崩溃,解析发现是因为存在大量指令47 FF 47 FF 47 FF 55 FB,对应的逻辑是LOAD_FAST(4294967291),这是根本不可能的,猜测是无效代码,批量替换为0x30(NOP)或修改成加载其他索引发现运行崩溃,说明在0xFFFFFFFB的位置真的有一个局部变量,或者是某种其他混淆操作,导致dis完全无法解析,尝试手动模拟pythonVM来解析:
1 | pyc_path = 'output.pyc' |
过程中注意到以下输出:
1 | [0x3bc] LOAD_FAST idx[0x000000c0] b'o' |
注意到文件长度应该是39944,后续看起来像很多比较操作
RES是一个43万项的数组,其中有所有的string和int,去除string,得到22万组数据,查看前几项:
1 | 39944, 1, # exit(1) |
发现并不是简单的直接比较,放弃trace,直接同构PythonVM虚拟机(opcode来自github上的cpython3.13):
1 | pyc_path = 'output.pyc' |
利用python的值加载规则即可提取参数和运算符,处理输出的EXP表达式列表(上面的脚本把所有和索引相同的数值删掉了,所以在前面会有几处丢失,在丢失位补上索引即可)(29、54、141、228行末项,52、78、98行xor后方,79、148行首项,158、174行xor前方,256行and前方):
1 | def apply_op(left, op_str, right): |
恢复出来是一张图片:

infobahn{this_is_by_far_the_worst_obfuscator_ive_had_the_displeasure_of_writing_i_dont_even_know_how_the_code_ran_e891ac534881}
最终两个脚本可以合并:
1 | with open('output.pyc', 'rb') as f: bin = f.read() |