【SWPUCTF2019】 easyRE 详细分析过程及解题脚本
字数 427 2025-08-29 08:30:36
SWPUCTF2019 easyRE 逆向分析详解
1. 程序概述
这是一个包含反调试保护和复杂加密逻辑的逆向工程挑战题,要求通过逆向分析找到正确的flag格式和内容。
2. 主要函数分析
2.1 主函数结构
int __cdecl main(int argc, const char **argv, const char **envp) {
_DWORD v4[28];
_DWORD *v5;
_DWORD *v6;
int v7;
char v8[108];
int v9;
if (sub_40EF90()) // 反调试检测
return 1;
sub_4026C0(0x6Cu);
sub_401FE0(v4[27], v5); // 初始化虚函数表和关键数据
v9 = 0;
v6 = v4;
sub_40F360(v8);
sub_40F080(v4[0], v4[1]);
v5 = v4;
sub_40F360(v8);
sub_40F150(argc, (int)argv); // 主要处理函数
v7 = 0;
v9 = -1;
sub_4021C0(v8);
return v7;
}
2.2 反调试检测函数
BOOL sub_40EF90() {
HANDLE v0;
NTSTATUS (__stdcall *NtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
HMODULE hModule;
int v4;
v4 = 0;
hModule = LoadLibraryA("Ntdll.dll");
NtQueryInformationProcess = (NTSTATUS (__stdcall *)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG))GetProcAddress(hModule, "NtQueryInformationProcess");
v0 = GetCurrentProcess();
NtQueryInformationProcess(v0, ProcessDebugPort, &v4, 4, 0);
return v4 != 0;
}
绕过方法:
- 直接nop掉检测后的跳转指令
- 先运行程序,再用调试器附加
2.3 初始化函数
_DWORD *__thiscall sub_401FE0(_DWORD *this) {
int i;
*this = &EASYRE::`vftable'; // 设置虚函数表
this[1] = 0;
// 初始化第一组比较数据(40字节)
*((_BYTE *)this + 52) = 8;
*((_BYTE *)this + 53) = 0xEA;
*((_BYTE *)this + 54) = 0x58;
*((_BYTE *)this + 55) = 0xDE;
*((_BYTE *)this + 56) = 0x94;
*((_BYTE *)this + 57) = 0xD0;
*((_BYTE *)this + 58) = 0x3B;
*((_BYTE *)this + 59) = 0xBE;
*((_BYTE *)this + 60) = 0x88;
*((_BYTE *)this + 61) = 0xD4;
*((_BYTE *)this + 62) = 0x32;
*((_BYTE *)this + 63) = 0xB6;
*((_BYTE *)this + 64) = 0x14;
*((_BYTE *)this + 65) = 0x82;
*((_BYTE *)this + 66) = 0xB7;
*((_BYTE *)this + 67) = 0xAF;
*((_BYTE *)this + 68) = 0x14;
*((_BYTE *)this + 69) = 0x54;
*((_BYTE *)this + 70) = 0x7F;
*((_BYTE *)this + 71) = 0xCF;
// 初始化第二组比较数据(20字节)
qmemcpy(this + 18, "03\"3 0 203\"$", 20);
sub_4030A0(this + 23);
sub_402DE0(this + 26);
for (i = 0; i < 40; ++i)
*((_BYTE *)this + i + 12) = 0;
return this;
}
2.4 主要处理函数
int sub_40F150(int a1, int a2, ...) {
va_list va;
va_start(va, a2);
sub_40F5B0(std::cout, "Please input your flag : ");
std::ostream::operator<<(v2, sub_40F8F0);
sub_40F930(std::cin, flag);
if (sub_4024B0(flag)) { // 关键校验函数
sub_40F5B0(std::cout, &unk_4122F0); // "Congratulations"
result = 1;
} else {
sub_40F5B0(std::cout, &unk_41231C); // "I'm sorry"
result = 0;
}
return result;
}
2.5 关键校验函数
BOOL __thiscall sub_4024B0(_DWORD *this, int flag) {
this[2] = flag;
result = 0;
if (sub_402500(flag)) { // 校验flag格式
sub_4026E0(); // 处理flag
if (sub_402A00()) // 比较结果
result = 1;
}
return result;
}
3. Flag格式校验
通过分析sub_402500函数,发现flag格式为:
^swpuctf\{\w{4}\-\w{4}\-\w{4}\-\w{4}\-\w{4}\}$
即:swpuctf{xxxx-xxxx-xxxx-xxxx-xxxx}
4. 核心加密逻辑
4.1 字符处理函数
int __thiscall sub_402730(_DWORD *this, int a2) {
// 获取flag的4个字符部分
v10 = this[2] + 5 * a2 + 8;
// 对每个字符进行处理
for (i = 0; i < 4; ++i) {
// 计算左移进位次数
v4 = *((_BYTE *)&v13 + v2);
_DL = v4;
__asm { rcl dl, 1 } // 循环左移直到进位
// 计算右边0的个数
do {
v6 = v4 & 1;
v4 = (v4 >> 1) | v8;
++v7;
} while (v6);
// 计算结果
res1 = 右边0的个数 + 左移进位次数;
res2 = (flag << 左移进位次数) & 0xff >> res1;
res3 = (flag << (8 - 0的个数)) | ((flag >> (8 - 左移次数)) << 左移次数);
}
return sub_402F80(&v13);
}
4.2 关键比较函数
int __thiscall sub_402A00(unsigned __int8 *this) {
for (i = 0; i < 40; ++i) {
if (this[i + 52] != this[i + 12])
return 0;
}
return 1;
}
5. 解题思路
-
分析加密过程:
- 每个字符独立处理,不受前后字符影响
- 对每个字符进行位移运算,生成3个结果
- 通过5次循环处理,每次处理4个字符
-
逆向算法:
- 根据已知的比较数据逆向推导字符
- 使用暴力破解方法尝试可能的字符组合
6. 解题脚本
import string
# 计算左移进位次数
def check_1(c):
num = 0
while True:
c = c << 1
num += 1
if c & 0x100:
return num
# 计算右边0的个数
def check_0(c):
num = 0
while True:
if c & 1:
return num
num += 1
c = c >> 1
# 生成三个结果
def generate_0(c):
res1 = check_0(c) + check_1(c)
res2 = ((c << check_1(c)) & 0xff) >> res1
res3 = ((c >> (8 - check_1(c))) << check_1(c)) | ((c << (8 - check_0(c))) & 0xff) >> res1
return [res1, res2, res3]
# 分类可能字符
def classify():
for_each = string.ascii_lowercase + string.ascii_uppercase + string.digits
second_part_res = '03"3 0 203"$'
res = {}
d = dict.fromkeys(list(set(second_part_res)))
for i in list(set(second_part_res)):
d[i] = []
for i in for_each:
tmp = check_part(ord(i))
if tmp:
d[tmp].append(i)
return d
# 检查字符是否符合第二部分
def check_part(c):
tmp = list(set('03"3 0 203"$'))
tmp2 = check_0(c) | (16 * check_1(c))
for i in tmp:
if tmp2 == ord(i):
return i
return ''
# 测试第一部分
def test_1(c, v14):
exam = {c: generate_0(ord(c))}
v14 = v14 - (8 - exam[c][0])
tmp = exam[c][1] << v14
return tmp, v14
# 测试第二部分
def test_2(c, v14):
exam = {c: generate_0(ord(c))}
v14 = v14 - exam[c][0]
tmp = exam[c][2] << v14
return tmp, v14
# 检查第一部分
def check_first_part(second_part, first_part):
for i in d[second_part[0]]:
for j in d[second_part[1]]:
for k in d[second_part[2]]:
for m in d[second_part[3]]:
v14 = 0x20
tmp, v14 = test_1(i, v14)
tmp2, v14 = test_1(j, v14)
tmp3, v14 = test_1(k, v14)
tmp4, v14 = test_1(m, v14)
tmp5, v14 = test_2(i, v14)
tmp6, v14 = test_2(j, v14)
tmp7, v14 = test_2(k, v14)
tmp8, v14 = test_2(m, v14)
tmp = tmp | tmp2 | tmp3 | tmp4 | tmp5 | tmp6 | tmp7 | tmp8
if tmp == first_part:
return i + j + k + m
d = classify()
s2 = '03"3 0 203"$'
s = ['08', 'EA', '58', 'DE', '94', 'D0', '3B', 'BE', '88', 'D4', '32', 'B6', '14', '82', 'B7', 'AF', '14', '54', '7F', 'CF']
flag = 'swpuctf{'
for i in range(0, 5):
first_part = int(s[3 + 4 * i] + s[2 + 4 * i] + s[1 + 4 * i] + s[4 * i], 16)
second_part = s2[i * 4 : i * 4 + 4]
res = check_first_part(second_part, first_part)
if i == 4:
flag += res
break
flag += res + '-'
flag += '}'
print("Flag:", flag)
7. 最终Flag
swpuctf{we18-l8co-m1e4-58to-swpu}