James Hoi's Blog

DASCTF 三月 逆向

Word count: 2.3kReading time: 12 min
2021/03/27 Share

drinkSomeTea drinkSomeTea的附件.zip

看到题目盲猜一波TEA加密,果不其然。之前没好好学,这次至少会做题了,之后有空要好好再看看详细的加密流程。附件给了个tea.png.out,即需要解密图片。
32位exe,无壳
image.png
在一开始的sub_401000函数里面,应该是判断是不是调试器(我没仔细看),用调试器的时候会退出程序,直接把call sub_401000 nop掉即可。
image.png
应该是一个加密函数,点进去看发现加花了
image.png
去花,Create Function,F5,发现是TEA加密,关于TEA加密脚本可以参考这个
image.pngimage.png
魔改一下TEA加密即可,要注意的是,加密的时候需要用int指针(保留符号位),否则加密/解密出来的数据会不对。(我在这个地方调了很久,照着还原一次加密过程,动调的时候发现加密出来的数据怎么都不对)
最后用解密函数解密tea.png.out,脚本如下

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


void encrypt(int* v, uint32_t* k) {
int v0 = v[0], v1 = v[1], sum = 0, i; /* set up */
int delta = 0x61C88647; /* a key schedule constant */
int 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 >> 5) + k1) ^ (v1 + sum) ^ ((v1 << 4) + k0);
v1 += ((v0 >> 5) + k3) ^ (v0 + sum) ^ ((v0 << 4) + k2);
} /* end cycle */
v[0] = v0; v[1] = v1;
}

void decrypt(int* v, uint32_t* k) {
int v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i; /* set up */
int delta = 0x61C88647; /* a key schedule constant */
int 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 >> 5) + k3) ^ (v0 + sum) ^ ((v0 << 4) + k2);
v0 -= ((v1 >> 5) + k1) ^ (v1 + sum) ^ ((v1 << 4) + k0);
sum += delta;
} /* end cycle */
v[0] = v0; v[1] = v1;
}

int main()
{
DWORD key[4] = { 0x67616C66, 0x6B61667B, 0x6C665F65, 0x7D216761 };
HANDLE file = CreateFileA("tea.png.out", 0xC0000000, 0, 0, 3u, 0x80u, 0);
HANDLE v4 = file;
char *v7;
DWORD v8;
HANDLE v9;
void *v10;
DWORD NumberOfBytesRead;
DWORD NumberOfBytesWritten;
DWORD v6 = GetFileSize(file, 0);
DWORD dword_409988[15000];
if (v6 < 0xEA60)
{
SetFilePointer(v4, 0, 0, 0);
NumberOfBytesRead = 0;
ReadFile(v4, &dword_409988, v6, &NumberOfBytesRead, 0);
CloseHandle(v4);
if (v6 >> 3)
{
v7 = (char *)&dword_409988;
v8 = v6 >> 3;
do
{
decrypt((int *)v7, (uint32_t *)key);
v7 += 8;
--v8;
} while (v8);
}
v9 = CreateFileA("tea.png", 0xC0000000, 0, 0, 2u, 0x80u, 0);
v10 = v9;
NumberOfBytesWritten = 0;
WriteFile(v9, &dword_409988, v6, &NumberOfBytesWritten, 0);
CloseHandle(v10);
}
}

得到flag: DASCTF{09066cbb91df55502e6fdc83bf84cf45}
tea.png

Enjoyit-1 Enjoyit-1的附件.zip

Exeinfope发现是c#写的,用dnspy32位打开
image.png
一开始定位加密位置的时候想了一会,emm我最后用Console.WriteLine定位的,其实好像点开就行…
image.png
发现应该先是base64验证,中间有Thread.Sleep,然后最后直接输出flag
image.png
打开class b的定义,base64换表了
image.png

1
2
3
4
5
6
my_base64table = "abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ"
std_base64table ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
s = "yQXHyBvN3g/81gv51QXG1QTBxRr/yvXK1hC="
s = s.translate(str.maketrans(my_base64table,std_base64table))
print(base64.b64decode(s))
#combustible_oolong_tea_plz

