DASCTF三月赛

drinkSomeTea

题目本身不难,就是一个tea加密,但是用python写这个脚本属实是坑多,给折磨了好久

主要的坑

  • python 的右移运算符 >> 是逻辑右移(左补0),c语言里的 >> 是算术右移(左补符号位)
  • 文件读写的大端序小端序的问题,也借此简单学习了一下python的struct库

以后遇见这种文件读写的题目还是用c写吧,害

分析

通过下面这里fake_flag在内存中的排放可知,这个程序是小端序的,所以写脚本的时候也要注意小端序输出字节流

1

然后看一下加密的核心源码(润色后的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int *__cdecl enc_fuc(int *pfile, _DWORD *fake_flag)
{
int *result; // eax
int p0; // [esp+Ch] [ebp-14h]
int i; // [esp+14h] [ebp-Ch]
int key; // [esp+18h] [ebp-8h]
int p1; // [esp+1Ch] [ebp-4h]

key = 0;
p1 = pfile[1];
p0 = *pfile; // 每次对8个字节进行修改
for ( i = 0; i < 32; ++i )
{
key -= 0x61C88647;
p0 += (fake_flag[1] + (p1 >> 5)) ^ (key + p1) ^ (*fake_flag + 16 * p1);
p1 += (fake_flag[3] + (p0 >> 5)) ^ (key + p0) ^ (fake_flag[2] + 16 * p0);
}
pfile[1] = p1;
result = pfile;
*pfile = p0;
return result;
}

exp

直接放解密脚本吧(python),里面的算术右移函数是抄的Mas0n师傅的脚本

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
import struct

def sar(DWORD=0, num=0):
"""
python没有算数右移,笨办法模拟了一下算数右移
:param DWORD: 四字节 整数
:param num:右移量
:return:移位后整数
"""
binWORD = bin(DWORD) # 四字节转二进制
signbit = binWORD[2:3] # 取符号位
DWORD = DWORD >> num # 逻辑右移
if signbit == 0 or len(binWORD[2:]) != 32: # 高位为 0 或 不足32位
return DWORD # 直接返回右移结果
else:
bit = ["1"] * num # 高位 补num位 1
DWORD = "0b" + "".join(bit) + bin(DWORD).replace('0b', '').zfill(8) # 组合成二进制形式
return int(DWORD, 2) # 转整数

filein=open('tea.png.out', mode='rb')
fileout=open('tea.png', mode='wb')
file_thing=filein.read()
v8=len(file_thing)

flag=b''
#fake = 'flag{fake_flag!}' # 16个字节
fake = [1734437990, 1801545339, 1818648421, 2099341153]
for i in range(v8 // 8):
p0=file_thing[8*i] | file_thing[8*i+1] << 8 | file_thing[8*i+2] << 16 | file_thing[8*i+3] <<24
p1=file_thing[8*i+4] | file_thing[8*i+5] << 8 | file_thing[8*i+6] << 16 | file_thing[8*i+7] <<24
key=0xc6ef3720
for j in range(32):
p1 -= (fake[3] + sar(p0, 5)) ^ (key + p0) ^ (fake[2] + 16 * p0)
p1 = p1 & 0xffffffff
p0 -= (fake[1] + sar(p1, 5)) ^ (key + p1) ^ (fake[0] + 16 * p1)
p0 = p0 & 0xffffffff
key -= 0x9e3779b9
key = key & 0xffffffff

flag += struct.pack('<I',p0)
flag += struct.pack('<I',p1)

fileout.close()
filein.close()

2

一个小技巧

一般常见格式的文件,像这次这种png,他都有一些固定的文件头,可以观察正确格式下对应文件的文件头来辅助逆向分析

Enjoyit-1

这题其实蛮简单,边吃饭边看,吃完饭就出解了

分析

是c#写的,拖进dnSpy,贴一下主要逻辑代码

3

  1. 先是一个魔改了编码表的base64加密后密文的判断
  2. 然后中间具体的先不看,密文判断完后,有一个很长的sleep(图中只sleep了1秒,原来好像是10w秒来着,直接给他改了),然后好像是输出正确flag
  3. 修改一下源码,保存,再运行,flag直接出,关键的解密逻辑甚至看都不用看

4

replace

虽然说这题难度标的是困难,但给我的感觉倒不是最难的,那道扫雷到现在我都还一点头绪没有2333(稍微玩了一下,应该是少了一行,没法全扫光)

说回这道题目,一开始没去管太多,看到一个异或加密,直接写个脚本跑一下,好家伙果然是fake flag hhh

然后再仔细分析了下,发现一些反调试和花指令,去花之后真正的逻辑就容易看出来了

这题没什么太多新东西,就直接贴脚本了

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
text = [39, 3, 10, 118, 30, 26, 54, 46, 73, 46, 48, 28, 118, 28, 95, 99, 86, 40, 19, 105, 116, 95, 31, 85]
print(len(text))
text2 = [0x41, 0x6f, 0x6b, 0x11, 0x65, 0x49, 0x43, 0x5c, 0x2c, 0x0f, 0x11, 0x43, 0x17, 0x43, 0x39, 0x02, 0x3d, 0x4d,
0x4c, 0x0f, 0x18, 0x3e, 0x78, 0x28]
chars2 = [128, 101, 47, 52, 18, 55, 125, 64, 38, 22, 75, 77, 85, 67, 92, 23, 63, 105, 121, 83, 24, 2, 6, 97, 39, 8, 73,
74, 100, 35, 86, 91, 111, 17, 79, 20, 4, 30, 94, 45, 42, 50, 43, 108, 116, 9, 110, 66, 112, 90, 113, 28, 123,
44, 117, 84, 48, 126, 95, 14, 1, 70, 29, 32, 60, 102, 107, 118, 99, 71, 106, 41, 37, 78, 49, 19, 80, 81, 51,
89, 26, 93, 68, 62, 40, 15, 25, 46, 5, 98, 76, 58, 33, 69, 31, 56, 127, 87, 61, 27, 59, 36, 65, 119, 109, 122,
82, 115, 7, 16, 53, 10, 13, 3, 11, 72, 103, 21, 120, 12, 96, 57, 54, 34, 124, 88, 114, 104]
tmp=[]
for i in range(4):
tmp.append(text2[i])
tmp.append(text2[i+4])
tmp.append(text2[i+8])
tmp.append(text2[i+12])
tmp.append(text2[i+16])
tmp.append(text2[i+20])
for i in range(len(tmp)):
print(hex(tmp[i]),',',end='')
print()
for i in range(5):
for j in range(24):
tmp[j] = chars2.index(tmp[j])
for i in range(24):
print(chr(tmp[i]),end='')
print()

# for i in range(24):
# print(chr(text2[i] ^ text[i]),end='')

flag{Sh1t_you_dec0d3_it}

Comments