wtf

WTF is white tight feet.

  1. 1. calc

calc

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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
int __cdecl main(int argc, const char **argv, const char **envp)
{
ssignal(14, timeout);
alarm(60);
puts("=== Welcome to SECPROG calculator ===");
fflush(stdout);
calc();
return puts("Merry Christmas!");
}
unsigned int calc()
{
int nums[101]; // [esp+18h] [ebp-5A0h] BYREF
char expr[1024]; // [esp+1ACh] [ebp-40Ch] BYREF
unsigned int canary; // [esp+5ACh] [ebp-Ch]

canary = __readgsdword(0x14u);
while ( 1 )
{
bzero(expr, 0x400u);
if ( !get_expr(expr, 1024) )
break;
init_pool(nums);
if ( parse_expr((int)expr, nums) )
{
printf("%d\n", nums[nums[0]]);
fflush(stdout);
}
}
return __readgsdword(0x14u) ^ canary;
}
int __cdecl get_expr(int expr, int _1024)
{
int v2; // eax
char v4; // [esp+1Bh] [ebp-Dh] BYREF
int v5; // [esp+1Ch] [ebp-Ch]

v5 = 0;
while ( v5 < _1024 && read(0, &v4, 1) != -1 && v4 != 10 )
{
if ( v4 == '+' || v4 == '-' || v4 == '*' || v4 == '/' || v4 == '%' || v4 > '/' && v4 <= '9' )
{
v2 = v5++;
*(_BYTE *)(expr + v2) = v4;
}
}
*(_BYTE *)(v5 + expr) = 0;
return v5;
}
int __cdecl parse_expr(int expr, _DWORD *nums)
{
int v3; // eax nums[0] 表示数量,nums 保存数字。
int prev_num_temp; // [esp+20h] [ebp-88h]
int i; // [esp+24h] [ebp-84h]
int seqopr; // [esp+28h] [ebp-80h]
int num_len; // [esp+2Ch] [ebp-7Ch]
char *prev_nums_str; // [esp+30h] [ebp-78h]
int prev_nums; // [esp+34h] [ebp-74h]
char operator[100]; // [esp+38h] [ebp-70h] BYREF
unsigned int v11; // [esp+9Ch] [ebp-Ch]

v11 = __readgsdword(0x14u);
prev_num_temp = expr;
seqopr = 0;
bzero(operator, 0x64u); // 将 s 清零
for ( i = 0; ; ++i )
{
if ( (unsigned int)(*(char *)(i + expr) - 48) > 9 ) // 判断是否为 符号
{ // 如果为符号就继续往下走
num_len = i + expr - prev_num_temp;
prev_nums_str = (char *)malloc(num_len + 1);
memcpy(prev_nums_str, prev_num_temp, num_len);
prev_nums_str[num_len] = 0;
if ( !strcmp(prev_nums_str, "0") ) // 符号的左操作数若为0则直接退出,即使不是除法
{
puts("prevent division by zero");
fflush(stdout);
return 0;
}
prev_nums = atoi(prev_nums_str);
if ( prev_nums > 0 )
{
v3 = (*nums)++; // *nums表示是已经有几个数字
nums[v3 + 1] = prev_nums; // nums[[nums]+1] = prev_nums 将数字接着数组存入
}
if ( *(_BYTE *)(i + expr) && (unsigned int)(*(char *)(i + 1 + expr) - 48) > 9 )// 连续两个符号以上就不行
{
puts("expression error!");
fflush(stdout);
return 0;
}
prev_num_temp = i + 1 + expr; // 运算符右操作数
if ( operator[seqopr] ) // operator[seqopr]为上一个运算符,判断上一个操作符是否为0
{ // 若不为0,则当前运算符不为第一个运算符,那么对之前的表达式进行计算。
switch ( *(_BYTE *)(i + expr) ) // 若为0,则当前运算符为第一个运算符,继续取操作数,不计算。
{
case '%':
case '*':
case '/':
if ( operator[seqopr] != '+' && operator[seqopr] != '-' ) {
eval(nums, operator[seqopr]);
operator[seqopr] = *(_BYTE *)(i + expr);
}
else
operator[++seqopr] = *(_BYTE *)(i + expr);
break;
case '+':
case '-':
default:
eval(nums, operator[seqopr--]);
break;
}
}
else
{
operator[seqopr] = *(_BYTE *)(i + expr);
}
if ( !*(_BYTE *)(i + expr) )
break;
}
}
while ( seqopr >= 0 )
eval(nums, operator[seqopr--]);
return 1;
}
_DWORD *__cdecl init_pool(_DWORD *nums)
{
_DWORD *result; // eax
int i; // [esp+Ch] [ebp-4h]

result = nums;
*nums = 0;
for ( i = 0; i <= 99; ++i )
{
result = nums;
nums[i + 1] = 0;
}
return result;
}
_DWORD *__cdecl eval(_DWORD *a1, char a2)
{
_DWORD *result; // eax

if ( a2 == '+' )
{
a1[*a1 - 1] += a1[*a1];
}
else if ( a2 > '+' )
{
if ( a2 == 45 )
{
a1[*a1 - 1] -= a1[*a1];
}
else if ( a2 == '/' )
{
a1[*a1 - 1] /= (int)a1[*a1];
}
}
else if ( a2 == '*' )
{
a1[*a1 - 1] *= a1[*a1];
}
result = a1;
--*a1;
return result;
}

