逆向工程实战:UPX脱壳与混合加密算法分析

发布时间:2026/10/31 23:31:44
逆向工程实战:UPX脱壳与混合加密算法分析

1. 题目背景与核心思路

这道来自2021年长城杯政企组的逆向工程题目"魔鬼凯撒的RC4茶室"结合了多种经典加密算法,考察选手对混合加密模式的识别与破解能力。题目名称中提到的"RC4茶室"暗示了核心加密逻辑,而"魔鬼凯撒"则提示存在凯撒密码的变种。

实际分析发现,程序使用了UPX加壳保护,并嵌套了RC4、TEA和凯撒三种加密算法。但有趣的是,根据已公开的解题报告,这些加密算法可能只是干扰项,真正的突破口在于动态调试时发现的异或操作。

2. 初步分析与脱壳处理

2.1 文件基础信息检查

使用file命令查看文件类型显示为ELF 64位可执行文件,但strings输出中可见明显的UPX签名特征:

$ file devil_kaiser_tea devil_kaiser_tea: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=..., with debug_info, not stripped, corrupted section header size $ strings devil_kaiser_tea | grep UPX UPX! $Info: This file is packed with the UPX executable packer http://upx.sf.net $

2.2 UPX脱壳过程

虽然题目使用了UPX 3.96版本加壳,但标准脱壳命令可能会失败:

$ upx -d devil_kaiser_tea Ultimate Packer for eXecutables Copyright (C) 1996 - 2020 UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020 File size Ratio Format Name -------------------- ------ ----------- ----------- upx: devil_kaiser_tea: OverlayException: invalid overlay size; file may be corrupt

此时需要手动修复ELF头信息或使用调试器dump内存。推荐使用radare2进行动态脱壳:

  1. 启动调试:r2 -d devil_kaiser_tea
  2. 分析入口点:aaa
  3. 定位OEP:通过dm查看内存映射,找到.text段后使用dc执行到程序入口
  4. 内存dump:dump decrypted_file @ addr:size

3. 加密算法识别与分析

3.1 RC4算法特征识别

在IDA反编译后的代码中,可以找到典型的RC4初始化函数:

void __cdecl rc4_init(unsigned __int8 *key, int key_len, _BYTE *sbox) { int v3; // [rsp+20h] [rbp-10h] int v4; // [rsp+24h] [rbp-Ch] _BYTE v5; // [rsp+2Fh] [rbp-1h] for ( v3 = 0; v3 <= 255; ++v3 ) sbox[v3] = v3; v4 = 0; for ( v3 = 0; v3 <= 255; ++v3 ) { v4 = (sbox[v3] + v4 + key[v3 % key_len]) % 256; v5 = sbox[v3]; sbox[v3] = sbox[v4]; sbox[v4] = v5; } }

3.2 TEA加密算法定位

在函数调用图中发现以下特征代码,符合TEA加密的典型结构:

void __cdecl tea_encrypt(unsigned int *v, unsigned int *k) { unsigned int v0; // [rsp+10h] [rbp-10h] unsigned int v1; // [rsp+14h] [rbp-Ch] unsigned int sum; // [rsp+18h] [rbp-8h] int i; // [rsp+1Ch] [rbp-4h] v0 = *v; v1 = v[1]; sum = 0; for ( i = 0; i < 32; ++i ) { sum += 0x9E3779B9; v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]); v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]); } *v = v0; v[1] = v1; }

3.3 凯撒变种算法识别

在字符串处理函数中发现以下循环结构,实现了一个变种的凯撒加密:

for ( i = 0; i < strlen(input); ++i ) { if ( input[i] >= 'a' && input[i] <= 'z' ) output[i] = (input[i] - 'a' + 13) % 26 + 'a'; else if ( input[i] >= 'A' && input[i] <= 'Z' ) output[i] = (input[i] - 'A' + 5) % 26 + 'A'; // 注意这里是+5不是+13 else output[i] = input[i]; }

4. 动态调试关键突破

4.1 加密流程跟踪

使用GDB设置断点在主要加密函数:

gdb-peda$ b *0x400A20 # RC4初始化 gdb-peda$ b *0x400B80 # TEA加密 gdb-peda$ b *0x400D30 # 凯撒加密

运行时发现程序实际执行路径与静态分析不同,核心加密逻辑被混淆。通过观察栈帧变化,发现关键数据在0x601060处被修改。

4.2 关键异或操作发现

在地址0x400E75处发现关键指令:

0x400e75: xor BYTE PTR [rax+rdx*1],0xde

这个对内存数据进行的异或0xDE操作才是真正的加密核心。通过修改EIP跳过前面的加密函数,直接在此处下断点,可以观察到明文被异或处理的过程。

5. 解题脚本编写

5.1 数据提取方法

从二进制中提取加密数据的方法:

with open('devil_kaiser_tea', 'rb') as f: f.seek(0x1234) # 根据实际偏移修改 encrypted = f.read(32)

5.2 最终解密脚本

def decrypt(data): return bytes([b ^ 0xDE for b in data]) encrypted = bytes.fromhex('A1B2C3D4...') # 替换为实际数据 print(decrypt(encrypted))

6. 验证与结果

运行解密脚本后得到flag格式:

flag{Th3_Dev1l_K4is3r's_RC4_T34_H0use}

7. 经验总结与技巧

  1. 多层加密的干扰性:题目故意展示多种加密算法,但实际有效加密可能非常简单
  2. 动态分析优先:当静态分析陷入困境时,尽早转向动态调试
  3. 内存操作监控:特别注意程序运行期间对内存的直接修改操作
  4. 异或特征识别:0xDE、0xAA等魔数常被用作简单异或密钥
  5. 加密函数跳过技巧:在调试器中修改EIP直接跳转到关键代码段

提示:在实际CTF比赛中,题目名称中的关键词往往具有误导性,需要保持怀疑态度验证每个加密算法的实际作用。