本练习破解的CrackMe可以在此处下载:https://reverse.put.as/wp-content/uploads/2010/05/SmellsGood.zip。
运行截图如下。破解生成这个CrackMe的注册机相对来说有点难度。主要涉及到SHA1加密算法,且代码量长。
0x1 静态代码分析
在Hopper中打开SmellsGood,可以看到如下关键函数Check。
; ================ B E G I N N I N G O F P R O C E D U R E ================
; Variables:
; _cmd: void *12
; self: void *8
; var_14: -20
; var_18: -24
; var_1C: -28
; var_20: -32
; var_24: -36
; var_28: -40
-[SmellsGood_AppDelegate check:]:
00002a63 push ebp ; Objective C Implementation defined at 0x4118 (instance method)
00002a64 mov ebp, esp
00002a66 push esi
00002a67 push ebx
00002a68 sub esp, 0x20
00002a6b mov esi, dword [ebp+self]
00002a6e mov eax, dword [objc_msg_stringValue] ; @selector(stringValue)
00002a73 mov edx, dword [esi+8]
00002a76 mov dword [esp+0x28+var_24], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002a7a mov dword [esp+0x28+var_28], edx ; argument #1 for method imp___jump_table__objc_msgSend
00002a7d call imp___jump_table__objc_msgSend
00002a82 mov edx, dword [esi+0xc]
00002a85 mov ebx, eax
00002a87 mov eax, dword [objc_msg_stringValue] ; @selector(stringValue)
00002a8c mov dword [esp+0x28+var_28], edx ; argument #1 for method imp___jump_table__objc_msgSend
00002a8f mov dword [esp+0x28+var_24], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002a93 call imp___jump_table__objc_msgSend
00002a98 mov dword [esp+0x28+var_1C], ebx
00002a9c mov dword [esp+0x28+var_20], eax
00002aa0 mov eax, dword [objc_msg_checkCode_forName_] ; @selector(checkCode:forName:)
00002aa5 mov dword [esp+0x28+var_28], esi ; argument #1 for method imp___jump_table__objc_msgSend
00002aa8 mov dword [esp+0x28+var_24], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002aac call imp___jump_table__objc_msgSend
00002ab1 test al, al
00002ab3 je loc_2aef
00002ab5 mov eax, dword [esi+4]
00002ab8 mov dword [esp+0x28+var_18], 0x0 ; argument #5 for method imp___jump_table__NSRunAlertPanelRelativeToWindow
00002ac0 mov dword [esp+0x28+var_1C], 0x0 ; argument #4 for method imp___jump_table__NSRunAlertPanelRelativeToWindow
00002ac8 mov dword [esp+0x28+var_20], 0x3018 ; @"Okay", argument #3 for method imp___jump_table__NSRunAlertPanelRelativeToWindow
00002ad0 mov dword [esp+0x28+var_14], eax ; argument #6 for method imp___jump_table__NSRunAlertPanelRelativeToWindow
00002ad4 mov dword [esp+0x28+var_24], 0x3028 ; @"It worked!", argument #2 for method imp___jump_table__NSRunAlertPanelRelativeToWindow
00002adc mov dword [esp+0x28+var_28], 0x3038 ; @"Success", argument #1 for method imp___jump_table__NSRunAlertPanelRelativeToWindow
00002ae3 call imp___jump_table__NSRunAlertPanelRelativeToWindow
00002ae8 add esp, 0x20
00002aeb pop ebx
00002aec pop esi
00002aed leave
00002aee ret
; endp
loc_2aef:
00002aef add esp, 0x20 ; CODE XREF=-[SmellsGood_AppDelegate check:]+80
00002af2 pop ebx
00002af3 pop esi
00002af4 leave
00002af5 jmp imp___jump_table__NSBeep
; endp
这是点击界面上Check按钮时触发的校验函数。这个函数的作用在于,取得输入的Name和Code,调用-[SmellsGood_AppDelegate checkCode:forName:]去判断输入是否有效。如果通过检验,提示成功,不然提示失败。所以如何校验还得看[SmellsGood_AppDelegate checkCode: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
-[SmellsGood_AppDelegate checkCode:forName:]:
00002afa push ebp ; Objective C Implementation defined at 0x410c (instance method)
00002afb mov ebp, esp
00002afd push ebx
00002afe sub esp, 0x14
00002b01 mov ebx, dword [ebp+arg_8]
00002b04 mov eax, dword [objc_msg_componentsSeparatedByString_] ; @selector(componentsSeparatedByString:)
00002b09 mov dword [esp+0x18+var_10], 0x3048 ; @"-"
00002b11 mov dword [esp+0x18+var_18], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002b14 mov dword [esp+0x18+var_14], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002b18 call imp___jump_table__objc_msgSend
00002b1d mov edx, dword [objc_msg_count] ; @selector(count)
00002b23 mov dword [esp+0x18+var_14], edx ; argument #2 for method imp___jump_table__objc_msgSend
00002b27 mov dword [esp+0x18+var_18], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002b2a call imp___jump_table__objc_msgSend
00002b2f cmp eax, 0x5
00002b32 jne loc_2b80
00002b34 mov eax, dword [objc_msg_uppercaseString] ; @selector(uppercaseString)
00002b39 mov dword [esp+0x18+var_18], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002b3c mov dword [esp+0x18+var_14], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002b40 call imp___jump_table__objc_msgSend
00002b45 mov ebx, eax
00002b47 mov eax, dword [ebp+arg_C]
00002b4a mov dword [esp+0x18+var_10], eax
00002b4e mov eax, dword [objc_msg_generateCodeForName_] ; @selector(generateCodeForName:)
00002b53 mov dword [esp+0x18+var_14], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002b57 mov eax, dword [ebp+self]
00002b5a mov dword [esp+0x18+var_18], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002b5d call imp___jump_table__objc_msgSend
00002b62 mov dword [esp+0x18+var_18], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002b65 mov dword [esp+0x18+var_10], eax
00002b69 mov eax, dword [objc_msg_isEqualToString_] ; @selector(isEqualToString:)
00002b6e mov dword [esp+0x18+var_14], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002b72 call imp___jump_table__objc_msgSend
00002b77 mov edx, 0x1
00002b7c test al, al
00002b7e jne loc_2b82
loc_2b80:
00002b80 xor edx, edx ; CODE XREF=-[SmellsGood_AppDelegate checkCode:forName:]+56
loc_2b82:
00002b82 add esp, 0x14 ; CODE XREF=-[SmellsGood_AppDelegate checkCode:forName:]+132
00002b85 mov eax, edx
00002b87 pop ebx
00002b88 leave
00002b89 ret
; endp
分析此[SmellsGood_AppDelegate checkCode:forName:],首先,将输入的Code字符串通过“-”分割,判断产出的子串数,是否为5,如果不是,失败。如果等于5,继续检验。(这里给出Code的组成规则)。然后将输入的Code转为大写字符(uppercaseString);调用generateCodeForName方法,根据输入的Name产生目标Code。将此Code和输入的Code(转大写后)进行比较,相等则通过。
下面再看generateCodeForName是如何生成Code的。代码如下。
; ================ B E G I N N I N G O F P R O C E D U R E ================
; Variables:
; arg_8: 16
; _cmd: void *12
; self: void *8
; var_10: -16
; var_14: -20
; var_18: -24
-[SmellsGood_AppDelegate generateCodeForName:]:
00002b8a push ebp ; Objective C Implementation defined at 0x4100 (instance method)
00002b8b mov ebp, esp
00002b8d push ebx
00002b8e sub esp, 0x14
00002b91 mov ebx, dword [ebp+self]
00002b94 mov dword [esp+0x18+var_10], 0x4
00002b9c mov eax, dword [objc_msg_dataUsingEncoding_] ; @selector(dataUsingEncoding:)
00002ba1 mov dword [esp+0x18+var_14], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002ba5 mov eax, dword [ebp+arg_8]
00002ba8 mov dword [esp+0x18+var_18], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002bab call imp___jump_table__objc_msgSend
00002bb0 mov dword [esp+0x18+var_10], eax
00002bb4 mov eax, dword [objc_msg_digest_] ; @selector(digest:)
00002bb9 mov dword [esp+0x18+var_18], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002bbc mov dword [esp+0x18+var_14], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002bc0 call imp___jump_table__objc_msgSend
00002bc5 mov dword [esp+0x18+var_10], 0x3058 ; @" "
00002bcd mov edx, dword [objc_msg_componentsSeparatedByString_] ; @selector(componentsSeparatedByString:)
00002bd3 mov dword [esp+0x18+var_14], edx ; argument #2 for method imp___jump_table__objc_msgSend
00002bd7 mov dword [esp+0x18+var_18], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002bda call imp___jump_table__objc_msgSend
00002bdf mov dword [esp+0x18+var_10], eax
00002be3 mov eax, dword [objc_msg_shuffle_] ; @selector(shuffle:)
00002be8 mov dword [esp+0x18+var_18], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002beb mov dword [esp+0x18+var_14], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002bef call imp___jump_table__objc_msgSend
00002bf4 mov dword [esp+0x18+var_10], 0x3048 ; @"-"
00002bfc mov edx, dword [objc_msg_componentsJoinedByString_] ; @selector(componentsJoinedByString:)
00002c02 mov dword [esp+0x18+var_14], edx ; argument #2 for method imp___jump_table__objc_msgSend
00002c06 mov dword [esp+0x18+var_18], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002c09 call imp___jump_table__objc_msgSend
00002c0e mov edx, dword [objc_msg_uppercaseString] ; @selector(uppercaseString)
00002c14 mov dword [ebp+_cmd], edx
00002c17 mov dword [ebp+self], eax
00002c1a add esp, 0x14
00002c1d pop ebx
00002c1e leave
00002c1f jmp imp___jump_table__objc_msgSend
; endp
[SmellsGood_AppDelegate generateCodeForName:]方法先取得Name的二进制字节Data,传给[SmellsGood_AppDelegate digest:],进行加密。对加密的结果通过空格“ ”字符分割成数组,传给[SmellsGood_AppDelegate shuffle:]方法进行交换、重新排序,最后重新排序的字符数组通过"-"字符连接成整体,并转成大写字母。接着看是如何加密和交换的。
; ================ B E G I N N I N G O F P R O C E D U R E ================
; Variables:
; arg_8: 16
; _cmd: void *12
; self: void *8
; var_1C: -28
; var_2C: -44
; var_50: -80
; var_5C: -92
; var_60: -96
; var_64: -100
; var_68: -104
-[SmellsGood_AppDelegate digest:]:
00002c24 push ebp ; Objective C Implementation defined at 0x40f4 (instance method)
00002c25 mov ebp, esp
00002c27 push edi
00002c28 push esi
00002c29 push ebx
00002c2a sub esp, 0x5c
00002c2d mov edi, dword [ebp+arg_8]
00002c30 call imp___jump_table__EVP_sha1
00002c35 lea esi, dword [ebp+var_2C]
00002c38 mov dword [esp+0x68+var_68], esi ; argument #1 for method imp___jump_table__EVP_DigestInit
00002c3b mov dword [esp+0x68+var_64], eax ; argument #2 for method imp___jump_table__EVP_DigestInit
00002c3f call imp___jump_table__EVP_DigestInit
00002c44 mov eax, dword [objc_msg_length] ; @selector(length)
00002c49 mov dword [esp+0x68+var_68], edi ; argument #1 for method imp___jump_table__objc_msgSend
00002c4c mov dword [esp+0x68+var_64], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002c50 call imp___jump_table__objc_msgSend
00002c55 mov dword [esp+0x68+var_68], edi ; argument #1 for method imp___jump_table__objc_msgSend
00002c58 mov ebx, eax
00002c5a mov eax, dword [objc_msg_bytes] ; @selector(bytes)
00002c5f mov dword [esp+0x68+var_64], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002c63 call imp___jump_table__objc_msgSend
00002c68 mov dword [esp+0x68+var_60], ebx ; argument #3 for method imp___jump_table__EVP_DigestUpdate
00002c6c lea ebx, dword [ebp+var_50]
00002c6f mov dword [esp+0x68+var_68], esi ; argument #1 for method imp___jump_table__EVP_DigestUpdate
00002c72 mov dword [esp+0x68+var_64], eax ; argument #2 for method imp___jump_table__EVP_DigestUpdate
00002c76 call imp___jump_table__EVP_DigestUpdate
00002c7b lea eax, dword [ebp+var_1C]
00002c7e mov dword [esp+0x68+var_64], ebx ; argument #2 for method imp___jump_table__EVP_DigestFinal
00002c82 mov dword [esp+0x68+var_68], esi ; argument #1 for method imp___jump_table__EVP_DigestFinal
00002c85 mov esi, 0x1
00002c8a mov dword [esp+0x68+var_60], eax ; argument #3 for method imp___jump_table__EVP_DigestFinal
00002c8e call imp___jump_table__EVP_DigestFinal
00002c93 mov eax, dword [ebp+var_1C]
00002c96 mov dword [esp+0x68+var_60], ebx
00002c9a mov dword [esp+0x68+var_5C], eax
00002c9e mov eax, dword [objc_msg_dataWithBytes_length_] ; @selector(dataWithBytes:length:)
00002ca3 mov dword [esp+0x68+var_64], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002ca7 mov eax, dword [cls_NSData] ; cls_NSData
00002cac mov dword [esp+0x68+var_68], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002caf call imp___jump_table__objc_msgSend
00002cb4 mov edx, dword [objc_msg_description] ; @selector(description)
00002cba mov dword [esp+0x68+var_64], edx ; argument #2 for method imp___jump_table__objc_msgSend
00002cbe mov dword [esp+0x68+var_68], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002cc1 call imp___jump_table__objc_msgSend
00002cc6 mov ebx, eax
00002cc8 mov eax, dword [objc_msg_length] ; @selector(length)
00002ccd mov dword [esp+0x68+var_68], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002cd0 mov dword [esp+0x68+var_64], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002cd4 call imp___jump_table__objc_msgSend
00002cd9 mov dword [esp+0x68+var_60], esi
00002cdd mov dword [esp+0x68+var_68], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002ce0 lea edi, dword [eax-2]
00002ce3 mov eax, dword [objc_msg_substringWithRange_] ; @selector(substringWithRange:)
00002ce8 mov dword [esp+0x68+var_5C], edi
00002cec mov dword [esp+0x68+var_64], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002cf0 call imp___jump_table__objc_msgSend
00002cf5 add esp, 0x5c
00002cf8 pop ebx
00002cf9 pop esi
00002cfa pop edi
00002cfb leave
00002cfc ret
; endp
; ================ B E G I N N I N G O F P R O C E D U R E ================
; Variables:
; arg_8: 16
; _cmd: void *12
; self: void *8
; var_1C: -28
; var_20: -32
; var_24: -36
; var_2C: -44
; var_30: -48
; var_34: -52
; var_38: -56
; var_3C: -60
; var_40: -64
; var_44: -68
; var_48: -72
-[SmellsGood_AppDelegate shuffle:]:
00002cfd push ebp ; Objective C Implementation defined at 0x40e8 (instance method)
00002cfe mov ebp, esp
00002d00 push edi
00002d01 push esi
00002d02 push ebx
00002d03 sub esp, 0x3c
00002d06 mov eax, dword [cls_NSArray] ; cls_NSArray
00002d0b mov ebx, dword [ebp+arg_8]
00002d0e mov dword [esp+0x48+var_40], 0x0
00002d16 mov dword [ebp+var_24], eax
00002d19 mov eax, dword [objc_msg_objectAtIndex_] ; @selector(objectAtIndex:)
00002d1e mov dword [esp+0x48+var_48], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002d21 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002d25 call imp___jump_table__objc_msgSend
00002d2a mov dword [esp+0x48+var_48], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002d2d mov dword [esp+0x48+var_40], 0x2
00002d35 mov dword [ebp+var_20], eax
00002d38 mov eax, dword [objc_msg_objectAtIndex_] ; @selector(objectAtIndex:)
00002d3d mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002d41 call imp___jump_table__objc_msgSend
00002d46 mov dword [esp+0x48+var_48], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002d49 mov dword [esp+0x48+var_40], 0x1
00002d51 mov dword [ebp+var_1C], eax
00002d54 mov eax, dword [objc_msg_objectAtIndex_] ; @selector(objectAtIndex:)
00002d59 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002d5d call imp___jump_table__objc_msgSend
00002d62 mov dword [esp+0x48+var_48], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002d65 mov dword [esp+0x48+var_40], 0x3
00002d6d mov edi, eax
00002d6f mov eax, dword [objc_msg_objectAtIndex_] ; @selector(objectAtIndex:)
00002d74 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002d78 call imp___jump_table__objc_msgSend
00002d7d mov dword [esp+0x48+var_48], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002d80 mov dword [esp+0x48+var_40], 0x4
00002d88 mov esi, eax
00002d8a mov eax, dword [objc_msg_objectAtIndex_] ; @selector(objectAtIndex:)
00002d8f mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002d93 call imp___jump_table__objc_msgSend
00002d98 mov edx, dword [ebp+var_20]
00002d9b mov dword [esp+0x48+var_38], edi
00002d9f mov dword [esp+0x48+var_3C], esi
00002da3 mov dword [esp+0x48+var_2C], 0x0
00002dab mov dword [esp+0x48+var_30], edx
00002daf mov edx, dword [ebp+var_1C]
00002db2 mov dword [esp+0x48+var_40], eax
00002db6 mov eax, dword [objc_msg_arrayWithObjects_] ; @selector(arrayWithObjects:)
00002dbb mov dword [esp+0x48+var_34], edx
00002dbf mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002dc3 mov eax, dword [ebp+var_24]
00002dc6 mov dword [esp+0x48+var_48], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002dc9 call imp___jump_table__objc_msgSend
00002dce add esp, 0x3c
00002dd1 pop ebx
00002dd2 pop esi
00002dd3 pop edi
00002dd4 leave
00002dd5 ret
; endp
可以看到digest是用SHA-1算法加密,取的是加密后二进制流的description,所以有空格。
shuffle对数组重新排列规则为[1,2,3,4,5] ->[5,4,2,3,1]
0x2 Keygen 算法
通过分析(当然包括动态分析,此处略)得出Keygen算法描述:
1. A = SHA-1(name)
2. Array B = A.splitBy(" ")
3. Array C = B.shuffle(),交换规则:[1,2,3,4,5] ->[5,4,2,3,1]
4. Code = C.jointStringBy("-")
0x3 测试验证
设输入Name为123456 ,则Name的SHA-1加密为:
7c4a8d09 ca3762af 61e59520 943dc264 94f8941b
分割成数组:
[ 7c4a8d09,ca3762af,61e59520,943dc264,94f8941b ]
交换数组元素:
[ 94f8941b,943dc264,ca3762af,61e59520,7c4a8d09 ]
最后连接生成Code:
94f8941b-943dc264-ca3762af-61e59520-7c4a8d09
输入Code,程序验证Success,:)!