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

海软25校赛CTF-Reverse逆向:Come_on

今天有兴趣玩了一下海软25年CTF校赛的培训练习题,发现一道题非常有意思:

Come_on

请找出答案,提交flag格式:flag{xxxx}。

下载附件

啥都没有提示,非常简洁

老规矩,先丢进IDA看看:

看这个程序的主函数main是一个无限循环,不断调用fun函数。而fun函数正是之前分析的验证逻辑,它要求输入一个字符串,经过异或和变换后与预设的字节数组比较,若匹配则输出经过大小写转换后的字符串作为 flag,

进入fun函数看看

程序首先在main函数中进入一个循环,反复调用核心处理函数fun。在fun函数内部,程序的主要执行路径是长这样的:

  • 信息输入:通过printf提示 "enter a flag:",随后使用getchar()while循环中逐字符读取用户输入并存入数组v4,直到读取到换行符(ASCII 10)为止。

  • 初始化数据

    • 设置v4[0]为72(字符'H')。

    • 将硬编码的字符串"a7shw9o10e63nfi19dk"拷贝至v3数组。

  • 加密算法:程序包含多层嵌套循环(由变量i,j,k控制),对输入的数据进行异或(XOR)运算。关键特征包括:

    • 使用了固定值0x53进行异或。

    • v3(初始字符串)作为动态密钥流参与运算。

    • 存在基于下标的反馈机制,例如v4[(k-1)%9]

根据Pseudocode-A窗口的伪代码,基本判断出算法的几个核心阶段:

阶段伪代码行号范围逻辑描述
数据采集20-28将用户输入填充至v4,限制长度由v16记录。
预处理29 - 37设置起始字符并初始化v3密钥数组。
第一层变换38 - 41v4的内容传递给辅助处理数组v7
异或混淆42-47执行嵌套循环,其中v5 = v7[j] ^ v7[j+1]v5 ^= 0x53。这是典型的变换逻辑。
密钥流更新48 之后动态更新v3数组,使后续每一位的加密都依赖于之前的状态。

要获取正确的 Flag,这里需要写一个python逆向脚本来撤销这些无脑的操作,同时注意 v3 数组在循环中的动态变化,这是解密脚本中最容易出错的地方,我在此处出错n次才正确。。。

  • 确定目标比对值:在伪代码下方未显示的区域或通过Hex View查找与v4最终比对的字节数组。

  • 模拟密钥生成:由于v3的更新逻辑是确定的,可以按照正向逻辑复现密钥流。

  • 逆推异或运算:由于异或运算的自反性($A \oplus B = C \implies C \oplus B = A$),我们可以利用已知输出和0x53逆推出原始的v4字符。

  • 格式化输出:最后一段逻辑会对字符的大小写进行翻转(偶数位加 32 转小写,奇数位减 32 转大写),在解密脚本中需反向处理。

算法加密逻辑梳理

根据伪代码,设定输入字符串长度$v9 = 19$(索引最大为18),程序的加密流程可归纳为以下四个核心步骤:

  1. 第一重变换:v7_{orig}[i] = v4[i] \oplus v7_{orig}[i-1]

  2. 第二重变换:v7_{new}[j] = v7_{orig}[j] \oplus v7_{orig}[j+1] \oplus 0x53

  3. 动态密钥流生成:v3[k] = v3_{init}[k] \oplus v4[(k-1)\%9] \oplus v4[(k-1)\%7] \oplus v3[k-1]

  4. 最终异或与比对:v7_{final}[m] = v7_{new}[m] \oplus v3[m] = target[m]

数学逆向推导

利用异或运算的自反性,可以大幅化简复杂的链式依赖。由第一重变换可知,相邻两项v7_{orig}的异或结果恰好是原始输入字符:

v7_{orig}[j] \oplus v7_{orig}[j+1] = v4[j+1]

将此结论直接代入第二重变换公式中,即可消除对v7_{orig}状态的依赖:

v7_{new}[j] = v4[j+1] \oplus 0x53 \implies v4[j+1] = v7_{new}[j] \oplus 0x53

结合第四重比对逻辑v7_{new}[j] = target[j] \oplus v3[j],可得到最终的线性解密公式

v4[j+1] = target[j] \oplus v3[j] \oplus 0x53

顺序数据解密

观察密钥流的生成递推式:在计算$v3[k]$时,其依赖的输入字符索引为(k-1)\%9与(k-1)\%7。这两个索引的值始终严格小于$k$。这意味着在任何给定的步骤$k$,所需的$v4$数据已经计算完毕,因此我们可以从头开始交替求解。

已知初始内存状态

  • 固定起始字符:v4[0] = \text{0x48 (H)}

  • 密钥流初值:v3[0] = \text{0x61 (a)}

  • 比对目标数据:target = \{0x47, 0x64, 0x18, 0x33, 0x10, 0x61, 0x51, 0x3B, 0x35, 0x5E, 0x63, 0x64, 0x1D, 0x74, 0x19, 0x4D, 0x60, 0x1B, 0x69\}

步进推导示例

  • 当$j=0$时:v4[1] = target[0] \oplus v3[0] \oplus 0x53 = 0x47 \oplus 0x61 \oplus 0x53 = 0x75 \text{ ('u')}

  • 当$k=1$时:v3[1] = v3_{init}[1] \oplus v4[0] \oplus v4[0] \oplus v3[0] = 0x37 \oplus 0 \oplus 0x61 = 0x56

  • 当$j=1$时:v4[2] = target[1] \oplus v3[1] \oplus 0x53 = 0x64 \oplus 0x56 \oplus 0x53 = 0x61 \text{ ('a')}

