拟态2023-RE

fisher

各种反调试检测

  • vmtoolsd.exe

  • NtQueryInformationProcess

  • 花指令

  • 随机数干扰

把反调试全部patch掉

很多的函数里对输入数据都做sha1校验,输入数据的sha1校验结果需要为2e95a5c5f9643df668d3965a6cdf19541bc0496b

看了好久没找到sha1突破点

一开始只看了反编译c代码,被错误的输出干扰了

需要注意try catch

try里面做了sha1校验,当校验通过时会抛出异常,进入到catch块,校验不通过返回0

突破点在catch块内,看catch块的末尾可以找到输出字符串的部分

左边是正确时的输出,字符串解密之后是you win! your flag: flag{your input}

核心的加密运算在call [rax+0x18],加密完跟N17EHf1DWHD40DWH/f79E05EfIH1E179E1字符串比较

函数里面第一个大循环把输入拷贝了一遍

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
v20 = (BYTE *)Src[1];
if ( v4 != v3 )
{
while ( 1 )
{
v21 = &v3[(unsigned int)v18];
if ( v20 == v48 )
{
sub_7FF6786A9B10((const void **)Src, v20, v21);
v22 = (BYTE *)Src[1];
}
else
{
*v20 = *v21;
v22 = (BYTE *)++Src[1];
}
v18 = (unsigned int)(v18 + 1);
a1 = v49;
if ( (unsigned int)v18 == *((_QWORD *)&v49 + 1) - (_QWORD)v49 )
break;
v23 = *(_BYTE *)(v49 + v18);
if ( v23 == *((_BYTE *)Src[0] + v19) ) // 后一个字符与前一个字符相同,填充X
{
v41[0] = 88;
if ( v22 == v48 )
{
sub_7FF6786A9B10((const void **)Src, v22, v41);
v20 = (BYTE *)Src[1];
}
else
{
*v22 = 88;
v20 = (BYTE *)++Src[1];
}
}
else
{
if ( v22 == v48 )
{
sub_7FF6786A9B10((const void **)Src, v22, (_BYTE *)(v49 + v18));
v20 = (BYTE *)Src[1];
}
else
{
*v22 = v23;
v20 = (BYTE *)++Src[1];
}
LODWORD(v18) = v18 + 1;
}
v19 += 2;
v3 = (BYTE *)v49;
a1 = *((_QWORD *)&v49 + 1) - v49;
if ( (unsigned __int64)(unsigned int)v18 >= *((_QWORD *)&v49 + 1) - (_QWORD)v49 )
goto LABEL_42;
}
v41[0] = 88;
if ( v22 == v48 ) // 长度为奇数,末尾补X
{
sub_7FF6786A9B10((const void **)Src, v22, v41);
v20 = (BYTE *)Src[1];
}
else
{
*v22 = 88;
v20 = (BYTE *)++Src[1];
}
}

第二个大循环对输入字符串做了一些处理,按两个字符一组,忽略字符表以外的字符,第一个字符和第二字符相同时在中间加X,字符串长度为奇数末尾加X

字符表ghijklpqrstuvwxyzABCabcDEFdef0123GHI4567JKL+/MNOmnoPQRSXYZ8TUVW9

