HappyTowns 41th CrackMe 算法分析
今天心情好!最近很忙,很久没练手了,刚好HappyTown又放出一个CrackMe,就试试吧。
用PEiD查壳,显示
Microsoft Visual C++ 6.0
经验证,确实没有加壳。
用PEiD的Krypto ANAlyzer插件检查,结果如下:
RC5 / RC6 [Init, -Delta] :: 00001BF4 :: 00401BF4
用IDA载入,并加载常用的sig。找到注册验证的关键函数,初步分析的结果如下:
.text:00401100 ; int __cdecl OnCheck(HWND hDlg)
.text:00401100 OnCheck proc near ; CODE XREF: DialogFunc+4Dp
.text:00401100
.text:00401100 hashout = dword ptr -914h
.text:00401100 var_90F = dword ptr -90Fh
.text:00401100 var_90B = dword ptr -90Bh
.text:00401100 var_907 = word ptr -907h
.text:00401100 var_905 = byte ptr -905h
.text:00401100 var_904 = dword ptr -904h
.text:00401100 var_900 = dword ptr -900h
.text:00401100 var_8FC = dword ptr -8FCh
.text:00401100 var_8F8 = dword ptr -8F8h
.text:00401100 var_8F4 = byte ptr -8F4h
.text:00401100 _ctx = dword ptr -8F0h
.text:00401100 szTeam = byte ptr -8D0h
.text:00401100 szSerial = byte ptr -6DCh
.text:00401100 szTemp = byte ptr -4E8h
.text:00401100 szName = byte ptr -2F4h
.text:00401100 rc4key = dword ptr -100h
.text:00401100 hDlg = dword ptr 4
.text:00401100
.text:00401100 sub esp, 914h
.text:00401106 push ebx
.text:00401107 push ebp
.text:00401108 push esi
.text:00401109 push edi
.text:0040110A xor dl, dl
.text:0040110C mov ecx, 124
.text:00401111 xor eax, eax
.text:00401113 lea edi, [esp+631h]
.text:0040111A mov [esp+924h+szName], dl
.text:00401121 mov [esp+924h+szTeam], dl
.text:00401125 rep stosd
.text:00401127 stosw
.text:00401129 stosb
.text:0040112A mov ecx, 124
.text:0040112F xor eax, eax
.text:00401131 lea edi, [esp+55h]
.text:00401135 mov [esp+924h+szSerial], dl
.text:0040113C rep stosd
.text:0040113E stosw
.text:00401140 stosb
.text:00401141 mov ecx, 124
.text:00401146 xor eax, eax
.text:00401148 lea edi, [esp+249h]
.text:0040114F mov [esp+924h+szTemp], dl
.text:00401156 rep stosd
.text:00401158 stosw
.text:0040115A stosb
.text:0040115B mov ecx, 124
.text:00401160 xor eax, eax
.text:00401162 lea edi, [esp+43Dh]
.text:00401169 mov esi, [esp+924h+hDlg]
.text:00401170 rep stosd
.text:00401172 stosw
.text:00401174 stosb
.text:00401175 xor eax, eax
.text:00401177 mov ecx, dword_40D050
.text:0040117D mov [esp+924h+hashout+1], eax
.text:00401181 mov edi, ds:GetDlgItemTextA
.text:00401187 mov [esp+924h+var_90F], eax
.text:0040118B mov byte ptr [esp+924h+hashout], dl
.text:0040118F mov [esp+924h+var_90B], eax
.text:00401193 mov edx, dword_40D054
.text:00401199 mov [esp+924h+var_907], ax
.text:0040119E push 501 ; nMaxCount
.text:004011A3 mov [esp+928h+var_905], al
.text:004011A7 mov eax, dword_40D058
.text:004011AC mov [esp+928h+var_8FC], eax
.text:004011B0 lea eax, [esp+928h+szName]
.text:004011B7 mov [esp+928h+var_904], ecx
.text:004011BB mov ecx, dword_40D05C
.text:004011C1 mov [esp+928h+var_900], edx
.text:004011C5 mov dl, byte_40D060
.text:004011CB push eax ; lpString
.text:004011CC push 1000 ; nIDDlgItem
.text:004011D1 push esi ; hDlg
.text:004011D2 mov [esp+934h+var_8F8], ecx
.text:004011D6 mov [esp+934h+var_8F4], dl
.text:004011DA call edi ; GetDlgItemTextA ; 取用户名
.text:004011DC lea ecx, [esp+924h+szTeam]
.text:004011E0 push 501 ; nMaxCount
.text:004011E5 push ecx ; lpString
.text:004011E6 push 1001 ; nIDDlgItem
.text:004011EB push esi ; hDlg
.text:004011EC mov ebx, eax
.text:004011EE call edi ; GetDlgItemTextA ; 取组织名
.text:004011F0 cmp ebx, 3
.text:004011F3 mov ebp, eax
.text:004011F5 jl loc_4012F9
.text:004011FB cmp ebp, 2
.text:004011FE jl loc_4012F9
.text:00401204 lea edx, [esp+924h+szSerial]
.text:0040120B push 501 ; nMaxCount
.text:00401210 push edx ; lpString
.text:00401211 push 1002 ; nIDDlgItem
.text:00401216 push esi ; hDlg
.text:00401217 call edi ; GetDlgItemTextA ; 取注册码
.text:00401219 cmp eax, 32
.text:0040121C jnz loc_4012F9
.text:00401222 lea eax, [esp+924h+szTemp]
.text:00401229 lea ecx, [esp+924h+szSerial]
.text:00401230 push eax
.text:00401231 push 32
.text:00401233 push ecx
.text:00401234 call str2bytes ; 16进制字符串转换为字节序列
.text:00401239 lea edx, [esp+930h+szTemp]
.text:00401240 push 16 ; size_t
.text:00401242 lea eax, [esp+934h+szSerial]
.text:00401249 push edx ; char *
.text:0040124A push eax ; char *
.text:0040124B call _strncpy ; 再放回原处
.text:00401250 lea ecx, [esp+93Ch+_ctx]
.text:00401254 push ecx
.text:00401255 call hash_init ; 修改过的hash算法
.text:0040125A lea edx, [esp+940h+szName]
.text:00401261 push ebx
.text:00401262 lea eax, [esp+944h+_ctx]
.text:00401266 push edx
.text:00401267 push eax
.text:00401268 call hash_update
.text:0040126D lea ecx, [esp+94Ch+_ctx]
.text:00401271 lea edx, [esp+94Ch+hashout]
.text:00401275 push ecx
.text:00401276 push edx
.text:00401277 call hash_final
.text:0040127C lea eax, [esp+954h+szTeam]
.text:00401283 push ebp
.text:00401284 lea ecx, [esp+958h+rc4key]
.text:0040128B push eax
.text:0040128C push ecx
.text:0040128D call rc4_set_key
.text:00401292 lea edx, [esp+960h+szTeam]
.text:00401299 lea eax, [esp+960h+hashout]
.text:0040129D push edx
.text:0040129E push 16
.text:004012A0 lea ecx, [esp+968h+rc4key]
.text:004012A7 push eax
.text:004012A8 push ecx
.text:004012A9 call rc4 ; RC4算法
.text:004012AE add esp, 4Ch
.text:004012B1 lea edx, [esp+924h+var_904]
.text:004012B5 push 16
.text:004012B7 push edx
.text:004012B8 call rc6_set_key
.text:004012BD lea eax, [esp+92Ch+szTemp]
.text:004012C4 lea ecx, [esp+92Ch+szSerial]
.text:004012CB push eax
.text:004012CC push ecx
.text:004012CD call rc6_decrypt ; RC6算法
.text:004012D2 lea edx, [esp+934h+szTemp]
.text:004012D9 push 16 ; size_t
.text:004012DB lea eax, [esp+938h+szTeam]
.text:004012DF push edx ; char *
.text:004012E0 push eax ; char *
.text:004012E1 call _strncmp ; 这里是关键比较
.text:004012E6 add esp, 1Ch
.text:004012E9 neg eax
.text:004012EB pop edi
.text:004012EC pop esi
.text:004012ED sbb eax, eax
.text:004012EF pop ebp
.text:004012F0 inc eax
.text:004012F1 pop ebx
.text:004012F2 add esp, 914h
.text:004012F8 retn
.text:004012F9 ; ---------------------------------------------------------------------------
.text:004012F9
.text:004012F9 loc_4012F9: ; CODE XREF: OnCheck+F5j
.text:004012F9 ; OnCheck+FEj ...
.text:004012F9 pop edi
.text:004012FA pop esi
.text:004012FB pop ebp
.text:004012FC xor eax, eax
.text:004012FE pop ebx
.text:004012FF add esp, 914h
.text:00401305 retn
.text:00401305 OnCheck endp
整个验证过程还比较简单,没有使用任何的密码学库,只用到了Hash算法、RC4算法和RC6算法。
那么这几种密码学算法是如何识别出来的呢?其实也没什么技巧,主要靠经验了。
Hash算法根据初始化的参数可大致判断出来,然后再对照输入输出,知道当中有修改,不是标准的sha算法。
RC4算法比较简单,以前分析过,很容易就识别出来了。
RC6算法前面用PEiD的密码学插件已经基本看出来了,然后对照源码可判断是RC6算法。再仔细看看发现
HappyTown改变了RC6算法中的一个常数。假如HappyTown将另一个重要常数也给改了,那么PEiD的密码学插件
可能就识别不了它,这样难度估计要大不少,我也不会这么快就搞定它。这是后话。
上述代码中的hash函数是经过修改的sha1函数,直接从CrackMe中提取汇编代码即可,无须自己逆向。
由以上分析可知注册验证过程如下:
1、读取用户名name,至少3个字符。读取组织名team,至少2个字符。
2、读取注册码serial,它应该是由32个16进制字符组成的字符串。将serial转化为对应的字节串serial2,长度是16个字节。
2、检查rc4(hash(name),team) ?= rc6_decrypt(serial2, key)。如果相同则注册成功,否则失败。
其中key是给定的一个128位的RC6密钥。
那么生成注册码的过程基本上也很清楚了:
1、读取用户名name,至少3个字符。读取组织名team,至少2个字符。
2、计算serial2 = rc6_encrypt(rc4(hash(name),team), key)
4、serial2长度是16个字节。将serial2转化为对应的16进制字符串serial,长度是32字节,即为所求注册码。
总结:
这个CrackMe综合运用了Hash算法、RC4算法和RC6算法。其难点在于算法的识别,以及对于算法是否被修改以及在何处修改的判断。
对于修改过的Hash算法,如果不想费力去还原它,那么可以直接提取汇编代码。
对于修改过的对称加密算法,只能根据源码对比着寻找被修改的地方,只有这样才能还原它。
这个CrackMe如果其中RC6算法的两个常数都被修改,以使插件不能识别,那么对于新手来说难度将增加很多。
附件中包含逆向出来的代码、注册机源码,以及编译过的可执行文件。
just for fun!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)