x64dbg-算法分析三.md
9unk Lv5

目标程序

程序下载:CRACKME2

解压密码:9unk

任务目标:分析程序的算法,并写出注册机程序。

观察程序

1.jpg

算法分析

加载程序到入口点

2.jpg

查看程序的 API ,并设置断点

3.jpg

继续运行程序

4.jpg

执行到返回,查看程序返回的参数

5.jpg

转到内存数据窗口,然后将对 password 设置内存访问断点

6.jpg

7.jpg

“F9” 继续运行程序,此时程序来到了判断序列号的地方。并单步运行并分析程序。

1
2
3
4
mov byte ptr ds:[0x00402118], 0x0
mov esi, dword ptr ss:[esp+0x4]
push esi
// 先把 password 存到栈中

8.jpg

1
2
3
4
5
mov al, byte ptr ds:[esi]   // 把 password 传入 al 中
test al, al // 测试 al 是否为空
je 0x00401390 // 如果 al 为空就跳转循环
inc byte ptr ds:[0x00402118] // al 不为零 00402118 内存地址的数据加 1。也就是说每循环一次加1,是用来记录循环的次数。
// 可以看到我们之前的 00402118 内存地址的数据设为 0

9.jpg

1
2
3
4
cmp al, 0x41    // al 的值和 A(0x41) 比较
jb 0x00401385 // 如果小于就跳转
cmp al, 0x5A // al 的值和 Z(0x5A) 比较
jae 0x00401388 // 如果高于等于就跳转

10.jpg

11.jpg

我们这里还需要再注意一下它跳转的地址,加上我们之前的跳转一共有三个。第一个跳转(程序正常循环结束:pop esi,并跳转到 call-2);第二个跳转(password 的值是数字:esi+1,并跳转循环);第三个跳转(password 的值是字母:跳转到 call-1)

很明显这里的第三次跳转是错误的,因为它没有正常的进行循环判断;第一次跳转可能是用来生成正确的序列号;第二次跳转可能是正常的序列号判断。

这里没判断到 A~Z 之间的字符串

我们正常循环结束到 call 指令处,可以看到这里把之前压入栈中的 password ,又重新弹出给 esi 寄存器了。

12.jpg

步入 call-2 的部分,查看其中的算法。

13.jpg

先把寄存器清零

14.jpg

然后将 4021A3 内存地址中的数据(Messing_in_byte)存入 cl

15.jpg

1
2
3
mov bl, byte ptr ds:[esi]   // password 存入 bl 中
test bl, bl // 测试 bl 是否为空
je 0x004013B1 // 值为空就跳出循环

16.jpg

1
2
xor bl, cl      // bl = bl xor cl
mov byte ptr ds:[esi], bl // 把 bl 存入 esi 内存地址处。

可以看到这里把异或出来的值替换到原来的 password 处

18.jpg

17.jpg

19.jpg

1
2
3
inc esi
inc edi
jmp 0x0040139D

结束第一次循环

20.jpg

循环结束后,可以看到此时的 “123456” 计算成了 “|W@G\X”。继续单步运行

21.jpg

继续单步运行

22.jpg

执行到 call 指令处

23.jpg

单步步入,寄存器清零

24.jpg

1
mov cl, byte ptr ds:[0x00402118]

我们转到内存数据窗口查看这个数据,发现这个就是我们输入序列号的字符数

25.jpg

1
mov esi, dword ptr ss:[esp+0x4]

生成的序列号存到 esi 中

26.jpg

这里多个反斜杠,这个应该是转义字符串

1
mov edi, 0x402150

27.jpg

1
2
3
4
// repe 是一个串操作前缀,它重复串操作指令,每重复一次 ECX 的值就减 1,一直到 CX 为 0 或 ZF 为 0 时停止。
// cmpsb 是字符串比较指令,把 ESI 指向的数据与 EDI 指向的数一个一个的进行比较。
repe cmpsb // 比较 esi 和 edi 的字符串。如果值相同就继续比较,如果不相同就不继续比较
// 也就是说如果 ecx 循环的值是 0 ,就说明两个值相同。ecx 的值不为 0,两个值就是不相同的。

28.jpg

29.jpg

我们这里把之前 xor 的值 和 比较的值都记录下来。

1
2
异或值:Messing_in_bytes
比较值:310x1F)、440x2C)、550x37)、540x36)、590x3B)、610x3D)、400x28)、250x19)、610x3D)、380x26)、260x1A)、490x31)、450x2D)、590x3B)、55(0x37)、62(0x3E)

继续单步执行

1
2
3
add esp, 0x4    // esp + 4
test cl, cl // 判断 cl 是否为 0
je 0x0040124A // cl 为 0 跳转正确,cl 不为 0 跳转错误

30.jpg

我们现在再来分析一下算法部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
序列号 xor Messing_in_bytes = 比较值

也就是说:序列号 = Messing_in_bytes xor 比较值


// 正确序列号
M(0x4D) xor 1F = 82 = R
e(0x65) xor 2C = 73 = I
s(0x73) xor 37 = 68 = D
s(0x73) xor 36 = 69 = E
i(0x69) xor 3B = 59 = ;
n(0x6E) xor 3D = 83 = S
......

31.jpg

32.jpg

这里的序列号是固定值,就不编写注册机了。

reference

repe cmpsb指令解析

  • 本文标题:x64dbg-算法分析三.md
  • 本文作者:9unk
  • 创建时间:2020-12-30 01:48:18
  • 本文链接:https://9unkk.github.io/2020/12/30/x64dbg-suan-fa-fen-xi-san/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!