当前位置: 首页 > news >正文

[GHCTF 2025]Mio?Ryo?Soyo?

PyInstaller 打包,使用 pyinstxtractor-ng 解包

反编译

使用 uncompyle6 将 pyc 转成 py 源文件

uncompyle6 program.pyc > program.py

# uncompyle6 version 3.9.2 # Python bytecode version base 3.8.0 (3413) # Decompiled from: Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] # Embedded file name: program.py from Secret import * if __name__ == "__main__": print("输入:", end="") aaaaaaaaaaaaa = input() wwwwwwwwwww = l(aaaaaaaaaaaaa) if sssssssssssss == wwwwwwwwwww.encode(): print("哦,对的。") else: print("哎,并非。") input() # okay decompiling program.pyc

发现导入了自定义包 Secret

在PYZ-00.pyz_extracted 文件夹下找到 Secret.pyc

同样的反编译uncompyle6 Secret.pyc > Secret.py

# uncompyle6 version 3.9.2 # Python bytecode version base 3.8.0 (3413) # Decompiled from: Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] # Embedded file name: Secret.py from SecretEncrypt import * sssssssssssss = bytes([57, 118, 33, 114, 68, 56, 117, 115, 34, 52, 52, 95, 78, 40, 49, 59, 95, 85, 63, 122, 54, 33, 77, 110, 49, 54, 34, 109, 106, 122, 60, 92, 108, 91, 61, 51, 42, 62, 35, 38, 52, 67, 62, 122, 116, 48, 76, 50, 67, 51, 59, 41, 122, 45, 45, 51, 90]) def l(_: str): return SSSooooyyooo(MMMMiiiiiio.MMMMiiooooooo(SSSooooyyooo(RRRRyyooo.RRRRRRRyyyyooooo(_.encode()), 7).encode()), 9) # okay decompiling Secret.pyc

导入了自定义包SecretEncrypt

同样uncompyle6 SecretEncrypt.pyc > SecretEncrypt.py

# uncompyle6 version 3.9.2 # Python bytecode version base 3.8.0 (3413) # Decompiled from: Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] # Embedded file name: SecretEncrypt.py import math class MMMMiiiiiio: MMiiiiiiooo = "".join([chr(Miiooooooooo) for Miiooooooooo in range(33, 118)]) @staticmethod def MMMMiiooooooo(MMMMMMMMMiiiooo: bytes) -> str: MMMMiiiiioooooooooo = "" MMMMMMMiiiiioo = (4 - len(MMMMMMMMMiiiooo) % 4) % 4 MMMMMMMMMiiiooo += b'\x00' * MMMMMMMiiiiioo for MMMMMMiiiiiio in range(0, len(MMMMMMMMMiiiooo), 4): MMMMiiiiiiooooo = MMMMMMMMMiiiooo[MMMMMMiiiiiio[:MMMMMMiiiiiio + 4]] MMMMMMiiioooooo = int.from_bytes(MMMMiiiiiiooooo, "big") MMMMMMMiiooooooooo = "" for _ in range(5): MMMMMMMiiooooooooo = MMMMiiiiiio.MMiiiiiiooo[MMMMMMiiioooooo % 85] + MMMMMMMiiooooooooo MMMMMMiiioooooo //= 85 else: MMMMiiiiioooooooooo += MMMMMMMiiooooooooo else: if MMMMMMMiiiiioo: MMMMiiiiioooooooooo = MMMMiiiiioooooooooo[None[:-MMMMMMMiiiiioo]] return MMMMiiiiioooooooooo class RRRRyyooo: RRRRyooooooo = "".join([chr(RRRRRRRyyyyyoooo) for RRRRRRRyyyyyoooo in range(48, 93)]) @staticmethod def RRRRRRRyyyyooooo(RRRRRRyyyoooooo: bytes) -> str: RRRRyyyyyooo = [] RRyyyyyyyyyoooooo = 0 while RRyyyyyyyyyoooooo < len(RRRRRRyyyoooooo): if RRyyyyyyyyyoooooo + 1 < len(RRRRRRyyyoooooo): RRRRRRRRRyyo = RRRRRRyyyoooooo[RRyyyyyyyyyoooooo] << 8 | RRRRRRyyyoooooo[RRyyyyyyyyyoooooo + 1] RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45]) RRRRRRRRRyyo //= 45 RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45]) RRRRRRRRRyyo //= 45 RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo]) RRyyyyyyyyyoooooo += 2 else: RRRRRRRRRyyo = RRRRRRyyyoooooo[RRyyyyyyyyyoooooo] RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45]) RRRRRRRRRyyo //= 45 RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo]) RRyyyyyyyyyoooooo += 1 return "".join(RRRRyyyyyooo) def SSSooooyyooo(SSSSooyoooooo, SSSSSoyyooooo): SSoooooyyyyyyoo = [] for SSSSSSSSSoyooo in SSSSooyoooooo: if "a" <= SSSSSSSSSoyooo <= "z": SSSSoooyooooooo = (ord(SSSSSSSSSoyooo) - ord("a") + SSSSSoyyooooo) % 26 SSoooooyyyyyyoo.append(chr(ord("a") + SSSSoooyooooooo)) elif "0" <= SSSSSSSSSoyooo <= "9": SSSSoooyooooooo = (ord(SSSSSSSSSoyooo) - ord("0") - SSSSSoyyooooo) % 10 SSoooooyyyyyyoo.append(chr(ord("0") + SSSSoooyooooooo)) else: SSoooooyyyyyyoo.append(SSSSSSSSSoyooo) else: return "".join(SSoooooyyyyyyoo) # okay decompiling SecretEncrypt.pyc

