BugKuCTF - 贪吃的杰瑞

杰瑞的奶酪

无壳、32位、C++;

这里简单修改了一下变量名字;

这里很明显的加密,对输入的flag增加了当前索引值再加1;那我们只需要拿到密文减掉当前索引值再减1即可;返回拿一下密文;

这里有对密文进行了处理,因为一开始以为是个key,懒得改名字了,改了前面的截图也得换;这里的key就是密文enc;跟进看看对密文干了啥;

一个简单的异或处理,加密流程很明了了,提取密文直接解密;

其实这里不太懂,按理来说我应该是可以给动调提取key的,奈何动调发现key点不了;不管了,先赛博吧;

1
2
3
4
5
enc=[0x67,0x6e,0x64,0x6b,0x3f,0x81,0x38,0x3a,0x3c,0x3a,0x44,0x55,0x58,0x5c,0x48,0x49,0x8e]
for i in range(len(enc)):
enc[i] = enc[i] - i -1
print(''.join(chr(c) for c in enc))
#flag:{12309IKN99}

这样就可以得到flag:{12309IKN99}

杰瑞的下午茶

无壳、32位、C/C++;

看名字就觉得是个TEA,怎么说呢,做到这道题就发现这出题人喜欢给输出的字符串之类的都异或加密一下;

so?什么袅玩意,调了半天,跟TEA没有半点关系;这边还感觉有个SMC自异或的,but调了半天感觉没啥鸟用啊…;一直自异或;密文直接丢脸上么?

修完之后发现,这里最后给跳转了个地址?不懂在干啥啊…

直接对输入进行硬件断点打一下吧,看看干了啥;

直接跳转这里来了,说明这里对输入进行了操作;异或了个’3‘; 下面两个++应该就是密文和明文的比对索引了;试一试;

​

没招了…算法不难,调试难绷了;flag{today__is__good}

杰瑞的箱子

无壳、32位、C++;

初看甚好,再看都是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
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
void __cdecl __noreturn sub_401820(int *p_n3553585)
{
_DWORD *v1; // ecx
_DWORD *v3; // esi
char *v4; // eax
char *v5; // edx
int v6; // eax
char n57; // al
char *v8; // edx
char v9; // cl
char *v10; // eax
_BYTE *v11; // edx
char v12; // bl
_BYTE *v13; // ecx
char n57_1; // cl
char v15; // cl
bool v16; // zf
char n57_2; // al
char v18; // al
_BYTE *v19; // ecx
char n57_3; // cl
char v21; // [esp+Ch] [ebp-10h]
_BYTE *v22; // [esp+10h] [ebp-Ch] BYREF
_BYTE *v23; // [esp+14h] [ebp-8h] BYREF

v3 = v1;
LABEL_2:
v3[8] = p_n3553585;
while ( 1 )
{
v4 = v3[8];
v5 = v4 + 1;
v6 = *v4 - 49;
v3[8] = v5;
switch ( v6 )
{
case 0:
sub_4011B0(v3);
continue;
case 1:
n57 = *v5;
v8 = v5 + 1;
v3[8] = v8;
if ( n57 == 57 )
{
v9 = *v8;
v10 = v8 + 1;
v3[8] = v8 + 1;
v11 = v8 + 2;
v12 = *v10;
v21 = v9;
v3[8] = v11;
switch ( v9 )
{
case '1':
v13 = *v3;
break;
case '2':
v13 = v3[1];
break;
case '3':
v13 = v3[2];
break;
case '4':
v13 = v3[3];
break;
case '5':
v13 = v3[4];
break;
case '6':
v13 = v3[5];
break;
case '7':
v13 = v3[6];
break;
case '8':
v13 = v3[7];
break;
case '9':
v13 = v11;
break;
default:
v13 = 0;
break;
}
switch ( v12 )
{
case '1':
v11 = *v3;
break;
case '2':
v11 = v3[1];
break;
case '3':
v11 = v3[2];
break;
case '4':
v11 = v3[3];
break;
case '5':
v11 = v3[4];
break;
case '6':
v11 = v3[5];
break;
case '7':
v11 = v3[6];
break;
case '8':
v11 = v3[7];
break;
case '9':
break;
default:
v11 = 0;
break;
}
v23 = &v13[v11];
sub_401AC0(v21, &v23, v13);
}
continue;
case 2:
sub_4014B0(v3);
continue;
case 3:
n57_1 = *v5;
v3[8] = v5 + 1;
if ( n57_1 == 57 || n57_1 == 64 )
{
v15 = v5[1];
v16 = v3[9] == 1;
v3[8] = v5 + 2;
if ( !v16 )
goto LABEL_50;
}
continue;
case 4:
n57_2 = *v5;
v3[8] = v5 + 1;
if ( n57_2 == 57 )
{
v18 = v5[1];
v19 = v5 + 2;
v3[8] = v5 + 2;
switch ( v18 )
{
case '1':
v19 = *v3;
break;
case '2':
v19 = v3[1];
break;
case '3':
v19 = v3[2];
break;
case '4':
v19 = v3[3];
break;
case '5':
v19 = v3[4];
break;
case '6':
v19 = v3[5];
break;
case '7':
v19 = v3[6];
break;
case '8':
v19 = v3[7];
break;
case '9':
break;
default:
v19 = 0;
break;
}
v22 = v19 + 1;
sub_401AC0(v18, &v22, v19);
}
continue;
case 5:
n57_3 = *v5;
v3[8] = v5 + 1;
if ( n57_3 == 57 || n57_3 == 64 )
{
v15 = v5[1];
v16 = v3[9] == 1;
v3[8] = v5 + 2;
if ( v16 )
goto LABEL_50;
}
continue;
case 6:
v3[8] = v5 + 1;
v15 = v5[1];
v3[8] = v5 + 2;
LABEL_50:
switch ( v15 )
{
case 1:
p_n3553585 = v3[112];
goto LABEL_2;
case 2:
p_n3553585 = v3[113];
goto LABEL_2;
case 3:
p_n3553585 = v3[114];
goto LABEL_2;
}
break;
case 7:
sub_401700(v3);
default:
continue;
}
}
}

