CTF BUUOJ [DASCTF NOV X联合出题人2022年度积分榜争夺赛!]easy_hash Writeup

CTF BUUOJ [DASCTF NOV X联合出题人2022年度积分榜争夺赛!]easy_hash Writeup —— 多项式与自定义哈希的逆向之旅

一、题目信息

  • 题目名称:easy_hash
  • 比赛名称:DASCTF NOV X联合出题人2022年度积分榜争夺赛
  • 题目描述:闲来无事自己写了个hash,你能看懂嘛?
  • 附件interesting.py

二、题目分析

拿到题目后,首先分析附件代码的逻辑。题目主要包含两个核心函数:myhashencode

1. 自定义哈希函数 myhash

这个函数是题目的核心难点之一,它的处理流程如下:

defmyhash(x): res =[] end =b"" bytescipher = long_to_bytes(x)# 1. 处理前缀(无法被8整除的剩余部分) a = bytescipher[:len(bytescipher)%8] res.append(a) res.append(long_to_bytes(crc32(a)))# 添加前缀的CRC32校验值# 2. 处理8字节对齐的数据块 t =(len(bytescipher)//8) bytescipher = bytescipher[len(bytescipher)%8:]for i inrange(t): a = bytescipher[i*8:i*8+8] res.append(a) res.append(long_to_bytes(crc32(a)))# 添加每个数据块的CRC32校验值# 3. 拼接并转换for i in res: end += i res = bytes_to_long(end)# 4. 截断操作 res =(res +(res >>500))&2**(500)-1return res 

关键点分析

  • 输入数据被分割成若干块:一个长度为 len % 8 的前缀,和若干个 8 字节的块。
  • 输出结构为:[前缀] + [CRC32(前缀)] + [块1] + [CRC32(块1)] + ...
  • 最后进行了截断,保留了低500位。

2. 加密函数 encode

defencode(pt): a=[] b=[] a.append(myhash(pt))for i inrange(3): a.append(myhash(a[i]))# 构建哈希链# 计算多项式for j inrange(4): secret=(a[0]+ a[1]* a[j]+ a[2]* a[j]**2+ a[3]* a[j]**3)% P b.append([a[j],secret])return b 

逻辑梳理

  • a[0] = myhash(flag)
  • a[1] = myhash(a[0])
  • a[2] = myhash(a[1])
  • a[3] = myhash(a[2])
  • 对于每个 a[j],计算多项式 f(x)=a0+a1x+a2x2+a3x3(modP)f(x) = a_0 + a_1 x + a_2 x^2 + a_3 x^3 \pmod Pf(x)=a0​+a1​x+a2​x2+a3​x3(modP)。

3. 已知条件

题目给出了 FLAG[1] 的值:

FLAG[1]=[a[1], secret]

其中 a[1] 和对应的 secret 是已知的整数,大质数 P 也是已知的。

三、解题思路

步骤一:计算哈希链的后续节点

题目给出了 a[1],根据哈希链的迭代关系:

  • a[2]=myhash(a[1])a[2] = \text{myhash}(a[1])a[2]=myhash(a[1])
  • a[3]=myhash(a[2])a[3] = \text{myhash}(a[2])a[3]=myhash(a[2])
    我们可以直接计算出 a[2]a[3]

步骤二:反推 a[0]

我们知道多项式方程:
secret≡a[0]+a[1]⋅a[1]+a[2]⋅a[1]2+a[3]⋅a[1]3(modP)secret \equiv a[0] + a[1] \cdot a[1] + a[2] \cdot a[1]^2 + a[3] \cdot a[1]^3 \pmod Psecret≡a[0]+a[1]⋅a[1]+a[2]⋅a[1]2+a[3]⋅a[1]3(modP)
现在除了 a[0]a[0]a[0] 以外,其他所有变量都已知。我们可以轻松反推 a[0]a[0]a[0]:
a[0]≡secret−a[1]2−a[2]⋅a[1]2−a[3]⋅a[1]3(modP)a[0] \equiv secret - a[1]^2 - a[2] \cdot a[1]^2 - a[3] \cdot a[1]^3 \pmod Pa[0]≡secret−a[1]2−a[2]⋅a[1]2−a[3]⋅a[1]3(modP)

步骤三:验证并逆向 myhash

计算出 a[0] 后,我们可以验证 myhash(a[0]) 是否等于已知的 a[1]。如果相等,说明我们的计算正确。
接下来是最关键的一步:从 a[0] 逆向还原出原始的 flag。
观察 myhash 的结构,它是 [数据块] + [CRC32校验值] 的拼接。
由于 CRC32 是确定性的校验算法,且输出为 4 字节,我们可以尝试解析 a[0] 的字节流:

  1. 尝试不同的前缀长度(0-7字节)。
  2. 提取前缀后的 4 字节,验证是否为该前缀的 CRC32 值。
  3. 如果匹配,继续以“8字节数据 + 4字节CRC32”的格式解析后续数据。
  4. 校验所有块的 CRC32 值,如果全部正确,则提取出的数据块拼接起来即为原始 flag。

