下载到程序,没有加壳,直接拖入IDA查看

main函数逻辑很简单

可以知道输入的长度是26,输入的内容不包括flag{xx},有两个判断函数。很明显第二个有些奇怪,并且进去后也是乱码。

先看第一个函数,前面有一大段复杂的代码

可以从v3判断出是处理了8个输入,不过具体干什么不清处,先放着。

紧接着又处理了2个字符

具体干什么也不清楚。

这里有一个is_input_ok判断输入是否合法,输入合法进入下一步操作,上面对lpBuffer进行了加解密操作,这里的是SSE指令。

do while循环里面就是典型的加解密操作,需要的参数就是 v20和v8,n16如果上面进行了SSE指令就会赋值为16,处理剩下的32字节。

v22和v25就是时间差反调试,__rdtsc获取当前CPU时间戳,如果出现调试,字节写入0地址引发异常。

下面就是将lpBuffer处的数据复制了96字节到lpBaseAddress中,让main函数里面lpBaseAddress恢复其功能。

目前还不知道前面10位的flag,我们可以直接在x64dbg中修改WriteProcessMemory的参数,把lpBuffer改成target_pwd地址,直接dump出副本。顺便看看上面的代码干了什么事情

输入 12345678AB,观察寄存器

可以发现前面8位直接转换成数字,剩下两位还在EDX上。这里比较难看出什么逻辑,可以观察XMM寄存器

XMM0和XMM5都被循环赋值了,上面的代码仔细静态分析也能得出这个结论。我们暂且放着

回到写入内存中,我们强行修改while循环里面的判断,让其直接进入写内存操作中。

直接dump出来,这里需要处理好OEP

这里就恢复了第二个判断函数,现在我们来分析一下第二个函数干了什么

这里是以8byte为一组做了加密,我们也能找到密钥:AFSAFCEDYCXCXACNDFKDCQXC

并且调用插件FindCrypt也能找到DES_Long常量,基本就可以判断就是DES加密

不过只是普通DES加密是不需要26字节长度的密钥的,并且密钥是分三组输入sub_401500,进入这个函数查看也能发现:

猜测是常规的DES3加密,先随便输入一个试试,再VS使用openssl加密测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DES_cblock orkey[3];
DES_key_schedule keys[3];

memcpy(&orkey[0], "AFSAFCEDYCXCXACNDFKDCQXC", 24);

for (int i = 0; i != 3; ++i) {
DES_set_key_unchecked(&orkey[i], &keys[i]);
}

unsigned char input[16] = { 0 };
unsigned char output[16] = { 0 };
memcpy(input, "1234567890ABCDEF", 16);

for (int i = 0; i < 16; i += 8) {
DES_ecb3_encrypt((DES_cblock*)&input[i], (DES_cblock*)&output[i], &keys[0], &keys[1], &keys[2], DES_ENCRYPT);
}
for (int i = 0; i != 16; ++i) {
printf("%02X ", (unsigned int)output[i]);
}
printf("\n%.16s", output);

测试数据完全吻合,说明就是des ecb 3加密

加密的数据就在上面,一共16个字节,多出的8字节可以不用管

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
DES_cblock orkey[3];
DES_key_schedule keys[3];

memcpy(&orkey[0], "AFSAFCEDYCXCXACNDFKDCQXC", 24);

for (int i = 0; i != 3; ++i) {
DES_set_key_unchecked(&orkey[i], &keys[i]);
}

unsigned char endata[16] = { 0 };

*(QWORD*)endata = 0xFACE0987E6A97C50uLL;
*((QWORD*)endata + 1) = 0x6C97BB90CF0DD520LL;

unsigned char buff[16] = { 0 };

for (int i = 0; i < 16; i += 8) {
DES_ecb3_encrypt((DES_cblock*)&endata[i], (DES_cblock*)&buff[i], &keys[0], &keys[1], &keys[2], DES_DECRYPT);
}

for (int i = 0; i != 16; ++i) {
printf("%02X ", (unsigned int)buff[i]);
}
printf("\n%.16s", buff);

解密出

0dcc509a6f75849b

