本练习破解的CrackMe可以在此处下载:https://reverse.put.as/wp-content/uploads/2010/05/CrackMe.1.by_.James_.Moriarty.zip。运行截图如下。当然,此CrackMe相对简单,仅作为破解入门练习,抛砖引玉。因为简单,如果暴力破解,相对容易,但是这不是破解CrackMe的目的。这里主要从代码分析入手,一步一步理解CrackMe注册码的生成规则,从而达到完美产生注册码的目的—— Keygen,所以,本文乃注册机生成之旅。
0x1 代码静态分析
在Hopper 中反汇编CrackMe,很容易看到validar:方法(validar 为西班牙语,对应英语validate),因为简单,不用怀疑,这就是验证name和serial的方法。代码如下:
; ================ B E G I N N I N G O F P R O C E D U R E ================
; Variables:
; _cmd: 12
; self: 8
; var_10: -16
; var_24: -36
; var_28: -40
; var_2C: -44
; var_3C: -60
; var_40: -64
; var_44: -68
; var_48: -72
-[Control validar:]:
000025d8 push ebp ; Objective C Implementation defined at 0x4314 (instance method)
000025d9 mov ebp, esp
000025db push ebx
000025dc push edi
000025dd push esi
000025de sub esp, 0x3c
000025e1 mov eax, dword [___stack_chk_guard_301c] ; ___stack_chk_guard_301c
000025e6 mov eax, dword [eax]
000025e8 mov dword [ebp+var_10], eax
000025eb mov esi, dword [ebp+self]
000025ee mov eax, dword [esi+8]
000025f1 mov ecx, dword [objc_msg_stringValue] ; @selector(stringValue)
000025f7 mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
000025fb mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
000025fe call imp___symbol_stub__objc_msgSend
00002603 mov edi, eax
00002605 mov eax, dword [esi+4]
00002608 mov ecx, dword [objc_msg_stringValue] ; @selector(stringValue)
0000260e mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002612 mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002615 call imp___symbol_stub__objc_msgSend
0000261a mov dword [ebp+var_28], eax
0000261d mov eax, dword [objc_msg_length] ; @selector(length)
00002622 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002626 mov eax, dword [ebp+var_28]
00002629 mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000262c call imp___symbol_stub__objc_msgSend
00002631 mov esi, eax
00002633 mov eax, dword [objc_msg_length] ; @selector(length)
00002638 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
0000263c mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000263f call imp___symbol_stub__objc_msgSend
00002644 mov dword [ebp+var_2C], eax
00002647 mov eax, dword [objc_msg_length] ; @selector(length)
0000264c mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002650 mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002653 call imp___symbol_stub__objc_msgSend
00002658 mov ecx, dword [objc_msg_substringFromIndex_] ; @selector(substringFromIndex:)
0000265e add eax, 0xfffffffe
00002661 mov dword [esp+0x48+var_40], eax
00002665 mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002669 mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000266c call imp___symbol_stub__objc_msgSend
00002671 mov ecx, dword [objc_msg_intValue] ; @selector(intValue)
00002677 mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
0000267b mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000267e call imp___symbol_stub__objc_msgSend
00002683 mov ebx, eax
00002685 cmp dword [ebp+var_28], 0x0
00002689 je loc_28d4
0000268f mov eax, dword [objc_msg_isEqual_] ; @selector(isEqual:)
00002694 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002698 mov eax, dword [ebp+var_28]
0000269b mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000269e mov dword [esp+0x48+var_40], 0x3058 ; @""
000026a6 call imp___symbol_stub__objc_msgSend
000026ab test al, al
000026ad jne loc_28d4
000026b3 cmp esi, 0x4
000026b6 jl loc_28d4
000026bc test edi, edi
000026be je loc_28d4
000026c4 mov eax, dword [objc_msg_isEqual_] ; @selector(isEqual:)
000026c9 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
000026cd mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
000026d0 mov dword [esp+0x48+var_40], 0x3058 ; @""
000026d8 call imp___symbol_stub__objc_msgSend
000026dd test al, al
000026df jne loc_28d4
000026e5 cmp dword [ebp+var_2C], 0xf
000026e9 jne loc_28d4
000026ef cmp esi, ebx
000026f1 jne loc_28d4
000026f7 mov eax, dword [objc_msg_substringWithRange_] ; @selector(substringWithRange:)
000026fc mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002700 mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002703 mov dword [esp+0x48+var_3C], 0x1
0000270b mov dword [esp+0x48+var_40], 0xa
00002713 call imp___symbol_stub__objc_msgSend
00002718 mov esi, eax
0000271a mov eax, dword [objc_msg_substringWithRange_] ; @selector(substringWithRange:)
0000271f mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002723 mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002726 mov dword [esp+0x48+var_3C], 0xa
0000272e mov dword [esp+0x48+var_40], 0x0
00002736 call imp___symbol_stub__objc_msgSend
0000273b mov ebx, eax
0000273d mov eax, dword [objc_msg_isEqualToString_] ; @selector(isEqualToString:)
00002742 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002746 mov dword [esp+0x48+var_48], esi ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002749 mov dword [esp+0x48+var_40], 0x3038 ; @"k"
00002751 call imp___symbol_stub__objc_msgSend
00002756 test al, al
00002758 je loc_28d4
0000275e mov eax, dword [objc_msg_substringWithRange_] ; @selector(substringWithRange:)
00002763 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002767 mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000276a mov dword [esp+0x48+var_3C], 0x1
00002772 mov dword [esp+0x48+var_40], 0xb
0000277a call imp___symbol_stub__objc_msgSend
0000277f mov ecx, dword [objc_msg_isEqualToString_] ; @selector(isEqualToString:)
00002785 mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002789 mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000278c mov dword [esp+0x48+var_40], 0x3048 ; @"n"
00002794 call imp___symbol_stub__objc_msgSend
00002799 test al, al
0000279b je loc_28d4
000027a1 mov eax, dword [objc_msg_substringWithRange_] ; @selector(substringWithRange:)
000027a6 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
000027aa mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
000027ad mov dword [esp+0x48+var_3C], 0x1
000027b5 mov dword [esp+0x48+var_40], 0xc
000027bd call imp___symbol_stub__objc_msgSend
000027c2 mov ecx, dword [objc_msg_isEqualToString_] ; @selector(isEqualToString:)
000027c8 mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
000027cc mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
000027cf mov dword [esp+0x48+var_40], 0x3038 ; @"k"
000027d7 call imp___symbol_stub__objc_msgSend
000027dc test al, al
000027de je loc_28d4
000027e4 mov eax, dword [objc_msg_dataUsingEncoding_] ; @selector(dataUsingEncoding:)
000027e9 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
000027ed mov eax, dword [ebp+var_28]
000027f0 mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
000027f3 mov dword [esp+0x48+var_40], 0x4
000027fb call imp___symbol_stub__objc_msgSend
00002800 mov esi, eax
00002802 mov eax, dword [objc_msg_length] ; @selector(length)
00002807 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
0000280b mov dword [esp+0x48+var_48], esi ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000280e call imp___symbol_stub__objc_msgSend
00002813 mov edi, eax
00002815 mov eax, dword [objc_msg_bytes] ; @selector(bytes)
0000281a mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
0000281e mov dword [esp+0x48+var_48], esi ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002821 call imp___symbol_stub__objc_msgSend
00002826 lea ecx, dword [ebp+var_24]
00002829 mov dword [esp+0x48+var_40], ecx ; argument "md" for method imp___symbol_stub__CC_SHA1
0000282d mov dword [esp+0x48+var_44], edi ; argument "len" for method imp___symbol_stub__CC_SHA1
00002831 mov dword [esp+0x48+var_48], eax ; argument "data" for method imp___symbol_stub__CC_SHA1
00002834 call imp___symbol_stub__CC_SHA1
00002839 mov eax, dword [cls_NSMutableString] ; cls_NSMutableString
0000283e mov ecx, dword [objc_msg_string] ; @selector(string)
00002844 mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002848 mov dword [esp+0x48+var_48], eax ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000284b xor esi, esi
0000284d call imp___symbol_stub__objc_msgSend
00002852 mov edi, eax
loc_2854:
00002854 movzx eax, byte [ebp+esi+var_24] ; CODE XREF=-[Control validar:]+675
00002859 mov ecx, dword [objc_msg_appendFormat_] ; @selector(appendFormat:)
0000285f mov dword [esp+0x48+var_3C], eax
00002863 mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002867 mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
0000286a mov dword [esp+0x48+var_40], 0x3068 ; @"%02x"
00002872 call imp___symbol_stub__objc_msgSend
00002877 inc esi
00002878 cmp esi, 0x14
0000287b jne loc_2854
0000287d mov eax, dword [objc_msg_substringWithRange_] ; @selector(substringWithRange:)
00002882 mov dword [esp+0x48+var_44], eax ; argument "selector" for method imp___symbol_stub__objc_msgSend
00002886 mov dword [esp+0x48+var_48], edi ; argument "instance" for method imp___symbol_stub__objc_msgSend
00002889 mov dword [esp+0x48+var_3C], 0xa
00002891 mov dword [esp+0x48+var_40], 0x0
00002899 call imp___symbol_stub__objc_msgSend
0000289e mov ecx, dword [objc_msg_isEqualToString_] ; @selector(isEqualToString:)
000028a4 mov dword [esp+0x48+var_40], eax
000028a8 mov dword [esp+0x48+var_44], ecx ; argument "selector" for method imp___symbol_stub__objc_msgSend
000028ac mov dword [esp+0x48+var_48], ebx ; argument "instance" for method imp___symbol_stub__objc_msgSend
000028af call imp___symbol_stub__objc_msgSend
000028b4 test al, al
000028b6 je loc_28d4
000028b8 mov eax, dword [ebp+self]
000028bb mov ecx, dword [eax+0xc]
000028be mov edx, dword [objc_msg_orderFront_] ; @selector(orderFront:)
000028c4 mov dword [esp+0x48+var_40], eax
000028c8 mov dword [esp+0x48+var_44], edx ; argument "selector" for method imp___symbol_stub__objc_msgSend
000028cc mov dword [esp+0x48+var_48], ecx ; argument "instance" for method imp___symbol_stub__objc_msgSend
000028cf call imp___symbol_stub__objc_msgSend
loc_28d4:
000028d4 mov eax, dword [___stack_chk_guard_301c] ; ___stack_chk_guard_301c, CODE XREF=-[Control validar:]+177, -[Control validar:]+213, -[Control validar:]+222, -[Control validar:]+230, -[Control validar:]+263, -[Control validar:]+273, -[Control validar:]+281, -[Control validar:]+384, -[Control validar:]+451, -[Control validar:]+518, -[Control validar:]+734
000028d9 mov eax, dword [eax]
000028db cmp eax, dword [ebp+var_10]
000028de jne loc_28e8
000028e0 add esp, 0x3c
000028e3 pop esi
000028e4 pop edi
000028e5 pop ebx
000028e6 pop ebp
000028e7 ret
; endp
loc_28e8:
000028e8 call imp___symbol_stub____stack_chk_fail ; CODE XREF=-[Control validar:]+774
; endp
这个方法比较长,看伪代码就比较清晰了:
void -[Control validar:](void * self, void * _cmd, void * arg2) {
var_10 = *___stack_chk_guard;
edi = [*(self + 0x8) stringValue];
var_28 = [*(self + 0x4) stringValue];
esi = [var_28 length];
var_2C = [edi length];
ebx = [[edi substringFromIndex:[edi length] + 0xfffffffe] intValue];
if (((((((var_28 != 0x0) && ([var_28 isEqual:@""] == 0x0)) && (esi >= 0x4)) && (edi != 0x0)) && ([edi isEqual:@""] == 0x0)) && (var_2C == 0xf)) && (esi == ebx)) {
esi = [edi substringWithRange:0xa];
ebx = [edi substringWithRange:0x0];
if ([esi isEqualToString:@"k"] != 0x0) {
if ([[edi substringWithRange:0xb] isEqualToString:@"n"] != 0x0) {
if ([[edi substringWithRange:0xc] isEqualToString:@"k"] != 0x0) {
esi = [var_28 dataUsingEncoding:0x4];
edi = [esi length];
eax = [esi bytes];
CC_SHA1(eax, edi, var_24);
esi = 0x0;
edi = [NSMutableString string];
do {
[edi appendFormat:@"%02x"];
esi = esi + 0x1;
} while (esi != 0x14);
if ([ebx isEqualToString:[edi substringWithRange:0x0]] != 0x0) {
[*(self + 0xc) orderFront:self];
}
}
}
}
}
if (*___stack_chk_guard != var_10) {
__stack_chk_fail();
}
return;
}
可以看出,先取了输入的name ,serial的值放在edi ,var_28,然后分别计算length。
后面是校验,判断edi的长度为15,var_28的长度不小于4,截取后面edi(substringFromIndex) 存入ebx,比较ebx的值与var_28的长度是否相等, 然后取edi的0xa 、0xb 、 0xc 处的substring,分别比较k,n,k,看是否相等。如这些条件都满足,对var_28 进行CC_SHA1加密,分别截取edi、截取CC_SHA1加密后的字符串,比较两者是否相等。如果相等,则是合法的注册码。
name是edi?,serial是var_28?这些截取子串的范围是多少呢?下面动态调试分析。
0x2动态分析
此CrackMe有反debug的措施,见如下的方法。
sub_2556:
00002556 push ebp ; CODE XREF=EntryPoint+48
00002557 mov ebp, esp
00002559 sub esp, 0x18
0000255c mov dword [esp+0x18+var_C], 0x0 ; argument "data" for method imp___symbol_stub__ptrace
00002564 mov dword [esp+0x18+var_10], 0x0 ; argument "addr" for method imp___symbol_stub__ptrace
0000256c mov dword [esp+0x18+var_14], 0x0 ; argument "pid" for method imp___symbol_stub__ptrace
00002574 mov dword [esp+0x18+var_18], 0x1f ; argument "request" for method imp___symbol_stub__ptrace
0000257b call imp___symbol_stub__ptrace
00002580 add esp, 0x18
00002583 pop ebp
00002584 jmp imp___symbol_stub__NSApplicationMain
此方法会检测ptrace,所以先将
0000257b call imp___symbol_stub__ptrace
改为:
0000257b nop dword [eax+eax]
去掉反debug,这样便可以在Hopper中直接debug运行了。运行如下:
经单步调试,得知:
name 存var_28, serial 存edi, name的长度不少于4,serial的长度为15。其中serial后两位为name的长度值,serial 的11,12,13位为k、n、k,serial剩下的前10位为SHA1加密name后的字符串的前10位。因此,Keygen算法如下:
1. name.length >= 4
2. searial.length = 15
3. searial =SHA1(name).subStringWithRange(0,10) +"k"+"n"+"k"+(name).length
例如输入name 为qwer,则name的SHA1加密为:1161e6ffd3637b302a5cd74076283a7bd1fc20d3。按照上述算法,得出serial:
1161e6ffd3knk04
验证测试,结果正确: