s0m1ng

二进制学习中

py逆向做题总结

题目给了exe或者直接给了pyc文件

处理exe(解包为 pyc 文件)

有的题直接给 .exe 文件,这通常是 PyInstaller 打包的 Python 程序
我们需要先将 .exe 提取出 .pyc 文件夹。

使用工具:pyinstxtractor.py

使用

1
2
# 用对应 Python 版本运行(与目标 exe 一致!)
python pyinstxtractor.py target.exe

运行后会生成 target.exe_extracted/ 文件夹。
在里面可以看到 .pyc 文件和 PYZ-00.pyz_extracted/ 目录。

注意事项

  • 反编译出的 .pyc 文件魔数要正确
    • 每个 Python 版本的魔数(magic number)不同;
    • 可以从同版本的 struct.pyc 或官方魔数表复制。
Python版本 魔数字节
Py2.x 8字节(magic + timestamp)
Py3.0–3.2 12字节(magic + timestamp + size)
Py3.3+ 16字节(magic + flags + timestamp/hash)
  • 一定要用与原程序一致的 Python 版本运行 pyinstxtractor.py,否则解包后的 PYZ-00.pyz_extracted 可能是空的。

处理 pyc 文件

解包后获得 .pyc 文件,我们需要将其反编译成 .py 源码。


工具一:uncompyle6

1
pip install uncompyle6
  • 命令行用法:
1
uncompyle6 1.pyc > 1.py

工具二:pycdc

1
pycdc.exe yourfile.pyc > yourfile.py

工具三(针对高版本)PyLingual

1
2

pylingual 1.pyc

注意事项

  • .pyc 反编译出来的 .py 文件可能:
    • whileif 分支混乱;
    • 列表、条件逻辑丢失;
    • 人工修复逻辑结构
  • .pyc 文件加入了花指令(junk bytecode),需手动修复。

pyc 字节码(Bytecode)分析

当题目直接给了 pyc 字节码或源码无法反编译时,可以手动分析。

工具:Python 内置 dis 模块

1
2
import dis
dis.dis(your_function)
  • 或直接命令行:
1
python -m dis yourfile.pyc

手动还原思路

  • 参考 u-tools 的「程序员手册」搜索 dis 指令说明;
  • 每个 Python 版本字节码结构略不同:
    • Python 2:每条指令 3 字节/1字节
    • Python 3:每条指令 2 字节/1字节

通过分析 opcode,可手动还原出 Python 逻辑。

pyc字节码花指令处理

花指令即插入无效或干扰反编译的字节码。

步骤流程

  1. 识别花指令

    • 结合 uncompyle6 的输出,如果pyc字节码加花没办法正确反编译回py文件
    • 实际字节码判断,若有无用字节码,就是花指令
    • 参考 Python Opcodes表: https://unpyc.sourceforge.net/Opcodes.html 查指令含义。
  2. 定位花指令

    • 用机器码定位
  3. 修改花指令

  4. 修改code object总长度

pyc文件结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct {
PyObject_HEAD
int co_argcount; /* #arguments, except *args */
int co_nlocals; /* #local variables */
int co_stacksize; /* #entries needed for evaluation stack */
int co_flags; /* CO_..., see below */
PyObject *co_code; /* instruction opcodes */
PyObject *co_consts; /* list (constants used) */
PyObject *co_names; /* list of strings (names used) */
PyObject *co_varnames; /* tuple of strings (local variable names) */
PyObject *co_freevars; /* tuple of strings (free variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
/* The rest doesn't count for hash/cmp */
PyObject *co_filename; /* string (where it was loaded from) */
PyObject *co_name; /* string (name, for reference) */
int co_firstlineno; /* first source line number */
PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */
} PyCodeObject;

其中我们关注的就是co_code字段

1
2
3
import marshal
code = marshal.load(open("1.pyc", "rb")) # 你的pyc路径
print(len(code.co_code))

根据长度在winhex搜索这个数值,找到的第一个,就是长度,code object长度用四个字节存储,这四个字节前是marshal中byte对象的标识,一个’s’(0x73),这四个字节后就是code object内容

PyArmor 加壳与加密分析

