本破解练习来自MSJ2009 Challenge#1,下载地址如下:https://reverse.put.as/wp-content/uploads/2010/05/MSJ20091.zip。
破解起来稍显麻烦,主要是Keygen的算法比较晦涩难懂。
打开运行,如下。输入正确的Name及Serial才能注册。
0x1 代码分析
在Hopper中打开Challenge #1,可以看到关键校验代码如下。
; ================ 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_1C: -28
; var_20: -32
; var_24: -36
; var_28: -40
; var_2C: -44
; var_30: -48
; var_3C: -60
; var_40: -64
; var_44: -68
; var_48: -72
-[Level1 validateSerial:forName:]:
00002a9e push ebp ; Objective C Implementation defined at 0x40f4 (instance method)
00002a9f mov ebp, esp
00002aa1 push edi
00002aa2 push esi
00002aa3 push ebx
00002aa4 sub esp, 0x3c
00002aa7 mov eax, dword [objc_msg_length] ; @selector(length)
00002aac mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002ab0 mov eax, dword [ebp+arg_8]
00002ab3 mov dword [esp+0x48+var_48], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002ab6 call imp___jump_table__objc_msgSend
00002abb cmp eax, 0x8
00002abe jne loc_2c4e
00002ac4 mov eax, dword [objc_msg_lossyCString] ; @selector(lossyCString)
00002ac9 mov esi, 0x4
00002ace xor ebx, ebx
00002ad0 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002ad4 mov eax, dword [ebp+arg_C]
00002ad7 mov dword [esp+0x48+var_48], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002ada call imp___jump_table__objc_msgSend
00002adf mov dword [esp+0x48+var_48], eax ; argument #1 for method imp___jump_table__strlen
00002ae2 mov edi, eax
00002ae4 call imp___jump_table__strlen
00002ae9 xor ecx, ecx
00002aeb mov dword [ebp+var_30], eax
00002aee jmp loc_2b27
loc_2af0:
00002af0 movsx eax, byte [edi+ebx] ; CODE XREF=-[Level1 validateSerial:forName:]+140
00002af4 inc ebx
00002af5 imul eax, esi
00002af8 add esi, 0x4
00002afb mov edx, eax
00002afd shl edx, 0x4
00002b00 sub edx, eax
00002b02 mov eax, 0x68db8bad
00002b07 lea ecx, dword [edx+ecx+0x29a]
00002b0e imul ecx
00002b10 mov eax, ecx
00002b12 sar eax, 0x1f
00002b15 sar edx, 0xc
00002b18 sub edx, eax
00002b1a mov eax, ecx
00002b1c imul edx, edx, 0x2710
00002b22 sub eax, edx
00002b24 mov dword [ebp+var_20], eax
loc_2b27:
00002b27 cmp dword [ebp+var_30], ebx ; CODE XREF=-[Level1 validateSerial:forName:]+80
00002b2a ja loc_2af0
00002b2c mov eax, dword [ebp+var_20]
00002b2f mov esi, 0x4
00002b34 xor ebx, ebx
00002b36 mov dword [esp+0x48+var_40], 0x3078 ; @"%i"
00002b3e mov dword [esp+0x48+var_3C], eax
00002b42 mov eax, dword [objc_msg_stringWithFormat_] ; @selector(stringWithFormat:)
00002b47 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002b4b mov eax, dword [cls_NSString] ; cls_NSString
00002b50 mov dword [esp+0x48+var_48], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002b53 call imp___jump_table__objc_msgSend
00002b58 mov dword [esp+0x48+var_48], edi ; argument #1 for method imp___jump_table__strlen
00002b5b mov dword [ebp+var_28], eax
00002b5e call imp___jump_table__strlen
00002b63 xor ecx, ecx
00002b65 mov dword [ebp+var_2C], eax
00002b68 jmp loc_2b9c
loc_2b6a:
00002b6a movsx eax, byte [edi+ebx] ; CODE XREF=-[Level1 validateSerial:forName:]+257
00002b6e inc ebx
00002b6f imul eax, esi
00002b72 add esi, 0x8
00002b75 lea edx, dword [eax+eax*4]
00002b78 lea edx, dword [eax+edx*2+0x2d]
00002b7c mov eax, 0x68db8bad
00002b81 add ecx, edx
00002b83 imul ecx
00002b85 mov eax, ecx
00002b87 sar eax, 0x1f
00002b8a sar edx, 0xc
00002b8d sub edx, eax
00002b8f mov eax, ecx
00002b91 imul edx, edx, 0x2710
00002b97 sub eax, edx
00002b99 mov dword [ebp+var_1C], eax
loc_2b9c:
00002b9c cmp ebx, dword [ebp+var_2C] ; CODE XREF=-[Level1 validateSerial:forName:]+202
00002b9f jb loc_2b6a
00002ba1 mov eax, dword [ebp+var_1C]
00002ba4 mov esi, 0x4
00002ba9 xor ebx, ebx
00002bab mov dword [esp+0x48+var_40], 0x3078 ; @"%i"
00002bb3 mov edi, 0x4
00002bb8 mov dword [esp+0x48+var_3C], eax
00002bbc mov eax, dword [objc_msg_stringWithFormat_] ; @selector(stringWithFormat:)
00002bc1 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002bc5 mov eax, dword [cls_NSString] ; cls_NSString
00002bca mov dword [esp+0x48+var_48], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002bcd call imp___jump_table__objc_msgSend
00002bd2 mov dword [esp+0x48+var_40], ebx
00002bd6 mov dword [esp+0x48+var_3C], esi
00002bda mov dword [ebp+var_24], eax
00002bdd mov eax, dword [objc_msg_substringWithRange_] ; @selector(substringWithRange:)
00002be2 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002be6 mov eax, dword [ebp+arg_8]
00002be9 mov dword [esp+0x48+var_48], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002bec call imp___jump_table__objc_msgSend
00002bf1 mov dword [esp+0x48+var_40], esi
00002bf5 mov dword [esp+0x48+var_3C], edi
00002bf9 mov ebx, eax
00002bfb mov eax, dword [objc_msg_substringWithRange_] ; @selector(substringWithRange:)
00002c00 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002c04 mov eax, dword [ebp+arg_8]
00002c07 mov dword [esp+0x48+var_48], eax ; argument #1 for method imp___jump_table__objc_msgSend
00002c0a call imp___jump_table__objc_msgSend
00002c0f mov dword [esp+0x48+var_48], ebx ; argument #1 for method imp___jump_table__objc_msgSend
00002c12 mov esi, eax
00002c14 mov eax, dword [ebp+var_28]
00002c17 mov dword [esp+0x48+var_40], eax
00002c1b mov eax, dword [objc_msg_isEqual_] ; @selector(isEqual:)
00002c20 mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002c24 call imp___jump_table__objc_msgSend
00002c29 test al, al
00002c2b je loc_2c4e
00002c2d mov eax, dword [ebp+var_24]
00002c30 mov dword [esp+0x48+var_48], esi ; argument #1 for method imp___jump_table__objc_msgSend
00002c33 mov dword [esp+0x48+var_40], eax
00002c37 mov eax, dword [objc_msg_isEqual_] ; @selector(isEqual:)
00002c3c mov dword [esp+0x48+var_44], eax ; argument #2 for method imp___jump_table__objc_msgSend
00002c40 call imp___jump_table__objc_msgSend
00002c45 mov edx, 0x1
00002c4a test al, al
00002c4c jne loc_2c50
loc_2c4e:
00002c4e xor edx, edx ; CODE XREF=-[Level1 validateSerial:forName:]+32, -[Level1 validateSerial:forName:]+397
loc_2c50:
00002c50 add esp, 0x3c ; CODE XREF=-[Level1 validateSerial:forName:]+430
00002c53 mov eax, edx
00002c55 pop ebx
00002c56 pop esi
00002c57 pop edi
00002c58 leave
00002c59 ret
; endp
首先,判断Serial的长度是否为8位,见下面代码。如不是8位,则失败,如果等于8位则继续下一步。
00002ab6 call imp___jump_table__objc_msgSend
00002abb cmp eax, 0x8
00002abe jne loc_2c4e
接着, loc_2af0 代码段为一段循环,遍历输入的Name,取其中每一位字符ASCII码计算,算法见loc_2af0。计算的结果为Serial的第一部分Part1。loc_2b6a代码段为第二个循环,同样遍历输入的Name,取其中每一位字符ASCII码计算,算法见loc_2b6a。计算的结果为Serial的第二部分Part2。
最后一段【00002ba1-00002c4c】代码,分别取输入的Serial的前4位和后4位字符串与上面计算等到的Part1 、Part2比较,相等则验证通过。
0x2 Keygen算法
根据分析,可以得出Keygen算法。具体的实现,使用一个小技巧,就是用高级语言完全复制上述计算Part1、Part2的汇编代码。这里用Objective C示例如下。
- (NSString*)part1ByName:(NSString *)name {
signed long esi = 0x4;
signed long ecx = 0;
signed long edx = 0;
signed long eax = 0;
for (int i = 0; i>32>>0x1f;
eax = ecx;
eax = eax>>0x1f;
edx =((ecx * 0x68db8bad) & 0xffffffff00000000)>>32>>(0xc);
edx = edx - eax;
eax = ecx;
edx *= 0x2710;
eax = eax - edx;
}
return [NSString stringWithFormat:@"%d",eax];
}
- (NSString*)part2ByName:(NSString *)name {
signed long esi = 0x4;
signed long ecx = 0;
signed long edx = 0;
signed long eax = 0;
for (int i = 0; i>32>>0x1f;
eax = ecx;
eax = eax>>0x1f;
edx =((ecx * 0x68db8bad) & 0xffffffff00000000)>>32>>(0xc);
edx = edx - eax;
eax = ecx;
edx *= 0x2710;
eax = eax - edx;
}
return [NSString stringWithFormat:@"%d",eax];
}
- (NSString *)keyGen:(id)sender {
NSString *name = @"qwer";
NSString *serial = [NSString stringWithFormat:@"%@%@",
[self part1ByName:name],
[self part2ByName:name]];
return serial;
}
0x3 程序验证
设输入的Name为qwer,根据上述Keygen算法得Serial为92648192,代入程序,验证Success,:)!