大概弄清楚了逆向逻辑,这里将逆向脚本编写工作交与AI Agent完成

def solve_flag(): # 初始化已知参数 v9 = 19 v4 = [0] * v9 v4[0] = ord('H') # 初始密钥流与目标比对数据 v3 = list(b"a7shw9o10e63nfi19dk") target = [71, 100, 24, 51, 16, 97, 81, 59, 53, 94, 99, 100, 29, 116, 25, 77, 96, 27, 105] # 核心解密循环:交替计算 v4 字符与下一轮 v3 密钥 for i in range(18): # 逆向推导 v4 v4[i + 1] = target[i] ^ v3[i] ^ 0x53 # 动态更新 v3 密钥流,为下一次循环做准备 k = i + 1 if k <= 18: v3[k] = v3[k] ^ v4[(k - 1) % 9] ^ v4[(k - 1) % 7] ^ v3[k - 1] # 尾部大小写格式化处理 for ii in range(0, v9, 2): # 偶数位大写转小写 if 64 < v4[ii] <= 90: v4[ii] += 32 # 奇数位小写转大写 if ii + 1 < v9: if 96 < v4[ii + 1] <= 122: v4[ii + 1] -= 32 # 拼接并输出最终 Flag flag_content = "".join(chr(c) for c in v4) print(f"flag{{{flag_content}}}") if __name__ == "__main__": solve_flag()

循环18 次后,可完整计算出底层的原始输入字符串:v4 = "Huan-y1n5-cAn5-5a1!"

最终格式化处理

在输出successful之后,程序对内存中的$v4$数组执行了最后一次位操作(大小写转换):

for ( ii = 0; ii < v9; ii += 2 ) { if ( v4[ii] > 64 && v4[ii] <= 90 ) v4[ii] += 32; if ( v4[ii + 1] > 96 && v4[ii + 1] <= 122 ) v4[ii + 1] -= 32; }

对推导出的Huan-y1n5-cAn5-5a1!执行该逻辑:

  • H(索引0,偶数) ->h

  • u(索引1,奇数) ->U

  • a(索引2,偶数,非大写) ->a

  • n(索引3,奇数) ->N

  • ...(依次处理剩余字符)

最终格式化后的字符串为:hUaN-Y1N5-cAn5-5a1!(拼音“欢迎参赛”的Leetspeak 变体)

最后拼接flag{},不得不说快赶上网鼎杯的强度了。此WP仅本人解题思路,欢迎讨论学习

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

相关文章:

  • linux sftp 设置了用户对目录有 0700权限,但在上传时报 Permission denied错误
  • C#进程和线程
  • 实战指南:如何用PyTorch实现DANN对抗迁移学习(附完整代码解析)
  • Allpairs工具与Deepseek联动实战:5分钟搞定正交表测试用例生成
  • STM32 CRYP硬件加密详解:CTR/GCM/CCM模式与中断恢复机制
  • 攻克股票数据接口难题:5个创新方案与底层原理
  • 高效3D模型编辑:NifSkope如何破解游戏开发中的格式兼容与效率难题
  • 华为交换机镜像端口配置进阶:基于ACL和MQC的流镜像详解
  • 网页设计毕业设计选题实战指南:从需求分析到可部署原型的全流程实现
  • MogFace工具完整使用指南:侧边栏上传+双列对比+原始数据查看
  • UE4 Niagara粒子碰撞实战:从参数解析到游戏特效优化(附常见问题解决方案)
  • 深度学习入门全解析:从核心概念到实战基础 | 技术研讨会精华总结
  • 如何用MATLAB高效处理医学影像RAW数据?512x512矩阵实战解析
  • 文墨共鸣效果展示:教育考试命题防重复系统|题干语义相似度阈值预警
  • 实战指南:基于快马平台构建高可用Copaw宠物服务官网
  • 360Controller安全机制全面解析:代码签名与系统扩展加载深度指南
  • 手把手教你部署MT5改写工具:30分钟搞定,文案润色不再难
  • nanobot实战案例:DevOps工程师用nanobot自动解析Jenkins日志报错原因
  • 高效全平台媒体采集工具:一站式无水印资源下载解决方案
  • python中Matplotlib模块介绍
  • StructBERT WebUI效果实测:支持GB2312/UTF-8编码自动识别与转换
  • 从凯撒密码到量子加密:密码学发展史中的5个关键转折点
  • GLM-OCR本地化部署详解:从OpenClaw社区获取灵感与支持
  • 避坑指南:bge-large-zh-v1.5部署中的5个常见错误及解决方法
  • 音频压缩新方案:用Qwen3-TTS-Tokenizer-12Hz实现低带宽高保真传输
  • Ollama进阶技巧:如何自定义ModelScope模型的量化精度与对话模板
  • LVGL 7.7.2 实战:如何用ARC样式打造炫酷进度条(附完整代码)
  • GME-Qwen2-VL-2B-Instruct网络故障排查助手:分析ping, tracert命令输出图
  • Vue3-Admin-Template:构建企业级管理系统的高效解决方案
  • 视频瘦身利器:让每个人都能轻松掌控文件大小的智能压缩方案