PyArmor 是常见的 Python 加密与授权保护工具。
PyArmor是把源码/字节码用加密包装并在运行时由专有 runtime(如 pyarmor_runtime.pyd)解密后执行
因此你看到的 .py 文件通常只包含一个启动壳(stub),它调用 runtime,并把加密的二进制 blob 交给 runtime 解密并加载——这就是你看到的 __pyarmor__(__name__, __file__, b'...')

原理图:

pyarmor原理

工具:PyArmor

1
pip install pyarmor
  • 常用命令:
1
2
3
4
5
6
7
8
9
# 初始化项目
pyarmor init --src=src --entry=main.py my_project

# 生成加密文件
cd my_project
pyarmor gen

# 直接加密单个文件
pyarmor gen main.py

常见特征

  • 目录中含 pytransform/license.lic

  • 存在 _pytransform.dll / pytransform.pyd

  • 反编译 .pyc 失败;

  • 程序运行时导入 pytransform 模块。

  • 文件夹中有pyarmor字样

解题:

GitHub - Lil-House/Pyarmor-Static-Unpack-1shot: ✅ No execution ✅ Pyarmor 8.0 - latest 9.1.x ✅ Universal ✅ Statically convert obfuscated scripts to disassembly and (experimentally) source code.

下载release

打开文件夹

pyarmor

如果runtime不用显式指定,在这个目录下用

1
python shot.py 被混淆程序所在文件路径

如果题目也把runtime给你了,那就用下面的包含.pyd的命令

1
python shot.py -r /path/to/pyarmor_runtime.pyd /path/to/obfuscated_scripts

有的程序为了防御这种攻击,会把PyArmor 加密文件的标识符去掉,我们给它在开头加上就行

比如:

