Hideit

巨恶心的SMC,先逐步锁定主函数:

由于地址是随机加载的,所以就通过行数来找吧,

image-20211129005019172

image-20211129004528323

在最后这个176行步入

进入到TestEvil.dll中,但是没法反编译

跟着汇编走,发现在call后停下来接收输入,发现这样一个函数

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
__int64 __fastcall sub_1F60E221BB0(__int64 a1)
{
__int64 v2; // rbx
unsigned int v3; // er9
int v4; // esi
unsigned int v5; // er10
unsigned int v6; // edi
unsigned int v7; // er11
__int64 v8; // r8
__int64 v10; // [rsp+40h] [rbp-C0h] BYREF
char v11; // [rsp+48h] [rbp-B8h]
__int64 v12; // [rsp+50h] [rbp-B0h] BYREF
int v13[4]; // [rsp+60h] [rbp-A0h]
__int128 v14[2]; // [rsp+70h] [rbp-90h] BYREF
char v15; // [rsp+90h] [rbp-70h]
char v16[48]; // [rsp+A0h] [rbp-60h] BYREF
__int64 v17; // [rsp+D0h] [rbp-30h]
int v18; // [rsp+D8h] [rbp-28h]
int v19; // [rsp+DCh] [rbp-24h]
char v20[512]; // [rsp+E0h] [rbp-20h] BYREF
char v21[560]; // [rsp+2E0h] [rbp+1E0h] BYREF
int v22; // [rsp+528h] [rbp+428h] BYREF
int v23; // [rsp+530h] [rbp+430h] BYREF
__int64 v24; // [rsp+538h] [rbp+438h] BYREF

v2 = 0i64;
v15 = 0;
v14[0] = 0i64;
v24 = 0i64;
v14[1] = 0i64;
if ( !(unsigned int)off_1F60E223000(-2147483646i64, aSoftwareClasse, &v24) )
{
v23 = 0;
((void (__fastcall *)(char *, _QWORD, __int64))unk_1F60E222A0C)(v21, 0i64, 520i64);
v22 = 66;
if ( !(unsigned int)off_1F60E223008(v24, aKeysSecret, 0i64, &v23, v21, &v22) )
off_1F60E223020(0i64, 0i64, v21, 0xFFFFFFFFi64, v14, 260, 0i64, 0i64);
}
off_1F60E2230F8(aFirstSecretHer);
v10 = 0i64;
v11 = 0;
((void (__fastcall *)(void *, __int64 *))unk_1F60E221B50)(&unk_1F60E22324C, &v10);
v12 = 0i64;
strcpy((char *)&v12, (const char *)&v10);
v13[0] = 114;
v13[1] = 514;
v13[2] = 19;
v13[3] = 19;
((void (__fastcall *)(char *, _QWORD, __int64))unk_1F60E222A0C)(v20, 0i64, 512i64);
v3 = HIDWORD(v12);
v4 = 32;
v5 = v12;
v6 = HIDWORD(v12);
v7 = 0;
do
{
v7 -= 1640531527;
v8 = (v7 >> 2) & 3;
v5 += ((v7 ^ v3) + (v6 ^ v13[v8])) ^ (((16 * v6) ^ (v3 >> 3)) + ((v6 >> 5) ^ (4 * v3)));
v3 += ((v7 ^ v5) + (v5 ^ v13[v8 ^ 1])) ^ (((16 * v5) ^ (v5 >> 3)) + ((v5 >> 5) ^ (4 * v5)));
v6 = v3;
--v4;
}
while ( v4 );
if ( v5 == 288407067 && v3 == 1668576323 )
{
v17 = 0i64;
v18 = (unsigned __int8)v10 | ((BYTE1(v10) | (WORD1(v10) << 8)) << 8);
v19 = BYTE4(v10) | ((BYTE5(v10) | (HIWORD(v10) << 8)) << 8);
((void (__fastcall *)(char *, __int64))unk_1F60E221000)(v16, a1);
((void (__fastcall *)(char *, __int128 *, char *, _QWORD))unk_1F60E221150)(v16, v14, v20, (unsigned int)(v4 + 32));
while ( byte_1F60E2231D0[v2] == v20[v2] )
{
if ( ++v2 >= 32 )
return off_1F60E2230F8(aYouFindLastSec);
}
}
return 0i64;
}

最后显然是验证完毕后return了一个正确,同时也有FirstSecret,所以这是我们要找的main函数。

由于有右移2与3,和TEA KEY,但是不是XXTEA,因为轮数又是确定的,所以只能写解密函数

image-20211129085013800

解密函数还是很简单的

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
#include <stdio.h>