接下来核心加密部分

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
87
88
89
90
91
92
93
94
    do
{
v10 = *((_BYTE *)*a2 + v6);
sub_7FF6786AA6D3(2i64, (__int64)a2);
v11 = 0;
v12 = 0i64;
v13 = a1 + 9;
while ( 2 )
{
v14 = 0;
for ( i = 0i64; i < 8; ++i )
{
if ( v10 == *((_BYTE *)v13 + i) )
{
i1 = v11;
i_ = v12;
j1 = v14;
goto LABEL_10;
}
++v14;
}
++v11;
++v12;
++v13;
if ( v12 < 8 )
continue;
break;
}
LABEL_10:
v16 = v6 + 1;
v17 = *((_BYTE *)*a2 + v16);
sub_7FF6786AA6D3(2i64, (__int64)v13);
v18 = 0;
v19 = 0i64;
v20 = a1 + 9;
while ( 2 )
{
v21 = 0;
for ( j = 0i64; j < 8; ++j )
{
if ( v17 == *((_BYTE *)v20 + j) )
{
i2 = v18;
j2 = v21;
goto LABEL_17;
}
++v21;
}
++v18;
++v19;
++v20;
if ( v19 < 8 )
continue;
break;
}
LABEL_17:
if ( i1 == i2 )
{
v23 = 8 * i_ + 72;
v24 = (char *)a1 + v23 + (int)(j1 + 1) % 8;
v25 = *(_BYTE **)(a3 + 8);
if ( v25 == *(_BYTE **)(a3 + 16) )
{
sub_7FF6786A9B10((const void **)a3, v25, v24);
}
else
{
*v25 = *v24;
++*(_QWORD *)(a3 + 8);
}
sub_7FF6786AA6D3(2i64, (__int64)v25);
v26 = (char *)a1 + v23 + (j2 + 1) % 8;
a2 = *(void ***)(a3 + 8);
if ( a2 == *(void ***)(a3 + 16) )
{
sub_7FF6786A9B10((const void **)a3, a2, v26);
}
else
{
*(_BYTE *)a2 = *v26;
++*(_QWORD *)(a3 + 8);
}
}
else
{
v27 = *a1;
if ( j1 == j2 )
(*(void (__fastcall **)(__int64 *, _QWORD, __int64, _QWORD, int))v27)(a1, j1, a3, i1, i2);
else
(*(void (__fastcall **)(__int64 *, _QWORD, _QWORD, __int64, int, int))(v27 + 8))(a1, i1, j1, a3, i2, j2);
}
v6 = v16 + 1;
}
while ( v6 < (unsigned int)len );

在while循环里,两个字符一组,查字符表索引,每个字符对应两个索引

1
index = 8 * i + j

用python还原一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
table = "ghijklpqrstuvwxyzABCabcDEFdef0123GHI4567JKL+/MNOmnoPQRSXYZ8TUVW9"
def enc(c1, c2):
i1 = table.index(c1) // 8
j1 = table.index(c1) % 8
i2 = table.index(c2) // 8
j2 = table.index(c2) % 8
if (i1 == i2):
indx1 = 8 * i1 + (j1 + 1) % 8
indx2 = 8 * i2 + (j2 + 1) % 8
else:
if (j1 == j2):
indx1 = 8 * ((i1 + 1) % 8) + j1
indx2 = 8 * ((i2 + 1) % 8) + j2
else:
indx1 = 8 * i1 + j2
indx2 = 8 * i2 + j1
return table[indx1]+table[indx2]

要根据两个字符的ij情况分类做替换,写出逆过程

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
def dec(c1, c2):
i1 = table.index(c1) // 8
j1 = table.index(c1) % 8
i2 = table.index(c2) // 8
j2 = table.index(c2) % 8
if (i1 == i2):
indx1 = 8 * i1 + (j1 - 1) % 8
indx2 = 8 * i2 + (j2 - 1) % 8
else:
if (j1 == j2):
indx1 = 8 * ((i1 - 1) % 8) + j1
indx2 = 8 * ((i2 - 1) % 8) + j2
else:
indx1 = 8 * i1 + j2
indx2 = 8 * i2 + j1
return table[indx1]+table[indx2]

flag = ""
for i in range(0, len(ans), 2):
flag += dec(ans[i], ans[i+1])

print(flag)

for i in range(0, len(flag), 2):
print(enc(flag[i], flag[i+1]), end='')
print("")
print(ans)

跑一下得到下面的flag

6c324d2c86a72b864a2X2f30e46d202X20

算hash不对,根据前面补X的规则,把两个X去掉

ack,得到flagflag{6c324d2c86a72b864a22f30e46d20220}


拟态2023-RE
https://blog.noxke.icu/2023/11/12/ctf_wp/拟态2023-RE/
作者
noxke
发布于
2023年11月12日
许可协议