yonkies_crackme2 writeup 及代码复用(编译成动态链接库)的技巧
字数 1562 2025-08-30 06:50:12

yonkies_crackme2 逆向分析与代码复用技术详解

1. 程序概述

yonkies_crackme2 是一个包含AES加密算法的逆向分析题目,主要考察对标准加密算法的识别能力和二进制层面的代码复用技巧。

2. 主程序分析

2.1 主函数结构

主函数逻辑清晰:

  1. 使用fgets从文件中读取一行输入
  2. 调用FUN_00401280进行正则表达式匹配
  3. 调用FUN_00401312从正则匹配结果中提取数据
  4. 调用CheckNameKey函数进行用户名和密钥的校验

2.2 关键校验函数 CheckNameKey

2.2.1 密钥预处理

  • 将输入的密钥字符串转换为16进制字节串

2.2.2 密钥初步校验

  • 检查前12字节的和:
    • 和的低8位必须等于key[15]
    • 和的高8位必须等于key[14]

2.2.3 固定随机数生成

srand(0x12345678);  // 固定种子

生成的固定随机数序列:
[0xE9,0x3F,0x0D,0xA1,0x96,0x95,0x31,0x04,0x49,0x2D,0x9E,0x61,0x83,0xCF,0x09,0x6F]

2.2.4 AES解密过程

  • FUN_00408d24: AES密钥扩展函数
  • FUN_0040a1b6: AES解密函数

2.2.5 解密后数据校验

解密后的16字节数据结构:

字段 长度 说明
username hash 4 用户名通过FUN_00401000计算的hash值
feature 2 16位特性标志,每bit代表一个特性
magic 2 固定值0x1979
序列号 4 任意值,仅展示
填充 4 用于满足密钥校验条件

3. 密钥生成(Keygen)实现

3.1 密钥生成思路

  1. 构造16字节明文数据
  2. 使用固定密钥进行AES加密
  3. 暴力尝试填充字段,使加密结果满足校验条件

3.2 C语言实现注意事项

  1. 架构问题:32位程序在64位系统上运行需要i386子系统
  2. AES库更新:需要添加aes_ni.h支持硬件加速
  3. ASLR问题:32位程序在amd64架构下可能需要关闭ASLR

3.3 Python实现方案

3.3.1 代码复用技术

FUN_00401000编译为32位共享库(.so),通过以下方式调用:

  1. 使用32位Python通过ctypes加载
  2. 64位Python通过subprocess调用32位Python获取结果

3.3.2 实现步骤

  1. namehash.asm:将目标函数编译为共享库
section .text
global function
function:
    ; 函数汇编代码
    ret

编译命令:

nasm -f elf32 namehash.asm -o namehash.o
gcc -m32 -shared namehash.o -o libnamehash.so
  1. namehash.py:32位Python调用库
from ctypes import CDLL, c_char_p, c_uint32

lib = CDLL('./libnamehash.so')
lib.function.restype = c_uint32

def get_name_hash(username):
    return lib.function(c_char_p(username.encode()))
  1. keygen.py:主程序
import subprocess

def get_name_hash_32bit(username):
    cmd = ['python3.4', 'namehash.py', username]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    return int(proc.stdout.read())

# 其余AES加密和校验逻辑

4. 二进制代码复用技术深入

4.1 适用条件

  1. 函数代码只使用寄存器和栈变量
  2. 无外部变量访问或远程调用
  3. 明确知道函数参数和返回值类型

4.2 实现方案比较

方案1:直接二进制加载

  • 优点:同一进程内调用,效率高
  • 缺点:需要处理重定位,仅适合简单算法

方案2:进程注入

  • 优点:通用性强
  • 缺点:受操作系统和架构限制

4.3 实践建议

  1. 优先考虑将目标函数编译为共享库
  2. 跨架构时使用进程间通信
  3. 复杂函数建议重写而非复用

5. 总结

