遇到需要密码的压缩包时,很多新手会直接放弃。但逆向工程中常见的"伪加密"技巧,往往就是突破口。我最近在CTF比赛中遇到一个典型例子:题目附件是个ZIP压缩包,表面看需要密码,实际上只是修改了加密标记位。
用010 Editor打开压缩包,找到全局加密标记位(通常位于0x09偏移处)。伪加密的特征是这个字节被故意改成了0x09,而实际上应该为0x00。修改后保存,就能直接解压出里面的32位EXE文件。这个EXE还套了ASP壳,用脱壳工具处理后会露出真面目。
动态调试才是重头戏。用x32dbg加载程序时,我发现调试器自动在系统断点处暂停。按F9运行到这个断点后,在关键跳转指令"jmp _unpacked.401280"处下新断点。跟进后发现程序在0x4012F0地址附近出现了"try again"字符串,这明显是核心验证逻辑所在。
通过Scylla插件dump出脱壳后的程序,用IDA分析更清晰。验证逻辑分三步:
逆向这个逻辑时,我最初用Python写解密脚本,但遇到中文输出问题。改用C语言后顺利得到flag:
c复制#include<stdio.h>
int main() {
int res[18] = { 0x6c,0x2f,0x30,0x31,0x32,0x33,0xffffffb6,0xffffffbf,0xffffffa0,0xffffffcf,0x7c,0x71,0x6a,0x6c,0x70,0x64,0x75,0x63 };
for (int i = 17; i>=0; i--) {
res[i] ^= 17-i;
printf("%c", res[i]);
}
printf("\n");
return 0;
}
这种题型的关键在于识别伪加密特征,以及耐心跟踪程序的关键跳转。很多CTF题目会故意把核心逻辑藏在看似普通的跳转之后。
逆向Python打包的exe是另一个常见场景。最近遇到一个用PyInstaller打包的题目,解包过程让我踩了不少坑。首先要用pyinstxtractor.py解包,这个工具能提取出exe中的pyc文件。
但直接反编译pyc文件可能会失败,我发现需要保证两个条件:
用uncompyle6反编译出源码后,看到了一个base58变种算法。这个b58encode函数做了三件事:
逆向时要注意base58的字符集是自定义的:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
解密脚本需要反向操作:
python复制def b58decode(tmp:str) -> str:
import binascii
base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
temp = []
for i in tmp:
temp.append(base58.index(i))
tmp = temp[0]
for i in range(len(temp)-1):
tmp = tmp * 58 + temp[i+1]
return binascii.unhexlify(hex(tmp)[2:].encode("utf-8")).decode("UTF-8")
check = ['A', '5', 'q', 'O', 'g', 'q', 'd', '\x7f', '[', '\x7f', 's', '{', 'G', 'A', 'x', '`', 'D', '@', 'K', 'c', '-', 'c', ' ', 'G', '+', '+', '|', 'x', '}', 'J', 'h', '\\', 'l']
for i in range(len(check)):
check[i]=chr(ord(check[i])^i)
print(b58decode("".join(c for c in check)))
实际逆向时最麻烦的是处理Python版本兼容性问题。不同版本的PyInstaller打包的exe,需要对应版本的Python环境来反编译。
迷宫类逆向题总是充满趣味性。最近遇到的这个题目,表面是个简单的12x12迷宫,但暗藏玄机。程序先用时间作为随机种子,然后在四个特定位置生成关键随机数:
python复制maze[1][1] = random.randint(987, 1000)
maze[3][4] = random.randint(345, 356)
maze[7][7] = random.randint(107, 116)
maze[11][8] = random.randint(833, 856)
这些随机数又作为其他位置的生成种子,使得整个迷宫的值充满随机性。但题目给出了关键约束:有效路径上的迷宫值必须在0到1234之间。
我的破解思路是:
python复制for i in range(12):
for j in range(12):
if maze[i][j]>=0 and maze[i][j]<=1234:
maze[i][j]=1
else:
maze[i][j]=0
print(maze[i])
得到清晰路径后,还需要处理随机数带来的验证问题。即使走对路径,由于迷宫值随机变化,可能需要多次尝试才能命中正确的SHA256校验值。我写了个爆破脚本来自动重试:
python复制from itertools import *
import subprocess
while(1):
flag="sdsdsddwwddsdddssaaassddddssasaaaaawwwaaasssdsdsdddddddd"
p = subprocess.Popen(["snake.exe"], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
p.stdin.write(flag.encode())
p.stdin.close()
out = p.stdout.read().decode()
p.stdout.close()
if "flag" in out:
print(out)
exit()
这种题型的关键在于识别出随机数影响的范围,以及找到不变的约束条件。迷宫类题目往往在路径约束中隐藏着突破口。
通过这三个典型案例,我总结出一套逆向工程的通用思考框架:
第一维度是文件分析:
第二维度是代码还原:
第三维度是算法逆向:
以迷宫题为例,完整解题流程应该是:
在实际CTF比赛中,逆向题目往往会在多个维度设置障碍。比如先用伪加密隐藏真实文件,再用壳保护代码,最后用随机数增加分析难度。掌握这种多维拆解思维,就能系统性地解决复杂逆向问题。