本练习破解的CrackMe可以在此处下载:https://reverse.put.as/wp-content/uploads/2010/05/2-Unicorn.zip。运行截图如下。
抛砖引玉——下面就来分析如何生成注册码。
0x1 静态分析
找到判断验证入口函数:
-[UnicornAppDelegate validate:]:
00002af0 push ebp ; Objective C Implementation defined at 0x40bc (instance method)
00002af1 mov ebp, esp
00002af3 sub esp, 0x38
00002af6 mov dword [ebp+var_C], ebx
00002af9 mov dword [ebp+var_8], esi
00002afc mov dword [ebp+var_4], edi
00002aff mov ebx, dword [ebp+self]
00002b02 mov eax, dword [ebx+8]
00002b05 mov esi, dword [objc_msg_stringValue] ; @selector(stringValue)
00002b0b mov dword [esp+0x38+var_34], esi ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002b0f mov dword [esp+0x38+var_38], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002b12 call imp___symbol_stub__objc_msgSend
00002b17 mov edi, eax
00002b19 mov eax, dword [ebx+0xc]
00002b1c mov dword [esp+0x38+var_34], esi ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002b20 mov dword [esp+0x38+var_38], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002b23 call imp___symbol_stub__objc_msgSend
00002b28 mov dword [esp+0x38+var_2C], edi
00002b2c mov dword [esp+0x38+var_30], eax
00002b30 mov eax, dword [objc_msg_validateSerial_forName_] ; @selector(validateSerial:forName:)
00002b35 mov dword [esp+0x38+var_34], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002b39 mov dword [esp+0x38+var_38], ebx ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002b3c call imp___symbol_stub__objc_msgSend
00002b41 test al, al
00002b43 jne loc_2b6e //Badboy
可以看到[UnicornAppDelegate validate:],只是取了输入的name和serial传给selector(validateSerial:forName:)去校验,如果不通过则提示失败。所以具体的校验还得看selector(validateSerial:forName:)。代码如下:
; ================ B E G I N N I N G O F P R O C E D U R E ================
; Variables:
; arg_C: 20
; arg_8: 16
; _cmd: void *12
; self: void *8
; var_10: -16
; var_14: -20
; var_18: -24
-[UnicornAppDelegate validateSerial:forName:]:
00002a41 push ebp ; Objective C Implementation defined at 0x40b0 (instance method)
00002a42 mov ebp, esp
00002a44 sub esp, 0x18
00002a47 mov dword [esp+0x18+var_10], 0x3044 ; @"+unicorn"
00002a4f mov eax, dword [objc_msg_stringByAppendingString_] ; @selector(stringByAppendingString:)
00002a54 mov dword [esp+0x18+var_14], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002a58 mov eax, dword [ebp+arg_C]
00002a5b mov dword [esp+0x18+var_18], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002a5e call imp___symbol_stub__objc_msgSend
00002a63 mov edx, dword [objc_msg_md5HexHash] ; @selector(md5HexHash)
00002a69 mov dword [esp+0x18+var_14], edx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002a6d mov dword [esp+0x18+var_18], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002a70 call imp___symbol_stub__objc_msgSend
00002a75 mov edx, dword [objc_msg_uppercaseString] ; @selector(uppercaseString)
00002a7b mov dword [esp+0x18+var_14], edx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002a7f mov dword [esp+0x18+var_18], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002a82 call imp___symbol_stub__objc_msgSend
00002a87 mov dword [esp+0x18+var_10], 0x14
00002a8f mov edx, dword [objc_msg_substringToIndex_] ; @selector(substringToIndex:)
00002a95 mov dword [esp+0x18+var_14], edx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002a99 mov dword [esp+0x18+var_18], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002a9c call imp___symbol_stub__objc_msgSend
00002aa1 mov edx, dword [ebp+arg_8]
00002aa4 mov dword [esp+0x18+var_10], edx
00002aa8 mov edx, dword [objc_msg_isEqualToString_] ; @selector(isEqualToString:)
00002aae mov dword [esp+0x18+var_14], edx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002ab2 mov dword [esp+0x18+var_18], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002ab5 call imp___symbol_stub__objc_msgSend
00002aba test al, al
00002abc setne al
00002abf leave
00002ac0 ret
这里看出,算法流程大概如此:
取name,加上字符串"+unicorn",得到的字符串,再做MD5HASH,得到MD5密文字符串。接着,密文字符串转为大写字母。最后截取前面的0x14子串和输入的serial校验。
0x2动态分析
在Hopper中打开调试Debug,输入name后单点运行。可以看到最后比较时,当输入name为qwer,密码 3E2E44B5F67C13E710A3已经显示出来,如下图。
而根据上述分析得到的Keygen算法,计算如下:
1.MD5("qwer+unicorn") = 3e2e44b5f67c13e710a3efbf509a682d
2.upperCase("3e2e44b5f67c13e710a3efbf509a682d") = 3E2E44B5F67C13E710A3EFBF509A682D
3.("3E2E44B5F67C13E710A3EFBF509A682D").substringToIndex(0x14) = 3E2E44B5F67C13E710A3
这和Debug的结果一致。输入测试,结果正确。:)!