吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 14205|回复: 26
收起左侧

[原创] 160 个 CrackMe 之 126 - NZC.1 手动脱壳和注册算法追踪及注册机实现

  [复制链接]
solly 发表于 2019-5-29 15:13
本帖最后由 solly 于 2019-5-29 23:47 编辑

160 个 CrackMe 之 126 - NZC.1 手动脱壳和注册算法追踪及注册机实现
CrackMe集合中的第126个 NZC.1 在 Windows 10 下是无法直接运行的,但在OD中可以载入,表示其可以在Windows10下运行,只是运行时执行了非法的指令(或访问了无效的内存),一般这些指令都是由那些老掉牙的AntiDebug检查用的,主要是针对SoftICE检查,直接执行了 int 68,int 3 等指令。
首先,我们检查一下软件壳。
01.jpg
显示有未知的壳。节的情况如下:
02.png
最后有一个 .defiler 的节,Defiler有掩蔽, 遮蔽之意,看来这个节就是壳了。


因为壳是未知的,只有手动脱壳,下面进行手动脱过程的讲解,内容有点长,有点长,有点长,截图就有50多个。


首先用OD载入 NZC.1.exe,入口第1条指令就是一个跳转指令,如下图,而且代码看起来也很乱,还有无效指令,如下图:
05.png

先不要运行,按回车跟随,来到下一条指令,如下图:
06.png
看到第2条执行的指令码为”EB 02“,也就是一条 JMP 指令,但跳转位置是在另一指令中间,并且上图中有好几个 "EB 02" 指令码及显示无效的指令,这些都符合”花指令“特征,因此,我们先处理这些花指令,便于我们后面的分析。
按”-“号,回退到程序入口,如下图:
07.png
点击“右键”,选择菜单项“去除花指令”=>“ObSiDiUm”,也就是子菜单的第1项,执行完后,弹出对话框,告诉我们去除了多少花指令:
08.png
去除花指令后,代码界面一下子多出了好多的 “NOP” 指令,有效指令一下子少了很多,如下图:
09.png