int main(int argc, char *argv[]) {
unsigned int v15[4] = {};
v15[0] = 114;
v15[1] = 514;
v15[2] = 19;
v15[3] = 19;
unsigned int v5, v6, v7, v8, v9, v10;
v5 = (unsigned int)1668576323;
v6 = 32;
v7 = (unsigned int)288407067;
v8 = 0;
v9 = (unsigned int)0x9E3779B9 * 32;

do
{
// v9 -= -0x9E3779B9;
// v10 = (v9 >> 2) & 3;
// v7 += ((v9 ^ v5) + (v8 ^ v15[v10])) ^ (((16 * v8) ^ (v5 >> 3)) + ((v8 >> 5) ^ (4 * v5)));
// v5 += ((v9 ^ v7) + (v7 ^ v15[v10 ^ 1])) ^ (((16 * v7) ^ (v7 >> 3)) + ((v7 >> 5) ^ (4 * v7)));
// v8 = v5;
v10 = (v9 >> 2) & 3;
v5 -= ((v9 ^ v7) + (v7 ^ v15[v10 ^ 1])) ^ (((16 * v7) ^ (v7 >> 3)) + ((v7 >> 5) ^ (4 * v7)));
v8 = v5;
v7 -= ((v9 ^ v5) + (v8 ^ v15[v10])) ^ (((16 * v8) ^ (v5 >> 3)) + ((v8 >> 5) ^ (4 * v5)));
v9 -= 0x9E3779B9;
--v6;
}
while ( v6 );
unsigned int a[] = {v7,v5, 0};
puts((char *) a);
}

得到dotitsit

第二个算法里,由于expand…知道是chacha20加密,解密即可

image-20211129092628944

1
2
3
4
5
6
7
from Crypto.Cipher import ChaCha20
import struct
key = b"0N3@aYI_M3l0dy_KurOm1_W_Suk1dqy0"
enc = bytes([0xEB, 0x8E, 0x5C, 0xA5, 0x62, 0xB4, 0x1C, 0x84, 0x5C, 0x59, 0xFC, 0x0D, 0x43, 0x3C, 0xAB, 0x20, 0xD8, 0x93, 0x33, 0x13, 0xA1, 0x9E, 0x39, 0x00, 0x76, 0x14, 0xB5, 0x04, 0x58, 0x9D, 0x06, 0xB8])
# print(struct.pack("2L", 0x69746f64, 0x74697374))
flag = ChaCha20.new(key=key, nonce=b"dotitsit").decrypt(enc)
print(flag)

Shell

这个题涉及傀儡进程,并且根据进程的返回值控制进程的控制流(RIP),质量很高。

dump子进程的方法可以用任务管理器、火绒剑转储,https://github.com/glmcdona/Process-Dump这个也可以。

总之弄下来之后,首先看主函数

image-20211130104129607

用了一个waitfordebugevent,结合子进程中的int 3,猜测是接收中断信号,然后进行操作。

在结合main函数里的这个

image-20211130104401963

得知base这个变量是子进程的基址(我这里重命名了)

这里整体是对什么东西进行0x78异或

image-20211130104723266

这里通过读取RIP+1,设置RIP为两个地方

image-20211130104854996

在子进程中:一个是0x1035,这里和int3连在一起了,需要先undefined再创建code

image-20211130104920366

0x1242

image-20211130105033254

那我们结合子进程的main函数

image-20211130105407115

int 3之后 mov对应的是0x48,所以是到0x1242处进行处理,代码实际上在1035也跳转了(但是不知道为什么),1035转成代码是这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def enc(i, a):
m = a
n = i
ax = m & n
ax = ax ^ 0xff
J = ax
ax = ax & m
ax = ax ^ 0xff
k = ax
ax = J & n
ax = ax ^ 0xff
ax = ax & K
ax = ax ^ 0xff
return ax

异或1是取反,而与非是异或,实际上代码可以简化成

1
2
3
4
5
6
def enc(i, a):
m = a
n = i
ax = m ^ n ^ n ^ m ^ n ^ m
# ax = i ^ a
return ax

其实就是下标异或

所以代码为

1
2
3
4
5
6
key = [0x1E, 0x15, 0x1B, 0x1C, 0x07, 0x4D, 0x1F, 0x1B, 0x12, 0x17, 0x4B, 0x44, 0x47, 0x58, 0x12, 0x47, 0x58, 0x58, 0x47, 0x5F, 0x54, 0x54, 0x58, 0x42, 0x59, 0x57, 0x50, 0x01, 0x49, 0x51, 0x53, 0x57, 0x3D, 0x6B, 0x3E, 0x6F, 0x3D, 0x6D, 0x6C, 0x3E, 0x69, 0x2C]

flag = []
for i in range(42):
flag.append(chr(key[i] ^ 0x78 ^ i))
print(''.join(flag)) #flag{0adbf973-d001-4896-962b-450e2d4a02a9}