【vm逆向】【buu】[网鼎杯 2020 青龙组]singal


建议先把这个看下,虽然这道题没这么复杂

【系统学习vm虚拟机逆向】https://blog.csdn.net/weixin_43876357/article/details/108570305?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172161411916800178553714%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=172161411916800178553714&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-108570305-null-null.142


查壳无壳32位进ida

main

将unk_403040赋值给v4

跟进vm_operad

太长了,直接黏贴代码了

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
int __cdecl vm_operad(int *a1, int a2)
{
int result; // eax
char Str[200]; // [esp+13h] [ebp-E5h] BYREF
char v4; // [esp+DBh] [ebp-1Dh]
int v5; // [esp+DCh] [ebp-1Ch]
int v6; // [esp+E0h] [ebp-18h]
int v7; // [esp+E4h] [ebp-14h]
int v8; // [esp+E8h] [ebp-10h]
int v9; // [esp+ECh] [ebp-Ch]

v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
v5 = 0;
while ( 1 )
{
result = v9;
if ( v9 >= a2 )
return result;
switch ( a1[v9] )
{
case 1:
Str[v6 + 100] = v4;
++v9;
++v6;
++v8;
break;
case 2:
v4 = a1[v9 + 1] + Str[v8];
v9 += 2;
break;
case 3:
v4 = Str[v8] - LOBYTE(a1[v9 + 1]);
v9 += 2;
break;
case 4:
v4 = a1[v9 + 1] ^ Str[v8];
v9 += 2;
break;
case 5:
v4 = a1[v9 + 1] * Str[v8];
v9 += 2;
break;
case 6:
++v9;
break;
case 7:
if ( Str[v7 + 100] != a1[v9 + 1] )
{
printf("what a shame...");
exit(0);
}
++v7;
v9 += 2;
break;
case 8:
Str[v5] = v4;
++v9;
++v5;
break;
case 10:
read(Str);
++v9;
break;
case 11:
v4 = Str[v8] - 1;
++v9;
break;
case 12:
v4 = Str[v8] + 1;
++v9;
break;
default:
continue;
}
}
}

我们会发现第一个参数a1是int,占4位,是dword

把unk_403040转换成dword形式然后导出。

unk

这里有几个补码形式的数字,我也不知道ida为什么会逆成这样,总之看最后一位数字就对了

这里说下几个部分的代码:

1
2
3
4
5
case 1:
Str[v6 + 100] = v4;
++v9;
++v6;
++v8;

这是将Str[100]和之后的一段空间用来储存v4

1
2
3
4
5
6
7
8
9
case 7:
if ( Str[v7 + 100] != a1[v9 + 1] )
{
printf("what a shame...");
exit(0);
}
++v7;
v9 += 2;
break;

这是经过了一段操作,然后将明文加密后的结果和预留密文做比对

这也说明了是7后面的数字就是密文

1
Str[v8]

这可以视为一个暂存位,因为后面还需要用Str[v8]进行计算操作存到v4然后经过case 1操作存到Str[100+]

基本把不明点说的差不多了,写个爆破脚本吧

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

int main(void)
{
int key[] = {34, 63, 52, 50, 114, 51, 24, 167, 49, 241, 40, 132, 193, 30, 122};
int a1[] ={4, 16, 8, 3, 5, 1, 4, 32, 8, 5, 3, 1, 3, 2, 8, 11, 1, 12, 8, 4, 4, 1, 5, 3, 8, 3, 33, 1, 11, 8, 11, 1, 4, 9, 8, 3, 32, 1, 2, 81, 8, 4, 36, 1, 12, 8, 11, 1, 5, 2, 8, 2, 37, 1, 2, 54, 8, 4, 65, 1, 2, 32, 8, 5, 1, 1, 5, 3, 8, 2, 37, 1, 4, 9, 8, 3, 32, 1, 2, 65, 8, 12, 1, 7, 34, 7, 63, 7, 52, 7, 50, 7, 114, 7, 51, 7, 24, 7, 167, 7, 49, 7, 241, 7, 40, 7, 132, 7, 193, 7, 30, 7, 122};
int v9 = 0;
int v4 = 0;
int i, j;
int flag;
int index = 0;

for ( i = 0; i < 15; i++, index++)
{
for ( j = 48; j < 123; j++)
{
flag = j;
v9 = index;

while ( a1[v9] != 1)
{
switch ( a1[v9] )
{
case 2:
v4 = a1[v9 + 1] + flag;
v9 += 2;
break;
case 3:
v4 = flag - a1[v9 + 1];
v9 += 2;
break;
case 4:
v4 = a1[v9 + 1] ^ flag;
v9 += 2;
break;
case 5:
v4 = a1[v9 + 1] * flag;
v9 += 2;
break;
case 6:
++v9;
break;
case 8:
flag = v4;
++v9;
break;
case 11:
v4 = flag - 1;
++v9;
break;
case 12:
v4 = flag + 1;
++v9;
break;
default:
continue;
}
}
if ( v4 == key[i])
{
index = v9;
printf("%c", j);
break;
}
}
}
getchar ();
return 0;
}

index表示的是当前v9的位置,在爆破过程中如果有个数字符合当前操作,需要进行下一个操作,所以需要index暂存一下