因为默认情况,直接执行去除花指令的操作只处理 0x400个字节内的花指令,并没有扫描全部代码,我们手动拖动滚动条来到0x0040D400处,果然没有处理完毕,还有一些花指令:
10.png
再次在这里("0x0040D400“)右键菜单执行一次前面的操作,这次又去除了  41 条花指令:
11.png
去除花指令的步骤先到此暂停,后面还有去除花指令的操作,不过现在无法执行,因为那些代码还是加密的,如下图所示,在没解密前也去不了花指令了:
12.png



现在我们开始跟踪CM的壳代码。


再次回到原点,如下图:
13.png
按几次F8,慢点按,按快了就可能要重新来一次了。
来到下图位置:
14.png
这里有一个常用的手法,就是 call 下一条代码(指令码为 E8 00000000),然后一个 pop 指令将返回地址取出来,这个就是用来取得指令区的当前执行指令的地址的,这里相当于取得  0x0040D246这个地址,并存放到 EBP 中了,所以执行这样的 call 调用时,一定要按F7进入,因为这个call是不会返回来了的。
F7进入后,再按F8往下走,来到这里:
15.png

来到这个指令位置 0x0040D25B call 0x0040D100,不了解功能,只有按F7跟踪进去,在进去前,先在这个call的下一条指令上按F2下一个中断,防止不小心按了F9返回时没停下来(后面有些进入call的情况也可以这样处理),返回来后再禁用或取消这个断点。
16.png
进入后又是一个调用,老办法,再次按 F7 进入调用,仔细看,其实就是调用了后面10个字节后的指令。
17.png

这个调用功能很简单,就是寻找系统库 Kernel32.dll 文件在内存中位置,这个位置也就是该dll的 hInstance,后面的代码需要用到这个 hInstance。
因为 Windows 加载程序或dll一般都是按 0x10000 大小对齐内存的,所以只要找到一个库中的地址,这里是在栈中读取的用于返回  KERNEL32.BaseThreadInitThunk 的地址,并去掉低16位,形成 0x75460000的对齐地址,以此为基础,寻找 Kernel32.dll在内存中的存放位置,并取得 hInstance。同时,这两次call调用是利用同一个 retn 指令返回去的。
这两层 call 调用代码很短,几个F8后就找到hInstance并返回了,再次F8往下走,来到第1个解码代码处:
18.png

这里是对数据区的字符串资源进行解码,定位到JNZ指令的下一条指令,按F4执行解码循环,解码结果如下:
19.png

解码出来的是一些API函数名和库名。后面要用到这些名字,取得API的的入口地址,下面是关键代码和数据变化情况:
[Asm] 纯文本查看 复制代码
解码数据1:
0040D29E    B9 66000000         mov     ecx, 66                                                        ; 开始数据解码,API函数库和函数名等字符串数据
0040D2A3    8D9D F8284000       lea     ebx, dword ptr [ebp+4028F8]                                    ; ebx == 0x0040D8CC, EBX ==>解码开始位置
0040D2A9    300B                xor     byte ptr [ebx], cl                                             ; CL == 0x66
0040D2AB    8003 40             add     byte ptr [ebx], 40
0040D2AE    43                  inc     ebx
0040D2AF    49                  dec     ecx
0040D2B0    85C9                test    ecx, ecx                                                       ; ecx=0x66,长度为  0x66字节
0040D2B2  ^ 75 F5               jnz     short 0040D2A9

解码前:
0040D8CC  61 40 50 73 50 4E 43 5E 7A 79 6E 7E 69 6A 98 48  a@PsPNC^zyn~ij楬
0040D8DC  7A 7A 64 76 7C 91 5C 60 6F 69 40 62 68 7B 69 75  zzdv|慲oi@bh{iu
0040D8EC  7F 44 84 4E 67 72 73 1E 19 18 3E 14 02 38 F8 32  D凬grs>8?
0040D8FC  0E 1C 00 23 00 1E 13 0A 1D 1E EC 38 0F 1D 3C 0E  .#..?<
0040D90C  0B 00 16 E3 30 04 01 3B 0E 2F 33 38 3F 2A 2B 1A  .?;/38?*+
0040D91C  33 38 3B 21 2B D1 05 3C 2B 3F FF F9 CA 02 2D 35  38;!+?<+??-5
0040D92C  28 20 28 F0 F0 C1                                ( (痧?.
解码后:
0040D8CC  47 65 74 50 72 6F 63 41 64 64 72 65 73 73 00 5F  GetProcAddress._
0040D8DC  6C 6F 70 65 6E 00 4C 6F 61 64 4C 69 62 72 61 72  lopen.LoadLibrar
0040D8EC  79 41 00 4D 65 73 73 61 67 65 42 6F 78 41 00 45  yA.MessageBoxA.E
0040D8FC  78 69 74 50 72 6F 63 65 73 73 00 53 65 74 54 69  xitProcess.SetTi
0040D90C  6D 65 72 00 52 65 61 64 50 72 6F 63 65 73 73 4D  mer.ReadProcessM
0040D91C  65 6D 6F 72 79 00 55 73 65 72 33 32 00 4B 65 72  emory.User32.Ker
0040D92C  6E 65 6C 33 32 00                                nel32.

将API解码出来后,下一步是寻找这些API的入口地址了,一路F8来到 0x0040D2E3 ,如下图:
20.png
来到  0x0040D2E3后,又是一个 call 调用,我们按  F7 跟进去,并F8来到下面:
21.png

这一段代码的主要功能就是在 Kernel32.dll 中的导出表中的函数名列表中寻找 LoadLibraryA()函数名,并计算得到其入口地址。这里会用前面寻到 kernel32 的 hInstance,并会对其进行PE文件格式判断,然后遍历导出表中的函数名列表,找到需要的函数并计算该函数的入口地址,代码如下,我把花指令的NOP去掉了,所以指令地址不是连续的了:
[Asm] 纯文本查看 复制代码
0040D005    90                    nop                                                ; Get_API_Address Proc
0040D009    58                    pop     eax
0040D00E    5B                    pop     ebx
0040D013    5F                    pop     edi
0040D019    59                    pop     ecx
0040D01E    50                    push    eax
0040D024    8BD3                  mov     edx, ebx                                   ; ebx == hInstance
0040D02A    66:813B 4D5A          cmp     word ptr [ebx], 5A4D                       ; 'MZ'
0040D033    0F85 C4000000         jnz     0040D0FD
0040D03E    0FB743 3C             movzx   eax, word ptr [ebx+3C]
0040D046    03D8                  add     ebx, eax
0040D04D    66:813B 5045          cmp     word ptr [ebx], 4550                       ; 'PE'
0040D056    0F85 A1000000         jnz     0040D0FD
0040D060    8B5B 78               mov     ebx, dword ptr [ebx+78]
0040D067    03DA                  add     ebx, edx                                   ; edx=0x756D0000,Kernel32.dll的hInstance
0040D06D    0BFF                  or      edi, edi                                   ; EDI ===> "LoadLibraryA", "GetProcAddress"
0040D073    74 78                 je      short 0040D0ED
0040D079    8B73 20               mov     esi, dword ptr [ebx+20]
0040D080    03F2                  add     esi, edx
0040D086    8B4B 18               mov     ecx, dword ptr [ebx+18]                    ; ecx == 0x643
0040D08D    53                    push    ebx
0040D092    33DB                  xor     ebx, ebx
0040D094    90                    nop                                                ; 循环开始位置
0040D098    AD                    lods    dword ptr [esi]                            ; eax ===> 内存区缓冲区,全0
0040D09D    03C2                  add     eax, edx                                   ; eax == 7576B1F2, EAX ==>"AcquireSRWLockExclusive"
0040D0A3    56                    push    esi
0040D0A4    57                    push    edi
0040D0A9    87FE                  xchg    esi, edi                                   ; ESI ===> "LoadLibraryA", "GetProcAddress"
0040D0AF    97                    xchg    eax, edi                                   ; EDI ===> "AcquireSRWLockExclusive"
0040D0B4    AC                    lods    byte ptr [esi]                             ; al = 0x4C, "LoadLibraryA"的第1个字母的ASCII码值
0040D0B9    0AC0                  or      al, al                                     ; 检查导入表是否结束
0040D0C0    75 0B                 jnz     short 0040D0CD
0040D0C2    90                    nop
0040D0C6    803F 00               cmp     byte ptr [edi], 0
0040D0C9    74 0B                 je      short 0040D0D6
0040D0CB    EB 07                 jmp     short 0040D0D4
0040D0CD    3807                  cmp     byte ptr [edi], al                         ; 检查 [edi] 是否等于 al == 'L', 查找 LoadLibraryA 的位置
0040D0CF    75 03                 jnz     short 0040D0D4
0040D0D1    47                    inc     edi
0040D0D2  ^ EB DC                 jmp     short 0040D0B0                             ; 循环比较字符串是否"LoadLibraryA"
0040D0D4    B0 01                 mov     al, 1
0040D0D6    5F                    pop     edi
0040D0D7    5E                    pop     esi
0040D0D8    0AC0                  or      al, al
0040D0DA    74 06                 je      short 0040D0E2                             ; al=0则中断循环,表示找到了"LoadLibraryA"
0040D0DC    43                    inc     ebx
0040D0DD  ^ E2 B5                 loopd   short 0040D094                             ; 循环结束位置
0040D0DF    5B                    pop     ebx
0040D0E0    EB 1B                 jmp     short 0040D0FD
0040D0E2    93                    xchg    eax, ebx                                   ; 循环中断跳出到这里,计算LoadLibraryA的入口地址
0040D0E3    5B                    pop     ebx
0040D0E4    8B73 24               mov     esi, dword ptr [ebx+24]
0040D0E7    03F2                  add     esi, edx
0040D0E9    0FB70C46              movzx   ecx, word ptr [esi+eax*2]
0040D0ED    2B4B 10               sub     ecx, dword ptr [ebx+10]
0040D0F0    8B73 1C               mov     esi, dword ptr [ebx+1C]
0040D0F3    03F2                  add     esi, edx
0040D0F5    91                    xchg    eax, ecx
0040D0F6    8B4486 04             mov     eax, dword ptr [esi+eax*4+4]
0040D0FA    03C2                  add     eax, edx                                   ; eax == LoadLibraryA的入口地址
0040D0FC    C3                    retn                                               ; Get_API_Address Proc 结束
0040D0FD    33C0                  xor     eax, eax
0040D0FF    C3                    retn                                               ; Get_API_Address Proc 结束


代码执行完后,会将 LoadLibraryA()函数的入口地址置于 EAX 中并返回,如果没有找到该函数,EAX=0。
22.png
从函数返回后,取得了 Kernel32.LoadLibraryA()的入口,然后通过调用 LoadLibraryA("User32"),取得 User32.dll的 hInstance,如下图所示。
23.png
然后,再次调用该函数:LoadLibraryA("Kernel32"),取得 Kernel32 的 hInstance,不过这个好象有点多余了,前面已经取得了这个 hInstance 了。
24.png

上面取得的 hInstance 与前面 0x0040D25B call 0x0040D100 取得的 hInstance 是一样的。
再次几个  F8,来到0x0040D369,这里再次调用 call 0x0040D005,这次是从 User32 中取 GetProcAddress() 函数的地址了,如下图:
25.png

取得 GetProcAddress()的入口地址后,通过与LoadLibraryA()得到的 hInstance 配合,就可以得到其它几个函数(如 MessageBoxA(), ExitProcess(), SetTimer(), ReadProcessMemory(), _lopen() 等几个函数)的入口地址了,这一段代码比较长,多按 F8 向下走:

26.png

取得这些系统 API 调用的入口后,再F8向下走,来到 0x0040D472,就可以看到程序将调用 SetTimer()函数设置一个定时器(我们需要跳过这段代码的执行,减少调试时的干扰)这时可以看到还有4个push 指令,这些指令也不要执行,如下图:

27.png

通过在OD中改变EIP的值就可以跳过定时器的调用,如下:

28.png

那几个给 SetTimer() 设置参数的 Push 指令也要跳过,我们在 0x0040D493 的位置点击右键,选择“此处为新 EIP”就可以跳过 SetTimer()函数的调用。
点击后,OD会弹出一个提示对话框,选择“是(Y)”即可,如下所示:
29.png
跳过定时器后,按 F8 来到 0x0040D4AE 处,如下图,这是一段解码程序,这里一共有三个解码循环,第1个也是解码字符串资源,第2、3个是解码代码:
30.png
第1段代码如下,去掉了无用的  NOP 指令,因此指令地址不连续了:
[Asm] 纯文本查看 复制代码
0040D497    8D9D 78284000         lea     ebx, dword ptr [ebp+402878]                ; ebx = 0x0040D84C,开始第2次数据解码,解码反SoftICE调试字符串等
0040D4A2    8D8D CB284000         lea     ecx, dword ptr [ebp+4028CB]                ; ecx = 0x0040D89F
0040D4AC    2BCB                  sub     ecx, ebx                                   ; 长度 0x53 字节, 解码数据
0040D4AE    90                    nop                                                ; 循环开始
0040D4B2    8033 64               xor     byte ptr [ebx], 64
0040D4B9    8003 65               add     byte ptr [ebx], 65
0040D4C0    C003 66               rol     byte ptr [ebx], 66
0040D4C8    43                    inc     ebx
0040D4CD  ^ E2 DF                 loopd   short 0040D4AE;
解码前:
0040D84C  37 A1 33 23 23 23 33 23 23 27 23 23 23 27 23 FF  7?###3##'###'#?
0040D85C  01 45 40 DF DF 3A CC 3C 48 54 CC 00 E4 38 08 7F  E@哌:?HT??
0040D86C  40 E4 7F 48 54 50 24 28 54 00 37 FF B0 3C 54 7B  @?HTP$(T.7??T{
0040D87C  7F B4 44 4C 58 7F 54 00 0C 34 44 7F 8C 3C 50 08  碊LXT..4D?P
0040D88C  A4 CC D4 7F 1C 54 4C 58 7B FF 68 68 37 68 8C A4  ぬ?TLX{?hh7h尋
0040D89C  CC D4 FF 
解码后:
0040D84C  2E 8A 2F 2B 2B 2B 2F 2B 2B 2A 2B 2B 2B 2A 2B 00  .?+++/++*+++*+.
0040D85C  B2 A1 62 08 08 F0 43 6F 64 65 43 72 79 70 74 20  病b餋odeCrypt
0040D86C  62 79 20 64 65 66 69 6C 65 72 2E 00 4E 6F 65 21  by defiler..Noe!
0040D87C  20 4D 61 63 68 20 65 72 73 6D 61 20 53 6F 66 74   Mach ersma Soft
0040D88C  49 43 45 20 77 65 63 68 21 00 5C 5C 2E 5C 53 49  ICE wech!.\\.\SI
0040D89C  43 45 00                                         CE.


然后就是2段代码解码的循环,第1段如下所示,去掉了无用的“NOP”指令:
[Asm] 纯文本查看 复制代码
0040D4CF    B9 78284000           mov     ecx, 00402878                              ; ASCII "rr"
0040D4D8    B8 7C254000           mov     eax, 0040257C
0040D4E1    2BC8                  sub     ecx, eax                                   ; 长度 0x2FC和 可变密钥2,两层功能
0040D4E7    8D9D 7C254000         lea     ebx, dword ptr [ebp+40257C]                ; ebx = 0x0040D550,指向解码代码的一个Proc,不过这个Proc需要先解码
0040D4F1    8A95 EB284000         mov     dl, byte ptr [ebp+4028EB]                  ; ss:[0040D8BF] == 0xB4,解码密钥1
0040D4FC    90                    nop                                                ; 代码解码循环1开始
0040D501    3013                  xor     byte ptr [ebx], dl                         ; 解码1,固定密钥, dl = 0xB4
0040D508    300B                  xor     byte ptr [ebx], cl                         ; 解码2, 可变密钥, ecx=0x2D9, 0x2D8
0040D50E    43                    inc     ebx
0040D513    49                    dec     ecx
0040D518    85C9                  test    ecx, ecx
0040D51E  ^ 75 DC                 jnz     short 0040D4FC                             ; 代码解码循环1结束

第2段,也去掉了无用的"NOP"指令了:
[Asm] 纯文本查看 复制代码
0040D524    B9 78284000           mov     ecx, 00402878                              ; ASCII "rr"
0040D52D    B8 7C254000           mov     eax, 0040257C
0040D536    2BC8                  sub     ecx, eax                                   ; ecx = 0x02FC, 解码长度和可变密钥
0040D53C    8D9D 8B254000         lea     ebx, dword ptr [ebp+40258B]                ; ebx = 0x0040D55F, 第2个代码解码的开始地址
0040D546    8A95 88284000         mov     dl, byte ptr [ebp+402888]                  ; dl = 0xB2, 固定密钥
0040D550    C00B 04               ror     byte ptr [ebx], 4
0040D553    3013                  xor     byte ptr [ebx], dl
0040D555    43                    inc     ebx
0040D556    49                    dec     ecx
0040D557    85C9                  test    ecx, ecx
0040D559  ^ 75 F5                 jnz     short 0040D550                             ; 解码完成后,再次去花指令(开始位置:0x0040D55B,长度:0x0300,93个)

运行到 0x0040D55B 后(我们可以在 0x0040D55B 处选中该行,按 F4 直接运行到这里),如下图:
31.png
这个时候,我们又看到一堆“EB 02”指令了,这是代码解码后,花指令也解码出来了,前面去花指令并不会把这些加密的花指令去掉,所以我们再进行一次去花指令的操作,这次不要在右键菜单上操作,因为那个默认长度是 0x400,可能会超出本节的内存范围,导致  OD 都会被操作系统关闭。所以,我们要从主菜单的“插件”菜单中来调用去花指令的功能:
32.png
如上图所示,选择“去除花指令”,会弹出一个对话框:
33.png
我们在上面填好开始地址和长度,长度的计算:由前面节信息可知为0x932字节,减去开始地址:0x55B,为0x3D7,由于后面还会有一些变量的空间或无用空间,我们只取0x300试试。
点“确定”,完成花指令的去除:
34.png
又干掉了一批花指令,一下子代码又变得清新了:
35.png
可以看到一个 INT 68 指令,就是这个指令,导致其不能在 Windows NT 系列的系统上运行,执行到这里就会被关闭,所以我们也要跳过这个指令,如下所示:
36.png
我们执行到 0x0040D56F,给 ah 赋值后就可以了,然后跳过 INT 68 指令,由于 ECX 不为 0,下面这几个对 ecx 的检测和跳转指令也不用执行了:
37.png
直接来到 0x0040D59A 处,设置为新的 EIP,如下所示:
38.png
由于 ah 前面赋值为 0x43,也就不会等于 0xF386了,如下所示,跳转会成功:
39.png

这段指令整理后如下,是一个循环执行 int 68 指令的代码。

[Asm] 纯文本查看 复制代码
0040D564    B9 FFFF0100           mov     ecx, 1FFFF                                 ; 调用 int 68 次数
0040D569    90                    nop                                                ; 开始循环检测
0040D56D    B4 43                 mov     ah, 43                                     ; 功能号
0040D572    90                    nop
0040D573    CD 68                 int     68                                         ; 中断调用,检测 SoftICE 是否加载,对 OD 没有用,但会引起NT内核系统关闭CM。
0040D575    90                    nop
0040D579    49                    dec     ecx
0040D57F    85C9                  test    ecx, ecx
0040D585  ^ 75 E2                 jnz     short 0040D569                             ; 循环调用 int 68
0040D58C    85C9                  test    ecx, ecx
0040D592    0F85 96020000         jnz     0040D82E                                   ; 跳转到错误地址去了
0040D59C    66:3D 86F3            cmp     ax, 0F386                                  ; 如果 SoftICE 在运行,int 68调用将把 ax 设值为 0xF386
0040D5A5    0F85 97000000         jnz     0040D642                                   ; 如果没有检测到SoftICE, 就开始跳转去解码原始代码了。
0040D5AB    90                    nop

如果上面代码最后没有跳转,就会去执行一段覆盖CM壳代码的操作代码,以及显示一个失败对话框。代码如下:
[Asm] 纯文本查看 复制代码
0040D5AF    C785 84284000 22000000   mov     dword ptr [ebp+402884], 22
0040D5BE    8DBD 6E264000            lea     edi, dword ptr [ebp+40266E]                ; edi == 0x0040D642
0040D5C8    B9 FF000000              mov     ecx, 0FF                                   ; exc 为循环次数
0040D5D1    F2:AA                    repne   stos byte ptr es:[edi]                     ; 覆盖代码区内容,清成 AL 中的值, 直到 ecx 变为 0
0040D5D7    B9 D7254000              mov     ecx, 004025D7
0040D5E0    BB 95254000              mov     ebx, 00402595
0040D5EA    2BCB                     sub     ecx, ebx                                   ; exc 为循环次数
0040D5F0    8DBD 95254000            lea     edi, dword ptr [ebp+402595]                ; edi ===> 0x0040D5F6
0040D5FA    F2:AA                    repne   stos byte ptr es:[edi]                     ; 覆盖代码区内容,清成 AL 中的值, 直到 ecx 变为 0
0040D5FF    90                       nop
0040D600    8D8D A4284000            lea     ecx, dword ptr [ebp+4028A4]                ; ecx ==> "Noe! Mach ersma SoftICE wech!"
0040D60A    8D9D 8E284000            lea     ebx, dword ptr [ebp+40288E]                ; ebx ==> "CodeCrypt by defiler."
0040D614    6A 30                    push    30                                         ; 显示信息
0040D61B    53                       push    ebx
0040D620    51                       push    ecx
0040D626    6A 00                    push    0
0040D62C    FF95 D3284000            call    dword ptr [ebp+4028D3]                     ; call MessageBoxA
0040D636    FF95 D7284000            call    dword ptr [ebp+4028D7]                     ; ExitProcess
0040D640    33ED                     xor     ebp, ebp                                   ; 清0了本地变量基址指针

由于ah=0x43,AX != 0xF386,不会执行上面的代码,我们成功跳转到了新的解码代码,来到 0x0040D642,如下图所示:
40.png
这里是对CM的原有代码进行解密,多次解密后,就会跳转到 OEP,准备执行 CM 原有的程序代码,到这个时候也可以脱壳了。
下面具体説明,下图是对原始代码进行第1次解密,如下所示:
41.png
第1次解密后,还有一次对  SoftICE 的检查,就是通过打开"\\\\.\\SICE"文件句柄来实现的,如下图所示。

42.png

不过我们用的是OD,这个检查无效,我们可以执行,并且能够完成检查也不会引起异常。

接下来是第2次对原有代码进行重复解密运算,如下图所示:

43.png

搞完2次解密后,原始代码还没有出来,这个时候,CM壳又对整个壳代码进行了一次8位"校验和"的运算处理,如下图:

44.png

按常理来説,这个校验是包括了花指令在内的校验运算,不过,经过测试,去除花指令后的“校验和”与没有去除花指令的“校验和”是一样的,都是0x98。后来观察,有些花指令中间无用数据并不一样,有几个是不同的,可能是作者调整了的。。。。降低点工作量(不过我在带花指令的情况也试了下,确实是一样的“校验和”)。
“校验和”计算完后,来到下面:
45.png
CM壳并不对”校验和“进行检查,直接保存到内存变量,为什么呢,因为,还要用这个”校验和“对原始代码继续进行解码操作,没错,用这个”校验和“作为密钥来解码代码,如下图,dl中就是这个”校验和“的值:
46.png
上面一波解码操作完成后,才正式完成壳的全部解码功能,可以看到原始未加密的代码了:
47.png
以及原始代码的入口,即OEP了,到这个时候,壳终于脱掉了:
48.png
上面这一波操作的壳代码如下,去掉了无用的“NOP"指令:
[Asm] 纯文本查看 复制代码
0040D642    8B8D 84284000            mov     ecx, dword ptr [ebp+402884]                ; ecx == 0x00001000,开始解码非壳代码
0040D64D    8B85 7C284000            mov     eax, dword ptr [ebp+40287C]                ; eax == 0x00400000
0040D657    0385 80284000            add     eax, dword ptr [ebp+402880]                ; eax == 00401000, 指向非壳代码区
0040D661    8BD8                     mov     ebx, eax                                   ; ebx == eax == 0x00401000
0040D668    66:51                    push    cx                                         ; 开始第1次循环解码非壳代码,字节数:ecx == 0x1000
0040D66A    8A8D 8D284000            mov     cl, byte ptr [ebp+40288D]                  ; cl == 0xF0, key
0040D670    000B                     add     byte ptr [ebx], cl                         ; 解码1
0040D677    D20B                     ror     byte ptr [ebx], cl                         ; 解码2
0040D679    66:59                    pop     cx
0040D67F    43                       inc     ebx
0040D680  ^ E2 E6                    loopd   short 0040D668
0040D686    9C                       pushfd
0040D68C    58                       pop     eax                                        ; eax = flag register == 0x0216
0040D691    F6C4 01                  test    ah, 1                                      ; ah == 0x02, 不等于 0x01
0040D698    0F85 90010000            jnz     0040D82E
0040D69E    8D8D C2284000            lea     ecx, dword ptr [ebp+4028C2]                ; ecx ===> "\\\\.\\SICE"
0040D6A8    6A 40                    push    40
0040D6AE    51                       push    ecx                                        ; ecx===>"\\\\.\\SICE"
0040D6B3    FF95 E3284000            call    dword ptr [ebp+4028E3]                     ; call _lopen("\\\\.\\SICE"),检查 SoftICE 是否运行
0040D6BD    83F8 FF                  cmp     eax, -1                                    ; eax == 0xFFFFFFFF,表示 SoftICE 没有运行
0040D6C4  ^ 0F85 E1FEFFFF            jnz     0040D5AB                                   ; 不等-1表示SICE在运行,跳去破坏代码、显示消息并退出
0040D6CF    8B8D 84284000            mov     ecx, dword ptr [ebp+402884]                ; ecx == 0x00010000, 解码长度,开始第2次非壳代码解码
0040D6DA    8B85 7C284000            mov     eax, dword ptr [ebp+40287C]                ; eax == 0x00400000,基址
0040D6E4    0385 80284000            add     eax, dword ptr [ebp+402880]                ; eax == 0x00401000, 指向非壳代码
0040D6EF    8BD8                     mov     ebx, eax                                   ; ebx = eax = 0x00401000, p
0040D6F5    8A95 88284000            mov     dl, byte ptr [ebp+402888]                  ; dl == 0xB2, key2
0040D6FF    33C0                     xor     eax, eax                                   ; key1=0
0040D705    FEC0                     inc     al                                         ; key1++
0040D70B    3003                     xor     byte ptr [ebx], al
0040D711    C00B 05                  ror     byte ptr [ebx], 5
0040D718    3013                     xor     byte ptr [ebx], dl
0040D71E    C003 07                  rol     byte ptr [ebx], 7
0040D725    43                       inc     ebx                                        ; p++
0040D72A    49                       dec     ecx                                        ; i--
0040D730    85C9                     test    ecx, ecx                                   ; i>0
0040D737  ^ 75 CC                    jnz     short 0040D705
0040D741    8B85 7C284000            mov     eax, dword ptr [ebp+40287C]                ; 基址(eax == 0x00400000),开始第3次非壳代码解码
0040D74B    0385 80284000            add     eax, dword ptr [ebp+402880]
0040D755    8BD8                     mov     ebx, eax                                   ; ebx == 0x00401000, p
0040D75B    8B03                     mov     eax, dword ptr [ebx]                       ; 读取指令码
0040D762    3985 89284000            cmp     dword ptr [ebp+402889], eax                ; [ebp+0x00402889] == 0x080862A1,检查第2次解码是否正确
0040D76C    0F85 BC000000            jnz     0040D82E                                   ; 相等则解码正确
0040D777    33C0                     xor     eax, eax                                   ; checkSum = 0
0040D779    8D8D 78284000            lea     ecx, dword ptr [ebp+402878]                ; ecx == 0x0040D84C,OEP
0040D782    90                       nop                                                ; 下面是对壳代码进行校验和(checkSum)运算
0040D783    8D9D 2C204000            lea     ebx, dword ptr [ebp+<&MFC42.#3346_CWinThre>; ebx == 0x0040D000, 指向壳的入口地址,(ebp == 0x0000AFD4)
0040D789    2BCB                     sub     ecx, ebx                                   ; ecx == 0x0000084C, 循环次数
0040D78B    0203                     add     al, byte ptr [ebx]                         ; 循环对壳代码字节进行求和,这个是对壳的修改校验(因为去除花指令,求和后的值可能是错的),也是下面解码用的密钥
0040D78D    43                       inc     ebx
0040D78E    49                       dec     ecx
0040D793    85C9                     test    ecx, ecx
0040D795  ^ 75 F4                    jnz     short 0040D78B
0040D79A    90                       nop
0040D79B    8885 EB284000            mov     byte ptr [ebp+4028EB], al                  ; al == 0x98,保存“校验和”,因去除了花指令,该值不一定准确(后来验证是正确的)
0040D7A1    8B8D 84284000            mov     ecx, dword ptr [ebp+402884]                ; ecx == 0x00010000,解码长度
0040D7AC    8B85 7C284000            mov     eax, dword ptr [ebp+40287C]                ; eax == 0x00400000,基址
0040D7B6    0385 80284000            add     eax, dword ptr [ebp+402880]                ; eax == 0x00401000,解码开始地址
0040D7C1    8BD8                     mov     ebx, eax                                   ; pointer p
0040D7C7    8A95 EB284000            mov     dl, byte ptr [ebp+4028EB]                  ; dl == 0x98, 解码密钥,也是前面求得的壳代码的校验和(checkSum)
0040D7D1    33C0                     xor     eax, eax                                   ; n = 0
0040D7D6    90                       nop
0040D7D7    FEC0                     inc     al                                         ; n++,开始循环解码,第3次解码
0040D7DD    D20B                     ror     byte ptr [ebx], cl                         ; cl, 可变密解(循环变量)
0040D7E4    3013                     xor     byte ptr [ebx], dl                         ; dl 为壳代码的校验和(checkSum)
0040D7EA    2813                     sub     byte ptr [ebx], dl                         ; dl 为壳代码的校验和(checkSum)
0040D7F0    43                       inc     ebx
0040D7F5    49                       dec     ecx
0040D7FB    85C9                     test    ecx, ecx
0040D802  ^ 75 D3                    jnz     short 0040D7D7
0040D804    90                       nop
0040D808    90                       nop                                                ; 全部解码完成,准备转去OEP
0040D809    8B85 78284000            mov     eax, dword ptr [ebp+402878]                ; eax = 0x00401A50, OEP
0040D817    5A                       pop     edx
0040D81C    59                       pop     ecx
0040D822    5B                       pop     ebx
0040D827    5D                       pop     ebp
0040D828    FFE0                     jmp     eax                                        ; goto OEP
0040D82A    90                       nop

下面,F8完 JMP EAX 后,我们正式进入 CM 的原始代码区了,如下图所示:
49.png

这个时候就可以开始脱壳了,如下图所示,进行脱壳操作:
50.jpg
弹出脱壳对话框,全部默认设置,直接点击”Dump“即可!
51.png

脱壳完成后,我们直接 F9 运行CM的代码,由于跳过了int68指令,CM可以正常进入了,看到了亲切的窗口界面:

54.png

还有一个就是对序列号的验证了(上图中水印太大,挡住了两个按钮,汗!!!)。

下面我们先找一下进行注册码验证的代码在哪里。
回到OD界面,看一下代码,这个CM是VC+MFC框架编写的,可以看到大量 MFC 对象的操作代码。
最早做过MFC4~6版本下应用开发的应该知道,那个时候MFC是通过事件和成员变量的映射来处理消息的,映射好界面控件和成员变量之间的关系后,只要执行 MFC 框架的 CWnd::UpdateData()就可以将界面中输入的值,自动赋给前面映射的成员变量,所以,我们先找找这个 UpdateData()函数,很快,我们就找到了,并且也只出现了一次:
52.png

可以肯定,这段代码就是注册码处理验证的地方了。

在这个函数的前面下一个断点,就可以进行下一步的注册码验证操作了。



回到 CM 界面。

输入一个用户名和注册码假码"787878787878",如下图:
55.png
点击”OK!“(按钮被水印挡住了)就会断下来,进入OD调试,跟踪注册码的算法,具体跟踪、分析放一起了,如下所示:
[Asm] 纯文本查看 复制代码
00401410   .  6A FF                  push    -1                                                      ;  消息处理映射函数
00401412   .  68 181D4000            push    00401D18                                                ;  SE 处理程序安装
00401417   .  64:A1 00000000         mov     eax, dword ptr fs:[0]
0040141D   .  50                     push    eax
0040141E   .  64:8925 00000000       mov     dword ptr fs:[0], esp
00401425   .  83EC 38                sub     esp, 38
00401428   .  53                     push    ebx                                                     ;  开始计算注册码
00401429   .  55                     push    ebp
0040142A   .  8BE9                   mov     ebp, ecx
0040142C   .  56                     push    esi
0040142D   .  57                     push    edi
0040142E   .  8D4C24 14              lea     ecx, dword ptr [esp+14]
00401432   .  E8 67050000            call    <jmp.&MFC42.#540_CString::CString>
00401437   .  33DB                   xor     ebx, ebx
00401439   .  8D4C24 10              lea     ecx, dword ptr [esp+10]
0040143D   .  895C24 50              mov     dword ptr [esp+50], ebx                                 ;  [esp+50] 字符串计数器
00401441   .  E8 58050000            call    <jmp.&MFC42.#540_CString::CString>
00401446   .  8D4C24 20              lea     ecx, dword ptr [esp+20]
0040144A   .  C64424 50 01           mov     byte ptr [esp+50], 1                                    ;  [esp+50] 字符串计数器
0040144F   .  E8 4A050000            call    <jmp.&MFC42.#540_CString::CString>
00401454   .  8D4C24 30              lea     ecx, dword ptr [esp+30]
00401458   .  C64424 50 02           mov     byte ptr [esp+50], 2
0040145D   .  E8 3C050000            call    <jmp.&MFC42.#540_CString::CString>
00401462   .  8D4C24 1C              lea     ecx, dword ptr [esp+1C]
00401466   .  C64424 50 03           mov     byte ptr [esp+50], 3
0040146B   .  E8 2E050000            call    <jmp.&MFC42.#540_CString::CString>
00401470   .  6A 01                  push    1
00401472   .  8BCD                   mov     ecx, ebp
00401474   .  C64424 54 04           mov     byte ptr [esp+54], 4                                    ;  字符串计数器
00401479   .  E8 80050000            call    <jmp.&MFC42.#6334_CWnd::UpdateData>                     ;  读取数据给相应控件的映射变量(MFC映射成员变量)
0040147E   .  8D45 60                lea     eax, dword ptr [ebp+60]
00401481   .  8D4C24 14              lea     ecx, dword ptr [esp+14]
00401485   .  50                     push    eax
00401486   .  E8 6D050000            call    <jmp.&MFC42.#858_CString::operator=>                    ;  str1, eax==0x0019F4C8, [eax] ===> 用户名,solly
0040148B   .  8D4D 64                lea     ecx, dword ptr [ebp+64]
0040148E   .  51                     push    ecx
0040148F   .  8D4C24 14              lea     ecx, dword ptr [esp+14]
00401493   .  E8 60050000            call    <jmp.&MFC42.#858_CString::operator=>                    ;  str2, eax==0x0019F4C4, [eax] ===> 注册码,"787878787878"
00401498   .  8B7C24 14              mov     edi, dword ptr [esp+14]                                 ;  edi ===> 用户名,"solly"
0040149C   .  83C9 FF                or      ecx, FFFFFFFF
0040149F   .  33C0                   xor     eax, eax
004014A1   .  68 50304000            push    00403050                                                ;  ASCII "Ocbk~mxy`mxecb6Ucy+zi,o~mogih,ai-"
004014A6   .  F2:AE                  repne   scas byte ptr es:[edi]
004014A8   .  F7D1                   not     ecx
004014AA   .  49                     dec     ecx                                                     ;  int len = ecx == strlen(str1),用户名长度
004014AB   .  8BF1                   mov     esi, ecx                                                ;  esi = ecx = length(username) == 5,用户名长度
004014AD   .  8D4C24 24              lea     ecx, dword ptr [esp+24]
004014B1   .  E8 E2040000            call    <jmp.&MFC42.#860_CString::operator=>                    ;  str3, eax==0x0019F4D4, [eax] ===>"Ocbk~mxy`mxecb6Ucy+zi,o~mogih,ai-"
004014B6   .  68 4C304000            push    0040304C
004014BB   .  8D4C24 34              lea     ecx, dword ptr [esp+34]
004014BF   .  E8 D4040000            call    <jmp.&MFC42.#860_CString::operator=>                    ;  str4, eax==0x0019F4E4, [eax] ===> "4",用于检查SN最后一位是否为"8"
004014C4   .  8D4C24 2C              lea     ecx, dword ptr [esp+2C]
004014C8   .  E8 D1040000            call    <jmp.&MFC42.#540_CString::CString>                      ;  eax == 0x0019F4E0
004014CD   .  8D4C24 18              lea     ecx, dword ptr [esp+18]
004014D1   .  C64424 50 05           mov     byte ptr [esp+50], 5
004014D6   .  E8 C3040000            call    <jmp.&MFC42.#540_CString::CString>                      ;  eax == 0x0019F4CC
004014DB   .  8D4C24 28              lea     ecx, dword ptr [esp+28]
004014DF   .  C64424 50 06           mov     byte ptr [esp+50], 6
004014E4   .  E8 B5040000            call    <jmp.&MFC42.#540_CString::CString>                      ;  eax==0x0019F4DC
004014E9   .  8D4C24 24              lea     ecx, dword ptr [esp+24]
004014ED   .  C64424 50 07           mov     byte ptr [esp+50], 7
004014F2   .  E8 A7040000            call    <jmp.&MFC42.#540_CString::CString>                      ;  eax==0x0019F4D8
004014F7   .  8D5424 10              lea     edx, dword ptr [esp+10]                                 ;  edx = sn ===> "787878787878"
004014FB   .  8D4C24 2C              lea     ecx, dword ptr [esp+2C]
004014FF   .  52                     push    edx
00401500   .  C64424 54 08           mov     byte ptr [esp+54], 8                                    ;  字符串计数器
00401505   .  E8 EE040000            call    <jmp.&MFC42.#858_CString::operator=>                    ;  str5, eax==0x0019F4E0, [eax] ===>"787878787878"
0040150A   .  68 48304000            push    00403048                                                ;  [00403048] ===> "-"
0040150F   .  8D4C24 14              lea     ecx, dword ptr [esp+14]                                 ;  [ecx] ===> 注册码假码"787878787878"
00401513   .  895C24 38              mov     dword ptr [esp+38], ebx
00401517   .  E8 D6040000            call    <jmp.&MFC42.#2764_CString::Find>                        ;  在序列号中查找分隔符"-"
0040151C   .  8BF8                   mov     edi, eax                                                ;  没找到分隔符,则eax == 0xFFFFFFFF,否则为"-"号的位置值
0040151E   .  83FF FF                cmp     edi, -1                                                 ;  位置值 保存于 EDI
00401521   .  75 0D                  jnz     short 00401530
00401523   .  8B45 00                mov     eax, dword ptr [ebp]                                    ;  eax == 0x00402350
00401526   .  8BCD                   mov     ecx, ebp
00401528   .  FF50 60                call    dword ptr [eax+60]                                      ;  call [0x004023B0], MFC42.#2446_CWnd::DestroyWindow
0040152B   .  E9 E8010000            jmp     00401718                                                ;  序列号格式不对,结束
00401530   >  8D4C24 10              lea     ecx, dword ptr [esp+10]                                 ;  [ecx] ===> 注册码假码"7878-8787878",手动加了一个"-"
00401534   .  51                     push    ecx
00401535   .  8D4C24 2C              lea     ecx, dword ptr [esp+2C]
00401539   .  E8 BA040000            call    <jmp.&MFC42.#858_CString::operator=>                    ;  str6, eax==0x0019F4DC, [eax] ===> 注册码"7878-8787878"
0040153E   .  8D5424 38              lea     edx, dword ptr [esp+38]
00401542   .  57                     push    edi                                                     ;  长度(left)
00401543   .  52                     push    edx                                                     ;  buffer
00401544   .  8D4C24 30              lea     ecx, dword ptr [esp+30]                                 ;  引用 str6
00401548   .  E8 9F040000            call    <jmp.&MFC42.#4129_CString::Left>                        ;  str7, eax == 0x0019F4EC, [eax] ==>"7878"
0040154D   .  8B00                   mov     eax, dword ptr [eax]                                    ;  eax ===> "7878"
0040154F   .  8D4C24 3C              lea     ecx, dword ptr [esp+3C]                                 ;  ecx == 0x0019F4F0, 保存结果 str_sn1
00401553   .  6A 0A                  push    0A                                                      ; /radix = A (10.)
00401555   .  51                     push    ecx                                                     ; |endptr
00401556   .  50                     push    eax                                                     ; |str_sn1
00401557   .  FF15 CC214000          call    dword ptr [<&MSVCRT.strtol>]                            ; \MSVCRT.strtol(),转成整数 sn1
0040155D   .  8BD8                   mov     ebx, eax                                                ;  eax==0x1EC6 = 7878
0040155F   .  83C4 0C                add     esp, 0C                                                 ;  恢复 esp 堆栈指针
00401562   .  8D4C24 38              lea     ecx, dword ptr [esp+38]                                 ;  ecx == 0x0019F4EC,[ecx] ===> "7878"
00401566   .  895C24 40              mov     dword ptr [esp+40], ebx                                 ;  保存 sn1
0040156A   .  E8 51030000            call    <jmp.&MFC42.#800_CString::~CString>                     ;  删除字符串"7878"
0040156F   .  8D5424 2C              lea     edx, dword ptr [esp+2C]
00401573   .  8D4C24 18              lea     ecx, dword ptr [esp+18]
00401577   .  52                     push    edx
00401578   .  E8 7B040000            call    <jmp.&MFC42.#858_CString::operator=>                    ;  str8, eax == 0x0019F4CC, [eax] ===> 注册码假码"7878-8787878"
0040157D   .  47                     inc     edi                                                     ;  edi=5, 指向"-"号的下一个字符位置
0040157E   .  8D4424 38              lea     eax, dword ptr [esp+38]                                 ;  str9
00401582   .  57                     push    edi                                                     ;  起始位置(mid)
00401583   .  50                     push    eax
00401584   .  8D4C24 20              lea     ecx, dword ptr [esp+20]
00401588   .  E8 59040000            call    <jmp.&MFC42.#4277_CString::Mid>                         ;  str9 = str8.Mid(n) ===> [eax]
0040158D   .  8B00                   mov     eax, dword ptr [eax]                                    ;  eax == 0x00BC0DE0 ===> "8787878",SN的后半截, 保存为 str_sn2
0040158F   .  8D4C24 3C              lea     ecx, dword ptr [esp+3C]                                 ;  ecx==0x0019F4F0
00401593   .  51                     push    ecx                                                     ; /endptr
00401594   .  50                     push    eax                                                     ; |str_sn2
00401595   .  FF15 D4214000          call    dword ptr [<&MSVCRT.strtod>]                            ; \strtod(), 转成双精度 sn2 保存在浮点寄存器ST0, ST0 == 8787878.000
0040159B   .  DA6424 48              fisub   dword ptr [esp+48]                                      ;  sn2 = sn2 - sn1, ST0 == 8780000.000
0040159F   .  83C4 08                add     esp, 8                                                  ;  恢复 esp 堆栈指针
004015A2   .  8D4C24 38              lea     ecx, dword ptr [esp+38]                                 ;  ecx == 0x0019F4EC, [ecx] ===> "8787878"
004015A6   .  DD5C24 40              fstp    qword ptr [esp+40]                                      ;  保存 ST0 在 [0x0019F4F0], 8字节
004015AA   .  E8 11030000            call    <jmp.&MFC42.#800_CString::~CString>                     ;  删除"8787878"
004015AF   .  8D5424 14              lea     edx, dword ptr [esp+14]                                 ;  [edx] ===> "solly"
004015B3   .  8D4C24 24              lea     ecx, dword ptr [esp+24]                                 ;  ecx == 0x0019F4D8
004015B7   .  52                     push    edx
004015B8   .  E8 3B040000            call    <jmp.&MFC42.#858_CString::operator=>                    ;  eax == 0x0019F4D8, [eax] ===>"solly"
004015BD   .  DD4424 40              fld     qword ptr [esp+40]                                      ;  载入sn2到ST0, [0x0019F4F4] == 8780000.000
004015C1   .  DC1D 28244000          fcomp   qword ptr [402428]                                      ;  ST0浮点数与常量47391894.0比较,[0x00402428] == 47391894.000
004015C7   .  DFE0                   fstsw   ax
004015C9   .  F6C4 41                test    ah, 41                                                  ;  SN2需要大于47391894.000
004015CC   .  74 0D                  je      short 004015DB
004015CE   .  8B45 00                mov     eax, dword ptr [ebp]
004015D1   .  8BCD                   mov     ecx, ebp
004015D3   .  FF50 60                call    dword ptr [eax+60]                                      ;  MFC42.#2446_CWnd::DestroyWindow
004015D6   .  E9 3D010000            jmp     00401718                                                ;  退出
004015DB   >  8B7C24 24              mov     edi, dword ptr [esp+24]                                 ;  edi ==> "solly", SN2>47391894.000,来到这里
004015DF   .  83C9 FF                or      ecx, FFFFFFFF                                           ;  int len = -1
004015E2   .  33C0                   xor     eax, eax                                                ;  char a=0
004015E4   .  83F3 6F                xor     ebx, 6F                                                 ;  sn1 = ebx = sn1 xor 0x6F = 0x1EA9 = 7849
004015E7   .  8A5437 FF              mov     dl, byte ptr [edi+esi-1]                                ;  dl = name[len-1]
004015EB   .  895C24 40              mov     dword ptr [esp+40], ebx                                 ;  [0x0019F4F4] = sn1 == 0x00053297
004015EF   .  F2:AE                  repne   scas byte ptr es:[edi]                                  ;  计算 name 的长度
004015F1   .  DB4424 40              fild    dword ptr [esp+40]                                      ;  ST0 = sn1 = 7849
004015F5   .  F7D1                   not     ecx
004015F7   .  49                     dec     ecx                                                     ;  int len2 = name.Length() = 5,用户名的长度
004015F8   .  83E2 7F                and     edx, 7F                                                 ;  edx = edx and 0x7F, int 变 char
004015FB   .  0FAFCA                 imul    ecx, edx                                                ;  int lc = len2 * (char)name[len-1] = 0x025D,用户名长度x用户名最后一个字符
004015FE   .  33D2                   xor     edx, edx                                                ;  int i=0
00401600   .  85F6                   test    esi, esi                                                ;  len>0, 检查用名名长度>0, len == esi, 其在 0x004014AB处赋值
00401602   .  7E 1C                  jle     short 00401620                                          ;  for(int sum=0, int i=0; i<n; i++) {
00401604   .  8B7C24 14              mov     edi, dword ptr [esp+14]                                 ;  edi ===> "solly"
00401608   >  8A043A                 mov     al, byte ptr [edx+edi]                                  ;  a = name[i]
0040160B   .  8B5C24 34              mov     ebx, dword ptr [esp+34]                                 ;  [0x0019F4E8] == 0x00
0040160F   .  83E0 7F                and     eax, 7F                                                 ;  a = a and 0x7F
00401612   .  0FAFC1                 imul    eax, ecx                                                ;  a = a * lc
00401615   .  03D8                   add     ebx, eax                                                ;  sum = sum + a;
00401617   .  42                     inc     edx                                                     ;  i++;
00401618   .  3BD6                   cmp     edx, esi                                                ;  i<n;
0040161A   .  895C24 34              mov     dword ptr [esp+34], ebx                                 ;  循环结束时,sum = 0x00053287
0040161E   .^ 7C E8                  jl      short 00401608                                          ;  }
00401620   >  8B4424 34              mov     eax, dword ptr [esp+34]                                 ;  eax = sum = 0x00053287 = 340615
00401624   .  8B4C24 30              mov     ecx, dword ptr [esp+30]                                 ;  ecx = 0x00BC0D90, ecx ===> str4
00401628   .  83F0 10                xor     eax, 10                                                 ;  int sum2 = sum xor 0x10 = 0x00053297 = 340631
0040162B   .  894424 40              mov     dword ptr [esp+40], eax                                 ;  保存 sum2, 用于与输入的SN1比较
0040162F   .  8A01                   mov     al, byte ptr [ecx]                                      ;  al = '4', str4,用于检查SN最后一位是否为"8"
00401631   .  DB4424 40              fild    dword ptr [esp+40]                                      ;  ST0 = 340631, ST1=原ST0=sn1=7849
00401635   .  D9C9                   fxch    st(1)                                                   ;  交换ST0, ST1
00401637   .  D8D9                   fcomp   st(1)                                                   ;  比较大小
00401639   .  34 0C                  xor     al, 0C                                                  ;  int a4 = al = al xor 0x0C = 0x38, 异或后变成字符"8"
0040163B   .  884424 34              mov     byte ptr [esp+34], al                                   ;  保存 a4
0040163F   .  DFE0                   fstsw   ax                                                      ;  ax==0x3920
00401641   .  F6C4 40                test    ah, 40                                                  ;  sum2必须等于sn1
00401644   .  DDD8                   fstp    st                                                      ;  清空ST0
00401646   .  0F84 C4000000          je      00401710                                                ;  (sum2 != sn1)则退出
0040164C   .  8B5424 34              mov     edx, dword ptr [esp+34]                                 ;  edx == a4 == (char)0x00053238(0x38 == "4" xor 0x0C)
00401650   .  6A 01                  push    1                                                       ;  1个字节
00401652   .  52                     push    edx                                                     ;  a4
00401653   .  8D4C24 40              lea     ecx, dword ptr [esp+40]
00401657   .  E8 84030000            call    <jmp.&MFC42.#536_CString::CString>                      ;  str11, eax == 0x0019F4EC, (char)[eax] == 0x38 ===>"8",CString(TCHAR ch,int n = 1);
0040165C   .  8D4424 40              lea     eax, dword ptr [esp+40]                                 ;  eax = sum = 0x00053297
00401660   .  6A 01                  push    1
00401662   .  50                     push    eax                                                     ;  指向返回值缓冲区的指针 char **
00401663   .  8D4C24 20              lea     ecx, dword ptr [esp+20]                                 ;  buffer ;[ecx] = str2 ===> "7878-8787878"
00401667   .  C64424 58 09           mov     byte ptr [esp+58], 9                                    ;  字符串计数器
0040166C   .  E8 69030000            call    <jmp.&MFC42.#5710_CString::Right>                       ;  str12 = Right(sn, 1), eax = 0x0019F4F4, [eax]="8", 注册码的最后一位字符
00401671   .  8B4C24 38              mov     ecx, dword ptr [esp+38]                                 ;  ecx == 0x00BC0DE0 ===> "8"(0x34 xor 0x0C后的字符)
00401675   .  8B00                   mov     eax, dword ptr [eax]                                    ;  eax == 0x00B30E30 ===> "8" (注册码的最后一位字符)
00401677   .  51                     push    ecx                                                     ; /strParam2
00401678   .  50                     push    eax                                                     ; |strParam1
00401679   .  FF15 D8214000          call    dword ptr [<&MSVCRT._mbscmp>]                           ; \MSVCRT._mbcmp(), 比较SN最后一位是否为'8',相等返回 0
0040167F   .  83C4 08                add     esp, 8
00401682   .  8D4C24 40              lea     ecx, dword ptr [esp+40]                                 ;  str11, ecx = 0x0019F4F4, 准备删除该字符串
00401686   .  85C0                   test    eax, eax                                                ;  检查字符串比较结果
00401688   .  0F94C3                 sete    bl                                                      ;  相等时,bl = 1
0040168B   .  E8 30020000            call    <jmp.&MFC42.#800_CString::~CString>                     ;  清空 str11
00401690   .  8D4C24 38              lea     ecx, dword ptr [esp+38]
00401694   .  C64424 50 08           mov     byte ptr [esp+50], 8                                    ;  字符串计数
00401699   .  E8 22020000            call    <jmp.&MFC42.#800_CString::~CString>
0040169E   .  84DB                   test    bl, bl                                                  ;  检查字符串比较结果,bl==1表示相等
004016A0   .  74 6E                  je      short 00401710                                          ;  bl等于0,则退出,bl等于1则显示 Crack 成功!!!
004016A2   .  8B7C24 20              mov     edi, dword ptr [esp+20]                                 ;  一个加密静态初始化的字符串 [0x00BC0D40] ===> "Ocbk~mxy`mxecb6Ucy+zi,o~mogih,ai-"
004016A6   .  83C9 FF                or      ecx, FFFFFFFF
004016A9   .  33C0                   xor     eax, eax                                                ;  '\0'
004016AB   .  33F6                   xor     esi, esi                                                ;  int i=0
004016AD   .  F2:AE                  repne   scas byte ptr es:[edi]
004016AF   .  F7D1                   not     ecx
004016B1   .  49                     dec     ecx                                                     ;  计算长度
004016B2   .  8BF9                   mov     edi, ecx                                                ;  edi 为长度
004016B4   .  85FF                   test    edi, edi
004016B6   .  7E 43                  jle     short 004016FB
004016B8   >  8B5424 20              mov     edx, dword ptr [esp+20]                                 ;  str3, edx == 0x00BC0D40 ===> "Ocbk~mxy`mxecb6Ucy+zi,o~mogih,ai-"
004016BC   .  8D4C24 1C              lea     ecx, dword ptr [esp+1C]                                 ;  ecx == 0x0019F4D0,临时结果
004016C0   .  8A0416                 mov     al, byte ptr [esi+edx]                                  ;  al = str[i]
004016C3   .  8D5424 40              lea     edx, dword ptr [esp+40]                                 ;  str11, edx == 0x0019F4F4, 临时存放解码后CString字符
004016C7   .  34 0C                  xor     al, 0C                                                  ;  str[i] = str[i] xor 0x0C
004016C9   .  884424 34              mov     byte ptr [esp+34], al                                   ;  保存字符 str[i]
004016CD   .  8B4424 34              mov     eax, dword ptr [esp+34]                                 ;  eax ===> str[i]
004016D1   .  50                     push    eax
004016D2   .  51                     push    ecx
004016D3   .  52                     push    edx
004016D4   .  E8 FB020000            call    <jmp.&MFC42.#923_operator+>                             ;  连接字符串,eax == 0x0019F4F4, [eax] ===> 解码后的字符串
004016D9   .  50                     push    eax
004016DA   .  8D4C24 20              lea     ecx, dword ptr [esp+20]
004016DE   .  C64424 54 0A           mov     byte ptr [esp+54], 0A                                   ;  字符串计数
004016E3   .  E8 10030000            call    <jmp.&MFC42.#858_CString::operator=>                    ;  字符串赋值,str13 = "C"
004016E8   .  8D4C24 40              lea     ecx, dword ptr [esp+40]
004016EC   .  C64424 50 08           mov     byte ptr [esp+50], 8                                    ;  字符串计数
004016F1   .  E8 CA010000            call    <jmp.&MFC42.#800_CString::~CString>                     ;  清除临时字符串2个
004016F6   .  46                     inc     esi                                                     ;  i++
004016F7   .  3BF7                   cmp     esi, edi
004016F9   .^ 7C BD                  jl      short 004016B8                                          ;  循环解码得到字符串 [0x00BC0DE0] ===> "Congratulation:You've cracked me!"
004016FB   >  8B4424 1C              mov     eax, dword ptr [esp+1C]
004016FF   .  6A 40                  push    40
00401701   .  68 38304000            push    00403038                                                ;  ASCII "[NZC] Crackme"
00401706   .  50                     push    eax
00401707   .  8BCD                   mov     ecx, ebp
00401709   .  E8 C0020000            call    <jmp.&MFC42.#4224_CWnd::MessageBoxA>                    ;  显示 Crack 成功
0040170E   .  EB 08                  jmp     short 00401718
00401710   >  8B55 00                mov     edx, dword ptr [ebp]
00401713   .  8BCD                   mov     ecx, ebp
00401715   .  FF52 60                call    dword ptr [edx+60]                                      ;  MFC42.#2446_CWnd::DestroyWindow
00401718   >  8D4C24 24              lea     ecx, dword ptr [esp+24]
0040171C   .  C64424 50 07           mov     byte ptr [esp+50], 7                                    ;  字符串计数
00401721   .  E8 9A010000            call    <jmp.&MFC42.#800_CString::~CString>
00401726   .  8D4C24 28              lea     ecx, dword ptr [esp+28]
0040172A   .  C64424 50 06           mov     byte ptr [esp+50], 6
0040172F   .  E8 8C010000            call    <jmp.&MFC42.#800_CString::~CString>
00401734   .  8D4C24 18              lea     ecx, dword ptr [esp+18]
00401738   .  C64424 50 05           mov     byte ptr [esp+50], 5
0040173D   .  E8 7E010000            call    <jmp.&MFC42.#800_CString::~CString>
00401742   .  8D4C24 2C              lea     ecx, dword ptr [esp+2C]
00401746   .  C64424 50 04           mov     byte ptr [esp+50], 4
0040174B   .  E8 70010000            call    <jmp.&MFC42.#800_CString::~CString>
00401750   .  8D4C24 1C              lea     ecx, dword ptr [esp+1C]
00401754   .  C64424 50 03           mov     byte ptr [esp+50], 3
00401759   .  E8 62010000            call    <jmp.&MFC42.#800_CString::~CString>
0040175E   .  8D4C24 30              lea     ecx, dword ptr [esp+30]
00401762   .  C64424 50 02           mov     byte ptr [esp+50], 2
00401767   .  E8 54010000            call    <jmp.&MFC42.#800_CString::~CString>
0040176C   .  8D4C24 20              lea     ecx, dword ptr [esp+20]
00401770   .  C64424 50 01           mov     byte ptr [esp+50], 1
00401775   .  E8 46010000            call    <jmp.&MFC42.#800_CString::~CString>
0040177A   .  8D4C24 10              lea     ecx, dword ptr [esp+10]
0040177E   .  C64424 50 00           mov     byte ptr [esp+50], 0
00401783   .  E8 38010000            call    <jmp.&MFC42.#800_CString::~CString>
00401788   .  8D4C24 14              lea     ecx, dword ptr [esp+14]
0040178C   .  C74424 50 FFFFFFFF     mov     dword ptr [esp+50], -1
00401794   .  E8 27010000            call    <jmp.&MFC42.#800_CString::~CString>
00401799   .  8B4C24 48              mov     ecx, dword ptr [esp+48]
0040179D   .  5F                     pop     edi
0040179E   .  5E                     pop     esi
0040179F   .  5D                     pop     ebp
004017A0   .  5B                     pop     ebx
004017A1   .  64:890D 00000000       mov     dword ptr fs:[0], ecx
004017A8   .  83C4 44                add     esp, 44
004017AB   .  C3                     retn
004017AC      90                     nop

根据上面对算法的分析,写出注册机,经Dev-C++调试通过,代码如下:
[C++] 纯文本查看 复制代码
#include "stdio.h"
#include "String.h"

void getCode(char * name);

int main() {

    char name[256];

    printf("Keygen for NZC.1\n");

    printf("Input your name: ");

    gets(name);

    getCode(name);

    return 0;
}

void getCode(char * name) {
    int len = strlen(name);
    int lc = len * (char)name[len-1];
    int sum = 0;
    for (int i=0; i<len; i++) {
        sum += lc * (char)(name[i] & 0x7F);
    }

    int sn1 = sum ^ 0x10 ^ 0x6F;
    int sn2 = sn1 + 47391894 + 2; // (sn2-sn1)>47391894, 加2是保证下面将尾数变成8后还是大于47391894
    sn2 = sn2 + (8 - (sn2 % 10)); //// 将个位数变为8
    
    printf("serial: %d-%d\n", sn1, sn2);
}

运算结果如下:
[HTML] 纯文本查看 复制代码
Keygen for NZC.1
Input your name: solly
serial: 340728-47732628
--------------------------------
Process exited after 1.482 seconds with return value 0
请按任意键继续. . .

我们再次在 CM 中输入上面得序列号,得到CM的提示如下:
56.png

分析完毕!
后面是脱壳后的文件信息。
03.jpg
04.png
53.png

免费评分

参与人数 14吾爱币 +22 热心值 +13 收起 理由
liphily + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hszt + 1 + 1 热心回复!
alderaan + 1 谢谢@Thanks!
it.liyong + 1 + 1 热心回复!
alanyh + 1 + 1 用心讨论,共获提升!
Avenshy + 2 + 1 牛批
软硬兼嗜 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
tchivs + 3 + 1 谢谢@Thanks!
yixi + 1 + 1 谢谢@Thanks!
simplex + 1 谢谢@Thanks!
黑的思想 + 2 + 1 用心讨论,共获提升!
pk8900 + 3 + 1 这个当时研究了一会,没头绪,直接跳过了,还得大神出手啊。
SomnusXZY + 1 + 1 热心回复!
笙若 + 2 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| solly 发表于 2019-5-29 16:30
本帖最后由 solly 于 2019-5-29 16:56 编辑

补充一下关于那个定时器的説明,定时器也是用于反调试,检查本 CM 所用的API是否被 BPX 中断,如有中断,则破坏CM代码并引起异常,导致系统关闭CM。
生成定时器的代码:
[Asm] 纯文本查看 复制代码
0040D467    8D8D 67214000            lea     ecx, dword ptr [ebp+402167]                             ; ecx 指向指令代码区的地址,ecx==0x0040D13B, Timer回调函数
0040D472    51                       push    ecx                                                     ; lpTimerFunc, ecx==0x0040D13B,指令区地址 TimerProc
0040D478    68 F4010000              push    1F4                                                     ; uElapse == 500ms
0040D481    6A 00                    push    0                                                       ; nIDEvent
0040D487    6A 00                    push    0                                                       ; hWnd
0040D48D    FF95 DB284000            call    dword ptr [ebp+4028DB]                                  ; call User32.SetTimer(0,0,500, 0x0040D13B)
0040D493    90                       nop

定时器的回调代码:
[Asm] 纯文本查看 复制代码
0040D13B    90                       nop                                                             ; 0x0040D467处的指令中的ecx 指向这里, 定时器回调函数 TimerProc
0040D14C    60                       pushad
0040D151    E8 05000000              call    0040D15B
0040D156    00000000                 dd      0x00000000                                              ; 0x0040D1CF处指令引用这里,保存数据用
0040D15A    00                       db      0x00                                                    ; 0x0040D207处指令引用这里,保存数据用
;---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0040D15B    5D                       pop     ebp                                                     ; 取得call后的返回地址
0040D160    81ED 82214000            sub     ebp, 00402182
0040D16A    33C9                     xor     ecx, ecx                                                ; i = 0
0040D170    8D940D CB284000          lea     edx, dword ptr [ebp+ecx+4028CB]                         ; edx=0040D89F, edx[i] ==> API 入口
0040D17B    8B12                     mov     edx, dword ptr [edx]                                    ; [edx] ==> LoadLibraryA, GetProcAddress, MessageBoxA, ExitProcess, SetTimer, ReadProcessMemory, _lopen
0040D181    85D2                     test    edx, edx
0040D188    74 39                    je      short 0040D1C3
0040D18E    E8 5F000000              call    0040D1F2                                                ; 读取系统API(LoadLibraryA等)第1个字节,防 Int3 调试
0040D197    80BD 86214000 CC         cmp     byte ptr [ebp+402186], 0CC                              ; [0x0040D15A] != 0xCC, 反INT3调试
0040D1A3    74 28                    je      short 0040D1CD
0040D1A9    C685 86214000 00         mov     byte ptr [ebp+402186], 0                                ; 置 0x0040D15A 为 0, 隐藏  0xCC 反调试检查
0040D1B5    83C1 04                  add     ecx, 4                                                  ; i++,指向下一个 API 入口
0040D1BD  ^ EB B1                    jmp     short 0040D170                                          ; 循环继续
0040D1C3    90                       nop                                                             ; TimerProc 检查 API 正常后,来到这里
0040D1C7    61                       popad
0040D1CC    C3                       retn                                                            ; TimerProc 结束
;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0040D1CD    90                       nop                                                             ; TimerProc 内如果检测到 API 被 BPX 中断,则来到这里
0040D1D1    8DBD 87214000            lea     edi, dword ptr [ebp+402187]                             ; edi == 0x0040D15B, 指向上面的代码
0040D1DB    B9 91000000              mov     ecx, 91
0040D1E4    33C0                     xor     eax, eax
0040D1EA    F2:AA                    repne   stos byte ptr es:[edi]                                  ; 覆盖破坏上面的代码,共破坏0x91字节
0040D1EC    90                       nop                                                             ; 会一直覆盖到这里, 不留一点痕迹
0040D1F0    8B00                     mov     eax, dword ptr [eax]                                    ; 因为eax==0,这里是非法内存访问,导至程序会被系统关闭
;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0040D1F2    60                       pushad                                                          ; TimerProc 函数中 0x0040D18E 处的代码调用这里
0040D1F3    B8 FFFFFF7F              mov     eax, 7FFFFFFF
0040D1FC    8D8D 82214000            lea     ecx, dword ptr [ebp+402182]                             ; ECX == 0x0040D156
0040D207    8D9D 86214000            lea     ebx, dword ptr [ebp+402186]                             ; EBX == 0x0040D15A
0040D211    51                       push    ecx                                                     ; lpNumberOfBytesRead , ReadProcMemory 返回读了多少字节数据
0040D217    6A 01                    push    1                                                       ; nSize
0040D21D    53                       push    ebx                                                     ; lpBuffer,ReadProcMemory读取的内容
0040D222    52                       push    edx                                                     ; lpBaseAddress, 指向 LoadLibraryA() 等函数第一个字节的指令
0040D227    50                       push    eax                                                     ; hProcess
0040D22C    FF95 DF284000            call    dword ptr [ebp+4028DF]                                  ; call ReadProcessMemory,用于防止本CM所调用API的第1个指令字节是 0xCC(int3 中断)
0040D236    61                       popad
0040D237    C3                       retn


附件是前面所有截图的打包。

images.rar

1.74 MB, 下载次数: 9, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
z0j + 1 + 1 谢谢@Thanks!
wshze + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

pk8900 发表于 2019-5-29 21:07
帖子加到我那个索引贴里了,写这个帖子估计得半天时间吧~~
 楼主| solly 发表于 2019-5-29 21:22
pk8900 发表于 2019-5-29 21:07
帖子加到我那个索引贴里了,写这个帖子估计得半天时间吧~~

还不止半天,截图处理费时。文字大部分在跟踪时就写在OD中了。
kanxue2018 发表于 2019-5-29 23:36
非常好的练习资料,感谢楼主
cpj1203 发表于 2019-5-30 01:01
受益良多
学士天下 发表于 2019-5-30 08:25
谢谢,让我好好学一下
AAQZzx 发表于 2019-5-30 10:20
支持 楼主  辛苦了
wapj152321 发表于 2019-6-13 07:44
谢谢分享
 楼主| solly 发表于 2019-6-13 12:12
liphily 发表于 2019-6-13 11:20
%D2%BB%BF%B4%BE%CD%B6%AE%A3%AC%D2%BB%D7%F6%BE%CD%B7%CF%A1%A3%0D%0A%D1%DB%A3%BA%CE%D2%BB%E1%C1%CB%0D% ...

看多了就会了
一看就懂,一做就废。
眼:我会了
脑:你不会  


您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 13:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表