在进行pyc文件反编译时遇到如下报错:

这个问题是我们生成的pyc文件本身存在的问题
pyc文件生成时,头部的magic number被清理,需要我们另外补上。
我们先观察对比一下正确的pyc文件头与报错的pyc文件头:
正常的pyc文件

丢头部的magic number的pyc文件

可以看到,头部确实是不一样的
33 0D 0D 0A 0E 58 1D 65 C2 01 00 00
尝试直接将正常的pyc文件头复制给丢头部的magic number的pyc文件:

还是报错,查看生成的py文件也是空

在Python3.7及以上版本的编译后二进制文件中,头部除了四字节Magic Number,还有四个字节的空位和八个字节的时间戳+大小信息,后者对文件反编译没有影响,全部填充0即可;
Python3.3 - Python3.7(包含3.3)版本中,只需要Magic Number和八位时间戳+大小信息
Python3.3 以下的版本中,只有Magic Number和四位时间戳。
但是我没找到这个(我的是Python3.11 ╮(╯▽╰)╭ )
只能给大家附上一些早期版本的Magic Number的对照表:
- Known values:
- # Python 1.5: 20121
- # Python 1.5.1: 20121
- # Python 1.5.2: 20121
- # Python 1.6: 50428
- # Python 2.0: 50823
- # Python 2.0.1: 50823
- # Python 2.1: 60202
- # Python 2.1.1: 60202
- # Python 2.1.2: 60202
- # Python 2.2: 60717
- # Python 2.3a0: 62011
- # Python 2.3a0: 62021
- # Python 2.3a0: 62011 (!)
- # Python 2.4a0: 62041
- # Python 2.4a3: 62051
- # Python 2.4b1: 62061
- # Python 2.5a0: 62071
- # Python 2.5a0: 62081 (ast-branch)
- # Python 2.5a0: 62091 (with)
- # Python 2.5a0: 62092 (changed WITH_CLEANUP opcode)
- # Python 2.5b3: 62101 (fix wrong code: for x, in ...)
- # Python 2.5b3: 62111 (fix wrong code: x += yield)
- # Python 2.5c1: 62121 (fix wrong lnotab with for loops and
- # storing constants that should have been removed)
- # Python 2.5c2: 62131 (fix wrong code: for x, in ... in listcomp/genexp)
- # Python 2.6a0: 62151 (peephole optimizations and STORE_MAP opcode)
- # Python 2.6a1: 62161 (WITH_CLEANUP optimization)
- # Python 2.7a0: 62171 (optimize list comprehensions/change LIST_APPEND)
- # Python 2.7a0: 62181 (optimize conditional branches:
- # introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
- # Python 2.7a0 62191 (introduce SETUP_WITH)
- # Python 2.7a0 62201 (introduce BUILD_SET)
- # Python 2.7a0 62211 (introduce MAP_ADD and SET_ADD)
- # Python 3000: 3000
- # 3010 (removed UNARY_CONVERT)
- # 3020 (added BUILD_SET)
- # 3030 (added keyword-only parameters)
- # 3040 (added signature annotations)
- # 3050 (print becomes a function)
- # 3060 (PEP 3115 metaclass syntax)
- # 3061 (string literals become unicode)
- # 3071 (PEP 3109 raise changes)
- # 3081 (PEP 3137 make __file__ and __name__ unicode)
- # 3091 (kill str8 interning)
- # 3101 (merge from 2.6a0, see 62151)
- # 3103 (__file__ points to source file)
- # Python 3.0a4: 3111 (WITH_CLEANUP optimization).
- # Python 3.0b1: 3131 (lexical exception stacking, including POP_EXCEPT
- #3021)
- # Python 3.1a1: 3141 (optimize list, set and dict comprehensions:
- # change LIST_APPEND and SET_ADD, add MAP_ADD #2183)
- # Python 3.1a1: 3151 (optimize conditional branches:
- # introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE
- #4715)
- # Python 3.2a1: 3160 (add SETUP_WITH #6101)
- # tag: cpython-32
- # Python 3.2a2: 3170 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR #9225)
- # tag: cpython-32
- # Python 3.2a3 3180 (add DELETE_DEREF #4617)
- # Python 3.3a1 3190 (__class__ super closure changed)
- # Python 3.3a1 3200 (PEP 3155 __qualname__ added #13448)
- # Python 3.3a1 3210 (added size modulo 2**32 to the pyc header #13645)
- # Python 3.3a2 3220 (changed PEP 380 implementation #14230)
- # Python 3.3a4 3230 (revert changes to implicit __class__ closure #14857)
- # Python 3.4a1 3250 (evaluate positional default arguments before
- # keyword-only defaults #16967)
- # Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override
- # free vars #17853)
- # Python 3.4a1 3270 (various tweaks to the __class__ closure #12370)
- # Python 3.4a1 3280 (remove implicit class argument)
- # Python 3.4a4 3290 (changes to __qualname__ computation #19301)
- # Python 3.4a4 3300 (more changes to __qualname__ computation #19301)
- # Python 3.4rc2 3310 (alter __qualname__ computation #20625)
- # Python 3.5a1 3320 (PEP 465: Matrix multiplication operator #21176)
- # Python 3.5b1 3330 (PEP 448: Additional Unpacking Generalizations #2292)
- # Python 3.5b2 3340 (fix dictionary display evaluation order #11205)
- # Python 3.5b3 3350 (add GET_YIELD_FROM_ITER opcode #24400)
- # Python 3.5.2 3351 (fix BUILD_MAP_UNPACK_WITH_CALL opcode #27286)
- # Python 3.6a0 3360 (add FORMAT_VALUE opcode #25483)
- # Python 3.6a1 3361 (lineno delta of code.co_lnotab becomes signed #26107)
- # Python 3.6a2 3370 (16 bit wordcode #26647)
- # Python 3.6a2 3371 (add BUILD_CONST_KEY_MAP opcode #27140)
- # Python 3.6a2 3372 (MAKE_FUNCTION simplification, remove MAKE_CLOSURE
- # #27095)
- # Python 3.6b1 3373 (add BUILD_STRING opcode #27078)
- # Python 3.6b1 3375 (add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes
- # #27985)
- # Python 3.6b1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL
- #27213)
- # Python 3.6b1 3377 (set __class__ cell from type.__new__ #23722)
- # Python 3.6b2 3378 (add BUILD_TUPLE_UNPACK_WITH_CALL #28257)
- # Python 3.6rc1 3379 (more thorough __class__ validation #23722)
- # Python 3.7a1 3390 (add LOAD_METHOD and CALL_METHOD opcodes #26110)
- # Python 3.7a2 3391 (update GET_AITER #31709)
- # Python 3.7a4 3392 (PEP 552: Deterministic pycs #31650)
- # Python 3.7b1 3393 (remove STORE_ANNOTATION opcode #32550)
- # Python 3.7b5 3394 (restored docstring as the first stmt in the body;
- # this might affected the first line number #32911)
- # Python 3.8a1 3400 (move frame block handling to compiler #17611)
- # Python 3.8a1 3401 (add END_ASYNC_FOR #33041)
- # Python 3.8a1 3410 (PEP570 Python Positional-Only Parameters #36540)
- # Python 3.8b2 3411 (Reverse evaluation order of key: value in dict
- # comprehensions #35224)
- # Python 3.8b2 3412 (Swap the position of positional args and positional
- # only args in ast.arguments #37593)
- # Python 3.8b4 3413 (Fix "break" and "continue" in "finally" #37830)
Magic Number是4字节的二进制数据,找到对应的十进制数据后,需要通过以下代码得到相应的二进制数据:
- MAGIC_NUMBER = (3413).to_bytes(2, 'little') + b'\r\n'
- _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
即:
- enum PycMagic {
- MAGIC_1_0 = 0x00999902,
- MAGIC_1_1 = 0x00999903, /* Also covers 1.2 */
- MAGIC_1_3 = 0x0A0D2E89,
- MAGIC_1_4 = 0x0A0D1704,
- MAGIC_1_5 = 0x0A0D4E99,
- MAGIC_1_6 = 0x0A0DC4FC,
-
- MAGIC_2_0 = 0x0A0DC687,
- MAGIC_2_1 = 0x0A0DEB2A,
- MAGIC_2_2 = 0x0A0DED2D,
- MAGIC_2_3 = 0x0A0DF23B,
- MAGIC_2_4 = 0x0A0DF26D,
- MAGIC_2_5 = 0x0A0DF2B3,
- MAGIC_2_6 = 0x0A0DF2D1,
- MAGIC_2_7 = 0x0A0DF303,
-
- MAGIC_3_0 = 0x0A0D0C3A,
- MAGIC_3_1 = 0x0A0D0C4E,
- MAGIC_3_2 = 0x0A0D0C6C,
- MAGIC_3_3 = 0x0A0D0C9E,
- MAGIC_3_4 = 0x0A0D0CEE,
- MAGIC_3_5 = 0x0A0D0D16,
- MAGIC_3_5_3 = 0x0A0D0D17,
- MAGIC_3_6 = 0x0A0D0D33,
- MAGIC_3_7 = 0x0A0D0D42,
- MAGIC_3_8 = 0x0A0D0D55,
- MAGIC_3_9 = 0x0A0D0D61,
- };
将版本对应的二进制码添加到pyc文件头部即可正常进行反编译。