# CISCN_2022 popcalc

一道 vm 的题目。一开始出题人就搞活,之前也遇到过几次,这次也是稍微学到了。

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
unsigned int v3; // eax
int i; // [rsp+Ch] [rbp-64h]
int v6; // [rsp+10h] [rbp-60h]
void **dest; // [rsp+18h] [rbp-58h]
char src[56]; // [rsp+20h] [rbp-50h] BYREF
unsigned __int64 v9; // [rsp+58h] [rbp-18h]

v9 = __readfsqword(0x28u);
dest = (void **)mmap((void *)0xDEAD0000LL, 0x30uLL, 3, 33, -1, 0LL);
sub_401286(src);
memcpy(dest, src, 0x30uLL);
v6 = read(0, dest[2], 0x10000uLL);
for ( i = 0; i < v6 / 4; ++i )
{
v3 = *((_DWORD *)dest[2] + i) - 49;
if ( v3 <= 8 )
__asm { jmp rax }
}
sub_4012FC(src);
return 0LL;




//汇编代码
.text:00000000004014F1 lea rdx, unk_402010
.text:00000000004014F8 add rax, rdx
.text:00000000004014FB db 3Eh
.text:00000000004014FB jmp rax

修复之后的代码

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
__int64 __fastcall main(int a1, char **a2, char **a3)
{
_DWORD *v3; // rbx
_DWORD *v4; // rbx
_DWORD *v5; // rbx
_DWORD *v6; // rbx
_DWORD *v7; // rbx
_DWORD *v8; // rbx
_DWORD *v9; // rbx
_DWORD *v10; // rbx
_DWORD *v11; // rbx
_DWORD *v12; // rbx
_DWORD *v13; // rbx
_DWORD *v14; // rbx
_DWORD *v15; // rcx
__int64 v16; // rdx
__int64 v17; // rsi
int v18; // eax
int i; // [rsp+Ch] [rbp-64h]
int v21; // [rsp+Ch] [rbp-64h]
int v22; // [rsp+Ch] [rbp-64h]
int v23; // [rsp+Ch] [rbp-64h]
int v24; // [rsp+Ch] [rbp-64h]
int v25; // [rsp+Ch] [rbp-64h]
int v26; // [rsp+Ch] [rbp-64h]
int v27; // [rsp+10h] [rbp-60h]
int v28; // [rsp+14h] [rbp-5Ch]
__int64 *dest; // [rsp+18h] [rbp-58h]
char src[56]; // [rsp+20h] [rbp-50h] BYREF
unsigned __int64 v31; // [rsp+58h] [rbp-18h]

v31 = __readfsqword(0x28u);
dest = (__int64 *)mmap((void *)0xDEAD0000LL, 0x30uLL, 3, 33, -1, 0LL);
sub_401286(src);
memcpy(dest, src, 0x30uLL);
v27 = read(0, (void *)dest[2], 0x10000uLL);
for ( i = 0; i < v27 / 4; ++i )
{
switch ( *(_DWORD *)(4LL * i + dest[2]) )
{
case '1':
v21 = i + 1;
sub_401334(dest, *(unsigned int *)(4LL * v21 + dest[2]));
i = v21 + 1;
sub_401334(dest, *(unsigned int *)(4LL * i + dest[2]));
v3 = (_DWORD *)dest[3];
*v3 = sub_4013AC(dest);
v4 = (_DWORD *)(dest[3] + 4);
*v4 = sub_4013AC(dest);
*(_DWORD *)(dest[3] + 8) = *(_DWORD *)dest[3] + *(_DWORD *)(dest[3] + 4);
sub_401334(dest, *(unsigned int *)(dest[3] + 8));
break;
case '2':
v22 = i + 1;
sub_401334(dest, *(unsigned int *)(4LL * v22 + dest[2]));
i = v22 + 1;
sub_401334(dest, *(unsigned int *)(4LL * i + dest[2]));
v5 = (_DWORD *)dest[3];
*v5 = sub_4013AC(dest);
v6 = (_DWORD *)(dest[3] + 4);
*v6 = sub_4013AC(dest);
*(_DWORD *)(dest[3] + 8) = *(_DWORD *)dest[3] - *(_DWORD *)(dest[3] + 4);
sub_401334(dest, *(unsigned int *)(dest[3] + 8));
break;
case '3':
v23 = i + 1;
sub_401334(dest, *(unsigned int *)(4LL * v23 + dest[2]));
i = v23 + 1;
sub_401334(dest, *(unsigned int *)(4LL * i + dest[2]));
v7 = (_DWORD *)dest[3];
*v7 = sub_4013AC(dest);
v8 = (_DWORD *)(dest[3] + 4);
*v8 = sub_4013AC(dest);
*(_DWORD *)(dest[3] + 8) = *(_DWORD *)(dest[3] + 4) * *(_DWORD *)dest[3];
sub_401334(dest, *(unsigned int *)(dest[3] + 8));
break;
case '4':
v24 = i + 1;
sub_401334(dest, *(unsigned int *)(4LL * v24 + dest[2]));
i = v24 + 1;
sub_401334(dest, *(unsigned int *)(4LL * i + dest[2]));
v9 = (_DWORD *)dest[3];
*v9 = sub_4013AC(dest);
v10 = (_DWORD *)(dest[3] + 4);
*v10 = sub_4013AC(dest);
*(_DWORD *)(dest[3] + 8) = *(_DWORD *)dest[3] / *(_DWORD *)(dest[3] + 4);
sub_401334(dest, *(unsigned int *)(dest[3] + 8));
break;
case '5':
v25 = i + 1;
sub_401334(dest, *(unsigned int *)(4LL * v25 + dest[2]));
i = v25 + 1;
sub_401334(dest, *(unsigned int *)(4LL * i + dest[2]));
v11 = (_DWORD *)(dest[3] + 4);
*v11 = sub_4013AC(dest);
v12 = (_DWORD *)dest[3];
*v12 = sub_4013AC(dest);
*(_DWORD *)(dest[3] + 8) = *(_DWORD *)dest[3] << *(_DWORD *)(dest[3] + 4);
sub_401334(dest, *(unsigned int *)(dest[3] + 8));
break;
case '6':
v26 = i + 1;
sub_401334(dest, *(unsigned int *)(4LL * v26 + dest[2]));
i = v26 + 1;
sub_401334(dest, *(unsigned int *)(4LL * i + dest[2]));
v13 = (_DWORD *)(dest[3] + 4);
*v13 = sub_4013AC(dest);
v14 = (_DWORD *)dest[3];
*v14 = sub_4013AC(dest);
*(_DWORD *)(dest[3] + 8) = *(int *)dest[3] >> *(_DWORD *)(dest[3] + 4);
sub_401334(dest, *(unsigned int *)(dest[3] + 8));
break;
case '7':
if ( *((int *)dest + 2) > 65 )
{
*dest = ((__int64 (__fastcall *)(__int64))dest[4])(256LL);
*((_DWORD *)dest + 2) = 0;
}
v15 = (_DWORD *)(dest[3] + 4LL * *(int *)(4LL * ++i + dest[2]));
v16 = *dest;
*(_DWORD *)(4LL * (int)++*((_DWORD *)dest + 2) + v16) = *v15;
break;
case '8':
v28 = *(_DWORD *)(4LL * ++i + dest[2]);
v17 = *dest;
v18 = *((_DWORD *)dest + 2);
*((_DWORD *)dest + 2) = v18 - 1;
*(_DWORD *)(dest[3] + 4LL * v28) = *(_DWORD *)(v17 + 4LL * v18);
break;
case '9':
if ( *((_DWORD *)dest + 10) )
*(_DWORD *)(*dest + 4LL * *((int *)dest + 2)) += *(_DWORD *)(4LL * ++i + dest[2]);
break;
default:
continue;
}
}
sub_4012FC(src);
return 0LL;
}

逆向的工作量比较大。但是大概理了下里面数据的存储格式,以 4 字节作为基本的处理单元,每次处理 3 个或者两个,第一个用作 case 的判断。

  1. 加法
  2. 减法
  3. 乘法
  4. 除法
  5. 左移位
  6. 右移位
  7. push RI count 大于 65, 重新申请一个 0x100 的 chunk 用作函数的 stack。储存算术的数据,但是,不会检查 idx,可能会导致越界。
  8. pop RI
  9. push imm32

以上均为接受两个 int 类型,我们的基本操作快的数量不超过 64,为什么这这样说,加载操作数的时候,加两次,将操作数拿出来,减去两次,储存计算结果加一次。

程序实现基本的算术运算以及逻辑运算。并且采用了 stack 进行存储,但是程序没有输出啊。

# 尚未解决

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

dreamcat WeChat Pay

WeChat Pay

dreamcat Alipay

Alipay

dreamcat PayPal

PayPal