四、详细解题过程

1. 计算 a[2]a[3]

利用题目给出的 myhash 函数和已知的 a[1]

a2 = myhash(known_a1) a3 = myhash(a2)

2. 求解 a[0]

代入多项式方程求解:

# f(a[1]) = a[0] + a[1]*a[1] + a[2]*a[1]**2 + a[3]*a[1]**3 term1 = known_a1 * known_a1 term2 = a2 *(known_a1 **2) term3 = a3 *(known_a1 **3) a0 =(known_secret - term1 - term2 - term3)% P 

验证 myhash(a[0]) == known_a1,结果为 True,验证通过。

3. 逆向解析 Flag

得到的 a[0] 转换为字节后长度为 54 字节。我们编写脚本尝试解析:

a0_bytes = long_to_bytes(a0)# 尝试前缀长度从 0 到 7for prefix_len inrange(8):# ... 提取前缀和CRC ...# 验证 CRC32# ... 提取后续数据块并验证 ...

经过尝试,当前缀长度为 2 时,所有 CRC32 校验均通过:

  • 前缀 (2字节): DA
  • 数据块 1 (8字节): SCTF{th1
  • 数据块 2 (8字节): s_is_the
  • 数据块 3 (8字节): _fe3st_q
  • 数据块 4 (8字节): uest1on}
    拼接得到完整 Flag。

五、完整解题代码

from Crypto.Util.number import bytes_to_long, long_to_bytes from zlib import crc32 # ==================== 已知参数 ==================== P =93327214260434303138080906179883696131283277062733597039773430143631378719403851851296505697016458801222349445773245718371527858795457860775687842513513120173676986599209741174960099561600915819416543039173509037555167973076303047419790245327596338909743308199889740594091849756693219926218111062780849456373# 题目给出的 FLAG[1] known_a1 =1768672211043417187765307394749760760531160781992300779145800061219666992833602547480090118225665457075744297987672863699370162614965380859290914620736 known_secret =89139545215288033432210221492974990584987914397112840989583439688211128705545477536596587262069032020212762581490561288493533363888589066045095054475929099275247145877699370608950340925139625068446642116123285918461312297390611577025368805438078034230342490499137494400676347225155752865648820807846513044723# ==================== 函数定义 ====================defmyhash(x): res =[] end =b"" bytescipher = long_to_bytes(x) a = bytescipher[:len(bytescipher)%8] res.append(a) res.append(long_to_bytes(crc32(a))) t =(len(bytescipher)//8) bytescipher = bytescipher[len(bytescipher)%8:]for i inrange(t): a = bytescipher[i*8:i*8+8] res.append(a) res.append(long_to_bytes(crc32(a)))for i in res: end += i res = bytes_to_long(end) res =(res +(res >>500))&2**(500)-1return res # ==================== 解题步骤 ====================# Step 1: 计算哈希链后续节点 a2 = myhash(known_a1) a3 = myhash(a2)# Step 2: 从多项式方程反推 a[0]# f(a[1]) = a[0] + a[1]*a[1] + a[2]*a[1]**2 + a[3]*a[1]**3 term1 = known_a1 * known_a1 term2 = a2 *(known_a1 **2) term3 = a3 *(known_a1 **3) a0 =(known_secret - term1 - term2 - term3)% P # Step 3: 验证哈希链assert myhash(a0)== known_a1,"验证失败"# Step 4: 逆向解析 myhash a0_bytes = long_to_bytes(a0) flag_bytes =b""# 尝试不同的前缀长度for prefix_len inrange(8):iflen(a0_bytes)< prefix_len +4:continue prefix = a0_bytes[:prefix_len] prefix_crc = a0_bytes[prefix_len:prefix_len+4]if prefix_crc != long_to_bytes(crc32(prefix)):continue# 前缀CRC匹配,开始解析后续块 blocks =[prefix] pos = prefix_len +4 valid =Truewhile pos +12<=len(a0_bytes): block = a0_bytes[pos:pos+8] block_crc = a0_bytes[pos+8:pos+12]if block_crc == long_to_bytes(crc32(block)): blocks.append(block) pos +=12else: valid =Falsebreakif valid and pos ==len(a0_bytes): flag_bytes =b''.join(blocks)breaktry:print(f"Flag: {flag_bytes.decode()}")except:print(f"Flag (hex): {flag_bytes.hex()}")

六、运行结果

Flag: DASCTF{th1s_is_the_fe3st_quest1on} 

七、总结

这道题虽然名为 easy_hash,但考察的知识点比较全面:

  1. 代码审计能力:理解自定义哈希函数和加密流程。
  2. 代数基础:利用模运算性质求解多项式方程中的未知数。
  3. 逆向思维:在无法直接求逆的情况下,利用哈希函数的结构特性(数据+校验)进行数据还原。
  4. CRC32特性:理解CRC32是校验码而非加密算法,可用于数据完整性验证和结构解析。
    整体来说是一道质量很高的密码学题目,不仅考察了数学推导,还考验了对数据结构的分析能力。