优化变量名

# uncompyle6 version 3.9.2 # Python bytecode version base 3.8.0 (3413) # Decompiled from: Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] # Embedded file name: program.py from Secret import * if __name__ == "__main__": print("输入:", end="") user_input = input() encrypted_input = encrypt_flow(user_input) if TARGET_HASH == encrypted_input.encode(): print("哦,对的。") else: print("哎,并非。") input() # okay decompiling program.pyc
# uncompyle6 version 3.9.2 # Python bytecode version base 3.8.0 (3413) # Decompiled from: Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] # Embedded file name: Secret.py from SecretEncrypt import * TARGET_HASH = bytes([57, 118, 33, 114, 68, 56, 117, 115, 34, 52, 52, 95, 78, 40, 49, 59, 95, 85, 63, 122, 54, 33, 77, 110, 49, 54, 34, 109, 106, 122, 60, 92, 108, 91, 61, 51, 42, 62, 35, 38, 52, 67, 62, 122, 116, 48, 76, 50, 67, 51, 59, 41, 122, 45, 45, 51, 90]) def encrypt_flow(plain_text: str): return caesar_cipher(Base85Encoder.encode(caesar_cipher(Base45Encoder.encode(plain_text.encode()), 7).encode()), 9) # okay decompiling Secret.pyc
# uncompyle6 version 3.9.2 # Python bytecode version base 3.8.0 (3413) # Decompiled from: Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] # Embedded file name: SecretEncrypt.py import math class Base85Encoder: CHARSET = "".join([chr(i) for i in range(33, 118)]) @staticmethod def encode(data: bytes) -> str: result = "" padding_size = (4 - len(data) % 4) % 4 data += b'\x00' * padding_size for i in range(0, len(data), 4): chunk = data[i[:i + 4]] val = int.from_bytes(chunk, "big") chunk_res = "" for _ in range(5): chunk_res = Base85Encoder.CHARSET[val % 85] + chunk_res val //= 85 else: result += chunk_res else: if padding_size: result = result[None[:-padding_size]] return result class Base45Encoder: CHARSET = "".join([chr(i) for i in range(48, 93)]) @staticmethod def encode(data: bytes) -> str: res = [] i = 0 while i < len(data): if i + 1 < len(data): val = data[i] << 8 | data[i + 1] res.append(Base45Encoder.CHARSET[val % 45]) val //= 45 res.append(Base45Encoder.CHARSET[val % 45]) val //= 45 res.append(Base45Encoder.CHARSET[val]) i += 2 else: val = data[i] res.append(Base45Encoder.CHARSET[val % 45]) val //= 45 res.append(Base45Encoder.CHARSET[val]) i += 1 return "".join(res) def caesar_cipher(text, shift): res = [] for char in text: if "a" <= char <= "z": new_val = (ord(char) - ord("a") + shift) % 26 res.append(chr(ord("a") + new_val)) elif "0" <= char <= "9": new_val = (ord(char) - ord("0") - shift) % 10 res.append(chr(ord("0") + new_val)) else: res.append(char) else: return "".join(res) # okay decompiling SecretEncrypt.pyc

通过caesar_cipher(Base85Encoder.encode(caesar_cipher(Base45Encoder.encode(plain_text.encode()), 7).encode()), 9)可以得知加密顺序为:

  • Base45 加密 码表0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\
  • 凯撒密码 偏移 7
  • Base85 加密 码表!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu
  • 凯撒密码 偏移 9

最后与TARGET_HASH对比验证

代码中的凯撒密码为魔改版,Base45 为非标准码表,Base85 为标准Adobe Ascii85(还有另一种常见的是IPv6 / RFC 1924)

EXP

将加密函数修改成解密函数