通过分析yonkies_crackme2,我们学习了:

  1. AES标准算法的识别技巧
  2. 二进制层面的代码复用方法
  3. 跨架构编程的解决方案
  4. 密钥生成器的实现思路

关键点:

  • 注意标准算法的特征常数
  • 代码复用要考虑架构兼容性
  • Python的subprocess是解决跨架构问题的有效工具

6. 参考资源

  1. Brian Gladman's AES实现
  2. NASM汇编器文档
  3. Python ctypes官方文档
yonkies_ crackme2 逆向分析与代码复用技术详解 1. 程序概述 yonkies_ crackme2 是一个包含AES加密算法的逆向分析题目,主要考察对标准加密算法的识别能力和二进制层面的代码复用技巧。 2. 主程序分析 2.1 主函数结构 主函数逻辑清晰: 使用 fgets 从文件中读取一行输入 调用 FUN_00401280 进行正则表达式匹配 调用 FUN_00401312 从正则匹配结果中提取数据 调用 CheckNameKey 函数进行用户名和密钥的校验 2.2 关键校验函数 CheckNameKey 2.2.1 密钥预处理 将输入的密钥字符串转换为16进制字节串 2.2.2 密钥初步校验 检查前12字节的和: 和的低8位必须等于key[ 15 ] 和的高8位必须等于key[ 14 ] 2.2.3 固定随机数生成 生成的固定随机数序列: [0xE9,0x3F,0x0D,0xA1,0x96,0x95,0x31,0x04,0x49,0x2D,0x9E,0x61,0x83,0xCF,0x09,0x6F] 2.2.4 AES解密过程 FUN_00408d24 : AES密钥扩展函数 FUN_0040a1b6 : AES解密函数 2.2.5 解密后数据校验 解密后的16字节数据结构: | 字段 | 长度 | 说明 | |------|------|------| | username hash | 4 | 用户名通过 FUN_00401000 计算的hash值 | | feature | 2 | 16位特性标志,每bit代表一个特性 | | magic | 2 | 固定值0x1979 | | 序列号 | 4 | 任意值,仅展示 | | 填充 | 4 | 用于满足密钥校验条件 | 3. 密钥生成(Keygen)实现 3.1 密钥生成思路 构造16字节明文数据 使用固定密钥进行AES加密 暴力尝试填充字段,使加密结果满足校验条件 3.2 C语言实现注意事项 架构问题 :32位程序在64位系统上运行需要i386子系统 AES库更新 :需要添加 aes_ni.h 支持硬件加速 ASLR问题 :32位程序在amd64架构下可能需要关闭ASLR 3.3 Python实现方案 3.3.1 代码复用技术 将 FUN_00401000 编译为32位共享库(.so),通过以下方式调用: 使用32位Python通过ctypes加载 64位Python通过subprocess调用32位Python获取结果 3.3.2 实现步骤 namehash.asm :将目标函数编译为共享库 编译命令: namehash.py :32位Python调用库 keygen.py :主程序 4. 二进制代码复用技术深入 4.1 适用条件 函数代码只使用寄存器和栈变量 无外部变量访问或远程调用 明确知道函数参数和返回值类型 4.2 实现方案比较 方案1:直接二进制加载 优点 :同一进程内调用,效率高 缺点 :需要处理重定位,仅适合简单算法 方案2:进程注入 优点 :通用性强 缺点 :受操作系统和架构限制 4.3 实践建议 优先考虑将目标函数编译为共享库 跨架构时使用进程间通信 复杂函数建议重写而非复用 5. 总结 通过分析yonkies_ crackme2,我们学习了: AES标准算法的识别技巧 二进制层面的代码复用方法 跨架构编程的解决方案 密钥生成器的实现思路 关键点: 注意标准算法的特征常数 代码复用要考虑架构兼容性 Python的subprocess是解决跨架构问题的有效工具 6. 参考资源 Brian Gladman's AES实现 NASM汇编器文档 Python ctypes官方文档