尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

BUUCTF reverse3-学习笔记

BUUCTF reverse3-学习笔记
📅 发布时间:2026/7/28 13:39:18
    • 前置知识
    • 正文
      • 一、UPX脱壳
      • 二、IDA静态分析
      • 三、编写逆向脚本,获取flag

前置知识

BUUCTF 新年快乐-学习笔记

正文

一、UPX脱壳

使用exeinfope检查文件是否有壳:

没有壳。

二、IDA静态分析

用IDA软件打开,shift+F12,CTRL+F搜索fl,点击进去看看:

CTRL+X查看谁调用了这个地址:

F5反编译一下,分析伪代码:

用AI 分析一下:

// 主函数 main_0// __cdecl:C 标准调用约定,参数从右往左入栈,调用者负责清理栈int__cdeclmain_0(intargc,constchar**argv,constchar**envp){size_tv3;// eax,保存字符串长度constchar*Source;// eax,保存处理后的字符串指针size_tMaxCount;// eax,保存最终比较的字符串长度charv7;// [esp+0h] [ebp-188h],临时字符变量,实际用于函数调用传参charv8;// [esp+0h] [ebp-188h],另一个临时字符变量,用于函数调用传参signedinti;// 循环变量 iintn0x64;// 循环变量 n0x64,用于初始化 Destination 数组signedintv11;// 保存 Destination 字符串长度charDestination[108];// 目标数组,用于存放变换后的用户输入charStr[28];// 用户输入缓冲区,最多读取 20 个字符_BYTE v14[8];// 8 字节临时数组,可能用于加密/编码函数的输出或上下文// ==============================// 第一部分:清空 Destination 数组// 将 Destination 的前 100 个字节置 0// ==============================for(n0x64=0;n0x64<100;++n0x64){// 边界检查:如果 n0x64 >= 100,报错if((unsignedint)n0x64>=0x64)j____report_rangecheckfailure();// Destination[n0x64] = 0// 清空数组,避免栈上垃圾数据影响后续逻辑Destination[n0x64]=0;}// ==============================// 第二部分:打印提示信息// sub_41132F("please enter the flag:", v7);// 这个函数大概率是 printf / puts 封装// 作用:输出 "please enter the flag:"// ==============================sub_41132F("please enter the flag:",v7);// ==============================// 第三部分:读取用户输入// sub_411375("%20s", (char)Str);// 这个函数大概率是 scanf / fgets 封装// %20s:最多读取 20 个字符,防止溢出// 用户输入会存入 Str 数组// ==============================sub_411375("%20s",(char)Str);/==============================// 第四部分:计算用户输入长度// ==============================v3=j_strlen(Str);// ==============================// 第五部分:调用 sub_4110BE 处理用户输入// Source = sub_4110BE(Str, v3, v14)// 参数:// Str:用户输入原始字符串// v3:字符串长度// v14:8 字节临时数组// 返回值:处理后的字符串指针// 这里 sub_4110BE 很可能是一个加密/编码/变换函数// ==============================Source=(constchar*)sub_4110BE(Str,v3,v14);// ==============================// 第六部分:将处理后的字符串拷贝到 Destination// strncpy(Destination, Source, 0x28u)// 0x28 = 40// 最多拷贝 40 个字符// ==============================strncpy(Destination,Source,0x28u);// ==============================// 第七部分:计算 Destination 的长度// ==============================v11=j_strlen(Destination)// ==============================// 第八部分:对 Destination 做逐字符变换// 第 0 个字符 += 0// 第 1 个字符 += 1// 第 2 个字符 += 2// ...// 第 i 个字符 += i// ==============================for(i=0;i<v11;++i)Destination[i]+=i;// ==============================// 第九部分:再次计算 Destination 长度// MaxCount = strlen(Destination)// ==============================MaxCount=j_strlen(Destination);// ==============================// 第十部分:与目标字符串 Str2 比较// if (!strncmp(Destination, Str2, MaxCount))// Str2 = "e3nifIH9b_C@n@dH"// 如果变换后的 Destination 等于 Str2,输出正确// ==============================if(!strncmp(Destination,Str2,MaxCount))sub_41132F("rigth flag!\n",v8);elsesub_41132F("wrong flag!\n",v8);return0;}

