0x00 分析

两个反调+魔改TEA

总体流程不难

0x01 解

无壳,直接拖IDA

发现main里面有个经典的检测调试器的函数IsDebuggerPresent

Tab查看汇编

这边patch直接将判断条件改为IsDebuggerPresent < 2即可

即将cmp eax, 1改为cmp eax, 2即可

之后再看看其他

发现调用了两次chrono::system_clock::now()这里使用的是一种反调试,具体原理可以看这个:RDTSC时钟检测反调试

也就是说在这两个之间如果做了中断行为,则if的结果就会不同,可以在这里下断点进行测试,尝试在call _ZNSt6chrono3_V212system_clock3nowEv之前下断点和不下断点观察结果

这边就不测试了(懒)

总之正常的结果应该走右边,即不使用mov rax, 22002200220022h所在的这块

这边改jlejmp就行

之后apply patch就可以愉快调试了

patch之后断在try again上面的判断,一般来说这里存的就是密文了

执行两次,输入两个不同的值,发现v8的值都不变,大概就能判断出v8里面就是密文了

dump下来

1
bytearray(b'vq\x9d\xe7pw?\xa3\x02\xf1\x8d\xc9\x02\xc6\xa2K\xba\x19V\x05\xf2\x89^\xe0')

然后就可以开始倒着看了

首先是这里一大堆__mm_xor_si128(),而且还给了个0x330033003300330033003300330033

合理怀疑这个0x330033003300330033003300330033就是异或的key了

去汇编验证一下发现确实是这么回事

所以第一步就是异或这些个0x33

然后往上走,可以发现这两个函数

调试一下可以发现他俩将数据进行了一个置换,置换的结果如下所示

1
2
3
4
输入为
0x666c6167,0x7b616161,0x61616161,0x6161617d
置换结果为
0x6161617d,0x61616161,0x7b616161,0x666c6167

相当于变成倒序了

这里看到有个24,据此可以判断flag长度应该为24

不过dump下来的密文长度也可以说明这点

之后再往上可以看到连续调用了一个函数sub_401E80三次

进去滑到最下面可以发现是TEA加密

不过魔改得挺多

那么接下来就是找key了

key好找,直接看xmmword_408040就好,不过需要注意使用的顺序

不过这还没有结束

往上看可以找到这个密钥的来源

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
v1 = dword_404010;
v2 = &xmmword_408040;
xmmword_408040 = (__int128)_mm_load_si128((const __m128i *)&xmmword_405050);
v3 = dword_404010 + 4;
do
{
v4 = v1;
if ( v1 <= 0 )
{
v12 = 1;
}
else
{
v5 = 0;
v6 = 0;
v7 = 1;
v8 = 1;
v9 = 1;
do
{
if ( (v4 & 1) != 0 )
{
v10 = v6;
v6 = v7 * v8 + v5 * v6;
v8 = v7 * v10 + v9 * v8;
}
v11 = v7 * v7;
v7 *= v5 + v9;
v5 = v11 + v5 * v5;
v4 >>= 1;
v9 = v9 * v9 + v11;
}
while ( v4 );
v12 = v8;
}
++v1;
*(_DWORD *)v2 = v12;
v2 = (__int128 *)((char *)v2 + 4);
}
while ( v1 != v3 );
dword_404010 = v1;

程序会将刚刚改的三目运算符的结果dword_404010丢进去进行运算才得出的密钥,而且每次运算后dword_404010的结果都会被保留,下次再调用加密函数的时候会用新的结果进行密钥的运算

这个时候就需要用动调获取出所有的key了

这里就不做过多的演示了,直接上三次的密钥

1
2
3
k1=[0x5,0x3,0xD,0x8]
k2=[0x22,0x15,0x59,0x37]
k3=[0xE9,0x90,0x262,0x179]

之后就可以写脚本了,将原始的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
41
42
43
44
45
46
from ctypes import * 
import struct

def decrypt(v,k):
for i in range(2):
v[i]^=0x33333333
v.reverse()
v0=c_uint32(v[0])
v1=c_uint32(v[1])
delta=0x9E3449B8
sum1=0x987E55D0
while(sum1 != 0):
v1.value-=((v0.value+sum1)^((v0.value>>4)+k[2])^((v0.value<<5)+k[3]))& 0xFFFFFFFF
v0.value-=((v1.value+sum1)^((v1.value>>4)+k[0])^((v1.value<<5)+k[1]))& 0xFFFFFFFF
sum1-=delta
sum1 &= 0xFFFFFFFF
return v0.value,v1.value

ba = bytearray(b'vq\x9d\xe7pw?\xa3\x02\xf1\x8d\xc9\x02\xc6\xa2K\xba\x19V\x05\xf2\x89^\xe0')
enc = struct.unpack(">"+"i"*6,ba)
enc=list(enc)
for i in range(len(enc)):
enc[i] &= 0xFFFFFFFF
k1=[0x5,0x3,0xD,0x8]
k2=[0x22,0x15,0x59,0x37]
k3=[0xE9,0x90,0x262,0x179]

e1 = enc[0:2]
e2 = enc[2:4]
e3 = enc[4:6]
flag = bytearray()

res = decrypt(e3,k1)
tmp = struct.pack("ii",res[0],res[1])
flag += tmp

res = decrypt(e2,k2)
tmp = struct.pack("ii",res[0],res[1])
flag += tmp

res = decrypt(e1,k3)
tmp = struct.pack("ii",res[0],res[1])
flag += tmp

print(flag)
# flag{y0u_reallyl1ke_te@}