solly 发表于 2019-6-11 11:34

160 个 CrackMe 之 121 - n0p3x.8 脱壳及去除光盘验证

本帖最后由 solly 于 2019-6-11 12:10 编辑

160 个 CrackMe 之 121 - n0p3x.8 是进行光盘验证的一个CrackMe,其使用9个方法进行验证,只要有一个方法没有通过,则表示CrackMe不是在光盘运行。
这个 CrackMe 也是加壳的,并且该CrackMe是由 Borland C++ 编译的,需要调用 CW3220.DLL 才能运行。同时,代码加壳,壳代码在 Win NT 系列内核下无法正常运行,脱壳后可以正常在 Win 10 上运行。
其壳与 CrackMe 087 是一样的,脱壳方法及注意事项是一样的,脱壳请参考我的这个贴子:


https://www.52pojie.cn/thread-969755-1-1.html


脱壳的关键地方如下:
00409162    8B5F 04            mov   ebx, dword ptr
00409165    8D8430 00700000    lea   eax, dword ptr
0040916C    01F3               add   ebx, esi
0040916E    50               push    eax                                 ; eax ===> "CW3220.DLL"
0040916F    83C7 08            add   edi, 8
00409172    FF55 00            call    dword ptr                      ; call LoadLibraryA(), 执行到这里先暂停,并去下面的代码(0x00401811处)设置断点。
00409175    92               xchg    eax, edx                            ; 这里也先设置一个断点,再执行上面的一行代码。
00409176    8A07               mov   al, byte ptr
00409178    47               inc   edi
00409179    08C0               or      al, al0040917B^ 74 DF            je      short 0040915C
需要手动调整EIP的地方如下:
00401810    55                     push    ebp
00401811    8BEC                     mov   ebp, esp                        ; 在这里下中断,中断后修改EIP为 0x0040184A
00401813    8B45 08                  mov   eax, dword ptr
00401816    64:8B15 04000000         mov   edx, dword ptr fs:
0040181D    8B4A F8                  mov   ecx, dword ptr
00401820    83C1 30                  add   ecx, 30
00401823    890D 00001458            mov   dword ptr , ecx         ; 这里会产生异常
00401829    C705 0000143C 00001528   mov   dword ptr , 28150000
00401833    C705 00001440 00001518   mov   dword ptr , 18150000
0040183D    C700 49737282            mov   dword ptr , 82727349
00401843    C740 04 00001430         mov   dword ptr , 30140000
0040184A    5D                     pop   ebp                               ; 修改EIP后到这里,F9执行,会在0x00409175处断下,再继续脱壳
0040184B    C3                     retn

而上面代码进行的处理,是为了避免内存访问异常,引发该异常是由于 CW3220.DLL 的回调引起的,CW3220.DLL中回调的代码如下:
; CW3220.DLL 的回调代码
;
0052593D|.33FF          xor   edi, edi
0052593F|.6A 00         push    0                                 ; /pModule = NULL
00525941|.E8 A3E70100   call    <jmp.&KERNEL32.GetModuleHandleA>; \GetModuleHandleA
00525946|.85C0          test    eax, eax
00525948|.74 0D         je      short 00525957
0052594A|.68 90585400   push    00545890                        ; /ProcNameOrOrdinal = "__GetExceptDLLinfo"
0052594F|.50            push    eax                               ; |hModule
00525950|.E8 A0E70100   call    <jmp.&KERNEL32.GetProcAddress>    ; \GetProcAddress
00525955|.8BF8          mov   edi, eax                        ;n0p3x_8.00401074
00525957|>85FF          test    edi, edi
00525959|.74 1C         je      short 00525977
0052595B|.8D45 F8       lea   eax, dword ptr
0052595E|.50            push    eax
0052595F|.FFD7          call    edi                               ; call __GetExceptDLLinfo(),出错
00525961|.817D F8 49737>cmp   dword ptr , 82727349
00525968|.59            pop   ecx
00525969|.72 0C         jb      short 00525977
0052596B|.817D F8 59737>cmp   dword ptr , 82727359
00525972|.77 03         ja      short 00525977
00525974|.8B5D FC       mov   ebx, dword ptr
00525977|>85DB          test    ebx, ebx
00525979|.75 0D         jnz   short 00525988
脱壳过程与CrackMe 087 一样,这里不细说。
脱壳运行后,界面如下:

如果没有进行 patch,点"CheckCD",会弹出一个提示信息:


下面是通过 DlgProc 找到的 "CheckCD" 按钮事件处理过程的代码,并在代码中交待了需要破解的地方:
; WM_COMMAND 事件处理过程
;
0040118B    8B4D 10                  mov   ecx, dword ptr                ; WM_COMMAND Handler
0040118E    83E9 65                  sub   ecx, 65                                 ; wParam == 0x65, CheckCD
00401191    74 20                  je      short 004011B3
00401193    49                     dec   ecx                                     ; wParam == 0x66, Exit
00401194    0F85 A7020000            jnz   00401441                              ; other control id
0040119A    FF35 A8254000            push    dword ptr                       ; Exit Handler
004011A0    53                     push    ebx
004011A1    E8 C6070000            call    0040196C                              ; jmp 到 USER32.KillTimer
004011A6    6A 01                  push    1
004011A8    53                     push    ebx
004011A9    E8 88070000            call    00401936                              ; jmp 到 USER32.EndDialog
004011AE    E9 8E020000            jmp   00401441
004011B3    68 EB234000            push    004023EB                              ; ASCII "KERNEL32.DLL"
004011B8    E8 61070000            call    0040191E                              ; KERNEL32.LoadLibraryA
004011BD    8BF0                     mov   esi, eax
004011BF    8D85 E8FBFFFF            lea   eax, dword ptr
004011C5    50                     push    eax                                     ; ===> 0x0000000E
004011C6    68 F8234000            push    004023F8                              ; "GetDriveTypeA", DRIVE_CDROM==5, DRIVE_FIXED==3
004011CB    E8 C3020000            call    00401493                              ; 字符串格式转换,类似 Uncode2ascii()
004011D0    83C4 08                  add   esp, 8
004011D3    8D95 E8FBFFFF            lea   edx, dword ptr                 ; edx ===> "GetDriveTyprA"
004011D9    52                     push    edx
004011DA    56                     push    esi
004011DB    E8 20070000            call    00401900                              ; KERNEL32.GetProcAddress()
004011E0    6A 00                  push    0                                       ; uses "current directory"
004011E2    FFD0                     call    eax                                     ; call GetDriveTyprA()
004011E4    8BF8                     mov   edi, eax                              ; eax == 3 表示硬盘,5 表示光盘
004011E6    56                     push    esi
004011E7    E8 20070000            call    0040190C                              ; jmp 到 KERNEL32.FreeLibrary
004011EC    68 2C010000            push    12C
004011F1    8D85 BCFAFFFF            lea   eax, dword ptr
004011F7    50                     push    eax                                     ; eax 结果缓冲区,结果:"C:\WINDOWS"
004011F8    E8 EB060000            call    004018E8                              ; jmp 到 KERNEL32.GetWindowsDirectoryA
004011FD    68 80000000            push    80
00401202    8D95 E8FBFFFF            lea   edx, dword ptr
00401208    52                     push    edx                                     ; edx 结果缓冲区,结果为 CrackMe 完整路径文件名
00401209    FF35 B0254000            push    dword ptr                       ; push 0x00400000, hModule
0040120F    E8 DA060000            call    004018EE                              ; jmp 到 KERNEL32.GetModuleFileNameA
00401214    8A8D BCFAFFFF            mov   cl, byte ptr                   ; Windows 所在分区有盘符:'C'
0040121A    3A8D E8FBFFFF            cmp   cl, byte ptr                   ; CrackMe 所在分区的盘符:'F'
00401220    75 18                  jnz   short 0040123A                        ; 破解1:改成 jmp (EB)
00401222    6A 00                  push    0
00401224    6A 00                  push    0                                       ; Title ===> null
00401226    8D45 B0                  lea   eax, dword ptr                ; Text == eax ===> "Hehe.. Try again!"
00401229    50                     push    eax
0040122A    53                     push    ebx
0040122B    E8 1C020000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
00401230    83C4 10                  add   esp, 10
00401233    33C0                     xor   eax, eax
00401235    E9 09020000            jmp   00401443
0040123A    83EF 05                  sub   edi, 5                                  ; 破解2:改成 and edi, 0 (83E7 00)
0040123D    0F85 96010000            jnz   004013D9                              ; 驱动器类型,是否光驱?不是则结束()
00401243    8D95 74FCFFFF            lea   edx, dword ptr
00401249    52                     push    edx                                     ; lpTotalNumberOfClusters
0040124A    8D8D 78FCFFFF            lea   ecx, dword ptr
00401250    51                     push    ecx                                     ; lpNumberOfFreeClusters
00401251    8D85 7CFCFFFF            lea   eax, dword ptr
00401257    50                     push    eax                                     ; lpBytesPerSector
00401258    8D95 80FCFFFF            lea   edx, dword ptr
0040125E    52                     push    edx                                     ; lpSectorsPerCluster
0040125F    6A 00                  push    0                                       ; lpRootPathName, 0 表示当前目录所在的驱动器
00401261    E8 BE060000            call    00401924                              ; jmp 到 KERNEL32.GetDiskFreeSpaceA
00401266    83BD 78FCFFFF 00         cmp   dword ptr , 0                  ; 剩余空间是不否为0,(如果是光盘,则剩余空间应该是0)
0040126D    74 18                  je      short 00401287                        ; 破解3: 改成 JMP(EB),强制认为剩余空间为0
0040126F    6A 00                  push    0
00401271    6A 00                  push    0
00401273    8D4D B0                  lea   ecx, dword ptr
00401276    51                     push    ecx
00401277    53                     push    ebx
00401278    E8 CF010000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
0040127D    83C4 10                  add   esp, 10
00401280    33C0                     xor   eax, eax
00401282    E9 BC010000            jmp   00401443
00401287    68 80000000            push    80                                    ; nFileSystemNameSize
0040128C    8D95 BCF9FFFF            lea   edx, dword ptr
00401292    52                     push    edx                                     ; lpFileSystemNameBuffer
00401293    8D8D 68FCFFFF            lea   ecx, dword ptr
00401299    51                     push    ecx                                     ; lpFileSystemFlags
0040129A    8D85 6CFCFFFF            lea   eax, dword ptr
004012A0    50                     push    eax                                     ; lpMaximumComponentLength
004012A1    8D95 70FCFFFF            lea   edx, dword ptr
004012A7    52                     push    edx                                     ; lpVolumeSerialNumber
004012A8    68 80000000            push    80                                    ; nVolumeNameSize
004012AD    8D8D 3CFAFFFF            lea   ecx, dword ptr
004012B3    51                     push    ecx                                     ; lpVolumeNameBuffer
004012B4    6A 00                  push    0                                       ; lpRootPathName,0 表示当前目录所在的驱动器
004012B6    E8 39060000            call    004018F4                              ; jmp 到 KERNEL32.GetVolumeInformationA
004012BB    81BD 70FCFFFF 21787573   cmp   dword ptr , 73757821         ; 卷标是否为:"!xus"
004012C5    0F85 F9000000            jnz   004013C4                              ; 破解4:改成 nop, 6个 nop(或把 F9 改成 00)
004012CB    F685 68FCFFFF 10         test    byte ptr , 10                  ; 文件系统标志是否不为 FILE_FILE_COMPRESSION, 0x00000010,光盘不可压缩
004012D2    74 18                  je      short 004012EC                        ; 破解5:改成 JMP (EB)
004012D4    6A 00                  push    0
004012D6    6A 00                  push    0
004012D8    8D45 B0                  lea   eax, dword ptr
004012DB    50                     push    eax
004012DC    53                     push    ebx
004012DD    E8 6A010000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
004012E2    83C4 10                  add   esp, 10
004012E5    33C0                     xor   eax, eax
004012E7    E9 57010000            jmp   00401443
004012EC    F685 69FCFFFF 80         test    byte ptr , 80                  ; 文件系统标志是否不为 0x00008000, FILE_VOLUME_IS_COMPRESSED,光盘不能为压缩卷
004012F3    74 18                  je      short 0040130D                        ; 破解6:改成 JMP (EB)
004012F5    6A 00                  push    0
004012F7    6A 00                  push    0
004012F9    8D55 B0                  lea   edx, dword ptr
004012FC    52                     push    edx
004012FD    53                     push    ebx
004012FE    E8 49010000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
00401303    83C4 10                  add   esp, 10
00401306    33C0                     xor   eax, eax
00401308    E9 36010000            jmp   00401443
0040130D    68 14244000            push    00402414                              ; ASCII "Stack"
00401312    8D95 3CFAFFFF            lea   edx, dword ptr                 ; 分区的卷标名
00401318    52                     push    edx
00401319    E8 88050000            call    004018A6                              ; jmp 到 cw3220._strcmp
0040131E    83C4 08                  add   esp, 8
00401321    85C0                     test    eax, eax                              ; 破解7:改成 xor eax, eax (33 C0)
00401323    0F85 86000000            jnz   004013AF                              ; 防止后面对磁盘的读写,这里可以改成 JMP 0x00401396(8586-->846D)
00401329    68 1A244000            push    0040241A                              ; ASCII "Overflow"
0040132E    6A 00                  push    0                                       ; lpRootPathName,0 为当前目录所在的驱动器
00401330    E8 DD050000            call    00401912                              ; jmp 到 KERNEL32.SetVolumeLabelA
00401335    48                     dec   eax                                     ; eax == 1, 可以设置卷标
00401336    75 26                  jnz   short 0040135E                        ; 这里改成下面破解,破解8:改成 JMP (EB)
00401338    8D8D 3CFAFFFF            lea   ecx, dword ptr
0040133E    51                     push    ecx
0040133F    6A 00                  push    0
00401341    E8 CC050000            call    00401912                              ; jmp 到 KERNEL32.SetVolumeLabelA
00401346    6A 00                  push    0
00401348    6A 00                  push    0
0040134A    8D45 B0                  lea   eax, dword ptr
0040134D    50                     push    eax
0040134E    53                     push    ebx
0040134F    E8 F8000000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
00401354    83C4 10                  add   esp, 10
00401357    33C0                     xor   eax, eax
00401359    E9 E5000000            jmp   00401443
0040135E    6A 00                  push    0
00401360    8D55 A8                  lea   edx, dword ptr                ; edx ===> "my.dog"
00401363    52                     push    edx
00401364    E8 91050000            call    004018FA                              ; jmp 到 KERNEL32._lcreat
00401369    83F8 FF                  cmp   eax, -1
0040136C    74 28                  je      short 00401396                        ; 破解9:改成 JMP (EB),会有文件没关闭之漏洞
0040136E    50                     push    eax
0040136F    E8 92050000            call    00401906                              ; jmp 到 KERNEL32._lclose
00401374    8D4D A8                  lea   ecx, dword ptr                ; edx ===> "my.dog"
00401377    51                     push    ecx
00401378    E8 35050000            call    004018B2                              ; jmp 到 cw3220._remove
0040137D    59                     pop   ecx
0040137E    6A 00                  push    0                                       ; 上面(破解9)有没关闭文件之漏洞,可以改这里(jmp 0x00401396 (EB 16))
00401380    6A 00                  push    0
00401382    8D45 B0                  lea   eax, dword ptr
00401385    50                     push    eax
00401386    53                     push    ebx
00401387    E8 C0000000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
0040138C    83C4 10                  add   esp, 10
0040138F    33C0                     xor   eax, eax
00401391    E9 AD000000            jmp   00401443
00401396    6A 00                  push    0                                       ; 到了这里,就是校验正确了,破解完成了
00401398    68 23244000            push    00402423                              ; Title ===> "Yep!"
0040139D    8D55 D4                  lea   edx, dword ptr
004013A0    52                     push    edx                                     ; Text == edx ===> "Yay! You cracked it!"
004013A1    53                     push    ebx                                     ; hDlg
004013A2    E8 A5000000            call    0040144C                              ; 显示破解成功
004013A7    83C4 10                  add   esp, 10
004013AA    E9 92000000            jmp   00401441
004013AF    6A 00                  push    0
004013B1    6A 00                  push    0
004013B3    8D4D B0                  lea   ecx, dword ptr
004013B6    51                     push    ecx
004013B7    53                     push    ebx
004013B8    E8 8F000000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
004013BD    83C4 10                  add   esp, 10
004013C0    33C0                     xor   eax, eax
004013C2    EB 7F                  jmp   short 00401443
004013C4    6A 00                  push    0
004013C6    6A 00                  push    0
004013C8    8D55 B0                  lea   edx, dword ptr
004013CB    52                     push    edx
004013CC    53                     push    ebx
004013CD    E8 7A000000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
004013D2    83C4 10                  add   esp, 10
004013D5    33C0                     xor   eax, eax
004013D7    EB 6A                  jmp   short 00401443
004013D9    6A 00                  push    0
004013DB    6A 00                  push    0
004013DD    8D55 B0                  lea   edx, dword ptr
004013E0    52                     push    edx
004013E1    53                     push    ebx
004013E2    E8 65000000            call    0040144C                              ; 显示错误消息框:"Hehe.. Try again!"
004013E7    83C4 10                  add   esp, 10
004013EA    33C0                     xor   eax, eax
004013EC    EB 55                  jmp   short 00401443
; 其它无关代码略
00401441    33C0                     xor   eax, eax
00401443    5F                     pop   edi
00401444    5E                     pop   esi
00401445    5B                     pop   ebx
00401446    8BE5                     mov   esp, ebp
00401448    5D                     pop   ebp
00401449    C2 1000                  retn    10

其中的call    0040144C 是一个用来显示提示信息的过程。这个过程中会对提示信息字符串进行一个转换,转换成 ANSI 格式后再显示,这个过程如下:
; 显示消息框
0040144C/$55               push    ebp
0040144D|.8BEC               mov   ebp, esp
0040144F|.81C4 00FEFFFF      add   esp, -200
00401455|.53               push    ebx
00401456|.8B5D 10            mov   ebx, dword ptr
00401459|.8D85 00FFFFFF      lea   eax, dword ptr
0040145F|.50               push    eax
00401460|.FF75 0C            push    dword ptr
00401463|.E8 2B000000      call    00401493                                        ; 字符串格式转换
00401468|.83C4 08            add   esp, 8
0040146B|.8D95 00FEFFFF      lea   edx, dword ptr
00401471|.52               push    edx
00401472|.53               push    ebx
00401473|.E8 1B000000      call    00401493                                        ; 字符串格式转换
00401478|.83C4 08            add   esp, 8
0040147B|.FF75 14            push    dword ptr                               ; /Style
0040147E|.53               push    ebx                                             ; |Title
0040147F|.8D8D 00FFFFFF      lea   ecx, dword ptr                         ; |
00401485|.51               push    ecx                                             ; |Text
00401486|.FF75 08            push    dword ptr                                ; |hOwner
00401489|.E8 D2040000      call    <jmp.&user32.MessageBoxA>                     ; \MessageBoxA
0040148E|.5B               pop   ebx
0040148F|.8BE5               mov   esp, ebp
00401491|.5D               pop   ebp
00401492\.C3               retn
上面过程中的 call    00401493 就是一个转换函数,如下所示:
00401493/$55               push    ebp
00401494|.8BEC               mov   ebp, esp
00401496|.53               push    ebx
00401497|.56               push    esi
00401498|.8B4D 0C            mov   ecx, dword ptr
0040149B|.8B55 08            mov   edx, dword ptr
0040149E|.85D2               test    edx, edx
004014A0|.74 21            je      short 004014C3
004014A2|.33C0               xor   eax, eax
004014A4|.EB 10            jmp   short 004014B6
004014A6|>8BD8               /mov   ebx, eax
004014A8|.D1EB               |shr   ebx, 1
004014AA|.8D3419             |lea   esi, dword ptr
004014AD|.8A5C02 01          |mov   bl, byte ptr
004014B1|.881E               |mov   byte ptr , bl
004014B3|.83C0 02            |add   eax, 2
004014B6|>807C02 01 00      cmp   byte ptr , 0
004014BB|.^ 75 E9            \jnz   short 004014A6
004014BD|.D1E8               shr   eax, 1
004014BF|.C60401 00          mov   byte ptr , 0
004014C3|>5E               pop   esi
004014C4|.5B               pop   ebx
004014C5|.5D               pop   ebp
004014C6\.C3               retn
上面函数转换如下所示的字符串:
0000207000 00 00 00 00 59 00 61 00 79 00 21 00 20 00 59 ; .....Y.a.y.!. .Y
0000208000 6F 00 75 00 20 00 63 00 72 00 61 00 63 00 6B ; .o.u. .c.r.a.c.k
0000209000 65 00 64 00 20 00 69 00 74 00 21 00 00 00 48 ; .e.d. .i.t.!...H
000020a000 65 00 68 00 65 00 2E 00 2E 00 20 00 54 00 72 ; .e.h.e..... .T.r
000020b000 79 00 20 00 61 00 67 00 61 00 69 00 6E 00 21 ; .y. .a.g.a.i.n.!
000020c000 00
这些字符串,与 Unicode 类似,只是是反序的,如 'Y' 的 ASCII 码为 0x59,其Unicode码为 0x0059,而本CrackMe中则表示成 0x5900,所以显示前需要上面函数转换一下格式。
这个CrackMe 相对简单,patch那几个地方后,再点“CheckCD",则显示如下:




分析完毕!!!!

JuncoJet 发表于 2019-6-11 12:11

所以 BCB 和 DELPHI 编译出来的代码是完全不一样的,看代码的结构和原理 DELPHI 应该性能强很多

solly 发表于 2019-6-11 12:39

本帖最后由 solly 于 2019-6-11 12:40 编辑

JuncoJet 发表于 2019-6-11 12:11
所以 BCB 和 DELPHI 编译出来的代码是完全不一样的,看代码的结构和原理 DELPHI 应该性能强很多
BCB 的 VCL和FMX库与Delphi一样,共用一套代码,只是语言、语法和编译器的差异,编译后的机器码差不多。

这个 CrackMe 不是 BCB 的,是早期(上世纪90年代中期) Borland C/C++ 5.0 的,那个时候还没有 VCL 库(VCL是90年代后期随 Delphi 1.0 才出来的),用的是叫 Object Window 的库。

JuncoJet 发表于 2019-6-11 13:47

solly 发表于 2019-6-11 12:39
BCB 的 VCL和FMX库与Delphi一样,共用一套代码,只是语言、语法和编译器的差异,编译后的机器码差不多。
...

这么古老的编译器写的么……

solly 发表于 2019-6-11 15:24

JuncoJet 发表于 2019-6-11 13:47
这么古老的编译器写的么……

整个 160 个 crackme 都是古董了。。。。。。

yyspawn 发表于 2019-6-12 07:30

li18363882821 发表于 2019-6-12 08:32

学习学习,终于解禁了{:301_992:}

lfh2366763lfh 发表于 2019-6-12 11:34

理论性很强,然后我看不懂……

殇情dd 发表于 2019-6-12 23:01

JuncoJet 发表于 2019-6-11 12:11
所以 BCB 和 DELPHI 编译出来的代码是完全不一样的,看代码的结构和原理 DELPHI 应该性能强很多

给大佬赞一个

鲶鱼的忧郁 发表于 2019-6-13 08:48

感谢楼主分享
页: [1]
查看完整版本: 160 个 CrackMe 之 121 - n0p3x.8 脱壳及去除光盘验证