所以这个程序的逻辑是:

用户输入 Str ->sub_4110BE 处理,传给Source ->拷贝到 Destination ->Destination[i]+=i ->与"e3nifIH9b_C@n@dH"比较

所以,这道题的逆向流程就是:

目标密文"e3nifIH9b_C@n@dH"->每个字符 - i ->得到 Source ->再逆向 sub_4110BE ->得到原始 flag

所以重点看sub_4110BE函数 ,双击一下,进去看看:

进去发现sub_4110BE只是个跳板函数,因为第一行注释// attributes: thunk是 IDA 自动标记,代表这是跳板中转函数:

  • 本身没有任何加密运算逻辑,不处理数据;
  • 功能只有一件事:把收到的所有参数原封不动转发给sub_411AB0,并且把sub_411AB0的返回值直接向上返回。

等价通俗理解:

sub_4110BE = 传话员,自己不干活,直接把全部参数交给真正干活的sub_411AB0
在之前的代码里,
之前我们猜测sub_4110BE是加密函数,现在证实:
✅真正的加密 / 字符串变换逻辑全部在sub_411AB0内部!

整条调用链简化:

main_0 → sub_4110BE(跳板) → sub_411AB0(真实算法)

双击进入sub_411AB0函数,复行数十步,豁然开朗:

扔给AI分析,告诉你这是base64加密函数核心:

// Base64编码核心函数// p_Str:原始输入字符串指针// a2:原始字符串长度// a3:外部传入的8字节数组,函数内部把输出长度写入*a3void*__cdeclsub_411AB0(char*p_Str,size_ta2,_BYTE*a3){intv4;// [esp+D4h] [ebp-38h] 输出缓冲区下标,记录当前写到第几个字节intv5;// [esp+D4h] [ebp-38h] case1临时下标副本intv6;// [esp+D4h] [ebp-38h] case2临时下标副本intv7;// [esp+D4h] [ebp-38h] case3临时下标副本intn3;// [esp+E0h] [ebp-2Ch] 本轮循环读取到的原始字节数量(最多3字节)size_tv9;// [esp+ECh] [ebp-20h] 原始字节分组数,每3字节一组intv10;// [esp+ECh] [ebp-20h] Base64编码后的输出长度(4字节每组)intv11;// [esp+ECh] [ebp-20h] 剩余待处理原始字节数量void*v12;// [esp+F8h] [ebp-14h] malloc分配的Base64输出缓冲区char*p_Str_1;// [esp+104h] [ebp-8h] 原始字符串遍历指针// 合法性判断:空指针 或 输入长度为0,直接返回NULLif(!p_Str||!a2)return0;// ====== 计算Base64输出需要多大内存 ======v9=a2/3;// 完整3字节组数量if((int)(a2/3)%3)// 存在不足3字节的剩余数据++v9;v10=4*v9;// Base64规则:每组3字节原始数据编码为4字节*(_DWORD*)a3=v10;// 将最终编码长度写入外部传入的a3缓冲区v12=malloc(v10+1);// 分配内存,+1预留末尾'\0'字符串结束符if(!v12)// 内存分配失败,返回0return0;j_memset(v12,0,v10+1);// 将缓冲区全部清零p_Str_1=p_Str;// 遍历指针指向原始字符串起始v11=a2;// 剩余字节总数初始化为输入长度v4=0;// 输出缓冲区写入位置,从0开始// 循环处理所有原始字节,每次最多取出3字节while(v11>0){// algn_41A145、byte_41A144 是IDA全局数组// byte_41A144[0/1/2]:存放本轮取出最多3个原始字节// algn_41A145 是字节移位临时缓存(IDA反编译变量命名优化缺陷)algn_41A145[1]=0;algn_41A145[0]=0;byte_41A144[0]=0;// 循环读取最多3字节原始数据for(n3=0;n3<3&&v11>=1;++n3){byte_41A144[n3]=*p_Str_1;// 读取1字节存入缓存--v11;// 剩余字节-1++p_Str_1;// 原始指针向后移动}if(!n3)// 没有读到任何字节,退出循环break;// 根据本轮读到字节数量n3,分3种Base64编码情况switch(n3){// 场景1:只读到1个原始字节case1:// 第1个Base64字符:取原始字节高6bit*((_BYTE*)v12+v4)=aAbcdefghijklmn[(int)(unsigned__int8)byte_41A144[0]>>2];v5=v4+1;// 第2个Base64字符:原始字节低2bit + 填充0bit*((_BYTE*)v12+v5)=aAbcdefghijklmn[((algn_41A145[0]&0xF0)>>4)|(16*(byte_41A144[0]&3))];// 后两个字符填充 = (下标64)*((_BYTE*)v12+++v5)=aAbcdefghijklmn[64];*((_BYTE*)v12+++v5)=aAbcdefghijklmn[64];v4=v5+1;// 更新输出下标break;// 场景2:读到2个原始字节case2:*((_BYTE*)v12+v4)=aAbcdefghijklmn[(int)(unsigned__int8)byte_41A144[0]>>2];v6=v4+1;*((_BYTE*)v12+v6)=aAbcdefghijklmn[((algn_41A145[0]&0xF0)>>4)|(16*(byte_41A144[0]&3))];*((_BYTE*)v12+++v6)=aAbcdefghijklmn[((algn_41A145[1]&0xC0)>>6)|(4*(algn_41A145[0]&0xF))];// 末尾1个填充 =*((_BYTE*)v12+++v6)=aAbcdefghijklmn[64];v4=v6+1;break;// 场景3:完整读到3个原始字节(标准分组,无填充符)case3:*((_BYTE*)v12+v4)=aAbcdefghijklmn[(int)(unsigned__int8)byte_41A144[0]>>2];v7=v4+1;*((_BYTE*)v12+v7)=aAbcdefghijklmn[((algn_41A145[0]&0xF0)>>4)|(16*(byte_41A144[0]&3))];*((_BYTE*)v12+++v7)=aAbcdefghijklmn[((algn_41A145[1]&0xC0)>>6)|(4*(algn_41A145[0]&0xF))];*((_BYTE*)v12+++v7)=aAbcdefghijklmn[algn_41A145[1]&0x3F];v4=v7+1;break;}}*((_BYTE*)v12+v4)=0;// 在编码结果末尾写入字符串结束符'\0'returnv12;// 返回Base64编码后的字符串指针(即main中的Source)}

三、编写逆向脚本,获取flag

设密文 Cipher = “e3nifIH9b_C@n@dH”
第一步逆运算:抵消 Destination[i] += i
第二步逆运算:Base64 解码 Source
解码结果就是最终 flag

importbase64 cipher=b"e3nifIH9b_C@n@dH"source=bytearray()foridx,chinenumerate(cipher):source.append(ch-idx)b64_str=source.decode("ascii")print("正确Base64字符串:",b64_str)flag=base64.b64decode(b64_str)print("flag =",flag.decode())

相关新闻

  • LangChain v1.0+内存管理机制与实战优化
  • 把握前沿!AI教材编写攻略,利用AI工具高效完成专业教材编写
  • 深度学习入门:核心概念与实践指南

最新新闻

  • 计算机专业英语学习网站开发全栈指南
  • 5步实现Steam游戏自动破解的完整解决方案:专业级自动化DRM移除工具终极指南
  • 绝区零蕾米埃尔培养材料介绍 蕾米埃尔培养需要什么材料
  • 渔人的直感:FF14钓鱼计时器的终极指南
  • 网络安全职业转型指南:从零基础到高薪就业
  • 工业物联网设备低功耗电源管理方案设计与优化

日新闻

  • 力旷智能:伺服驱动系统在制药收瓶设备中的应用解析
  • 2026 网安入门避坑指南,零基础如何避开无效学习直接上手实战
  • 揭秘CFC项目:如何通过手机摄像头实现850kbps无网络文件传输

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号