这是后面16字节的flag,现在来解决前面10位。我们已经知道了前面10位会处理为整形,并且存放再XMM寄存器中参与解密操作,我们仔细分析一下lpBuffer参与的解密代码:

1
2
3
4
5
6
7
8
9
10
11
12
lpBuffer_[0] = (__int128)_mm_xor_si128(
_mm_add_epi32((__m128i)XMM3, v8),
_mm_add_epi32(v20, (__m128i)lpBuffer_[0]));
lpBuffer_[1] = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)XMM2, (__m128i)XMM3), v8),
_mm_add_epi32(v20, (__m128i)lpBuffer_[1]));
lpBuffer_[2] = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)XMM2_0, (__m128i)XMM3), v8),
_mm_add_epi32(v20, (__m128i)lpBuffer_[2]));
lpBuffer_[3] = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)XMM1, (__m128i)XMM3), v8),
_mm_add_epi32(v20, (__m128i)lpBuffer_[3]));

v20其实就是 XMM4,v8就是只有两位的。SSE的寄存器结构就是一个[x1,x2,x3,x4],每个都是32位,128bit的结构。上面代码可以简化成:

1
lpBuffer_[0] = (XMM3+v8)^(v20+lpBuffer_[0])

这里的加法和异或都是4个32bit独立进行的,不会产生进位。下面的3行也是这样,只是多了一步加法。

我们可以使用z3求解器解出合适的解

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
from z3 import  *

a = BitVec('a',32)
c = BitVec('c',32)
b = BitVec ('b',8)

c = Concat(b,b,b,b)
# c = [b,b,b,b]
# (XMM3[0] +b)^(a+BUFF[0]) = 0x83EC8B55

xmm3 = [0x00000000, 0x00000001, 0x00000002, 0x00000003]
xmm2 = [0x00000004, 0x00000004, 0x00000004, 0x00000004]
xmm2_0 = [0x00000008, 0x00000008, 0x00000008, 0x00000008]
xmm1 = [0x0000000C, 0x0000000C, 0x0000000C, 0x0000000C]

xmm= []
xmm.extend(xmm3)
# xmm2+xmm3
xmm.extend([xmm2[i]+xmm3[i] for i in range(4)])
# xmm2_0 +xmm3
xmm.extend([xmm2_0[i]+xmm3[i] for i in range(4)])
# xmm1 + xmm3
xmm.extend([xmm1[i]+xmm3[i] for i in range(4)])
#len (xmm) = 16 =>4*16
print(xmm)

buff0 = [0x7AB29357, 0xE98FBCC7, 0xFD0E0A2C, 0x3D5E1084, #1
0x84C9FB26, 0xFB39F0A3, 0xED150C28, 0x3D4F7424, #2
0x3CCE6C2A, 0x065C0BEB, 0x28EA503D, 0x5715BAFE, #3
0x3D4F842B, 0xAF152E2F, 0x064DEFEC, 0x63184D41, #4
0x214A93F2, 0x218A98EF, 0xFD0E0DA0, 0x065E0C0B, #5
0xF8EA5047, 0xFD15648F, 0x884E3238, 0xFB55F0B5] #6

expect = [0x83EC8B55, 0xEC81F0E4, 0x00000278, 0x405004A1,
0x89C43300, 0x02742484, 0x100F0000, 0x4041A805,
0x41C0A000, 0x0F560040, 0x2C244411, 0x7E0FF357,
0x4041B805, 0xD60F6600, 0x0F402444, 0x6A0A4110,
0x24448840, 0x24848D4C, 0x000001FC, 0x0F50006A,
0x1C244411, 0x000F58E8, 0x8D406A00, 0x02482484]

s = Solver()

for i in range (16):
s.add( ( (xmm[i] + a) ^ (c + buff0[i]) ) == expect[i])

print(s.check())
if s.check() == sat:
mod =s.model()
print(mod[b])
print(mod[a])
#0dcc509a6f75849b
#1324 2238 16

最终可以求解出:1324223816

最终的flag就是flag{13242238160dcc509a6f75849b}

题目还是比较有难度的,考的知识点也比较多,识别3DES还是比较有难度的,SSE指令集也比较复杂,很容易把人劝退,z3求解也是需要稍稍思考