运行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜  calc ./calc 
=== Welcome to SECPROG calculator ===
2 + 32
34
4 * 8
32
3 + 0
prevent division by zero
3 + a
expression error!
3 ++
expression error!
k
Merry Christmas!

借助测试可以发现一些规则,比如输入 0 会发生“prevent division by zero”信息,输入字母也不能计算,只输入字母会直接退出。

会出现一些干扰利用的东西,我们直接来到 eval ,这个函数承担计算的主要功能。简化一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void eval(long long *initpool, char cur_operator) {
long long *eax;

if ( cur_operator == '+' )
initpool[*initpool - 1] += initpool[*initpool];
else if ( cur_operator == '-' )
initpool[*initpool - 1] -= initpool[*initpool];
else if ( cur_operator == '/' )
initpool[*initpool - 1] /= initpool[*initpool];
else if ( cur_operator == '*' )
initpool[*initpool - 1] *= initpool[*initpool];
eax = initpool;
*initpool = *initpool - 1;
}

*initpool 保存当前运算数个数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if ( operator[prev] ) {                              
switch ( *(_BYTE *)(i + expr) ) {
case '%':
case '*':
case '/':
if ( operator[prev] != '+' && operator[prev] != '-' ) {
eval(nums, operator[prev]);
operator[prev] = *(_BYTE *)(i + expr);
}
else
operator[++prev] = *(_BYTE *)(i + expr);//8*9+2
break;
case '+':
case '-':
default:
eval(nums, operator[prev--]);// loc_80491E3 <parse_expr + 441>
break;
}
} else {
operator[seqopr] = *(_BYTE *)(i + expr);
}

假如当前运算符为+或-,读到上面第 16 行的 eval 需要满足什么条件?(unsigned int)(*(char *)(i + expr) - 48) > 9 && strcmp(prev_nums_str, "0") && ( *(_BYTE *)(i + expr) && (unsigned int)(*(char *)(i + 1 + expr) - 48) > 9 ) && operator[prev] && ( *(_BYTE *)(i + expr) == '+' || *(_BYTE *)(i + expr) == '-')

  • 现在指针(i + expr)指向的是运算符。
  • 指针前面的数字(prev_nums_str)不能是 0,因为这里是 memcpy 复制过来的数字所以是字符串不能为 “0”。
  • 指针的下一位(i + 1 + expr)不能是运算符,即两个运算符不能挨在一起。
  • operator 里存放有运算符
  • 现在指针所指向运算符为 + 或者 -

然后应该是运算上一个数和上上一个数的运算公式。

1
2
3
4
5
6
+————————————————————————————+————————————————+
| 191910|op| 114514 |
+————————————————————————————+————————————————+
^ ^
| |
prev_num_temp i + expr = '+' or '-'

如果是第一次遇到运算符,如何处理?

  • 如果是 “%*/“ 中的一个运算符且前一个运算符也是,eval(initpool, operator[prev]); operator[prev] = *(_BYTE *)(i + expr);,可以看见这种运算符用完就丢掉。

breakpoint:

  • 0x8049130:prev_nums = atoi(prev_nums_str);
  • 0x8049354:while ( prev >= 0 ) eval(initpool, operator[prev--]);
  • 0x804928A:operator[++prev] = *(_BYTE *)(i + expr);现”%*/“,上一位”+-“
  • 0x804922C:eval(nums, operator[prev]);连续非+-号
  • 0x804922A:default
  • 0x80491E3:operator[seqopr] = *(_BYTE *)(i + expr);存符号

本文作者 : wtfff
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议(CC BY-NC-SA 4.0)进行许可。This blog is under a CC BY-NC-SA 4.0 Unported License
本文链接 : http://im0use.github.io/2022/06/18/pwnable/

本文最后更新于 天前,文中所描述的信息可能已发生改变