0%

Bytectf2022

Bytectf2022

题的质量都很高,而且绝大部分知识点都是我不会的,所以接下来一个月,抽时间就来复现。

OhMySolidity

这个题是一个智能合约题,第一次接触这种类型的题,发现反编译出来的东西很抽象。

给了一个txt文件

我们将第一段input输入https://ethervm.io/这个网站就可以得到反编译后的样子:

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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
contract Contract {
function main() {
memory[0x40:0x60] = 0x80;
var var0 = msg.value;

if (var0) { revert(memory[0x00:0x00]); }

if (msg.data.length < 0x04) { revert(memory[0x00:0x00]); }

var0 = msg.data[0x00:0x20] >> 0xe0;

if (var0 == 0x14edb54d) {
// Dispatch table entry for k1()
var var1 = 0x006f;
var var2 = k1();
var temp0 = memory[0x40:0x60];
memory[temp0:temp0 + 0x20] = var2 & 0xffffffff;
var temp1 = memory[0x40:0x60];
return memory[temp1:temp1 + (temp0 + 0x20) - temp1];
} else if (var0 == 0x58f5382e) {
// Dispatch table entry for challenge(string)
var1 = 0x014a;
var2 = 0x04;
var var3 = msg.data.length - var2;

if (var3 < 0x20) { revert(memory[0x00:0x00]); }

var1 = challenge(var2, var3);
var temp2 = memory[0x40:0x60];
var2 = temp2;
var3 = var2;
var temp3 = var3 + 0x20;
memory[var3:var3 + 0x20] = temp3 - var3;
var temp4 = var1;
memory[temp3:temp3 + 0x20] = memory[temp4:temp4 + 0x20];
var var4 = temp3 + 0x20;
var var6 = memory[temp4:temp4 + 0x20];
var var5 = temp4 + 0x20;
var var7 = var6;
var var8 = var4;
var var9 = var5;
var var10 = 0x00;

if (var10 >= var7) {
label_018A:
var temp5 = var6;
var4 = temp5 + var4;
var5 = temp5 & 0x1f;

if (!var5) {
var temp6 = memory[0x40:0x60];
return memory[temp6:temp6 + var4 - temp6];
} else {
var temp7 = var5;
var temp8 = var4 - temp7;
memory[temp8:temp8 + 0x20] = ~(0x0100 ** (0x20 - temp7) - 0x01) & memory[temp8:temp8 + 0x20];
var temp9 = memory[0x40:0x60];
return memory[temp9:temp9 + (temp8 + 0x20) - temp9];
}
} else {
label_0178:
var temp10 = var10;
memory[var8 + temp10:var8 + temp10 + 0x20] = memory[var9 + temp10:var9 + temp10 + 0x20];
var10 = temp10 + 0x20;

if (var10 >= var7) { goto label_018A; }
else { goto label_0178; }
}
} else if (var0 == 0x93eed093) {
// Dispatch table entry for 0x93eed093 (unknown)
var1 = 0x01cd;
var2 = func_056F();
var temp11 = memory[0x40:0x60];
memory[temp11:temp11 + 0x20] = var2 & 0xffffffff;
var temp12 = memory[0x40:0x60];
return memory[temp12:temp12 + (temp11 + 0x20) - temp12];
} else if (var0 == 0x9577a145) {
// Dispatch table entry for 0x9577a145 (unknown)
var1 = 0x0251;
var2 = 0x04;
var3 = msg.data.length - var2;

if (var3 < 0x80) { revert(memory[0x00:0x00]); }

func_0205(var2, var3);
stop();
} else if (var0 == 0xa7f81e6a) {
// Dispatch table entry for k2()
var1 = 0x025b;
var2 = k2();
var temp13 = memory[0x40:0x60];
memory[temp13:temp13 + 0x20] = var2 & 0xffffffff;
var temp14 = memory[0x40:0x60];
return memory[temp14:temp14 + (temp13 + 0x20) - temp14];
} else if (var0 == 0xf0407ca7) {
// Dispatch table entry for 0xf0407ca7 (unknown)
var1 = 0x0285;
var2 = func_0623();
var temp15 = memory[0x40:0x60];
memory[temp15:temp15 + 0x20] = var2 & 0xffffffff;
var temp16 = memory[0x40:0x60];
return memory[temp16:temp16 + (temp15 + 0x20) - temp16];
} else { revert(memory[0x00:0x00]); }
}

function challenge(var arg0, var arg1) returns (var r0) {
var temp0 = arg0;
arg0 = temp0 + arg1;
arg1 = temp0;
var var1 = msg.data[arg1:arg1 + 0x20];
var var0 = arg1 + 0x20;

if (var1 > 0x0100000000) { revert(memory[0x00:0x00]); }

var temp1 = arg1 + var1;
var1 = temp1;

if (var1 + 0x20 > arg0) { revert(memory[0x00:0x00]); }

var temp2 = var1;
var temp3 = msg.data[temp2:temp2 + 0x20];
var1 = temp3;
var temp4 = var0;
var0 = temp2 + 0x20;
var var2 = temp4;

if ((var1 > 0x0100000000) | (var0 + var1 > arg0)) { revert(memory[0x00:0x00]); }

var temp5 = var1;
var temp6 = memory[0x40:0x60];
memory[0x40:0x60] = temp6 + (temp5 + 0x1f) / 0x20 * 0x20 + 0x20;
memory[temp6:temp6 + 0x20] = temp5;
var temp7 = temp6 + 0x20;
memory[temp7:temp7 + temp5] = msg.data[var0:var0 + temp5];
memory[temp7 + temp5:temp7 + temp5 + 0x20] = 0x00;
arg0 = temp6;
arg1 = 0x60;
var0 = arg0;
var1 = 0x00;
var2 = 0x08;
var var3 = memory[var0:var0 + 0x20];

if (!var2) { assert(); }

if (var3 % var2 != var1) { revert(memory[0x00:0x00]); }

var1 = 0x60;
var temp8 = memory[var0:var0 + 0x20];
var temp9 = memory[0x40:0x60];
var2 = temp9;
var3 = temp8;
memory[var2:var2 + 0x20] = var3;
memory[0x40:0x60] = var2 + (var3 + 0x1f & ~0x1f) + 0x20;

if (!var3) {
var1 = var2;
var2 = 0xdeadbeef;
var3 = 0x00;

if (var3 >= memory[var0:var0 + 0x20]) {
label_0563:
return var1;
} else {
label_032D:
var var4 = 0x00;
var var5 = 0x00;
var var6 = 0x00;
var var7 = 0x00;

if (var7 & 0xff >= 0x04) {
label_03CD:
var7 = 0x00;

if (var7 & 0xff >= 0x20) {
label_047F:
var7 = 0x00;

if (var7 & 0xff >= 0x04) {
label_0554:
var3 = var3 + 0x08;

if (var3 >= memory[var0:var0 + 0x20]) { goto label_0563; }
else { goto label_032D; }
} else {
label_0493:
var temp10 = var7;
var var8 = (((var5 & 0xffffffff) >> (0x03 - temp10 * 0x08 & 0xff)) & 0xff) << 0xf8;
var var9 = var1;
var var10 = var3 + (temp10 & 0xff);

if (var10 >= memory[var9:var9 + 0x20]) { assert(); }

memory[var10 + 0x20 + var9:var10 + 0x20 + var9 + 0x01] = byte(var8 & ~0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x00);
var temp11 = var7;
var8 = (((var6 & 0xffffffff) >> (0x03 - temp11 * 0x08 & 0xff)) & 0xff) << 0xf8;
var9 = var1;
var10 = var3 + (temp11 & 0xff) + 0x04;

if (var10 >= memory[var9:var9 + 0x20]) { assert(); }

memory[var10 + 0x20 + var9:var10 + 0x20 + var9 + 0x01] = byte(var8 & ~0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x00);
var7 = var7 + 0x01;

if (var7 & 0xff >= 0x04) { goto label_0554; }
else { goto label_0493; }
}
} else {
label_03E1:
var temp12 = var4 + var2;
var4 = temp12;
var temp13 = var6;
var temp14 = var5 + (((temp13 & 0xffffffff) << 0x04) + (storage[0x00] & 0xffffffff) ~ temp13 + var4 ~ ((temp13 & 0xffffffff) >> 0x05) + (storage[0x00] / 0x0100 ** 0x04 & 0xffffffff));
var5 = temp14;
var6 = temp13 + (((var5 & 0xffffffff) << 0x04) + (storage[0x00] / 0x0100 ** 0x08 & 0xffffffff) ~ var5 + var4 ~ ((var5 & 0xffffffff) >> 0x05) + (storage[0x00] / 0x0100 ** 0x0c & 0xffffffff));
var7 = var7 + 0x01;

if (var7 & 0xff >= 0x20) { goto label_047F; }
else { goto label_03E1; }
}
} else {
label_034E:
var temp15 = var7;
var8 = 0x03 - temp15 * 0x08 & 0xff;
var9 = var0;
var10 = var3 + (temp15 & 0xff);

if (var10 >= memory[var9:var9 + 0x20]) { assert(); }

var5 = var5 + (((((memory[var10 + 0x20 + var9:var10 + 0x20 + var9 + 0x20] >> 0xf8) << 0xf8) >> 0xf8) & 0xff) << var8);
var temp16 = var7;
var8 = 0x03 - temp16 * 0x08 & 0xff;
var9 = var0;
var10 = var3 + (temp16 & 0xff) + 0x04;

if (var10 >= memory[var9:var9 + 0x20]) { assert(); }

var6 = var6 + (((((memory[var10 + 0x20 + var9:var10 + 0x20 + var9 + 0x20] >> 0xf8) << 0xf8) >> 0xf8) & 0xff) << var8);
var7 = var7 + 0x01;

if (var7 & 0xff >= 0x04) { goto label_03CD; }
else { goto label_034E; }
}
}
} else {
var temp17 = var3;
memory[var2 + 0x20:var2 + 0x20 + temp17] = code[code.length:code.length + temp17];
var1 = var2;
var2 = 0xdeadbeef;
var3 = 0x00;

if (var3 >= memory[var0:var0 + 0x20]) { goto label_0563; }
else { goto label_032D; }
}
}

function func_0205(var arg0, var arg1) {
var temp0 = arg0;
var temp1 = temp0 + 0x20;
arg0 = msg.data[temp0:temp0 + 0x20] & 0xffffffff;
var temp2 = temp1 + 0x20;
arg1 = msg.data[temp1:temp1 + 0x20] & 0xffffffff;
var var0 = msg.data[temp2:temp2 + 0x20] & 0xffffffff;
var var1 = msg.data[temp2 + 0x20:temp2 + 0x20 + 0x20] & 0xffffffff;
storage[0x00] = (arg0 & 0xffffffff) | (storage[0x00] & ~0xffffffff);
storage[0x00] = (arg1 & 0xffffffff) * 0x0100 ** 0x04 | (storage[0x00] & ~(0xffffffff * 0x0100 ** 0x04));
storage[0x00] = (var0 & 0xffffffff) * 0x0100 ** 0x08 | (storage[0x00] & ~(0xffffffff * 0x0100 ** 0x08));
storage[0x00] = (var1 & 0xffffffff) * 0x0100 ** 0x0c | (storage[0x00] & ~(0xffffffff * 0x0100 ** 0x0c));
}

function k1() returns (var r0) { return storage[0x00] / 0x0100 ** 0x04 & 0xffffffff; }

function func_056F() returns (var r0) { return storage[0x00] & 0xffffffff; }

function k2() returns (var r0) { return storage[0x00] / 0x0100 ** 0x08 & 0xffffffff; }

function func_0623() returns (var r0) { return storage[0x00] / 0x0100 ** 0x0c & 0xffffffff; }
}

