HappyTowns 40th CrackMe 算法分析及注册机
这个CrackMe放出的时间也比较早了,不过一直无人问津。我曾经尝试过,但当时没有搞定,就放下了。
最近看了几本书有了些新想法,于是重新捡起它来。此小文也算是对HT同学长期以来孜孜不倦写CrackMe的支持吧,呵呵
总体来说,这个CrackMe难度不大,但是需要一些技巧。
没有加壳。用IDA载入,并加载常用的sig。
找到算法验证的关键函数,初步分析的结果如下:
.text:004043A0 ; =============== S U B R O U T I N E =======================================
.text:004043A0
.text:004043A0
.text:004043A0 ; int __cdecl OnCheck(HWND hDlg)
.text:004043A0 OnCheck proc near ; CODE XREF: DialogFunc+55p
.text:004043A0
.text:004043A0 bn4 = dword ptr -784h
.text:004043A0 bn3 = dword ptr -780h
.text:004043A0 bn2 = dword ptr -77Ch
.text:004043A0 bn1 = dword ptr -778h
.text:004043A0 bn0 = dword ptr -774h
.text:004043A0 hash_out = dword ptr -770h
.text:004043A0 var_76B = dword ptr -76Bh
.text:004043A0 var_767 = dword ptr -767h
.text:004043A0 var_763 = word ptr -763h
.text:004043A0 var_761 = byte ptr -761h
.text:004043A0 var_760 = byte ptr -760h
.text:004043A0 var_75D = byte ptr -75Dh
.text:004043A0 var_75C = byte ptr -75Ch
.text:004043A0 var_75B = byte ptr -75Bh
.text:004043A0 var_75A = byte ptr -75Ah
.text:004043A0 var_759 = byte ptr -759h
.text:004043A0 var_758 = byte ptr -758h
.text:004043A0 var_757 = byte ptr -757h
.text:004043A0 var_756 = byte ptr -756h
.text:004043A0 var_755 = byte ptr -755h
.text:004043A0 var_754 = byte ptr -754h
.text:004043A0 var_753 = byte ptr -753h
.text:004043A0 var_752 = byte ptr -752h
.text:004043A0 var_751 = byte ptr -751h
.text:004043A0 var_750 = dword ptr -750h
.text:004043A0 ctx = dword ptr -6FCh
.text:004043A0 Name = byte ptr -6DCh
.text:004043A0 Magic = dword ptr -4E8h
.text:004043A0 Serial = byte ptr -2F4h
.text:004043A0 var_100 = dword ptr -100h
.text:004043A0 hDlg = dword ptr 4
.text:004043A0
.text:004043A0 sub esp, 784h
.text:004043A6 push ebx
.text:004043A7 push esi
.text:004043A8 push edi
.text:004043A9 xor ebx, ebx
.text:004043AB mov ecx, 124
.text:004043B0 xor eax, eax
.text:004043B2 lea edi, [esp+0B5h]
.text:004043B9 mov [esp+790h+Name], bl
.text:004043C0 rep stosd
.text:004043C2 stosw
.text:004043C4 stosb
.text:004043C5 mov ecx, 124
.text:004043CA xor eax, eax
.text:004043CC lea edi, [esp+790h+Magic+1]
.text:004043D3 mov byte ptr [esp+790h+Magic], bl
.text:004043DA rep stosd
.text:004043DC stosw
.text:004043DE stosb
.text:004043DF mov ecx, 124
.text:004043E4 xor eax, eax
.text:004043E6 lea edi, [esp+49Dh]
.text:004043ED mov [esp+790h+Serial], bl
.text:004043F4 rep stosd
.text:004043F6 stosw
.text:004043F8 stosb
.text:004043F9 xor eax, eax
.text:004043FB mov ecx, 21
.text:00404400 mov [esp+790h+hash_out+1], eax
.text:00404404 lea edi, [esp+790h+var_750]
.text:00404408 mov [esp+790h+var_76B], eax
.text:0040440C mov [esp+790h+var_760], 0A4h
.text:00404411 mov [esp+790h+var_767], eax
.text:00404415 mov byte ptr [esp+31h], 8Fh
.text:0040441A mov [esp+790h+var_763], ax
.text:0040441F mov byte ptr [esp+32h], 9Bh
.text:00404424 mov [esp+790h+var_75D], 0FFh
.text:00404429 mov [esp+790h+var_75C], 6Dh
.text:0040442E mov [esp+790h+var_75B], 3Ah
.text:00404433 mov [esp+790h+var_75A], 0FDh
.text:00404438 mov [esp+790h+var_759], 7Dh
.text:0040443D mov [esp+790h+var_758], 56h
.text:00404442 mov [esp+790h+var_757], 0CBh
.text:00404447 mov [esp+790h+var_756], 0ACh
.text:0040444C mov [esp+790h+var_755], 0D6h
.text:00404451 mov [esp+790h+var_754], 46h
.text:00404456 mov [esp+790h+var_753], 27h
.text:0040445B mov [esp+790h+var_752], 50h
.text:00404460 mov [esp+790h+var_751], 65h
.text:00404465 mov byte ptr [esp+790h+hash_out], bl
.text:00404469 rep stosd
.text:0040446B lea ecx, [esp+790h+bn1]
.text:0040446F mov [esp+790h+var_761], al
.text:00404473 push ecx ; int
.text:00404474 push offset a12754a064c91dd ; "12754A064C91DD0A8E26385EC9335A268192B73"...
.text:00404479 mov [esp+798h+bn4], ebx
.text:0040447D mov [esp+798h+bn1], ebx
.text:00404481 mov [esp+798h+bn3], ebx
.text:00404485 mov [esp+798h+bn0], ebx
.text:00404489 mov [esp+798h+bn2], ebx
.text:0040448D call _zhsread
.text:00404492 lea edx, [esp+798h+bn4]
.text:00404496 push edx ; int
.text:00404497 push offset a6f907aaa920daf ; "6F907AAA920DAF37AD19DD6974540903FBC772F"...
.text:0040449C call _zhsread
.text:004044A1 mov esi, [esp+7A0h+hDlg]
.text:004044A8 mov edi, ds:GetDlgItemTextA
.text:004044AE add esp, 10h
.text:004044B1 lea eax, [esp+790h+Name]
.text:004044B8 push 501 ; nMaxCount
.text:004044BD push eax ; lpString
.text:004044BE push 3EDh ; nIDDlgItem
.text:004044C3 push esi ; hDlg
.text:004044C4 call edi ; GetDlgItemTextA
.text:004044C6 cmp eax, 2
.text:004044C9 jnb short loc_4044D7
.text:004044CB pop edi
.text:004044CC pop esi
.text:004044CD xor eax, eax
.text:004044CF pop ebx
.text:004044D0 add esp, 784h
.text:004044D6 retn
.text:004044D7 ; ---------------------------------------------------------------------------
.text:004044D7
.text:004044D7 loc_4044D7: ; CODE XREF: OnCheck+129j
.text:004044D7 lea ecx, [esp+790h+Magic]
.text:004044DE push 1F5h ; nMaxCount
.text:004044E3 push ecx ; lpString
.text:004044E4 push 3EEh ; nIDDlgItem
.text:004044E9 push esi ; hDlg
.text:004044EA call edi ; GetDlgItemTextA
.text:004044EC cmp eax, 1
.text:004044EF jnb short loc_4044FD
.text:004044F1 pop edi
.text:004044F2 pop esi
.text:004044F3 xor eax, eax
.text:004044F5 pop ebx
.text:004044F6 add esp, 784h
.text:004044FC retn
.text:004044FD ; ---------------------------------------------------------------------------
.text:004044FD
.text:004044FD loc_4044FD: ; CODE XREF: OnCheck+14Fj
.text:004044FD lea edx, [esp+790h+Serial]
.text:00404504 push 501 ; nMaxCount
.text:00404509 push edx ; lpString
.text:0040450A push 3EFh ; nIDDlgItem
.text:0040450F push esi ; hDlg
.text:00404510 call edi ; GetDlgItemTextA
.text:00404512 test eax, eax
.text:00404514 jnz short loc_404520
.text:00404516 pop edi
.text:00404517 pop esi
.text:00404518 pop ebx
.text:00404519 add esp, 784h
.text:0040451F retn
.text:00404520 ; ---------------------------------------------------------------------------
.text:00404520
.text:00404520 loc_404520: ; CODE XREF: OnCheck+174j
.text:00404520 lea eax, [esp+790h+ctx]
.text:00404527 push eax
.text:00404528 call hash_init
.text:0040452D lea edi, [esp+794h+Name]
.text:00404534 or ecx, 0FFFFFFFFh
.text:00404537 xor eax, eax
.text:00404539 lea edx, [esp+794h+ctx]
.text:00404540 repne scasb
.text:00404542 not ecx
.text:00404544 dec ecx
.text:00404545 push ecx
.text:00404546 lea ecx, [esp+798h+Name]
.text:0040454D push ecx
.text:0040454E push edx
.text:0040454F call hash_update
.text:00404554 lea eax, [esp+7A0h+ctx]
.text:0040455B lea ecx, [esp+7A0h+hash_out]
.text:0040455F push eax
.text:00404560 push ecx
.text:00404561 call hash_final
.text:00404566 mov esi, ds:lstrlenA
.text:0040456C add esp, 18h
.text:0040456F lea edx, [esp+790h+Name]
.text:00404576 push edx ; lpString
.text:00404577 call esi ; lstrlenA
.text:00404579 mov ecx, eax
.text:0040457B xor eax, eax
.text:0040457D mov edx, ecx
.text:0040457F lea edi, [esp+790h+Name]
.text:00404586 shr ecx, 2
.text:00404589 rep stosd
.text:0040458B mov ecx, edx
.text:0040458D and ecx, 3
.text:00404590 rep stosb
.text:00404592 lea eax, [esp+790h+Name]
.text:00404599 lea ecx, [esp+790h+hash_out]
.text:0040459D push eax
.text:0040459E push 16
.text:004045A0 push ecx
.text:004045A1 call sub_4047B0
.text:004045A6 lea edx, [esp+79Ch+bn0]
.text:004045AA lea eax, [esp+79Ch+Name]
.text:004045B1 push edx ; int
.text:004045B2 push eax ; char *
.text:004045B3 call _zhsread
.text:004045B8 lea ecx, [esp+7A4h+bn3]
.text:004045BC lea edx, [esp+7A4h+Serial]
.text:004045C3 push ecx ; int
.text:004045C4 push edx ; char *
.text:004045C5 call _zhsread
.text:004045CA mov ecx, [esp+7ACh+bn4]
.text:004045CE mov edx, [esp+7ACh+bn1]
.text:004045D2 lea eax, [esp+7ACh+bn2]
.text:004045D6 push eax
.text:004045D7 mov eax, [esp+7B0h+bn3]
.text:004045DB push ecx
.text:004045DC push edx
.text:004045DD push eax
.text:004045DE call _zexpmod
.text:004045E3 mov ecx, [esp+7BCh+bn0]
.text:004045E7 mov edx, [esp+7BCh+bn2]
.text:004045EB push ecx
.text:004045EC push edx
.text:004045ED call _zcompare ; 关键比较!
.text:004045F2 mov edi, eax
.text:004045F4 lea eax, [esp+7C4h+bn3]
.text:004045F8 push eax
.text:004045F9 call _zfree
.text:004045FE lea ecx, [esp+7C8h+bn1]
.text:00404602 push ecx
.text:00404603 call _zfree
.text:00404608 lea edx, [esp+7CCh+bn4]
.text:0040460C push edx
.text:0040460D call _zfree
.text:00404612 add esp, 40h
.text:00404615 lea eax, [esp+790h+bn2]
.text:00404619 push eax
.text:0040461A call _zfree
.text:0040461F lea ecx, [esp+794h+bn0]
.text:00404623 push ecx
.text:00404624 call _zfree
.text:00404629 add esp, 8
.text:0040462C cmp edi, ebx
.text:0040462E jz short loc_40463C
.text:00404630 pop edi
.text:00404631 pop esi
.text:00404632 xor eax, eax
.text:00404634 pop ebx
.text:00404635 add esp, 784h
.text:0040463B retn
.text:0040463C ; ---------------------------------------------------------------------------
.text:0040463C
.text:0040463C loc_40463C: ; CODE XREF: OnCheck+28Ej
.text:0040463C lea edx, [esp+790h+Magic]
.text:00404643 push edx ; lpString
.text:00404644 call esi ; lstrlenA
.text:00404646 push eax
.text:00404647 lea eax, [esp+794h+Magic]
.text:0040464E lea ecx, [esp+794h+var_100]
.text:00404655 push eax
.text:00404656 push ecx
.text:00404657 call rc4_set_key
.text:0040465C add esp, 0Ch
.text:0040465F lea edx, [esp+790h+var_760]
.text:00404663 push offset String
.text:00404668 push edx ; lpString
.text:00404669 call esi ; lstrlenA
.text:0040466B push eax
.text:0040466C lea eax, [esp+798h+var_760]
.text:00404670 lea ecx, [esp+798h+var_100]
.text:00404677 push eax
.text:00404678 push ecx
.text:00404679 call rc4
.text:0040467E add esp, 10h
.text:00404681 mov eax, 1
.text:00404686 pop edi
.text:00404687 pop esi
.text:00404688 pop ebx
.text:00404689 add esp, 784h
.text:0040468F retn
.text:0040468F OnCheck endp
.text:0040468F
.text:00404690
.text:00404690
该CrackMe使用的是freelip大数运算库,还用到了修改过hash算法和RC4算法。
注册验证过程:
1、对用户名进行修改过的hash运算,将得到的结果转化为大数h。
2、将注册码转化为大数m,对其进行RSA加密:c=m^e mod n。
其中e、n在CrackMe中给出。
3、如果h=c则进行下一步,否则失败。
4、以用户输入的Magic字符串为key,用RC4算法对给定字符串解密,得到的明文
用于显示在界面上,表示注册成功或失败。
根据以上验证过程,可知生成注册码的过程如下:
1、对用户名进行修改过的hash运算,将得到的结果转化为大数h。
2、对大数h进行RSA解密,m=h^d mod n。
3、将大数m转化为16进制字符串,即为注册码。
4、寻找特定的key,作为Magic串。
修改过的hash算法,找修改点并逆向它的过程很麻烦,这里直接从IDA中提取相应汇编
代码实现它。见注册机源码。
这里的RSA算法中的参数e
12754A064C91DD0A8E26385EC9335A268192B730DE8541535695C9EC68ADD24F22C5DDC3CD
9D44EC38FA2F708640CB7189069E956FE84F10301128AEA613F70D
和参数n
6F907AAA920DAF37AD19DD6974540903FBC772FE38F314F4B058076B097911FEA8E7BE7525
4BDB6536F96C1A2F5BDB8C69EF81C61E369837F3B9CBC188BDCFB9
都是512位的,直接分解n很困难。
考虑到这个CrackMe肯定有解,故推断要用其它的RSA攻击方法。除大整数分解外比较有
名的攻击方法主要Weger方法和Wiener方法。
懒得自己编程实现这个算法,找了一个现成的工具(BlackEye的大作,见附件)。
选择 Wiener Attack (low private exponent),几秒钟内结果就出来了。
d = 10001
p = B90F461576D5B720E58227860DED0E063A5150C7772609F33065ABB99215561B
q = 9A54C944B5D37548A4232F13B984DC883A9DCC9ADDCC46F4A0EBCCBD993E5EBB
后一部分是RC4算法解密。最开始不知道是RC4算法,埋头逆向了半天,但越看越眼熟,
后来找了一份源码来比较,才最终确认。
已知密文,并且知道明文是由可打印字符组成的字符串,现要寻找key。
考虑到明文应该有意义,我觉得key应该是唯一的,故用穷举法寻找key。
编了一段程序,首先用0到0xFFFFFFFF以内的16进制字符串逐一试探,没想到轻松得到
一个结果,就没有再继续下去。
我找到的key是 “10001”。
总结:这个CrackMe需要一些技巧,或者说是运气吧。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)