import base64 def caesar_cipher(text, shift): res = [] for char in text: if "a" <= char <= "z": new_val = (ord(char) - ord("a") + shift) % 26 res.append(chr(ord("a") + new_val)) elif "0" <= char <= "9": new_val = (ord(char) - ord("0") - shift) % 10 res.append(chr(ord("0") + new_val)) else: res.append(char) else: return "".join(res) def Base45decoder(data): CHARSET = "".join([chr(i) for i in range(48, 93)]) result = "" for i in range(0, len(data), 3): a = CHARSET.index(data[i]) b = CHARSET.index(data[i+1]) c = CHARSET.index(data[i+2]) n = a + (b * 45) + (c * 45 * 45) result += chr(n >> 8) result += chr(n & 0xFF) return result enc = [57, 118, 33, 114, 68, 56, 117, 115, 34, 52, 52, 95, 78, 40, 49, 59, 95, 85, 63, 122, 54, 33, 77, 110, 49, 54, 34, 109, 106, 122, 60, 92, 108, 91, 61, 51, 42, 62, 35, 38, 52, 67, 62, 122, 116, 48, 76, 50, 67, 51, 59, 41, 122, 45, 45, 51, 90] # sourc enc = "".join([chr(i) for i in enc]) print(enc) # caesar enc = caesar_cipher(enc, -9) print(enc) # base85 enc = base64.a85decode(enc).decode() print(enc) # caesar enc = caesar_cipher(enc, -7) print(enc) # base45 enc = Base45decoder(enc) print(enc)
9v!rD8us"44_N(1;_U?z6!Mn16"mjz<\l[=3*>#&4C>zt0L2C3;)z--3Z 8m!iD7lj"33_N(0;_U?q5!Me05"daq<\c[=2*>#&3C>qk9L1C2;)q--2Z JX2NG:CM:KJ?S0=:>?NC>K5<V29Z5<Y:9C=;LA1RQ9G:7 JX9NG:CM:KJ?S7=:>?NC>K2<V96Z2<Y:6C=;LA8RQ6G:4 NSSCTF{Th3y'r3_a11_p1aY_Ba5e!}

NSSCTF{Th3y'r3_a11_p1aY_Ba5e!}

总结

本题考验对算法的熟悉程度,涉及 base45、base85、Caesar 算法,并且需要解包反编译 pyc、反编译自定义 python 库

http://www.jsqmd.com/news/264838/

相关文章:

  • 让老手机变智能!Open-AutoGLM低配设备适配经验
  • 从0开始学图像识别,阿里开源中文模型超详细教程
  • NotaGen:高质量符号化音乐生成,WebUI轻松上手
  • FSMN VAD社区贡献指南:提交PR和issue的正确姿势
  • Emotion2Vec+ Large前端界面解析:Gradio组件布局与交互逻辑
  • 轻量级视觉语言模型:Qwen3-VL-8B优势
  • 实测YOLOv13性能:小目标检测精度提升太明显
  • 多模型对比评测:cv_unet与RemBG抠图效果与性能全面PK
  • opencode build Agent使用:自动化编译流程实战
  • AI读脸术快速验证:上传自拍即刻获取性别年龄预测
  • FRCRN语音降噪部署:多卡并行推理配置指南
  • Qwen3-0.6B对话管理:状态跟踪与策略决策模块设计
  • AI智能文档扫描仪入门必看:无需模型权重的纯算法扫描方案
  • 边缘设备部署YOLOv9,Jetson上跑得流畅吗?
  • 从图片到文字:Qwen3-VL-8B保姆级使用教程
  • 轻量应用:Qwen2.5-0.5B指南
  • IndexTTS-2-LLM性能优化:让语音合成速度提升2倍
  • 工业网关连接中的USB Serial Port驱动下载详解
  • Kotaemon电商客服整合:商品说明书自动应答机器人
  • ACE-Step商业变现:AI作曲SaaS服务平台搭建思路
  • Qwen3-VL-2B电商应用案例:商品图自动描述生成部署实操
  • 亲测VibeVoice-TTS-Web-UI,4人对话播客自动生成太惊艳
  • Wan2.2商业变现案例:如何用AI视频月省万元成本
  • Qwen-Image-2512-ComfyUI最佳实践:提升出图质量的参数调优技巧
  • 在线会议系统升级:集成SenseVoiceSmall实现情绪可视化
  • RexUniNLU模型微调:领域适配实战教程
  • 如何提升fft npainting lama吞吐量?批处理优化实战
  • Elasticsearch基本用法:手把手教程实现关键词高亮显示
  • Image-to-Video在虚拟偶像动作生成中的应用
  • GPEN批量处理失败怎么办?常见问题排查与修复实战手册