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",则显示如下:
分析完毕!!!!
所以 BCB 和 DELPHI 编译出来的代码是完全不一样的,看代码的结构和原理 DELPHI 应该性能强很多 本帖最后由 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 的库。 solly 发表于 2019-6-11 12:39
BCB 的 VCL和FMX库与Delphi一样,共用一套代码,只是语言、语法和编译器的差异,编译后的机器码差不多。
...
这么古老的编译器写的么…… JuncoJet 发表于 2019-6-11 13:47
这么古老的编译器写的么……
整个 160 个 crackme 都是古董了。。。。。。 学习学习,终于解禁了{:301_992:} 理论性很强,然后我看不懂…… JuncoJet 发表于 2019-6-11 12:11
所以 BCB 和 DELPHI 编译出来的代码是完全不一样的,看代码的结构和原理 DELPHI 应该性能强很多
给大佬赞一个 感谢楼主分享
页:
[1]