然后直接一次性把全部定义都复制到自己新建的一个c#程序,运行一次就跑出来了(而且当然要注释掉Thread.Sleep)
(代码篇幅有点长,抱歉)

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
internal class b
{
// Token: 0x06000006 RID: 6 RVA: 0x00002234 File Offset: 0x00000434
public bool B(string A_0)
{
for (int i = 0; i < A_0.Length; i++)
{
if (A_0[i] < '_' || A_0[i] > 'z')
{
Console.WriteLine("Sorry,we don't have this tea");
return false;
}
}
return true;
}

// Token: 0x06000007 RID: 7 RVA: 0x00002278 File Offset: 0x00000478
public string c(string A_0)
{
string text = "";
int num = A_0.Length / 3;
int i;
for (i = 0; i < num; i++)
{
byte index = Convert.ToByte((int)('?' & A_0[i * 3] >> 2));
byte index2 = Convert.ToByte((int)((int)(A_0[i * 3] & '\u0003') << 4 | A_0[1 + i * 3] >> 4));
byte index3 = Convert.ToByte((int)((int)(A_0[1 + i * 3] & '\u000f') << 2 | A_0[2 + i * 3] >> 6));
byte index4 = Convert.ToByte((int)(A_0[2 + i * 3] & '?'));
text += this.a[(int)index].ToString();
text += this.a[(int)index2].ToString();
text += this.a[(int)index3].ToString();
text += this.a[(int)index4].ToString();
}
if (i * 3 < A_0.Length)
{
byte index = Convert.ToByte((int)('?' & A_0[i * 3] >> 2));
byte index2;
byte index3;
byte index4;
if (i * 3 + 1 < A_0.Length)
{
index2 = Convert.ToByte((int)((int)(A_0[i * 3] & '\u0003') << 4 | A_0[i * 3 + 1] >> 4));
index3 = Convert.ToByte((int)((int)(A_0[i * 3 + 1] & '\u000f') << 2));
index4 = 64;
}
else
{
index2 = Convert.ToByte((int)((int)(A_0[i * 3] & '\u0003') << 4));
index3 = 64;
index4 = 64;
}
text += this.a[(int)index].ToString();
text += this.a[(int)index2].ToString();
text += this.a[(int)index3].ToString();
text += this.a[(int)index4].ToString();
}
return text;
}

// Token: 0x06000008 RID: 8 RVA: 0x00002488 File Offset: 0x00000688
public void B(byte[] A_0)
{
string text = "";
for (int i = 0; i < A_0.Length; i++)
{
text += A_0[i].ToString("x2");
}
Console.WriteLine(text);
}

// Token: 0x06000009 RID: 9 RVA: 0x000024C8 File Offset: 0x000006C8
public void B(ref uint[] A_0, byte[] A_1)
{
uint num = 2654435464U;
uint num2 = A_0[0];
uint num3 = A_0[1];
uint num4 = 0U;
for (int i = 0; i < 32; i++)
{
num2 += ((num3 << 4 ^ num3 >> 5) + num3 ^ num4 + (uint)A_1[(int)(num4 & 3U)]);
num4 += num;
num3 += ((num2 << 4 ^ num2 >> 5) + num2 ^ num4 + (uint)A_1[(int)(num4 >> 11 & 3U)]);
}
A_0[0] = num2;
A_0[1] = num3;
}

// Token: 0x0600000A RID: 10 RVA: 0x00002534 File Offset: 0x00000734
public void c(ref uint[] A_0, byte[] A_1)
{
uint num = 2654435769U;
uint num2 = A_0[0];
uint num3 = A_0[1];
uint num4 = num * 32U;
for (int i = 0; i < 32; i++)
{
num3 -= ((num2 << 4 ^ num2 >> 5) + num2 ^ num4 + (uint)A_1[(int)(num4 >> 11 & 3U)]);
num4 -= num;
num2 -= ((num3 << 4 ^ num3 >> 5) + num3 ^ num4 + (uint)A_1[(int)(num4 & 3U)]);
}
A_0[0] = num2;
A_0[1] = num3;
}

// Token: 0x04000003 RID: 3
public string a = "abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ=";
}


static void Main(string[] args)
{
string text = "";
byte[] a_ = new byte[26];
byte[] array = new byte[8];
byte[] array2 = new byte[]
{
2,
5,
4,
13,
3,
84,
11,
4,
87,
3,
86,
3,
80,
7,
83,
3,
0,
4,
83,
94,
7,
84,
4,
0,
1,
83,
3,
84,
6,
83,
5,
80
};
uint[] array3 = new uint[]
{
288U,
369U
};
b b = new b();
Console.WriteLine("Welcome to my room, and please enjoy some tea by write what you want in this machine:");
string text2 = Console.ReadLine();
if (!b.B(text2))
{
//Thread.Sleep(1000000);
}
if (b.c(text2) != "yQXHyBvN3g/81gv51QXG1QTBxRr/yvXK1hC=")
{
Console.WriteLine("Oops");
//Thread.Sleep(1000000);
}
Console.WriteLine("And,wait a second!");
for (int i = 0; i < 100000; i++)
{
//Thread.Sleep(1000);
//Console.WriteLine(i + 1);
break;
}
a_ = Encoding.Default.GetBytes(text2);
b.B(ref array3, a_);
Console.WriteLine("Here is your tea, and flag!");
text += array3[0].ToString("x2");
text += array3[1].ToString("x2");
array = Encoding.Default.GetBytes(text);
Console.Write("flag{");
for (int j = 0; j < 32; j++)
{
byte[] array4 = array2;
int num = j;
array4[num] ^= array[j % array.Length];
}
Console.Write(Encoding.Default.GetString(array2));
Console.Write("}");
Console.ReadLine();
}
}
}