满满的VM啊,感觉像是把数组当作寄存器来操作,然后一堆操作数,其实感觉没什么实质性操作;

一个一个函数点进去,对比一下就很容易找到对比的地方其实;

这里的“=”很明显的对比,进去全给打上断点;

看左边的寄存器的比值,两个都是1;flag第一位应该就是’1‘;接着比较看看下一位;

’1‘和’9‘,那显然1是输入,9是密文了;修改一下寄存器的值,便于比较成功,继续一直动调就可以得到flag;

其实就是第二个;flag{191119118}

杰瑞的影分身

无壳、32位、C++;

找到正确的入口点;

动调一下不难发现这里有一个SMC;修一下;炸了很久…最后发现其实不用修,直接接续F7,后面会跳到一段爆红的代码;

在红色的这里按‘P’修一下

这里有个地方分析失败,直接下去nop掉;

​ 进入伪代码发现有个异或,提取一下;

这里异或一下输入可以得到这里使用的异或流;

下面这个循环对flag没有进行处理,,但是对key进行了操作,跟进看看变化后的key;

38行显示,长度不足的用0补齐了,下面这个函数,属实是没看懂这兄弟咱传进去的参数;但是这个位置不难猜出是个加密;

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
unsigned int __cdecl Src(int a1, int a2)
{
_BYTE v3[80]; // [esp+0h] [ebp-78h]
int v4; // [esp+50h] [ebp-28h]
int v5; // [esp+54h] [ebp-24h]
unsigned int i_1; // [esp+58h] [ebp-20h]
int v7; // [esp+5Ch] [ebp-1Ch]
int v8; // [esp+60h] [ebp-18h]
unsigned int v9; // [esp+64h] [ebp-14h]
unsigned int n15_1; // [esp+68h] [ebp-10h]
unsigned int v11; // [esp+6Ch] [ebp-Ch]
int n15; // [esp+70h] [ebp-8h]
unsigned int i; // [esp+74h] [ebp-4h]

n15_1 = psub_4026E0(a1);
i_1 = psub_4026E0(a2);
psub_4026E0(byte_406274);
n15 = 15;
v11 = 15 - n15_1;
if ( n15_1 != 15 )
return v11;
for ( i = 0; i < n15_1; ++i )
{
v4 = 0;
v9 = i % 3;
v7 = psub_401BD0(i % 3);//此函数在后面
v5 = (v7 + 2) ^ *(char *)(i + a1);
v8 = *(char *)(i + a2);
if ( i >= i_1 )
v8 = 0;
v3[i] = v8 + v5;
if ( (char)v3[i] != byte_406274[i] )
return i + 1;
}
return 0;
}




int __cdecl sub_401BD0(int a1)
{
int v2; // [esp+4h] [ebp-4h]

v2 = 0;
switch ( a1 )
{
case 0:
return 10;
case 1:
return 9;
case 2:
return 8;
}
return v2;
}

很明了的逻辑了,就是简单的一个+2和异或key,然后再加上另一个key,然后进行密文的比较,那直接逆回去就行

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
#include <stdio.h>
#include <stdint.h>
int sub_401BD0(int a1)
{
int v2; // [esp+4h] [ebp-4h]

v2 = 0;
switch (a1)
{
case 0:
return 10;
case 1:
return 9;
case 2:
return 8;
}
return v2;
}

int main()
{
uint8_t enc[] = {0xd3, 0x38, 0xD1, 0xD3, 0x7B, 0xAD, 0xB3, 0x66, 0x71, 0x3A, 0x59, 0x5F, 0x5F, 0x2D, 0x73};
uint8_t key[] = {0x65, 0x34, 0x62, 0x64, 0x74, 0x52, 0x56, 0x30, 0x32, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
uint8_t keyy[] = {0x4, 0x63, 0x4, 0x4, 0x77, 0x4, 0x4, 0x68, 0x4, 0x4, 0x61, 0x4, 0x4, 0x63, 0x4};
for (int i = 0; i < 15; i++)
{
enc[i] -= key[i];
enc[i] ^= (sub_401BD0(i % 3) + 2) ^ keyy[i];
printf("%c", enc[i]);
}

return 0;
}

flag{UUU123QWE}

杰瑞的钥匙

其实这道题感觉没上一道题难受,上一道题是耗时间最长的…也有可能是因为这道题和箱子差不多的缘故;

和箱子一样,打开直接看看有哪些加密就行;

​

老规矩,找到判断,直接给全打断点看看,‘c’和‘=’做比较?我输入的是“a”,也就是说有其他加密,回去找一找;

给输入直接打上硬件断点跟踪看一看;一路上不知道打了多少断点;调吐了;

调得眼花,第一个加密点,+3;

第二个加密点,^7;其实调的过程中以及上面的第一个密文就可以知道其实密文就是下面这个,不说了,试试看;

flag{79453189413576}