1
2
3
4
5
#src.py
from pyarmor_runtime_000000 import __pyarmor__
__pyarmor__(__name__, __file__, b'\x00\x03\r\x00\xf3\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00d\n\x00\x00\x12\t\x04\x00y"\x04"\xc0\xa4\t\xad2\\\x17\x13D\x0c\x81\xeb\x00\x00\x00\x00\x00\x00\x00\x00[\xc1%I\xfe56\xaa\x1cc\xc9\xf2E\xa3\x86\xc1\x88=sB\xf08\x14\'U\xfc\t\x10\xc7%\x1a\xb6\xc4\xa6QvBDK\xfd\xf5\xb0&NZ&n\xe41@VC\x11&j\x8fs\x18\xd6\xe3iQ\xf8_\x9e\xdb\xfa\x92~\xfb\xe7f\xdf#\x00\xa3fv\xb5\x9b\xa13\x99 \x83\xc3\x93\xf2\x1d=\xe2-\x93zb\xb0\x10&\n\xc4\xb8\xb9\x83\x99^\xd0\x94G9\xb9\xa0D\xedX\xff\x01\xbd>\xa9\xe8\xf03]\xdd7\x87\x84\x13;%\xae\xd5\xe8\xcc\xd1\xa8\xe0\xda\xc2\x9f0\x02\xb88J\xac\xe5pS-\x14\x86\xdb\xc4/\xea\xa8\xd3\x05\x04V\x94\xfd_\xf3\xd7\xf1\x7f#\x8d\x9e\xd71\xb1\xfd\xb4\xb9\xd9\xf7>y\xfb\x12\x8f\x9a\xe1\x8d\xe2X\xf9\xcb\xe2\xe3\xa2\xd7\xfc\xfa\xa0\x91n\xb9\xbc\xc0\xf0#\xfd\x83\xad\xb2\\K\xee\x88R\x80\xff\xcf\x93\x8f\x13\xe5\x01`\xbe$)\xad\xa6\xdc;\x8e\\\x0c\xae\x87yJ\xd9\xedF/P\x89\xe8\xb6\xebI\xb1u\x9b"pt\xc3v\x19 \x83\xae\x89\x07\x1fmR\xd9AX4C\x05fa\x81\xf5\xab\xf5\x9a\xfe\xe2\xa2\x88HX\'\x9c\x9f\xea\x1d\x7f\xeb\n-\xa3\tFDsM\x99MF\xb6\xa9.\x1a\x86n\x95\xc4\x85f\xf8\x90\x076\xb5_\xeb.\xddn\x94f\x12\xb7\x04\xbb_\xd1\x87XO\x054<\x9a\\1%H\xe4eBo)\xf2\xf5\xc6\xe1\xbd\x10i\xe2\xefD\x98\xdc\xea\xf0\xc0\xd1\xdc\x8c&J\x7fX\x87\xc5\xfb\x05d\xb2\xc4\xc5\x0b\xfc\xf8\x9e0R\xb2{em\xb2\x98\\\x05\xd3\xbe_\xf3\x07\x04\x05\x82p\x1b\xa7\x19\xf2\x02\x8e~\x12\xa3;\xb1\xfeb\xdd\x83\x04\xf4VM\xba\xe1}\x8ff\x91\xa0\x94}\xef\x12(\xdf\xa9j\xd4gA\xaa\tB\xf0\\=m%\xa4B\xbd\x1b\xe2AD\xfb\x98\xdf\xfd\xf6\xe5\x80-B\x90a\xf4E_\xa7\xec\x8eBx\xb8\x80\xef\x08X]\x0b\x18\xa3\xab\xf4\x8b\xe8\t\xfd\xd9o\'\x96<\x1d\xf6\xdb\xe1\xe4\xb5\x8ec5o\x85\x92\xf4\x1e<g\xecJI\xf3\x01\x90\x1e\xb7\x00\xda\t:}\xc7I\x8c\xa5\x01\x13b!\xf2\xcd6JUy\xc5\xfc\xd8Q\x8f\x13\xdb\x0f\xfe\xbf\xd7\xe8\x992\xe4\xdf\x17\xd0\xef\xa5QC\xa6przcd\x9f\x8aS\x963\xa7\x0c\xc4\x97\x11j\xa8\xcf\x19\x1a\xc6\xff\xb1\x1d|\xe9n\x129\x19\xad\x08\x80\xc9\x98=q6\xc7U\xc8\x12\xe2\x0bSt\xc9,\xb4\x07\xd5X\xad\r}\xb1 \xe4\xf6\xfb\xb6m\x11\xf9\x1e\x8c|\xd7\xf5\xd0\x99\x01t\xa39\xd0&X\xa8\'^\xa5\xd1\x98{\xed\xca\xe1\x95E\x08&\xd1\x0e\xecF^\xc6\xad\x15h\xadV\xfe~\xb2<Xe\xa2$\xf6\x82\xa8Wk\xd1%1\x9d\xcc\x08\x88\xfe\xbck\xb6t\xe4\xc0\xc8F\xf7\xa3\xee\xd8UMb(\xeb\xe9#\xfa( \x97Kp\xe4\xe6\x13>\xbcZ\xab\x89\x99Uy\xe5\xf7s\xcda\x8a\xcb`\x0f\x02]e\xc6:\x84\xf4J\xda3n\x1a\x01?\x83\x98L\xf2P\xae\x10\x80A\xf7\x86\x17F7\xb3 7\xe7\xfb\xed\xed\x0c"v\'\xf0W\x97\xc7\xdaMe=\xc1\x1c\xf9\xc3\xe2\xa3\x08\x0e\xe0\x0c\xd9\x10\xd7\x0e\x7fJ\xf5\x12\x94\x19\xbf\xf3\xc4K\xe1\xb2b\x07\xa6:\xa6a\x9eW\xf9B\x0e\xab\x9c5\xd4B\xdeN!\xeb\xa9\xb5K\xf4\xcan\xed\x04\x7fp\x87\x95\x14\x18\r-\xa9R\x95H\x9f++\x8f!!\xb5\xc8W\xbeG\x1a\xb0\x1c\x1b\xcdb\xb2y\x89\x16\xab\xbe\xdb)0\xe6\x07\x8a\x1fv\x02n\x83\xfd\x88q\xf3\xa8\xaf4\x84t\x0e\x95I\xf7\x11\x02Q\t.K\x10_\x88\x89\xf4\x98\xaf\xeb\x9d\xc6\x02"u\xb6\x1dbOv\xc6\xfed%\x83-3\x8e\xde\x0ed"\x16\x95j@\x07R\xd5PumB@"\xa4`\xa5\xe7*V\x12\xf2\x90A\x95\xd9\xaal\xb8\x1a\xe6\xcc\x0b1\\\xca\x03m\xa1\xaf;c{\x14\'\x14\xb4\xb2\x13\xf2\x9e\xe6\xaf\xcf<\x86\x16\xc6>\x86\x01?\xf6J\xa4\x9b/\xce\xd4\xec+\xc9\xf6Y\x9f\xef\x08\x88\x05\xe1B\x12\xc4\xca4\x03?\x9b\x9e\xa9r\x99\xeb\x08\xc9Tfm\xb9,\x8e"\xfcK\xd3\x9dx\x97\x0ff\x15\x08\xfb\x89\xdd\xd0Z\x85W?\x9c\xb9\xd3\x10\xaf\x9dG\x96f\xae ed\tu\x0e\x8e\xccX\xe1\xe4\x82\xbc\xe9\xeb\xa2\xd6\xa44\xf4\xeb]p\xad\xa0k\t\x84\x0f\xba\xed\xc8\x18\xbc\xd3\xc02\x97\x8a\x97B\xd69\xc4\x8c\x11\xca\xeb*\xbed\xefv\x95\x96H\xf7\xb4\xb6\xbd\xfb\xc6q\xc7\x90,eCkf\xc5\xb5\x91\xb1J\x85\xab\xc4\x06\xff\xdb\xae\x03]Cd\xa1\xe4\xdf\x80\xddk2\xb4\xe7\xac\xac\x05i\\6\xf8:\x87Ps\xbcM\x06\n\xce\xdc\xafo\xc1\xd8\xf4\xd5p\x8d\xe1\xa3P\x99e\xc6Z4t\xc5\xdc\xc02.\xa8\x08\x97\xbcJ\xc4ga\xd1\xe6\xc5t\xf1\xf9\x1a\t\xc3\x95\x8a\x05\xea\x99\xe3^\xe9\x05r\xda\xb5\x97\xd4\xbd\x01\x9f\x86\xae!?\x13\xf3\xeb\x93\xc5\x80v\x01F\x96\xd9\xb6\xf6C\xb0w\x9ciH\xbc\xd3\x1c\xa5R\xd0\x08;d\xd8\xac\xbf\x1c\xff\xfca\xf2Ey\xac\x00rY\xf2A\xeb\r\x9f\x8a\xcf\xb6\xa97\xf4\x82\x12\xee,<\xaa\xec\xa7RY`\xb1\x1e\xe3\xac,\xb8\xee\xe2\xc0\xc47\x9e\xf4B\\\xb1?\xd7[I\xf8\x19\xd5\xbe\x87(S\x9e$\xffq\xb0p\x8e2\x85XCE\x84\x8c\x97\xebw\xa3\x00\\_\x13\xf5Jw5\xfeK\x95\xb2\xbb\xd4\x92F\xa0\x9a\xb54\x97\x03\xaa\xe0\x98q\x0b\xdd\xbd\xf3\xe3\xfc\xfdU\x1aH\xbc\x13\x00gP\xe6\xe7\xb3\xa6\x91hP\xd3\'r\x8c\xab\xa39\xe6\x14\x0f\x15;\tt\x9bf\xa6\x7f\xc8\xe5\xf7\x16\x19\xf8\x9a\xc0n\x7fa\x1c\xf5I\xefU\xf5O\xfb\xfc\xad\x05\xadYB\x17\xb3~xL\x8d\x9f\x05\xc0\xcaWp\xf5x\xd4\xcf\xddX\x96\xad]P[`\x9ei\xfdap\xe2\xf4\xc2w\xf2\xbc\xd1 \xff\x13\xccz\xd0&+\xc8\xa00\xe2\x11\xdb\xa2.\x961\xa2\x0fs(R8\xdd\x898Ve}\x8f>S\x83\x14\x83P\x8bA@s\xe5\x8d\xda\xb2\xf9\xba\x00\xb3\xd0Q\x1d\xaaZ\xae\x03\x9e\x92=.\x13|\xb1\x8dK4\x96A\xa5\xdeM\xbc\xd9dOxB_\xc5\x10\xbdp\xccD1\xc1$d\xdc8\xc5\x18\xd3e\x1a)[s\xecn\xd2htl\x8c\x1a0y\xd8a&Q\xb5\xd7\xcd\xb2>\x1b\xe1\xfdKA\xeb^v{#\xba\x0eo\xef\xf5\x1c,\xe5X\x05\xc7s\xf6\xa7\xfa\xd9\xed\x87\x18\xa0{C\\\xc4\xef6^\xcf%\xc9[\xb7\x87\xb0\xac\tN\xd1\x04\xaa\xa2\x10\xa4kg\xa1Pn\xcb\xfd\xfflL]\x92h}<\xa2\xfej1\x83\xe6\xfb\x97\x9c\xae\xe8\x88\xf1\xbdN\x0f\xde\xfd\x8d\xe6\x87^S\x94\x0c\xad\xee%\t\xb5\xd6\xfb\xac\xb7d0\\\xe8\xb9\xfe\x04\xc2\x92"@\xd7\x08GY%\xdf%\xba\x83\x18\x17\x7f\x00\'\x1c\'/\xa7\x7f\xe2\xf7\xc8\x9b 2(\x9cO\xa7\x7fI\xc4\xe7l\x0b\x0f\xda\xe9uR%\xaeb\x9dm\x85\xab\xbf\xe7\x95\x88\xc9\xff\xe0\xd2\x85\xaah\x0e\xd3\xf8\xe2\x89\xd1\x96ix\xe7aic\xb8\x10\x08\x9cJ,\xc6o\xbb\xe0(\x10}\x0c\xb9\x9a\x11\xdf\rR\x9c\x00\x95\x88xO\xeb\xba\xbe+Qi[`u\x86\xce\xf4\xe2C\xd0\xb7\x00_#j\x19.{\xeb4\xecc\xb8N^K"\xab\x0b\x9e\xd7u\xe1\x1c\xe8\x1dL\xfcC\xf2\xbfS\xf5\x95\x00\xe7>\x0e\x9ew\xf6\x83\xee\n\xd1\x1e\xb5\xceV\x93\x9clg\xeaV\xab\xf9\xec\xc7\x01\x9f\x0c\xe5\xd1h0\xafbMC\x13\xd8zJ\xab\x81\x7f\x88|\x0b\x95<(\x15d\x90\xb7\xd7\x9e,\xb5\x14\n\xfe4\xe3\x1a\xa5\x9b}\xf1\xa0\r\x96.5\x99\xce\xc9E\xed\x97\xa3\n4O\xb5\xdb\xc8\xbf\xb4\x88\xdb\xd5V\x0f\xfd\xda\r\x1d9x\xe1\xa0\xc5\xc6L\x08\xda\xc8\x88\x87Z?\x9d\xa8\xd5\xcdI\x16\xb5Q`>\xb8\x1fSbN\xd9\xf2F\x01\xeb\x07\xf2.9\x87\x8e\xab\xb6\x8e\x1e\x8b\x04\xc3\xcb?\xd3F\x18g6r\xc4\x1dh$\xc9|w\xe5+/\xb4X\x94\x1ax\xf5a\x05::\x1c^\xa1\x8e\x08E\x84\xde)2m\x9cD\xb7UBsE\x82\x01\xf6\xa8\xd6E\'\xe4\x7f^\xdd\xf7\xbb4\xb0*}\x12=\x1f3\x1b9\x9d\xd1\xfc\x8c\xbd\x9bC\x83Z+\xdb\x06\'_\xf2\xb9\xd6\x8d\x8e\xa0M\xc8\x8a\x13\xfeDC\xc8\xa0\x96?h\xba\xb7"\xb7\x15x\xd2\xb0P\xc9\xc3c~\xdd`\x81\xb8JKJ\xe0\xcd\x80\xe6/}\xce&\x01h\xce\xa6\x8e\xfa*\xf4(l]\xc5\xb1a{"\xbe\xc9\xf3\xd3\xdc\xad\xe5!\xd3\xfd"hR\'&\xaf\xad\xf6\xb5\x04\xb5\xe1\x98g8\xcc\xe3f\xa9\x8d\'\xd0\xc4\x94\xf2\x865\x82>U\xdba\x98:\x02V\xad\xfb\x81\xb4PDy<\xdd\xeb\x01\xa9\x99^)\x8bK\xc8E\x04AM\x88\xd4\x99H\x9a\\\x8c\x13OIxmZ\xc5\x19\xe8\xc1\x19\x82E\xf7\x01SNr\xf70\xa4\xe1\x92\xa6\xed\x1d\tZ\xbfD0\xc6\x80\xe5\xf8\xdd\xa9\xd8\xe3\xa2\xb1y\xc5;\x9d\xd6c\x8as\x07\x0fBa"\xc2Mw\x9f,w\x1b\xc7\x98\x8a\xd3\x93\xb0\'\xdb\xact\x99\x11Vq<\x8d$c\x8bX\xd3\x04\x9d\x92\x17\xa9\x91}\x9fc\xc5x8\xc3\xea\xb2><\x88krU\x055\x1cN\x8d\xf7\'S>\x14\xc9pV\xbe\x1fj\xed\xa5\t\xbc\xbfD\xed$\x88\x00n7<\xf5\xd6\xe9h\xaf@\x94\xfb\x1am\x9a\xa4\x1a\xf5Z\xcb\xf9\x80\xb6\x9d\xd6\xf1\x9f\xce\x1e\xaf\x97=\xdcrj\xcfh\xc2\x1dS\xdd\x18\x15\xb5\xc3O=\x0e\xba\x07\x0b\xb1J\x01A\x7fl\xcf?\'\xb0e\x9aO\xed2\xf7\xf4\xa1\xd3\xdf\x11_H\x8f\x8f\x03\x7f\xeej\x954\r\x1aA\xdanM\x8f\xef\x9dq\xc6\xf5\x16E3\x10\x80\x88\xa0\xd0Q\xfc\xf1\x8b\xf1\xcf\xb5\xed\xa54P\x18\xb4\xc9\x02:}\xc6\xf8\x17\xaa\x8a\xcd\xb88\x03\xecI\xe7\x9a\xef\xab9\x1e\x7f9\x1ah\xef\x1c\xef\x13m\x9bfqb\x1a\x8a\xe1\x02\x8e\xf9\xa1\x9d\x90\xb2$\xcbS\x10QS\x96\xa8\xea\x8bx\xc11\x93\x1c\xb9\x0b\x80~\xde\x9b\xc4\xa2N\xe0N~Q\xaf\xc0@\x01\x9d=[@\r1T\xec\xb9\xd3\xe0q\x94\xd1\xf7\x983\x10*\xad\x1a\xd3hs\x98}\xd8\xfd\x05\n\xd1u\xf7d\xd2\x9a\xc6\x95\xba\x0cJ\\GSC\xbb\'\x18\x1f\xaa\x12/|q\x0b\x81\xb4)\x15}\xa0\xf9S\xb3\xcb\x86\xdcQ)2OH\x11\xde.\xcb\xe1Z!DA\xd3Q<N>\xa0k\n-Z)r5\xa3k\x9f\x91&\xc7\xb8\xfe\x1814\x16xh\x97^\xe3\x03:\xb0f\x14\xce\\\x85\xe4k_\xadf2\xe5\xb1`\x8f8\xfc\xf6#x\x1f\x98\xdex7+\x98=G\xb1M')


开头没有标识符,根据运行环境可知特征码是PY000000,我们帮它加上PY000000,工具才能正确识别这个pyarmor加密

1
2
3
from pyarmor_runtime_000000 import __pyarmor__
__pyarmor__(__name__, __file__, b'PY000000\x00\x03\r\x00\xf3\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00
后面的省略

pyarmor成功

成功后会出现这种图形化界面,现在我们去到被混淆的文件发现多了很多文件,后缀是.py的文件就是解密后的py源码

总结

阶段 工具 命令 作用
exe 解包 pyinstxtractor.py python pyinstxtractor.py target.exe 提取 pyc
pyc 反编译 uncompyle6 uncompyle6 -o ./out ./file.pyc 还原 py
pyc 反编译 pycdc pycdc file.pyc > file.py 还原 py
高版本 pyc pylingual python pylingual.py file.pyc 分析 py312
字节码分析 dis python -m dis file.pyc 查看汇编
修复花指令 WinHex 手动修补 去花
加壳识别 pyarmor python shot.py -r /path/to/pyarmor_runtime.pyd /path/to/obfuscated_scripts 加密分析
您的支持将鼓励我继续创作!

欢迎关注我的其它发布渠道