输入,然后得到flag
image.png
flag{4645e180540ffa7a67cfa174cde105a2}
PS:其实这题可能能直接把flag输出出来,但我没试…flag出来了就行

replace replace的附件.zip

64位,无壳
image.png
这题一开始难点我预计错了…下次应该好好先把全部函数分析完先。在main可以看出是24位flag,然后sub_401550是验证输入是否为flag格式(即flag{xxxxxx})
image.png
sub_401AE7是假的加密函数,因为之后会在sub_401925再覆盖掉所需要验证的字符串
image.png
题外话:
把这个解出来之后是fakeflag,但其实出题人粗心了,就算这个题目是这个fakeflag,最后也不对,因为有一项是计算出来是f,但没在f前面加零,416f6b116549435c2c0f1143
image.png
再点进sub_401925函数,这里把IsDebuggerPresent hook了,下一次调用IsDebuggerPresent的时候会先调用sub_4015C3
image.png
加花了,去花
image.png
sub_4015C3开头把hook脱钩了,然后下面进行加密
image.png
加密后的byte_4080E0再和416f6b116549435c2c0f1143174339023d4d4c0f183e7828做比较
image.png
下面来分析一下加密流程,unk_404020是一个128位的数组,赋值给v0,v2[j] = v0[v2[j]]即换对照表,将对应的char值转成128位数组的 char值位置。for循环1到<=5,即换对照表五次
image.png
将换表完成的24个数据,分成六组数据,每一组中的每一位数据占八位bit,再用或运算拼一起,即一组数据为24+8=32bits。当中每一组数据当中的从左往右数前八位为flag[i],再数八位为flag[i+6],以此类推,循环六次。最后再将每一组合成的32bits用sprintf转成十六进制,即8个字符(两个字符占8bits,八个字符占32bits)
按照上述流程逆推即得flag,详细见如下脚本

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
enflag = "416f6b11 6549435c 2c0f1143 17433902 3d4d4c0f 183e7828"
#为了方便所以手动分成六组字符串,每组之间加空格

gen_dict = [
0x80,0x65,0x2F,0x34,0x12,0x37,0x7D,0x40,0x26,0x16,
0x4B,0x4D,0x55,0x43,0x5C,0x17,0x3F,0x69,0x79,0x53,
0x18,0x02,0x06,0x61,0x27,0x08,0x49,0x4A,0x64,0x23,
0x56,0x5B,0x6F,0x11,0x4F,0x14,0x04,0x1E,0x5E,0x2D,
0x2A,0x32,0x2B,0x6C,0x74,0x09,0x6E,0x42,0x70,0x5A,
0x71,0x1C,0x7B,0x2C,0x75,0x54,0x30,0x7E,0x5F,0x0E,
0x01,0x46,0x1D,0x20,0x3C,0x66,0x6B,0x76,0x63,0x47,
0x6A,0x29,0x25,0x4E,0x31,0x13,0x50,0x51,0x33,0x59,
0x1A,0x5D,0x44,0x3E,0x28,0x0F,0x19,0x2E,0x05,0x62,
0x4C,0x3A,0x21,0x45,0x1F,0x38,0x7F,0x57,0x3D,0x1B,
0x3B,0x24,0x41,0x77,0x6D,0x7A,0x52,0x73,0x07,0x10,
0x35,0x0A,0x0D,0x03,0x0B,0x48,0x67,0x15,0x78,0x0C,
0x60,0x39,0x36,0x22,0x7C,0x58,0x72,0x68
]

flag = [0 for i in range(24)]
#将字符串转成十六进制数
data = [int(e,16) for e in enflag.split(" ")]

#把合成的数据分开,并保存到相应flag位置
for i in range(6):
flag[i] = ((data[i] >> 24)&0xFF)
flag[i+6] = ((data[i] >> 16)&0xFF)
flag[i+12] = ((data[i] >> 8)&0xFF)
flag[i+18] = ((data[i])&0xFF)

#换对照表五次
flag = [gen_dict.index(f) for f in flag]
flag = [gen_dict.index(f) for f in flag]
flag = [gen_dict.index(f) for f in flag]
flag = [gen_dict.index(f) for f in flag]
flag = [chr(gen_dict.index(f)) for f in flag]

print("".join(flag))

得到flag{Sh1t_you_dec0d3_it},题目要求最后md5提交
最终输入4820904ccb9343f00fb7ddf8acc31e85提交
image.png
PS: 激动地就要猝死,最后半分钟成功提交flag

CATALOG
  1. 1. drinkSomeTea drinkSomeTea的附件.zip
  2. 2. Enjoyit-1 Enjoyit-1的附件.zip
  3. 3. replace replace的附件.zip