CTF新手题easychallenge:Python脚本加密与逆向思路全解析
前两天在攻防世界的 CRYPTO 新手练习区刷题刷到 easychallenge 这一道名字起得特别诚实但实战起来也不能说完全 easy。它不考复杂的数学公式也不考高深的密码算法更像是给你一段加密脚本让你自己把它拆开、反转、还原。很多第一次接触 CTF 的新手往往不是不会写代码而是被“加密”两个字唬住绕不过“逆向”这个弯。这篇文章就把我当时的完整分析过程写出来从读脚本、拆函数到一步步写逆向脚本、排坑再到总结这一类题的通用套路。适合刚入门密码学、想搞懂基础逆向思路的朋友。1. 先搞清楚 easychallenge 到底要你解什么1.1 打开附件最先看到的东西题目附件通常是一个.py文件也可能直接把代码贴出来。打开之后第一眼是一堆def新手容易懵。先把几个函数的名字看一遍别急着跑。我当时拿到的脚本核心逻辑和下面这段等价变量名不一定完全一样但运算顺序是原题的顺序import base64 def encode1(ans): s for i in ans: x ord(i) ^ 36 x x 25 s chr(x) return s def encode2(ans): s for i in ans: x ord(i) 36 x x ^ 36 s chr(x) return s def encode3(ans): return base64.b64encode(ans) flag ?????????? if encode3(encode2(encode1(flag))) 这里是一长串密文: print(correct) else: print(wrong)如果直接把这段代码丢进 Python3大概率会在base64.b64encode(ans)这里报错因为 Python3 的 base64 接口要求传入字节串而不是字符串。这个问题不是解题重点后面我会单独说。现在只需要看逻辑程序对flag做了三层变换然后拿去和写死的密文比较。1.2 判断目标是“解密”还是“逆向”这题虽然被分到 CRYPTO 密码学类但它的本质更像是“读代码 逆向计算”。没有 RSA 那种大数运算也没有 AES 那种分组结构。它只是用了几个可逆操作异或、加法、base64 编码。题目要求你把最终的密文还原成最初的flag所以你需要做的是把变换过程完全倒过来。理解这一点很重要。很多人看到“密码学”就想去搜什么在线解密工具结果发现工具根本解不出来。原因很简单这不是标准加密算法而是手工拼出来的混淆逻辑。你要做的是“代码逆向”不是“算法破解”。1.3 环境上其实不需要折腾做题环境不需要装任何第三方库。一个 Python3 解释器就够了系统自带或者装个 Anaconda 都行。我平时是在本机终端里直接跑.py文件写代码用 VS Code其实用记事本加命令行也一样能完成。脚本总长度不超过三十行没必要上重量级工具。有些朋友习惯用在线 Python 环境也没问题。只是要注意在线环境可能默认是 Python3遇到原脚本 base64 传字符串的问题时要自己调整一下。后面我会给出适应 Python3 的解法脚本。2. 把三个封装函数拆开看一遍2.1 执行顺序比函数本身更容易出错先看主逻辑这一行encode3(encode2(encode1(flag)))Python 执行时会先计算最内层的encode1(flag)把算出的结果作为参数传给encode2再把encode2的结果传给encode3。所以真正的执行顺序是flag - encode1 - encode2 - encode3 - 密文打个比方这就像先穿袜子再穿鞋。最后你看到的是穿着鞋的样子。如果要还原必须先脱鞋、再脱袜子顺序反着来。很多人在这一步栽跟头他们先想去逆encode1但encode1的输出已经不是最外层可见的东西了不能直接从密文里抓到。2.2 encode1先异或 36再加 25看第一个函数for i in ans: x ord(i) ^ 36 x x 25 s chr(x)ord(i)拿字符的 ASCII 码^ 36表示和 36 做按位异或然后加 25最后chr(x)变成新字符。举个例子字符f的 ASCII 码是 102。102 和 36 异或得到 66再加 25 得到 91对应字符[。也就是说单个字符f经过encode1会被替换成[。这里要注意异或运算的特性某个数连续和同一个数异或两次会得到原来的数。也就是说x ^ 36 ^ 36等于x。但因为后面还有个加 25所以逆运算不能只做一次异或就完事。从输出往回走要先减 25再异或 36顺序不能反。2.3 encode2先加 36再异或 36第二个函数for i in ans: x ord(i) 36 x x ^ 36 s chr(x)注意这里的顺序变了先加 36再做异或 36。这跟encode1是相反的顺序。为什么我会强调这个因为在写逆运算的时候很多人想当然地认为“加密是先异或后加解密就先减后异或”遇到encode2也照搬结果全错。encode2正向是“先加后异或”所以从输出倒回去时必须“先异或后减”。也就是说拿到encode2输出字符的 ASCII 码先和 36 做异或还原成“加 36 之前”的那个数再减 36才得到真正的输入字符这两步的顺序一旦调换整个结果就是乱的。2.4 encode3base64 只是个外壳第三个函数最不起眼就是 base64 编码。def encode3(ans): return base64.b64encode(ans)很多新手看到 base64 就紧张觉得是加密算法。其实 base64 只是一种编码方式它把二进制数据转成可打印字符目的是方便传输而不是保密。它没有任何密钥也不用猜。判断字符串是不是 base64 有三个很明显的特征字符基本都在A-Z、a-z、0-9、、/里长度通常是 4 的倍数密文末尾可能有一到两个这题最外层就是 base64所以逆向脚本第一步应该是base64.b64decode而不是去手动还原什么字符替换表。3. 逆向脚本从最外层一步步倒回去3.1 逆运算顺序是严格反过来的加密时是encode1 - encode2 - encode3逆向时就要按反方向处理也就是decode3 - decode2 - decode1。换个更直白的说法先用 base64 解码得到encode2的输出再逆encode2得到encode1的输出最后逆encode1得到原始flag如果顺序搞错比如先 base64 解码后直接做decode1就等于拿encode2的输出去套encode1的逆函数两边根本对应不上。3.2 单字符上的四个逆运算核心操作其实就四个可以整理成一张对照表正向函数正向操作逆向函数逆向操作encode1先异或 36再加 25decode1先减 25再异或 36encode2先加 36再异或 36decode2先异或 36再减 36encode3base64 编码decode3base64 解码这张表背后的原则很简单正向的最后一步操作逆向时必须最先处理。encode1最后做的是加 25所以decode1先减 25encode2最后做的是异或 36所以decode2先异或 36。3.3 可直接跑的完整脚本下面是一个适配 Python3 的完整解密脚本import base64 cipher 把题目里的base64密文填到这里 # 第一步base64 解码得到 encode2 的输出 raw base64.b64decode(cipher) # 第二步逆 encode2。raw 是字节序列每个字节对应 encode2 输出字符的 ASCII 码 middle .join([chr((b ^ 36) - 36) for b in raw]) # 第三步逆 encode1 flag .join([chr((ord(c) - 25) ^ 36) for c in middle]) print(flag)运行之后终端会打印出flag。如果打印出来是一串完全可读的英文和花括号说明逆向成功。提示把cipher完整替换成题目给出的字符串不要带多余空格和引号。如果解出来开头不是flag多半是密文填错或者程序里的减法和异或顺序写反了。3.4 怎么确认自己解出来的结果是真的CTF 的 flag 一般有固定的格式常见的是flag{...}。所以最简单的确认方式就是看输出前几个字符是不是flag括号里是否像一句话或者一串有意义的字符。如果输出是一堆乱码不要急着怀疑题目先检查自己的逆向脚本。尤其是encode2的逆运算很多人会写成chr((ord(c) - 36) ^ 36)这是错的。因为正向是先加后异或反向必须先异或后减。写成先减后异或等于把异或作用到了错误的中间数值上。4. 我踩过的坑多数人不是不会逆而是逆错了顺序4.1 Python2 和 Python3 的 base64 字符差异原题脚本如果是用老代码写的里面很可能直接把字符串传给base64.b64encode。Python2 的年代字符串就是字节串这样传参没问题。但 Python3 里字符串是 Unicodebase64 接口不吃字符串只吃字节串所以原脚本直接复制到 Python3 会报错TypeError: a bytes-like object is required, not str第一次遇到这个报错时我愣了一下以为是自己解题思路错了后来才发现只是环境差异。解决办法也很简单如果在 Python3 里跑原题脚本做正向验证把base64.b64encode(ans)改成base64.b64encode(ans.encode()).decode()就行。而我们写逆向脚本时根本不用管正向脚本会不会报错。因为base64.b64decode()在 Python3 里返回的是bytes遍历它拿到的就是整数正好可以直接做位运算。4.2 运算符优先级让一行代码变得完全不一样这是这题里最容易踩的坑而且踩的人往往自己看不出来。看encode2的正向逻辑x ord(i) 36 x x ^ 36如果你图省事想把两行合成一行写成x ord(i) 36 ^ 36那就完全错了。Python 会先算加法再算异或吗并不是。运算符优先级里加法的优先级高于异或所以ord(i) 36 ^ 36会被解析成ord(i) (36 ^ 36)而36 ^ 36等于 0最终结果就是ord(i)本身。换句话说这行代码什么都没做。逆向脚本里也有同样的坑。正确的逆encode2写法是x (ord(c) ^ 36) - 36如果写成x ord(c) ^ 36 - 36因为减法的优先级高于异或Python 会把它当成ord(c) ^ (36 - 36)结果又变回ord(c)。看起来好像只是少了一对括号实际结果天差地别。表达式Python 实际理解结果ord(c) ^ 36 - 36ord(c) ^ (36 - 36)等于ord(c)错误(ord(c) ^ 36) - 36先异或再减正确所以我在这一步的建议很简单所有和位运算混在一起的四则运算一律加括号。不要相信自己的记忆括号多写几个不丢人。4.3 分步打印中间结果快速锁定出错位置如果一次性跑完得到乱码不要从头一行一行猜。直接在脚本里加几个打印语句raw base64.b64decode(cipher) print(raw:, raw[:50]) middle .join([chr((b ^ 36) - 36) for b in raw]) print(middle:, middle) flag .join([chr((ord(c) - 25) ^ 36) for c in middle]) print(flag:, flag)通过raw可以确认 base64 解码是否正常比如长度、前几个字节长什么样。通过middle可以判断逆encode2对不对。如果middle已经很接近可见的英文字母说明encode2没问题问题大概率出在最后一步如果raw一出来就是不可读的乱码那可能是密文填错或者这题的编码外壳不是 base64。这个习惯看着简单但在 CTF 题里特别管用。很多新手题出错不是思路大问题而是细节小问题。分步打印比反复猜快得多。5. 从 easychallenge 总结一套通用做题范式5.1 画一条从明文到密文的调用链遇到这种自定义加密脚本第一件事不是看工具而是把调用链写下来。比如这题可以写成flag - encode1 - encode2 - encode3 - 密文然后再在下面另起一行写出逆向链密文 - 逆 encode3 - 逆 encode2 - 逆 encode1 - flag虽然只是纸面上的两行字但它能帮你想清楚每一步的输入输出是谁。很多人写着写着就乱就是因为脑子里只有一个抽象的“解密”没有把中间数据流具体化。5.2 把每个函数降到字符级再反向不管自定义函数包装得多花哨只要它内部用的是ord、chr、、-、^这类基础操作都可以回到单个字符上分析。具体做法是挑一个明文比如字符a看它经过当前函数变成什么把操作拆成有序步骤从最后一步开始一步一步写出反向步骤如果函数里有判断、循环、查表也可以同样处理。只要每一步都是可逆的整条链路就能逆回去。这题里最需要注意的就是操作顺序正向的“最后一步”逆向时要“最先处理”。很多题目实际上不是难在算法而是难在顺序。5.3 用自测样例验证不要拿真题密文直接试我在做这类题时有个习惯先用一个假明文跑一遍正向脚本得到一串密文然后拿这串密文去跑自己写的逆向脚本看能不能还原。这样可以确认你的逆向逻辑没有写反避免在验证真题时盲目猜测。假如你手上已经保留了题目的正向函数可以这样自测test flag{self_test} cipher encode3(encode2(encode1(test))) back decode1(decode2(base64.b64decode(cipher))) print(back test)这一步如果能输出True说明你的逆向脚本基本可信再拿真题密文去跑就很稳。5.4 这类题可能出现的变形easychallenge只是新手区的一个典型样本。同一套思路可以变形出很多题目把固定常数 36、25 换成别的数把encode2的“先加后异或”改成“先异或后加”把 base64 换成 base32、hex任意外壳函数再多嵌套几层不管怎么变核心解法都一样先识别调用顺序再逐层逆运算。我自己的习惯是拿到脚本后先把原函数完整复制到编辑器里然后在旁边写逆函数一行行对照着改。宁可多写几行也别为了省事把两个函数合并那样出错了很难查。做完之后再做一个已知明文自测基本就不会翻车。希望这篇分析能帮你在遇到同类题目时少走点弯路。

相关新闻

ESP8266接OLED:从选型接线到点亮显示全教程

ESP8266接OLED:从选型接线到点亮显示全教程

你有没有过这种体验:ESP8266串口打印出来的数据堆了一屏,盯着串口监视器看得眼花缭乱,明明传感器读出了26.5℃,却没法让这个数字变成一眼能看懂的"画面"。我第一次做物联网小项目时就被这个问题卡了很久。后来接上一块0…

2026/10/15 21:22:32 阅读更多 →
嵌入式C++单链表实战:内存可控的类型擦除链表设计

嵌入式C++单链表实战:内存可控的类型擦除链表设计

1. 这不是教科书里的“单链表”,而是你明天就能抄进项目的实战结构 单链表——这三个字一出来,很多人脑子里立刻浮现出黑板上画的箭头、节点、next指针,还有老师念叨的“头结点不存数据”“遍历时间复杂度O(n)”……但现实里,我带…

2026/10/15 21:22:32 阅读更多 →
ESP8266驱动OLED屏入门:接线、配置与点亮指南

ESP8266驱动OLED屏入门:接线、配置与点亮指南

1. OLED屏与ESP8266的适配思路 手头正好有一块0.96寸的OLED屏,一直想做个小玩意儿,就顺手用ESP8266驱动了起来。先说结论:这对于入门嵌入式显示来说,是很典型也很有意思的组合——OLED功耗低、自发光、显示效果好,ESP8…

2026/10/15 21:22:32 阅读更多 →

最新新闻

零基础学Java:从环境配置到小项目实战的完整路径

零基础学Java:从环境配置到小项目实战的完整路径

简介:这份资源是韩顺平「循序渐进学Java零基础」课程的完整笔记PDF,面向零基础或刚入门Java编程的学习者,帮助其系统建立Java知识体系并了解实际就业方向。笔记按三个阶段组织:阶段一讲解Java语法、数据类型、控制结构与类对象等基…

2026/10/15 22:15:49 阅读更多 →
ASRock H77 Pro4-M 主板 coreboot 移植支持指南:规格、刷写与硬件监控

ASRock H77 Pro4-M 主板 coreboot 移植支持指南:规格、刷写与硬件监控

固件嵌入式操作系统驱动开发 【免费下载链接】coreboot Read-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We dont handle Pull Requests. 项目地址: https://gitcode.com/gh_mirrors/co/coreboot 点击查看 免费下载 本文以 cor…

2026/10/15 22:15:49 阅读更多 →
基于 puppeteer-extra-plugin-stealth 的 `_template` 模板开发自定义反检测插件实战指南

基于 puppeteer-extra-plugin-stealth 的 `_template` 模板开发自定义反检测插件实战指南

网页爬虫浏览器控制测试 【免费下载链接】puppeteer-extra 💯 Teach puppeteer new tricks through plugins. 项目地址: https://gitcode.com/gh_mirrors/pu/puppeteer-extra 点击查看 免费下载 本文以 packages/puppeteer-extra-plugin-stealth/evasio…

2026/10/15 22:15:49 阅读更多 →
2020-2021中国开发者调查报告:数据挖掘与工程化分析实践

2020-2021中国开发者调查报告:数据挖掘与工程化分析实践

简介:《2020-2021中国开发者调查报告》由CSDN官方出品,面向IT行业决策者、市场研究人员及广大开发者,系统呈现中国开发者群体的技术行为与行业趋势。报告基于CSDN自2004年积累的开发者调查数据,结合年度问卷重新设计,并…

2026/10/15 22:15:49 阅读更多 →
GoAnime 移动端与社区生态全解析:Android 版 + Discord 社区怎么加入

GoAnime 移动端与社区生态全解析:Android 版 + Discord 社区怎么加入

【免费下载链接】GoAnime Search, stream and download anime, movies and TV shows from the terminal, in English and Brazilian Portuguese. 项目地址: https://gitcode.com/gh_mirrors/go/GoAnime 点击查看 免费下载 GoAnime 是一款基于 Go 语言、运行在终端里…

2026/10/15 22:15:49 阅读更多 →
自建S3兼容对象存储实战:从部署到避坑

自建S3兼容对象存储实战:从部署到避坑

自建S3兼容对象存储这事儿,最近几年越来越多人问。公有云的对象存储接口确实好用,但很多场景——比如数据完全不能出内网、项目要离线交付、又或者就是单纯想省掉按量计费的账单——都想在自家服务器上搞一套同样的接口服务。我自己前后搭了好几套&#…

2026/10/15 22:14:47 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →