前言
好难的题啊,唉即使已经被各路大佬提示了好几次还是写不出来,我好菜我好菜我好菜…(此处省略100字)
前面的部分可以看liv师傅的视频,我主要补充下剩下来的汇编分析
https://www.bilibili.com/video/BV17hBQBqEda/?spm_id_from=333.1391.0.0&vd_source=97e6e1f7527112411786b01f30007712
分析
花指令分析
我们可以看到这里都是一堆
- sse/avx指令
- push reg ;pop reg ;call 某个地址
- 无意义的浮点数操作 这些我们都不要看,很有可能你单步执行跳来跳去半天才到真实的指令,所以要有耐心
获取输入的正确字符串长度
- 接下来的步骤建立在你已经看过视频,使驱动正常跑起来
我们在strlen下断点bp nt!strlen然后随便输入点字符串,便会断下,我们先用db rcx命令可以看到rcx的值也就是我们输入的值
点击step out,r rax命令可以看到我们strlen的返回值
小知识,x64调用约定
rcx,rdx,r8,r9分别是前四个参数,rax是返回值
下面使一堆奇奇怪怪的运算,最后eax cmp了一个奇奇怪怪的值,遇到这种混淆的,我们要抓住主干,比如这里使rax
mov rdi,rax ;这一步把我们的结果给了rdi
mov eax,r8d ;把r8d给了rax,那r8d呢?
;往上找找
mov r8d,dword ptr [easyDriver!malloc+0x18050 (fffff805`45fa9050)]
;也就是说rax被赋值了一个固定的值, 我调出来是:0x22222212
xor eax,edi
cmp eax,22222222h
最后得到我们的到输入rax应该是48才行
分析加密
我们在之后db rcx时,把字符串记录下来,下个硬件断点
ba r8 字符串地址
会先遇到
mov r8, 7EFEFEFEFEFEFEFFh
mov r11, 8101010101010100h
mov rdx, qword ptr [rax]
mov r9, r8
add rax, 8
add r9, rdx
not rdx
xor rdx, r9
and rdx, r11
je ... (回到 mov rdx,[rax] 那里)"
这是0字符扫瞄,我们继续运行,遇到
hit
mov eax, dword ptr [rdx] rdx=字符串地址
;eax 作为为前8位,我们计为left
mov r9d,dword ptr [rdx+4] rdx=字符串地址
;r9d 作为为前8-16位,我们计为right
接着看到
mov r10d,11111111h
xor r10d,dword ptr [easyDriver!malloc+0x18018 (fffff805`45fa9018)]
;然后就可以看到r10d变为000000009E3779B9这明显是tea的delta
继续
mov esi,r9d
mov edi,r9d
shl esi,4
shr edi,5
xor edi,esi
add edi,r9d
;整理一下就是temp = edi = (right<<4) ^ (right>>5) + right
继续
mov esi,r11d
;这里r11d位sum
and esi 3
mov esi,dword ptr [r8+rsi*4]
;这里r8就是key的地址,所以这里是key[sum & 3]
db r8可以看到key的值
kd> dd r8
ffffd20d`14cd6ad0 3c4ed885 12af3e87 d6e1b31f 25c10aa0
add esi,r11d
;所以是temp2 = esi = Key[Sum & 3] + Sum;
xor esi,edi ;esi = temp3 = temp1 ^ temp2
add eax,esi ;left += temp3
add r11d,r10d ;sum += delta
mov esi,eax ;esi = left
接下来类似
shl esi,4 ;esi << 4
mov edi,eax ;edi = left
shr edi,5 ;edi >> 5
xor edi,esi ;temp = (left<<4) xor ( left >> 5)
add edi,eax ;temp2 = temp + left
mov esi,r11d ;esi = sum
shr esi,9 ;esi = sum >> 9
and esi,0Ch ;esi = esi & 0xc
mov esi,dword ptr [r8+rsi] ;esi = key[(sum & 0xc)/4]
add esi,r11d ;esi += sum
xor esi,edi ;esi = edi ^ temp2
add r9d,esi ;left += esi
这边已经完成了对左右值的第一次变换然后看到一个dec ecx, 相当与轮数,可以看到ecx的值是0xff
差不多可以还原为
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
void encrypt(DWORD *data)
{
DWORD Key[]{0x3c4ed885, 0x12af3e87, 0xd6e1b31f, 0x25c10aa0};
DWORD Delta = 0x9e3779b9;
DWORD d1{}, d2{};
d1 = data[0];
d2 = data[1];
DWORD Sum{};
for (int i = 0; i < 0xff; i++)
{
DWORD tmp1{}, tmp2{}, tmp3{};
tmp1 = ((d2 << 4) ^ (d2 >> 5)) + d2;
tmp2 = Key[Sum & 3] + Sum;
tmp3 = tmp1 ^ tmp2;
d1 += tmp3;
Sum += Delta;
tmp1 = ((d1 << 4) ^ (d1 >> 5)) + d1;
tmp2 = Key[((Sum >> 9) & 0xC) / 4] + Sum;
tmp3 = tmp1 ^ tmp2;
d2 += tmp3;
}
data[0] = d1;
data[1] = d2;
}
我们直接继续,会断在
mov dword ptr [rdx], eax
mov dword ptr [rdx+4], ecx
这就是把值还回去了,继续运行可以看到
fffff805`45fa7ab3 483b0411 cmp rax,qword ptr [rcx+rdx]
这里可以看到对比的值,密文在rcx+rdx,我们看看
1
2
3
fffff805`45fa9090 2a90bfd5 aa53b7ef fc23b031 dd985d10
fffff805`45fa90a0 ff5ceea4 0ade98c1 c5bb1dd3 60a3d0cc
fffff805`45fa90b0 c02fcca9 dd9f252b 9750cd50 808a76e5
一点关于memcmp的细节:
memcmp 常见的三段式结构:
先按 32 字节块比 → 再按 8 字节块比 → 最后按 1~7 字节比(你没贴出来的是最后那段)
最后解题脚本
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
#include <iostream>
#include <windows.h>
void decrypt(DWORD *data)
{
DWORD Key[]{0x3c4ed885, 0x12af3e87, 0xd6e1b31f, 0x25c10aa0};
DWORD Delta = 0x9e3779b9;
DWORD d1{}, d2{};
d1 = data[0];
d2 = data[1];
DWORD Sum = Delta * 0x257;
for (int i = 0; i < 0x257; i++)
{
DWORD tmp1{}, tmp2{}, tmp3{};
tmp1 = ((d1 << 4) ^ (d1 >> 5)) + d1;
tmp2 = Key[((Sum >> 9) & 0xC) / 4] + Sum;
tmp3 = tmp1 ^ tmp2;
d2 -= tmp3;
Sum -= Delta;
tmp1 = ((d2 << 4) ^ (d2 >> 5)) + d2;
tmp2 = Key[Sum & 3] + Sum;
tmp3 = tmp1 ^ tmp2;
d1 -= tmp3;
}
data[0] = d1;
data[1] = d2;
}
int main()
{
DWORD Enc[]{0x2a90bfd5, 0xaa53b7ef, 0xfc23b031, 0xdd985d10, 0xff5ceea4, 0x0ade98c1, 0xc5bb1dd3, 0x60a3d0cc, 0xc02fcca9, 0xdd9f252b, 0x9750cd50, 0x808a76e5};
for (int i = 0; i < 8; i++)
{
decrypt((DWORD *)((BYTE *)Enc + i * 8));
}
printf("flag{%.48s}\n", Enc);
// flag{C3A1F8E0B9D24765A9C0E1B4F3D687029514A3E8B6D7C2F0}
return 0;
}