记一次手动脱壳

文件可以查到是一个壳,不过并不是常见的UPX壳,可以发现是一个压缩壳,所以我们可以尝试使用ESP定律脱壳。

切换到调试器,可以发现在程序入口处又PUSHAD和PUSHFD保存目前的寄存器,脱壳完成后就会还原寄存器,我们就往栈那边下一个访问断点

继续执行行,停在了

再前进一步

猜测这个就是OEP,我们直接dump出来(一定要重建导入表),然后拖入IDA

根据字符串定位到函数:

明显有混淆,处理一下

逻辑很清晰,核心就是一个tea的加密,这个我们查看这个tea加密函数,可以发现是被混淆了的

混淆出现在call $+5上,这个混淆很有意思,call $+5意思是目前字节的后5个字节作为起始位置,call一次,call操作会压入返回地址(就是add语句的地址),返回地址就在栈顶,call指令的长度恰好就是5,于是直接就到了add语句,add语句实现的就是把栈顶元素的值+7,var_44的值是-44,操作之后call的返回地址就变成了

00EC10E7 由于混淆,这里变成了奇奇怪怪的指令

在动调中可以观察的更加明显

我们将其复原

这是个复杂的加密,根据已知信息复原一下

遇到这种复杂加密,我们就看数据在哪里被修改了就行,干扰操作不需要关注

分析一下,就能发现一个很像tea加密的结构:

相关的数据操作也只在这里发生了,基本可以断定这里就是加密的地方,其他的不用关注。

除了加密,我们还需要知道循环了多少次,for循环里面的条件非常复杂,我们可以看n0x26 ,里面有大于等于38就跳出的判断,可以猜测就是38次,不放心可以动调测试。

具体逆向过程可以直接丢给AI解

现在可以写出逆向代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
void __cdecl init_tea_pwd(int tea_pwd) {
int i; // [esp+0h] [ebp-4h]

for (i = 0; i < 4; ++i)
*(_DWORD*)(tea_pwd + 4 * i) = rand();
}
#define C 0x114514u
#define ROUNDS 0x26u // 38

static inline uint32_t rol32(uint32_t x, unsigned int r) {
r &= 31;
return (x << r) | (x >> (32 - r));
}

static inline uint32_t ror32(uint32_t x, unsigned int r) {
r &= 31;
return (x >> r) | (x << (32 - r));
}

unsigned int* __cdecl tea_decrypt(unsigned int* a2, unsigned int* a3, const unsigned int* key) {
uint32_t v0 = *a2; // 对应原来的 v10
uint32_t v1 = *a3; // 对应原来的 v9

// 加密结束时 v11 = ROUNDS * C
uint32_t v11 = ROUNDS * C; // 38 * 0x114514

for (int r = ROUNDS - 1; r >= 0; --r) {
// ---------- 先逆第二步(还原 v1) ----------
uint32_t s2 = v11; // v11_after
uint32_t rot2 = s2 % 5u + 1u;
uint32_t k1 = key[(s2 >> 11) & 3u];

uint32_t t2 = v0 + (((v0 >> 5) ^ (16u * v0)));
uint32_t mix2 = (k1 + s2) ^ t2;

// v1_new -> v1_old
v1 = ror32(v1, rot2) - mix2;

// ---------- 再逆第一步(还原 v0) ----------
uint32_t s = s2 - C; // v11_before
uint32_t rot1 = s % 7u + 1u;
uint32_t k0 = key[s & 3u];

uint32_t t1 = v1 + (((v1 >> 5) ^ (16u * v1)));
uint32_t mix1 = (k0 + s) ^ t1;

// v0_new -> v0_old
v0 = ror32(v0, rot1) - mix1;

// 更新 v11,为下一轮(更早一轮)使用
v11 = s;
}

*a2 = v0;
*a3 = v1;
return a3;
}
int main() {
srand(2025);
unsigned int enflag[10] = {
0xD7E2CB3E, 0x1FDAAA30, 0x795CC461, 0x935C4CE6, 0xF587B2C4, 0x71417D92, 0x30059C32, 0x8F07B51F,
0xB53BB9AA, 0x9B981529
};

_DWORD* tea_pwd =(_DWORD*) malloc(16);
init_tea_pwd((uint32)tea_pwd);

for(int i=8;i>=0;--i)
tea_decrypt(&enflag[i], &enflag[i+1], (uint32*)tea_pwd);

char buff[40] = {0};

for (int i = 0; i != 10; ++i) {
// data2[i / 4] = Str[i + 3] | (Str[i + 2] << 8) | (Str[i + 1] << 16) | (Str[i] << 24);
buff[i * 4] = BYTE3(enflag[i]);
buff[i * 4 + 1] = BYTE2(enflag[i]);
buff[i * 4 + 2] = BYTE1(enflag[i]);
buff[i * 4 + 3] = BYTE0(enflag[i]);
}
printf("%s", buff);
printf("\n");
//buff = 0x0038fc80 "ISCTF{XTEA_14_POFP_N0_0ne_Can_Beat!!!!!}...
free(tea_pwd);
return 0;

}

ISCTF{XTEA_14_POFP_N0_0ne_Can_Beat!!!!!}