Read more

国内 AI 编程 Coding Plan 深度调研报告(2026年2月)

国内 AI 编程 Coding Plan 深度调研报告(2026年2月) 概述 2025年下半年至2026年初,国内多家 AI 大模型厂商密集推出面向开发者的 Coding Plan 编程订阅套餐,以固定月费替代按 Token 计费的模式,让开发者可以在 Claude Code、Cursor、Cline 等主流编程工具中使用国产大模型。目前主流平台包括火山方舟(字节跳动)、阿里云百炼、MiniMax、Kimi(月之暗面)、智谱 GLM 五大家,以及新兴的**无问芯穹(Infini)**聚合平台。本报告将从套餐定价、支持模型、真实可用额度、用户口碑、使用稳定性和方便性等维度进行全面对比分析。[^1] 六大平台快速对比 平台入门价首月特惠核心模型用量机制套餐档位核心亮点火山方舟¥40/月¥8.91豆包·DeepSeek·

By Ne0inhk

人工智能与机器学习在软件工程中的应用:探索AL和ML技术如何改变软件的开发方式

作为一名正在深入学习软件工程的学生,近期我在完成课程项目时,对“人工智能与机器学习如何改变软件开发”这一主题进行了初步探索。随着调研的深入,我愈发意识到,AI与机器学习不再仅仅是软件所实现的功能特性,它们正在从根本上改变软件的生产方式。在此,我将自己的学习笔记与思考整理成文,希望能与社区的前辈和同学们交流探讨。鉴于本人学识尚浅,文中如有不当之处,恳请各位批评指正。 一、集成开发环境的智能化与软件质量保障的变革 传统的手工编码方式正在被AI赋能的新型开发工具所补充甚至取代,其中最为显著的便是集成开发环境的智能化转型。以GitHub Copilot、Amazon CodeWhisperer为代表的AI编程助手,已超越了传统的语法补全功能,它们能够基于上下文理解开发者的意图,实现从函数体自动补全到基于自然语言注释的代码生成,这种能力催生了“意图驱动开发”的雏形,开发者越来越多地将精力从语法细节转移到逻辑审查与架构设计上,人与机器的协作关系正在被重新定义。与此同时,在软件质量保障领域,机器学习技术的引入使得测试与缺陷预测变得更加精准和具有前瞻性,机器学习模型能够分析代码路径和执行逻辑,自

By Ne0inhk
黄仁勋力荐:OpenClaw不止是下一个ChatGPT,更是AI“动手时代”的破局者

黄仁勋力荐:OpenClaw不止是下一个ChatGPT,更是AI“动手时代”的破局者

在2026年GTC大会上,英伟达创始人兼CEO黄仁勋抛出了一个振聋发聩的判断:“OpenClaw绝对是下一个ChatGPT”。 这一评价并非夸大其词,而是精准点出了AI产业的核心演进方向——从“被动回答”的语言交互,转向“主动行动”的任务执行。ChatGPT开启了大语言模型(LLM)的普及时代,让AI具备了理解和生成人类语言的能力,但它始终停留在“军师”的角色,只能提供方案建议;而OpenClaw的出现,彻底打破了这一局限,将AI变成了能动手干活的“数字员工”,完成了AI从“认知”到“执行”的关键跃迁,成为连接AI能力与现实场景的核心桥梁。 下面我将从技术本质出发,拆解OpenClaw的核心架构、关键技术实现,结合代码示例、架构图与流程图,深入解析其如何实现“行动型AI”的突破,以及为何能被黄仁勋寄予厚望,成为AI产业的下一个里程碑。 一、认知跃迁:从“回答型AI”到“行动型AI”的本质区别 要理解OpenClaw的价值,首先需要明确它与ChatGPT这类“回答型AI”的核心差异。

By Ne0inhk
OpenClaw WebSocket Channel开发实战:从零打造自定义 AI 通信通道

OpenClaw WebSocket Channel开发实战:从零打造自定义 AI 通信通道

🎯 项目背景 为什么做这个项目? 最近 OpenClaw 特别火🔥,这是一个强大的个人 AI 助手网关,支持接入 WhatsApp、Telegram、Discord 等 15+ 个消息平台。作为一个技术爱好者,我决定深入学习一下它的架构设计。 学习目标: * ✅ 理解多通道 AI 网关的架构模式 * ✅ 掌握 OpenClaw 插件化开发技能 * ✅ 实践 WebSocket 实时双向通信 * ✅ 为社区贡献一个实用的教学案例 项目定位:这不是一个生产级项目,而是一个学习性质的教学案例,帮助其他开发者快速上手 OpenClaw 插件开发。 技术栈 前端层:Vue 3 + WebSocket ↓ 服务端:Python + aiohttp + uv ↓ 通道层:Node.js + ws + OpenClaw Plugin SDK

By Ne0inhk