看这个东西,要看if中的语句,比如下面,当输入0x93eed093时,会调用func_056F函数

image-20221001101445496

我们的第二段输入

0x9577a1450000000000000000000000000000000000000000000000000000000010000000000000000000000000000876543210000000000000002345678000000000000000000000000000000000000000000000000000000000000000000000aabbccdd0000000000000000000000000000000000000000000000000000000044332211

我们直接看func_0205函数

image-20221001101725520

这就要猜了,能看出是赋值了4个值到storage,四个值分别对应0x12345678、0x87654321、0xaabbccdd、0x44332211

第三次输入时challenge方法:输入0x59f5382e,并输出了0xa625e97482f83d2b7fc5125763dcbbffd8115b208c4754eee8711bdfac9e3377622bbf0cbb785e612b82c7f5143d5333

challeng函数是一个魔改的tea

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

//加密函数
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i < 32; i++) { /* basic cycle start */
sum += delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
} /* end cycle */
v[0]=v0; v[1]=v1;
}
//解密函数
void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xdeadbeef*32, i; /* set up */
uint32_t delta=0xdeadbeef; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<32; i++) { /* basic cycle start */
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}

int main()
{
uint32_t v[12]={0xa625e974,0x82f83d2b,0x7fc51257,0x63dcbbff,0xd8115b20,0x8c4754ee,0xe8711bdf,0xac9e3377,0x622bbf0c,0xbb785e61,0x2b82c7f5,0x143d5333},k[4]={0x12345678,0x87654321,0xaabbccdd,0x44332211};
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
for(int i = 0; i < 6; i+=2)
{
decrypt(v + i, k);
printf("decode:%x %x\n",v[i],v[i + 1]);
}
return 0;
}
1
bytectf{f2d9e0b6-274d-47e0-bc4c-40967015ba79273}

maze6d

这个混淆看了一天,里面涉及到好多不清楚的地方,还有很多之前没搞明白的地方。

第一个点是GOT和PLT表:

GOT表是一个存储外部函数地址的表,PLT表是一个代码片段组成的表,每一个都能跳到GOT表项。众所周知,链接时要进行一个重定位,链接做的事情:对同名section进行合并,对代码段和数据段进行地址分配,进行重定位。

重定位是这样的:如果时在中间文件中定义好的函数,连接时直接可以重定位到函数地址,如果是动态链接库中的函数,只能生成额外的小段代码,就是PLT表,然后重定位到该代码片段。

运行时重定位:运行后加载动态库,把动态库中的相应函数地址填入GOT表

延迟重定位:只有动态库被调用时才会进行地址解析和重定位工作,这时候动态库函数地址被写到GOT表项中。

第一步由函数跳到PLT表中,第二步PLT表跳到GOT表(jmp),第三步GOT跳到PLT(push),并进行压栈,把代表函数的id压栈,第四步跳到公共PLT表项中(jmp),第五步进入GOT表,然后_dl_runtime_resolve对动态函数进行地址解析和重定位,第七步把动态函数真实的地址写入GOT表项,然后执行函数并retn。

image-20221001142814162