首页/新闻资讯/正文详情

CTF逆向入门:从love题目看Base64与位运算的还原

发布时间:2026/9/26 4:48:42 来源:云帆数科 栏目:资讯中心
CTF逆向入门:从love题目看Base64与位运算的还原
第一次在Bugku上看到Reverse分类里的love这道题我心里想的是坏了这名字起得这么甜题目估计不会简单。结果拖进IDA一看发现它几乎是给新手量身定做的入门教材——一个明显的main函数、一张Base64编码表、一个字符串比较外加一个有点意思的位运算。这篇文章把从拿到文件到解出flag的整个过程重新走一遍聊聊我是怎么定位关键函数、怎么判断加密逻辑、怎么写还原脚本的。同时也打算借这个例子给刚开始玩逆向的朋友整理一套可以复用的分析思路。内容不涉及高深技巧一个能装IDA和Python的Linux环境就够了。1. 拿到love这道题我第一步做了这几件事1.1 先确认文件类型和基础信息从平台下载的压缩包解开后出现在眼前的不是一个带后缀的可执行程序而是一个名称就叫love的普通文件。这种时候我的习惯是先用file命令确认格式别靠后缀猜$ file love love: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32看到ELF 64-bit LSB executable这条信息心里就有底了这是Linux下的64位可执行文件。CTF的Reverse题喜欢出Linux ELF原因很简单——环境统一一个Linux容器或者WSL就能覆盖大部分题目的运行需求而且这类题目结构简单动态链接、静态链接都不影响我们找main和关键比较函数。接下来跑一遍strings看看有没有直接暴露的关键词$ strings love /lib64/ld-linux-x86-64.so.2 ... Input Your Flag: right wrong ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/上面这段输出信息量不小。前三行告诉我们程序的交互逻辑是提示输入flag然后给出right或wrong。最后一串字符乍看像乱码实则是一个标准物——Base64编码表。一个程序不会平白无故把整张Base64表放在数据段里出现它基本可以断定程序内部有Base64编码或解码的操作。1.2 先运行一遍观察程序行为在Linux环境里直接跑一下看看这里我用的是WSL$ chmod x love $ ./love Input Your Flag: 123456 wrong随便输入一串数字程序只回了一个wrong。没有反调试、没有花指令、没有额外的提示信息。到这里这道题的性质已经很清楚了输入-处理-比较三段式结构。下一步就是用IDA打开把处理和比较这两个环节找出来。如果是在Windows上做题看到love是个没有后缀的文件不要试图改后缀双击运行。先file确认类型再放进WSL或虚拟机。这是很多新手踩的第一个坑。1.3 这几条线索能得出什么结论把前面观察到的信息汇总一下观察结果推断64位ELF动态链接需要在Linux环境运行出现Input Your Flag、right、wrong交互式判定输入正确性出现标准Base64编码表后续要重点关注查表操作运行时无复杂输出代码量不大适合静态分析这三条线索加起来基本可以放弃动态调试直接用IDA做静态反编译。love这种题目不会触发IDA的自动分析问题拖进去几秒钟就能出结果。2. IDA里的主要矛盾main函数和一个神秘的sub_400C8E2.1 从main函数入口看起用IDA打开love左侧函数列表里能看到为数不多的几个函数main就在其中。双击进入main按F5生成伪代码。不同IDA版本反编译出来的代码在变量名和行号上有差异但结构基本一致int __cdecl main(int argc, const char **argv, const char **envp) { char input[256]; unsigned int i; puts(Input Your Flag:); scanf(%s, input); for (i 0; i strlen(input); i) input[i] (input[i] 4) | (input[i] 4); if ( sub_400C8E(input, 这里是一串Base64字符串) ) puts(right!); else puts(wrong!); return 0; }第一次看这段代码我最容易跳过去的就是循环里那行input[i] (input[i] 4) | (input[i] 4);。很多新手会觉得这只是某种无所谓的运算不影响大局。但实际上它就是加密链路里的第一环而且是理解整道题的关键。2.2 逐行读出main的三段逻辑main的整体逻辑分三段第一段提示输入用scanf(%s, input)把用户字符串读进数组。第二段遍历input的每个字符做位运算变换。第三段调用sub_400C8E传入处理后的input和一个目标字符串根据返回值决定输出right还是wrong。这里有个小细节strlen(input)在循环条件里每轮都会重新计算但因为scanf读入的字符串长度在输入后是固定的循环次数就是输入长度不会变来变去。出题人源码里经常这么写分析时不用纠结。sub_400C8E返回非零输出right返回零输出wrong可以确定它就是最终比较函数。两个参数分别是处理后的输入和目标字符串。换句话说程序本质上是在做这样一件事把你的输入加工成某个目标样子再和目标字符串比较。相等就是对不相等就是错。2.3 核心运算(input[i] 4) | (input[i] 4) 在干嘛拿一个具体字符手算一遍就全明白了。假设输入字符是AASCII码是0x41二进制是0100 0001。0x41 4左移4位变成0001 0000。原高4位0100被推到了低4位区域高位补0。0x41 4右移4位变成0000 0100。原低4位0001被甩到了高4位区域。两者做或运算结果是0001 0100也就是0x14。原来0x41变成了0x14。这个变换的精确定义是一个字节的8个bit高4位和低4位互换。用循环移位的视角看就是对8位二进制数做4位循环移动左移4位和右移4位效果一样。2.4 这个变换是自逆的所以解题反而省事如果我们对0x14再做一次同样的操作0x14 4得到0x400x14 4得到0x01或运算结果0x41又回到了A这说明(c 4) | (c 4)是一个自逆变换连续做两次等于什么都没做。这对解题非常关键如果加密侧的流程是先做位变换再做Base64编码那解密侧的流程就是先做Base64解码再做完全相同的位变换。脚本里不需要额外推导逆运算直接照抄同一个式子即可。有个类型问题必须提醒一下。C语言里char参与运算会被提升为int如果变量是带符号的char移位过程中可能产生符号位干扰。实际出题用的是无符号字符但为了稳妥我写脚本时一律加 0xFF把中间结果截断到8位避免Python里整数无限扩展导致结果异常。3. 顺着数据流识别Base64和比较目标3.1 字符串窗口是逆向的第一快照在IDA里按ShiftF12打开Strings窗口刚才用strings命令见过的所有字符串都在这里。最扎眼的就是那行ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/。双击它IDA会跳到这串字符在数据段的位置。右键选择列出交叉引用就能看到哪些代码在引用它。顺着引用关系Base64编码的调用点就藏不住了。3.2 如何确认sub_400C8E内部就是标准Base64从编码表跳转过去最终会来到sub_400C8E。它的伪代码大概长这样bool __fastcall sub_400C8E(const char *data, const char *target) { // 计算data长度 // 申请输出缓冲区 // 3个字节一组拆成4个6位索引 // 用编码表查字符 // 尾部补 // strcmp(output, target) }只靠伪代码还不够稳我在汇编层面确认了Base64的特征。标准Base64编码在指令层有非常固定的套路三个连续字节被读取后用移位指令拆成4个6位索引每个索引用movzx eax, byte ptr [raxtable]这样的指令查表剩余不足3字节时有补的尾部逻辑另外Base64的原理本身也不复杂3个字节共24位每6位一组得到4个0到63之间的索引再用索引去查表得到4个可见字符。所以编码后的长度必然是4的倍数末尾可能出现填充。3.3 比较目标字符串就是我们的已知密文在sub_400C8E的调用点第二个参数就是比较目标。伪代码里它显示成字符串字面量双击可以直接复制。复制这个Base64字符串有几个雷区注意末尾的或少一个字符解码结果都不同IDA的字符串视图可能显示转义形式从字符串窗口完整复制更保险复制完后数一下长度必须是4的倍数不是的话说明复制少了到这里整个加密链路已经成型用户输入 → 每个字节高低4位互换 → Base64编码 → 与硬编码字符串比较比较目标字符串相当于已知密文。我们要求的是从密文倒推出用户输入而这个用户输入就是flag。在动手写脚本之前还要确认一件事sub_400C8E用的是不是标准Base64表。love这道题经确认是标准表但我在其他题里也见过打乱顺序的自定义表遇到那种情况解码脚本里的表字符要跟着换。4. 写脚本还原flag完整解密链路4.1 解密思路已知加密链输入 → 位变换T → Base64编码 → 密文那解密链就是密文 → Base64解码 → 位变换T → 输入(flag)为什么最后一步还是T而不是T的逆因为T是自逆变换前面已经验证过。如果这道题用的是异或固定密钥或者加法之类的操作这里就要小心推导逆操作了不能随手照抄加密函数。4.2 Python脚本import base64 # 在IDA中复制的目标字符串注意保留末尾的号 cipher 在这里粘贴从IDA复制的Base64密文 # 第一步Base64解码得到位变换后的字节序列 tmp base64.b64decode(cipher) # 第二步对每个字节再做一次高低4位互换 flag bytearray() for b in tmp: x ((b 4) | (b 4)) 0xFF flag.append(x) print(flag.decode())跑完脚本输出应该是一个以}结尾、整体可读的字符串格式通常是flag{...}或者bugku{...}。把这串内容填回程序验证一下$ ./love Input Your Flag: flag{你得到的字符串} right看到right整个闭环就完成了。我不在这里直接贴出题目里的实际flag是为了保留你自己提取密文并解码的完整过程。如果只是想抄答案CTF的乐趣就没了但如果你是想学分析方法上面这些已经足够。4.3 脚本里的两个常见翻车点第一个翻车点是Python3的字符串和字节类型。base64.b64decode返回的是bytes你遍历它取出来的元素是0到255的整数这符合预期。千万别提前.decode()成str再逐字符处理那样chr和ord来回切换极易把自己绕晕。第二个翻车点是输出时的不可见字符。如果flag里碰巧有非ASCII字符直接decode()可能报错。稳妥一点的写法是try: print(flag.decode()) except UnicodeDecodeError: print(bytes(flag).hex())不过love这种入门题的flag基本都是纯ASCII正常情况下走不到这里。我一般会留着这段try因为后面练更难的题时早晚用得上。除Python之外CyberChef也能解这道题先用From Base64把密文还原成字节再用Rotate Left按4位、8-bit模式旋转输出的内容就是flag。这条路径适合不想写代码的人但我还是建议自己写一遍脚本因为脚本能帮你理解位运算的细节。4.4 顺带说一句std::reverse的误传写题过程中我在网上翻资料看到有人问C11以下能用std::reverse吗然后试图把这几个字往love上套。这里说明一下std::reverse是C98标准库algorithm里的模板算法作用是把一段区间内的元素倒序C11以下完全可以用。love这道题的反编译结果里并没有std::reverse它走的是C语言风格的位运算加Base64。如果以后在别的高难度逆向题里看到std::reverse相关符号那才真是遇到了C STL代码把它等价成一个区间倒序操作即可和本题无关。5. 从love说开去这类入门逆向题的分析套路5.1 从最后比较反推数据流love给我最大的收获是让我第一次体会到从终点反推的逆向思维。大部分Reverse题不管做多少层变换最终都会落在一个比较函数上——可能是strcmp可能是memcmp也可能是一个逐字符比较的循环。分析方法永远是这几步找到最终比较点看比较点的两个操作数分别是什么顺着其中一个操作数向上追溯直到追到用户输入途中经过的每一次运算就是加密链的每一环然后在脚本里从已知密文出发沿着反方向逐层还原。有些人一上来就想着动态调试断点设在哪里都不知道。对这种静态逻辑清晰的题目IDA的F5已经完全够用动态调试反而多余。5.2 识别加密原语的通用技巧经过这道题我总结出几个常见的嗅探方法看到特征可以直接对号入座数据段有64个可见字符的表先怀疑Base64并对比表是否标准看到固定值异或^ 0x3F或者异或值和数组下标相关先怀疑XOR加密看到大量加法和减法遍历字符串先怀疑凯撒式移位或某种简单流密码看到循环移位或高低位互换记住这类操作大多是自逆的看到复杂数组、迷宫寻路、奇怪的递归多半是出题人在炫技入门阶段可以先绕开识别原语的过程很像看化验单汇编指令特征指向加密算法类型最后用脚本验证结论。love这道题就提供了一个很干净的示例一次位变换加一次Base64特征明显验证简单。5.3 新手容易卡住的三个点我带人看这道题大家最常卡的地方有这三个我单独拎出来说。一是环境。拿到了ELF文件不知道Linux环境怎么来。我的建议是装好WSL或者一个Ubuntu虚拟机比在Windows上做一堆兼容层操作省心得多。逆向题里有相当大比例是ELF这个环境迟早要配。二是IDA基本操作。F5只是生成伪代码不等于替你做题。要学会看交叉引用选中后按X键、看字符串引用在Strings窗口双击、修改变量类型按Y键。这几个操作能提升分析速度一大截。三是脚本里的位运算。和的优先级容易记混Python的整数移位又没有固定位数上限。写脚本时先拿一个已知字符手算一遍再跑全量可以有效降低出错率。5.4 下一站可以练什么做完love我建议按这个顺序往下练找一道用了自定义Base64表的题练习提取自定义表和重建解码器找一道XOR加密的题练一练从密文和已知明文推断密钥找一道带花指令混淆的题感受一下修复函数边界的过程换用Ghidra再做一遍love体验不同工具对同一道题的反编译差异别急着冲高难度的题。逆向是个刷够一定量就突然通透的领域love就是那个让你第一次感受到数据流追踪乐趣的起点。我自己最深的感觉是把这题的每个字节在纸上手算一遍比收藏十个writeup都有用。算完之后你再看别的Reverse题会不自觉地拆成输入、变换、比较三段这就是这道love留给你的最值钱的东西。

相关推荐

非标定制烧录设备全流程实战:从需求分析到产线验收
非标定制烧录设备全流程实战:从需求分析到产线验收

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

企业网络安全实战:从防护体系搭建到风险管控的落地指南
企业网络安全实战:从防护体系搭建到风险管控的落地指南

做了那么多年企业安全,我发现一个挺扎心的事实:不少公司买起设备来毫不含糊,防火墙、WAF、堡垒机、杀毒软件清一色配齐,但遇到真正的攻击——勒索病毒、钓鱼邮件、内网横向渗透——照样被按在地上摩擦。问题不是安全产品不够多&am… · 2026/9/26 4:48:42

OpenClaw本地部署安全指南:从环境到运维的加固清单
OpenClaw本地部署安全指南:从环境到运维的加固清单

先说实话:这篇文章的重点不是 OpenClaw 有哪些好玩的功能,而是把这类智能体框架本地部署起来之后,安全这一步到底该怎么做。我自己折腾了一圈,最大的感受是:本地部署不等于安全。很多人觉得数据放在自己机器上、模型跑… · 2026/9/26 4:48:36

产教融合落地路径:工业软件与人工智能如何重塑数智人才培养
产教融合落地路径:工业软件与人工智能如何重塑数智人才培养

1. 数智时代的教育困局与破局思路——为什么产教融合是必然选择1.1 从企业视角看人才缺口到底有多大这几年人工智能的落地速度远超高校课程更新的节奏。我经常和做工业软件、做智能制造的同行聊,大家最头疼的事几乎一致——招不到合适的人。不是说市场上没有人工智能… · 2026/9/26 6:34:54

JVM内存模型:理解Java程序的内存管理_jvm 内存模型,jvm 怎么管理的-CSDN博客
JVM内存模型:理解Java程序的内存管理_jvm 内存模型,jvm 怎么管理的-CSDN博客

首屏导读 本教程配套付费专栏: 大模型工程师修炼手记 19.9 元(AI 编程 / Agent 实战 | 本文同主题系统课程) AI时代程序员的自我提升 49.9 元(AI 时代成长方法论)。 单篇不过瘾?订阅解锁全量源… · 2026/9/26 6:34:54

RoundTable v1.0.0-rc.1:可辩论、可拍板、可落盘的多模型会议
RoundTable v1.0.0-rc.1:可辩论、可拍板、可落盘的多模型会议

我让三个大模型互相当红队:一个多模型圆桌插件的架构、踩坑与一次被否掉的方案 先说结论,免得你翻到最后: 多模型协作 ≠ 多问几个模型。 并列回答解决的是"覆盖率",会议解决的是"收敛"——后者需要主持人、需… · 2026/9/26 6:34:48

codex-desktop-linux 远程手机控制完整指南:如何用移动端远程驱动Linux桌面Codex
codex-desktop-linux 远程手机控制完整指南:如何用移动端远程驱动Linux桌面Codex

codex-desktop-linux 远程手机控制完整指南:如何用移动端远程驱动Linux桌面Codex 【免费下载链接】codex-desktop-linux Unofficial ChatGPT desktop app for Linux (formerly the Codex app), built locally from OpenAI’s official macOS app. Includes Chat, Wo… · 2026/9/26 6:34:42

生活 不会一帆风顺
生活 不会一帆风顺

生活从不会一直一帆风顺,难免会遇到疲惫、迷茫,甚至觉得努力看不到结果的时候。很多时候不是你不够好,只是沉淀需要时间,所有默默付出的汗水,都在悄悄积攒力量,不必急于求成,也别轻易否定自己。… · 2026/9/26 6:34:42

皮尔逊、斯皮尔曼、肯德尔:三种相关性分析方法实战选型指南
皮尔逊、斯皮尔曼、肯德尔:三种相关性分析方法实战选型指南

1. 为什么“相关性不等于因果”这句话被反复强调——从一场真实业务事故说起去年我参与一个电商用户复购预测项目,团队用皮尔逊相关系数发现“用户浏览商品详情页时长”与“7日内复购率”呈现0.82的强正相关。产品同学当场拍板:立刻上线“延长详情页停留… · 2026/9/26 6:34:42

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

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

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码