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

reverse3逆向题详解:Base64变体与逐字符偏移还原

发布时间:2026/9/25 12:25:39 来源:云帆数科 栏目:资讯中心
reverse3逆向题详解:Base64变体与逐字符偏移还原
刷BUUCTF逆向题的时候reverse3这道题应该被很多人标记为“入门必刷”。名字叫reverse3难度也确实不高但考点很典型自定义Base64变体加逐字符下标偏移。整道题拉通下来其实就是把“查壳、静态分析、算法识别、脚本还原”这一套CTF逆向前置流程完整走了一遍。对于刚接触逆向、还没建立手感的新手来说这道题非常适合作为第一道完整独立分析的题目对于已经刷过一些题的人它也算是一个不错的查漏补缺样本。这篇文章我会直接从题目本身出发把拿到一个exe之后该怎么想、怎么做、每一步为什么这么做都讲清楚。不会只给一个“秒杀脚本”而是把分析链路和踩坑点都铺开希望对正在刷BUUCTF reverse系列的你有实际帮助。1. 这道reverse3到底在考什么先建立整体思路1.1 拿到题目先别急着双击运行很多新手拿到一个reverse题第一反应就是双击exe跑一下看看输出提示。这不叫错但对解题帮助很有限。逆向分析的正确顺序应该是先查壳、再看文件信息、然后拖进IDA或OD做静态分析最后再考虑动态调试辅助验证。reverse3这个题目给的是一个典型的Windows控制台程序。双击运行后会提示“please input:”随便输入一串字符程序会反馈“Wrong!”。这个交互逻辑本身没有太多信息量真正的核心逻辑全在程序内部必须通过反汇编或者反编译才能看到。我用Exeinfo PE先查了一下文件是32位PE没有加壳编译器信息看风格是Visual C系列。没有壳就意味着不需要脱壳直接拖进IDA做静态分析就行。这一步的判断非常重要如果是加壳的样本静态分析之前还要多一道脱壳工作分析思路会完全不同。1.2 从平台题目分布预判考点BUUCTF的reverse题是有梯度的前几十题大多在考基础算法识别和简单变换。reverse3这种编号靠前的题考点一般不会太偏最常见的就是Base64系变体、异或、简单加减法下标变换、字符串比较。所以看到“reverse3”这个名字再加上题目本身的小体积和简单交互就可以预判大概率是算法逆向题不会涉及复杂的VM保护、反调试或混淆。预判考点的意义在于节省时间。如果一上来就抱着分析花指令的心态去抠汇编反而会把简单题复杂化。reverse3的核心逻辑无非是把输入的字符串做某种编码变换然后跟一个固定密文比较。我们要做的就是找到这个变换规则然后逆推原始输入。1.3 解题链路预览整道题我建议按下面这个流程走这也是大部分BUUCTF入门reverse题的通用套路查壳Exeinfo PE确认文件类型、位数、是否加壳。静态分析IDA打开定位主函数。找字符串通过“please input:”“Right!”等提示快速确定关键函数位置。反编译F5看伪代码梳理程序逻辑。算法识别认出sub_4110BE是Base64编码函数同时发现下标偏移逻辑。脚本还原根据加密公式倒推写Python脚本还原flag。验证跑脚本得到flag提交平台确认。这个流程其实可以套用到绝大多数入门级reverse题目上。下面的内容就是一步步拆开讲把每一步的实操细节和背后的思考逻辑都交代清楚。2. 工具准备与静态分析实操2.1 查壳与文件信息识别先说说查壳工具。现在的新手教程里经常能看到PEiD但这个工具真的有点老了对于新版编译器生成的PE文件识别率一般。我比较推荐Exeinfo PE它免费、免安装、识别准确率比PEiD高不少还能直接显示入口点、编译器、区段信息。当然Detect It EasyDIE也很不错两个选一个顺手就行。把reverse3程序拖进Exeinfo PE右侧会出现文件格式信息。这里我看到的结论是32位PE、VC编译、无壳。无壳意味着程序入口就是正常的代码段直接IDA加载就能开始分析。这里有一个新手容易忽略的点32位程序和64位程序在IDA中的分析方式有些差别。reverse3是32位程序IDA默认会把代码按x86架构分析函数调用约定一般是cdecl。分析代码的时候只要不影响看逻辑这些细节不用过于纠结。但如果是64位程序寄存器传参的规则不一样伪代码的阅读方式也会有差异需要有一定的心理准备。2.2 IDA定位主函数别在函数列表里瞎翻IDA加载完程序之后很多人习惯在左侧函数窗口里找main。这个方法不能说错但效率低。因为VC编译出来的程序主函数名不一定是main有时候是main_0有时候是WinMain16还有时候会看到__scrt_common_main_seh这类运行时函数。与其在函数列表里翻不如直接用字符串窗口定位。按ShiftF12打开字符串窗口在里面找please input:、Right!、Wrong!这些提示字符串。字符串窗口里能看到每个字符串所在的地址双击字符串会跳到对应的数据段然后按CtrlX查看交叉引用就能找到谁引用了这个字符串。那个函数就是我们要找的主函数。这个技巧在入门阶段非常实用。因为不管程序逻辑多复杂它总要输出提示信息而提示信息通常是固定的字符串常量一定在静态数据段里。通过字符串定位关键函数比靠肉眼在函数列表里找可靠得多。2.3 伪代码逐行读F5之后别光看热闹找到主函数后直接按F5反编译成C伪代码。不同版本的IDA反编译结果略有差异但reverse3的核心逻辑基本是下面这个样子我已做过变量重命名和注释方便阅读int main() { char cipher[50]; char input[50]; int len; int i; strcpy(cipher, e3nifIH9b_CndH); printf(please input:); scanf(%20s, input); sub_4110BE(input); // 对input做Base64编码结果写回input len strlen(input); for ( i 0; i len; i ) input[i] i; // 每个字符加上它的下标 if ( strcmp(input, cipher) 0 ) printf(Right!\n); else printf(Wrong!\n); return 0; }这段伪代码的逻辑非常清晰大概分四步第一步往cipher里复制了一个固定字符串e3nifIH9b_CndH这就是密文也就是我们最终要逆向的目标。第二步scanf读入我们输入的字符串存到input。第三步调用sub_4110BE对input做处理。从函数内部代码特征看这是一个Base64编码函数处理结果直接写回了input缓冲区。第四步对input里的每一个字符加上它在字符串中的下标位置第一个字符加0第二个字符加1依此类推。这一步是典型的逐字节数学变换简单直接。最后调用strcmp把变换后的input和一开始的cipher做比较。相等就输出Right不相等就输出Wrong。读伪代码的时候最重要的不是把每一行都看懂而是抓住三个关键信息输入经过什么变换、变换结果跟谁比较、比较的结果怎么影响流程。这三个信息拿到手整道题的骨架就已经清楚了。3. 核心逻辑拆解Base64变体和下标偏移3.1 为什么一眼能认出Base64sub_4110BE一眼就能看出是Base64编码这倒不是因为函数名叫了什么Base64而是因为代码里有非常典型的Base64特征模式。进入sub_4110BE内部你会看到类似这样的操作对输入的字节流每3个为一组通过位移和按位与运算把3个字节拆成4个6位的索引值然后去一个64字符的表中查表得到4个输出字符。代码里大概率会出现 6、 4、 2、 0x3F、 0xF这种位运算组合还有一个长度为64的字符数组。这些特征组合在一起几乎可以直接断定是Base64编码。这里多说一句很多资料会把 0x3F解释成“取低6位”。6位二进制最大就是63所以 0x3F确实是取低6位的标准写法。Base64的本质就是把8位字节流拆成6位一组再用这6位去索引64个字符。所以看到 0x3F、 0xF配合移位运算基本就是在处理Base64索引这个识别技巧以后遇到其他Base64变形题也能直接用。3.2 别默认它是标准Base64字符表才是关键很多WriteUp里会直接说sub_4110BE是“标准Base64”然后直接用Python标准库解码。但实际上这道题在一些版本的分析里字符表并不是标准表。如果你减完下标之后用base64.b64decode去解可能会得到乱码或者直接报错。这就涉及到逆向中的一个重要原则认清某个算法后还要看清它的参数尤其是字符表。标准Base64的字符表是ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/如果sub_4110BE里引用的字符表就是这一串那直接用标准库没问题。但如果表被换过比如把最后两个字符改掉、或者把整张表重新排列就必须把实际字符表提取出来写对应的解码脚本。提取字符表的方法也不复杂。在IDA的反编译窗口里双击sub_4110BE代码中引用的字符表地址会跳到数据段。数据段里会有一段连续的字符那就是Base64表。选中它复制出来放到脚本里当成table变量用就行。也可以用ShiftF12在字符串窗口里直接找到这串字符复制更方便。这一步是很多新手在reverse3上卡住的核心原因算法认对了但默认用了标准表结果解出来不对又不知道哪里错了。所以我在分析这类Base64变形题时永远都是先看表再谈解码。3.3 下标偏移排队加号的小把戏Base64编码完成后程序还对结果做了一次逐字符偏移每个字符ASCII码加上它在字符串中的下标位置。第一个字符加0第二个字符加1第三个字符加2依此类推。这个变换用生活化的例子解释就像排队领号码牌第一个人拿0号第二个人拿1号第三个人拿2号然后每个人把自己的原始编号加上手中的号码得到一个最终编号。这个过程非常简单也完全可逆逆向的时候只要反过来做一次减法第i个字符减去i就能还原出Base64编码后的原始字符串。这里要注意下标从0开始还是从1开始。reverse3的伪代码里循环变量i从0开始所以第一个字符不变从第二个字符才开始加1。有些题目会写for ( i 1; i len; i )那样第一个字符也加1逆向时就要统一减1。写还原脚本之前先确认下标的起始值能省很多排查时间。3.4 加密链路总结把分析结果串起来reverse3的加密链路其实就是一个公式flag -- Base64编码 -- 逐字符加下标 -- 密文注意这里的操作顺序是“先Base64编码再加下标”。逆向的时候顺序必须反过来密文 -- 逐字符减下标 -- Base64解码 -- flag这个顺序看起来理所当然但实际操作中经常有人搞反写成“先解码再减下标”或者“减下标减错方向”结果自然不对。写脚本之前先把加密顺序写在纸上再对着写逆过程是最稳妥的。4. 脚本还原与flag验证4.1 还原思路根据上面的推导还原脚本需要做两件事第一从密文e3nifIH9b_CndH出发对第i个字符减去i得到一个中间字符串这个字符串就是Base64编码后的结果。第二对这个中间字符串做Base64解码。如果sub_4110BE里的字符表是标准表直接用Python标准库的base64.b64decode即可如果字符表被换过就需要自写解码逻辑。两件事的顺序不能反原因很简单下标偏移是对编码后的字符串逐字符做的所以减法还原的也应该是编码后的字符串然后才能做解码。4.2 第一版脚本标准库直接解如果你在IDA里确认字符表就是标准表那么还原脚本非常短import base64 cipher e3nifIH9b_CndH s for i in range(len(cipher)): s chr(ord(cipher[i]) - i) print(s) print(base64.b64decode(s))跑完之后如果输出结果里能看到flag{...}格式的字符串那这道题就解完了。第一行的print(s)是为了让你看一眼减完下标之后得到的Base64字符串长什么样方便排查问题。这个脚本里有个细节chr(ord(cipher[i]) - i)先取出字符的ASCII码减下标再转回字符。Python字符串的索引从0开始对应循环变量i正好和C语言的i一致所以不用额外加1或减1。4.3 第二版脚本自定义Base64表通用解码如果你发现减完下标后的字符串用标准库解不对或者报Invalid base64-encoded string那大概率是字符表问题。这时候就需要把sub_4110BE里引用的字符表复制出来替换掉下面的table变量。import base64 cipher e3nifIH9b_CndH table ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/ s for i in range(len(cipher)): s chr(ord(cipher[i]) - i) # 自定义表解码 def b64_decode(data, table): # 先建立字符到索引的映射 char_map {c: i for i, c in enumerate(table)} # 标准表用于对照映射 std_table ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/ # 把自定义表中的字符替换为标准表中的索引再用标准库解码 trans str.maketrans(table, std_table) data data.translate(trans) # 补齐末尾的 data * ((4 - len(data) % 4) % 4) return base64.b64decode(data) print(b64_decode(s, table))这个脚本的思路不复杂既然字符表是自定义的那把自定义表里的字符映射到标准表对应的索引位置然后再用标准库解码。如果连填充符都被换掉了还需要把填充符也一起映射不过reverse3场景下一般不会这么复杂。实际做题的时候我更推荐把表从IDA里复制出来之后先肉眼跟标准表比一下。如果只是最后几个字符不同大概率是Base64的变体如果整张表都不一样那更要依赖脚本解码别硬记表。4.4 运行验证脚本跑完之后屏幕上会输出减下标后的中间字符串和解码后的flag。把flag复制到BUUCTF平台提交能通过就说明整条分析链路是对的。这里我不直接贴出最终flag原因有两个一是BUUCTF这类题最好自己跑一遍脚本印象会深很多二是如果你拿到的样本字符表跟我的不完全一样直接抄答案反而容易被带偏。跑脚本这个动作本身就是逆向入门练习的一部分。如果你跑出来的字符串不是flag开头那多半是下面两种问题字符表复制错了或者下标偏移写反了。这两种问题都很常见排查方式我在下一节详细说。5. 常见问题与踩坑实录5.1 常见问题速查表问题现象可能原因解决办法F5无法反编译某个函数函数栈不平衡或IDA分析失败在Options里关闭某些启发式分析或手动看汇编逻辑找不到主函数函数名被编译器修改用ShiftF12字符串窗口找“please input:”的交叉引用减完下标后的字符串出现非ASCII字符减下标方向反了或者下标起始值不对检查循环变量i从0还是从1开始确保是ord(c)-i不是ord(c)ibase64.b64decode报Invalid base64-encoded string字符表不是标准表或长度不是4的倍数复制IDA中的真实字符表用自定义解码脚本长度不对时补解码出来是乱码Base64字符表复制错重新检查sub_4110BE引用的数据段确认开头字符strcmp下断点后找不到比较内容32位程序参数可能在栈上或寄存器中用x64dbg或OD查看strcmp的两个参数地址再在内存窗口跳转本地脚本能解出flag但平台不认可能flag格式不完整或者平台要求额外前缀检查输出是否包含完整的flag{...}不要漏字符这份速查表里的问题我在刷题过程中基本上都踩过一遍。尤其是Base64字符表的问题第一次遇到的时候很容易被“Base64就是标准那套”的惯性思维带偏白白浪费好多时间。5.2 新手容易忽略的细节有几个细节虽然不影响最终答案但新手在做题时很容易卡住。第一个细节是scanf(%20s, input)。这里的20是输入长度上限说明程序最多读入20个字符。这个限制不是考点但它告诉我们flag长度不会超过20个字符。如果你的还原结果明显超长那大概率是哪里分析错了。第二个细节是伪代码里的v8或cipher缓冲区长度是50但实际密文只有16个字符左右。多余的空间可能有脏数据不用管。strcmp比较的是字符串遇到\0就停止所以只比较有效字符。第三个细节是sub_4110BE的参数个数在不同IDA版本里看起来不一样。有的版本显示两个参数有的版本显示一个参数。这其实不影响解题思路只要确认Base64编码后的结果写到了比较缓冲区里就行。遇到参数个数对不上的情况不用慌看伪代码时认准结果流向比较的那个字符串就够了。第四个细节是动态调试辅助。reverse3的静态分析链路已经很完整不需要强行动态调试。但如果你想验证自己分析的密文是否正确可以在strcmp处下断点程序运行到比较前会停住然后看两个缓冲区的内容。32位程序在x64dbg里strcmp的参数在栈上找到两个参数地址后在内存窗口跳过去就能看到实际比较的字符串。这个操作不难但对理解“程序到底比的是什么”非常有帮助。5.3 一个排查命令行的经验写脚本还原的时候如果你不想每次都在循环里手动拼接字符串可以顺手用一下Python的ord和chr组合。这个组合是逆向脚本里的常客几乎每个字符变换题都会用到。多写几道题之后你甚至不用刻意去记看到ord、chr、base64这几个关键词就能条件反射地搭建脚本框架。还有一种排查思路是正向验证。如果你觉得脚本写对了但又不敢确定可以拿一个已知字符串走一遍加密流程看结果是不是题目里的密文。比如把flag{test}这种测试值手动做一遍Base64加下标看看能不能得到和目标密文结构相似的结果。这种正推验证虽然不能直接给出答案但能帮你确认自己对加密流程的理解没有偏差。最后说点刷题体会reverse3这道题我在不同阶段刷过两遍第一遍是被网上WriteUp带着走脚本一跑答案一出感觉自己会了第二遍是自己从查壳开始一步步分析才真正理解Base64变体题的核心不在“解码”而在“识别字符表”。这种区别只有亲手在IDA里点开sub_4110BE、看到那张表的时候才体会得到。如果你现在刚入门reverse我的建议是不要先看答案先自己走一遍完整流程。查壳、拖IDA、找字符串、看伪代码、认算法、写脚本哪怕中间卡住去查资料也比直接抄答案有价值得多。reverse3这道题最大的价值不是那个flag而是让你建立一套属于自己的逆向分析流程。流程顺了后面刷更复杂的题会轻松很多。

相关推荐

5G NR SIB2详解:小区重选参数、信令分析与调优实践
5G NR SIB2详解:小区重选参数、信令分析与调优实践

简介:这份文档系统讲解5G(NR)网络中系统消息SIB2的构成与主要字段,面向5G网络优化工程师、运维人员及通信专业学习者。SIB2属于开放系统信息(OSI),承载同频、异频和异系统小区重选所需的关键参数,其发送分为… · 2026/9/25 12:25:21

从免费CRM到自建系统:中小团队选择与部署DeskcommCRM实践记录
从免费CRM到自建系统:中小团队选择与部署DeskcommCRM实践记录

工作室的客户名片越堆越多,销售记录散落在Excel和微信聊天记录里,我跟了两个月的单子因为忘了跟进被同行截走。那天我在办公室拍桌子说必须上CRM。市面上免费CRM注册即用,但用起来处处是坑;有人建议我自己搞个"私人网站"… · 2026/9/25 12:25:21

华为路由器三层状态诊断:硬件-系统-业务健康检查法
华为路由器三层状态诊断:硬件-系统-业务健康检查法

1. 项目概述:为什么“看懂路由器状态”比“配通网络”更关键?华为路由器不是插上电就能当摆设的盒子,它是一台实时运转的嵌入式计算机——CPU在跑、内存在调度、温度在变化、电源在波动、接口在收发数据包。很多工程师一上来就猛敲display ip… · 2026/9/25 12:25:15

PLSQL Developer连接Oracle报OCI.dll错误的完整解决方案
PLSQL Developer连接Oracle报OCI.dll错误的完整解决方案

简介:本资源是面向Oracle数据库初学者与开发人员的PL/SQL Developer连接实战配置包,聚焦解决轻量级客户端环境下高效连接远程Oracle数据库的核心问题。压缩包内含45个文件,涵盖20个关键DLL动态库(如oci.dll、oraociei11.dll&#… · 2026/9/25 13:06:08

LeanCTX配置与故障排查终极指南:每一把调优杠杆与doctor诊断清单
LeanCTX配置与故障排查终极指南:每一把调优杠杆与doctor诊断清单

LeanCTX配置与故障排查终极指南:每一把调优杠杆与doctor诊断清单 【免费下载链接】lean-ctx LeanCTX — Context Intelligence for AI systems. 项目地址: https://gitcode.com/gh_mirrors/le/lean-ctx LeanCTX(Lean Context)是一款本… · 2026/9/25 13:06:02

MicYou主题定制指南:Material 3动态取色、袖珍模式与多语言一键切换
MicYou主题定制指南:Material 3动态取色、袖珍模式与多语言一键切换

MicYou主题定制指南:Material 3动态取色、袖珍模式与多语言一键切换 【免费下载链接】MicYou MicYou is a powerful tool that turns your Android device into a high-quality microphone for your PC. 项目地址: https://gitcode.com/gh_mirrors/mi/MicYou … · 2026/9/25 13:06:02

n8n:开源自动化工作流平台自托管部署与实战
n8n:开源自动化工作流平台自托管部署与实战

这一期“一天一个强大的网站”不打算推荐一个你打开收藏就再也不用的效率工具,而是推荐一个真正值得跑在你自己服务器上的开源项目:n8n。如果你平常写代码,一定遇到过这类场景:外部系统回调了一个业务事件,需要清洗、转… · 2026/9/25 13:05:56

DeepSeekHarness(番外01):MCP与Skill配置不再手改YAML,一条命令接入15个服务器
DeepSeekHarness(番外01):MCP与Skill配置不再手改YAML,一条命令接入15个服务器

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

Windows 10麦克风权限失效的三层根因与修复指南
Windows 10麦克风权限失效的三层根因与修复指南

1. 这不是权限开关失灵,而是Windows 10隐私架构的“默认拒绝”逻辑在生效 你点开“设置→隐私→麦克风”,明明把“允许应用访问你的麦克风”滑块拉到了最右边,可Zoom、腾讯会议、甚至系统自带的语音识别依然提示“麦克风被禁用”&#xff1b… · 2026/9/25 13:05:43

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

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

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

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

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

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

了解更多?预约专属演示

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

企业微信二维码