【文章标题】: TMG Official Trial Keygenme #3分析
【文章作者】: HappyTown
【作者邮箱】: [email]wxr277@163.com[/email]
【作者主页】: www.pediy.com
【软件名称】: TMG Official Trial Keygenme #3
【下载地址】: 附件内
【加壳方式】: 无
【保护方式】: MD5 + ElGamal
【编写语言】: VC 5.0
【使用工具】: OD,DAMN_Hash,BigCalc
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
建议你在阅读该破文的同时对原程序进行调试,这样效果会更好。
一、基本信息:
1. PEiD查看,为VC 5编写,无壳;
2. 用PEiD的KANAL插件查看,MD5 inside;
3. 用IDA分析不出什么库的相关函数;
4. 什么都不输入,弹出错误信息,这个可以帮助定位应该下断的地方。
二、分析过程:
1. OD载入后,下断;
2. 查找文本字符串,根据"MIRACL error"我们发现的确使用了miracl库,这就需要手动定位库函数了,下面我会努力解释如何做到这一点;
3. 输入注册信息:
Username:happy
Registration Code:1234567890ABCDEF2222222233333333 (为什么是32位的原因在下面)
我把Registration Code分为sn_1和sn_2,各长16。
00401000 /$>sub esp, 1FC
00401006 |.>push ebx
00401007 |.>push ebp
00401008 |.>push esi
00401009 |.>mov esi, [<&USER32.SendDlgItem>; USER32.SendDlgItemMessageA
0040100F |.>lea eax, [esp+188]
00401016 |.>push edi
00401017 |.>mov edi, [esp+210]
0040101E |.>push eax ; /lParam
0040101F |.>push 41 ; |wParam = 41
00401021 |.>push 0D ; |Message = WM_GETTEXT
00401023 |.>push 3F1 ; |ControlID = 3F1 (1009.)
00401028 |.>push edi ; |hWnd
00401029 |.>call esi ; \SendDlgItemMessageA
0040102B |.>cmp eax, 5 ; name长度>=5
0040102E |.>mov [esp+20], eax
00401032 |.>jnb short 00401052
00401034 |.>push 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
00401036 |.>push 0040A264 ; |Title = "Error"
0040103B |.>push 0040A244 ; |Text = "Length of Username is invalid."
00401040 |.>push edi ; |hOwner
00401041 |.>call [<&USER32.MessageBoxA>] ; \MessageBoxA
00401047 |.>pop edi
00401048 |.>pop esi
00401049 |.>pop ebp
0040104A |.>pop ebx
0040104B |.>add esp, 1FC
00401051 |.>retn
00401052 |>>lea ecx, [esp+34]
00401056 |.>push ecx
00401057 |.>push 41
00401059 |.>push 0D
0040105B |.>push 3F2
00401060 |.>push edi
00401061 |.>call esi
00401063 |.>cmp eax, 20 ; sn长度必须为32
00401066 |.>je short 00401086
00401068 |.>push 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
0040106A |.>push 0040A264 ; |Title = "Error"
0040106F |.>push 0040A21C ; |Text = "Length of Registration code is invalid."
00401074 |.>push edi ; |hOwner
00401075 |.>call [<&USER32.MessageBoxA>] ; \MessageBoxA
0040107B |.>pop edi
0040107C |.>pop esi
0040107D |.>pop ebp
0040107E |.>pop ebx
0040107F |.>add esp, 1FC
00401085 |.>retn
00401086 |>>xor edx, edx
00401088 |>>/mov cl, [esp+edx+34]
0040108C |.>|cmp cl, 30
0040108F |.>|jnb short 00401092
00401091 |.>|dec eax
00401092 |>>|cmp cl, 46
00401095 |.>|jbe short 00401098
00401097 |.>|dec eax
00401098 |>>|cmp cl, 39
0040109B |.>|jbe short 004010A3
0040109D |.>|cmp cl, 41
004010A0 |.>|jnb short 004010A3
004010A2 |.>|dec eax
004010A3 |>>|inc edx
004010A4 |.>|cmp edx, 20
004010A7 |.>\jb short 00401088
004010A9 |.>cmp eax, 20
004010AC |.>je short 004010CC
004010AE |.>push 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
004010B0 |.>push 0040A210 ; |Title = "Bad luck"
004010B5 |.>push 0040A1E4 ; |Text = "Wild guessing won't help you too much :-("
004010BA |.>push edi ; |hOwner
004010BB |.>call [<&USER32.MessageBoxA>] ; \MessageBoxA
004010C1 |.>pop edi
004010C2 |.>pop esi
004010C3 |.>pop ebp
004010C4 |.>pop ebx
004010C5 |.>add esp, 1FC
004010CB |.>retn
004010CC |>>mov edx, [esp+44]
004010D0 |.>mov eax, [esp+48]
004010D4 |.>mov ecx, [esp+4C]
004010D8 |.>mov [esp+B4], edx
004010DF |.>mov edx, [esp+50]
004010E3 |.>push 0
004010E5 |.>mov [esp+BC], eax
004010EC |.>mov [esp+C0], ecx
004010F3 |.>mov [esp+C4], edx
004010FA |.>mov byte ptr [esp+C8], 0
00401102 |.>mov byte ptr [esp+48], 0
00401107 |.>call <mirvar> //可以看到下面一连串调用了这个函数,使用miracl的大数必须先用该函数初始化
//用鼠标点击这一行,然后按回车键,再按Shift + :(英文冒号),输入mirvar给
//该函数加个标签
0040110C |.>add esp, 4
0040110F |.>mov [esp+14], eax
00401113 |.>push 0
00401115 |.>call <mirvar>
0040111A |.>add esp, 4
0040111D |.>mov esi, eax
0040111F |.>push 0
00401121 |.>call <mirvar>
00401126 |.>add esp, 4
00401129 |.>mov [esp+18], eax
0040112D |.>push 0
0040112F |.>call <mirvar>
00401134 |.>add esp, 4
00401137 |.>mov [esp+10], eax
0040113B |.>push 0
0040113D |.>call <mirvar>
00401142 |.>add esp, 4
00401145 |.>mov [esp+1C], eax
00401149 |.>push 0
0040114B |.>call <mirvar>
00401150 |.>add esp, 4
00401153 |.>mov ebp, eax
00401155 |.>push 0
00401157 |.>call <mirvar>
0040115C |.>add esp, 4
0040115F |.>mov ebx, eax
00401161 |.>push 0
00401163 |.>call <mirvar>
00401168 |.>add esp, 4
0040116B |.>mov edi, eax
0040116D |.>lea eax, [esp+134]
00401174 |.>push eax
00401175 |.>call <MD5_Init> ;===>跟进去
//很明显的MD5初始化,和上面一样,我们加上MD5_Init标签,下面我就不再强调加标签了
00401880 >/$>mov eax, [esp+4]
00401884 |.>xor ecx, ecx
00401886 |.>mov [eax+14], ecx
00401889 |.>mov [eax+10], ecx
0040188C |.>mov dword ptr [eax], 67452301
00401892 |.>mov dword ptr [eax+4], EFCDAB89
00401899 |.>mov dword ptr [eax+8], 98BADCFE
004018A0 |.>mov dword ptr [eax+C], 10325476
004018A7 \.>retn
\\
0040117A |.>mov ecx, [esp+24]
0040117E |.>add esp, 4
00401181 |.>lea edx, [esp+18C]
00401188 |.>lea eax, [esp+134]
0040118F |.>push ecx ; name长度
00401190 |.>push edx ; name
00401191 |.>push eax
00401192 |.>call <MD5_Update> //再根据name长度和name两个参数,猜测应该为MD5_Update,可以跟进去分析
00401197 |.>add esp, 0C
0040119A |.>lea ecx, [esp+134]
004011A1 |.>lea edx, [esp+24]
004011A5 |.>push ecx
004011A6 |.>push edx ;先d edx一下
0012F950 09 00 00 00 09 00 00 00 5C 00 F5 72 8C F9 12 00 ........\.躜?.
004011A7 |.>call <MD5_Final> ; MD5(name)
004011AC |.>mov eax, [40CE08] //内存区是不是变成了MD5(happy)的值,用DAMNHash验证通过
0012F950 56 AB 24 C1 5B 72 A4 57 06 9C 5E A4 2F CF C6 40 V?鳞rぷ??掀@
004011B1 |.>add esp, 8
004011B4 |.>lea edx, [esp+24]
004011B8 |.>mov dword ptr [eax+22C], 100
004011C2 |.>mov ecx, [40CE08]
004011C8 |.>push edx
004011C9 |.>push ebp ; h = MD5(name)
004011CA |.>mov dword ptr [ecx+23C], 10
004011D4 |.>call <cinstr>
004011D9 |.>mov eax, [40CE08]
004011DE |.>add esp, 8
004011E1 |.>push 0040A1D0 ; ASCII "37A218F214C32D79"
004011E6 |.>push esi ; m,同时在这里d esi,查看内存区
00A32100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
空空如也。
004011E7 |.>mov dword ptr [eax+22C], 10
004011F1 |.>call <cinstr> //根据上面的ASCII "37A218F214C32D79"猜测这个应该是cinstr,
//功能是把字符串转化为大数,执行完这个函数后查看内存区
00A32100 02 00 00 00 79 2D C3 14 F2 18 A2 37 00 00 00 00 ...y-???....
最前面的02意思是,这个大数占2个32位,后面的数字是不是"37A218F214C32D79"?倒过来看。+标签嘛^_^
004011F6 |.>add esp, 8
004011F9 |.>push ebp ; h
004011FA |.>push esi ; m
004011FB |.>push 3
004011FD |.>push ebp ; h;d ebp一下:
00A32780 04 00 00 00 40 C6 CF 2F A4 5E 9C 06 57 A4 72 5B ...@葡/ま?Wを[
004011FE |.>call <power> ; h = h^3(mod m) = 0BD38C6FB54DE8FF
00A32780 02 00 00 00 FF E8 4D B5 6F 8C D3 0B 00 00 00 00 ...?M碉?....
执行完该call后,查看内存区;该函数共有4个参数,第二个为整型,常用的就只有power,
该函数的功能可查看miracl手册,BigCalc验证通过。
00401203 |.>add esp, 10
00401206 |.>mov edx, [esp+10]
0040120A |.>lea ecx, [esp+34]
0040120E |.>push ecx ; sn_1
0040120F |.>push edx
00401210 |.>call <cinstr>
00401215 |.>mov ecx, [esp+24]
00401219 |.>add esp, 8
0040121C |.>lea eax, [esp+B4]
00401223 |.>push eax ; sn_2
00401224 |.>push ecx
00401225 |.>call <cinstr>
0040122A |.>add esp, 8
0040122D |.>push 0040A1BC ; ASCII "C9D94F46D0984F42"
00401232 |.>push esi ; p_1
00401233 |.>call <cinstr>
00401238 |.>mov edx, [esp+1C]
0040123C |.>add esp, 8
0040123F |.>push 0040A1A8 ; ASCII "91D4D6EF46B05C78"
00401244 |.>push edx ; y
00401245 |.>call <cinstr>
0040124A |.>mov eax, [esp+20]
0040124E |.>add esp, 8
00401251 |.>push 0040A194 ; ASCII "4B45042B684BCBD1"
00401256 |.>push eax ; g
00401257 |.>call <cinstr>
0040125C |.>add esp, 8
0040125F |.>push esi ; p
00401260 |.>push 1
00401262 |.>push esi //d esi一下:
00A32100 02 00 00 00 42 4F 98 D0 46 4F D9 C9 00 00 00 00 ...BO?FO偕....
00401263 |.>call <incr> ; p = p_1 + 1 = C9D94F46D0984F43(素数,障眼法的使用?)
//执行完call后,再查看内存区:
00A32100 02 00 00 00 43 4F 98 D0 46 4F D9 C9 00 00 00 00 ...CO?FO偕....
变化在那里呢?呵呵,你肯定看出来了,42变成了43,所以这个函数应该为incr。
00401268 |.>mov ecx, [esp+28]
0040126C |.>mov edx, [esp+1C]
00401270 |.>add esp, 0C
00401273 |.>push ebx ; t
00401274 |.>push esi ; p
00401275 |.>push ecx ; sn_2
00401276 |.>push edx ; sn_1
00401277 |.>call <powmod> ; t = sn_1^sn_2 (mod p) = 5BA61D0BB0894B12
//使用miracl库却不使用powmod,那用它干什么?
0040127C |.>mov eax, [esp+20]
00401280 |.>mov ecx, [esp+24]
00401284 |.>add esp, 10
00401287 |.>push edi ; s
00401288 |.>push esi ; p
00401289 |.>push eax ; sn_1
0040128A |.>push ecx ; y
0040128B |.>call <powmod> ; s = y^sn_1 (mod p) = 240AB80995A29A16
00401290 |.>add esp, 10
00401293 |.>push edi ; s
00401294 |.>push esi ; p
00401295 |.>push esi ; p
00401296 |.>push edi ; s
00401297 |.>push edi ; s
00401298 |.>push ebx ; t
00401299 |.>call <mad> ; s = t*s (mod p) = 4369A79E096FF8B6
//这个函数相当好猜测,因为它有6个参数,另一个有6个参数的是powmod2(a,b,c,d,z,w),BigCalc验证应该为mad
0040129E |.>mov edx, [esp+30]
004012A2 |.>add esp, 18
004012A5 |.>push ebx ; t
004012A6 |.>push esi ; p
004012A7 |.>push ebp ; h
004012A8 |.>push edx ; g
004012A9 |.>call <powmod> ; t = g^h (mod p) = 66468FB8A8A01FDD
004012AE |.>add esp, 10
004012B1 |.>push edi ; s
004012B2 |.>push ebx ; t
004012B3 |.>call <compare> //如果你对miracl库有所了解,这个函数还是经常被使用的
//它的后面肯定跟一判断跳转指令。
004012B8 |.>add esp, 8
004012BB |.>test eax, eax
004012BD |.>jnz short 004012D4
004012BF |.>push eax
004012C0 |.>mov eax, [esp+214]
004012C7 |.>push 0040A188 ; ASCII "Well done!"
004012CC |.>push 0040A15C ; ASCII "Very good! Registration code is correct :-)"
004012D1 |.>push eax
004012D2 |.>jmp short 004012E8
004012D4 |>>mov ecx, [esp+210]
004012DB |.>push 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
004012DD |.>push 0040A210 ; |Title = "Bad luck"
004012E2 |.>push 0040A138 ; |Text = "Registration code is incorrect. :-("
004012E7 |.>push ecx ; |hOwner
004012E8 |>>call [<&USER32.MessageBoxA>] ; \MessageBoxA
004012EE |.>mov edx, [esp+14]
004012F2 |.>push edx
004012F3 |.>call <mirkill> //大数变量使用完后要手动销毁
004012F8 |.>add esp, 4
004012FB |.>push esi
004012FC |.>call <mirkill>
00401301 |.>mov eax, [esp+1C]
00401305 |.>add esp, 4
00401308 |.>push eax
00401309 |.>call <mirkill>
0040130E |.>mov ecx, [esp+14]
00401312 |.>add esp, 4
00401315 |.>push ecx
00401316 |.>call <mirkill>
0040131B |.>mov edx, [esp+20]
0040131F |.>add esp, 4
00401322 |.>push edx
00401323 |.>call <mirkill>
00401328 |.>add esp, 4
0040132B |.>push ebp
0040132C |.>call <mirkill>
00401331 |.>add esp, 4
00401334 |.>push ebx
00401335 |.>call <mirkill>
0040133A |.>add esp, 4
0040133D |.>push edi
0040133E |.>call <mirkill>
00401343 |.>add esp, 4
00401346 |.>pop edi
00401347 |.>pop esi
00401348 |.>pop ebp
00401349 |.>pop ebx
0040134A |.>add esp, 1FC
00401350 \.>retn
--------------------------------------------------------------------------------
【经验总结】
可以看出,这个keygenme使用了MD5和ElGamal,具体验证算法如下:
1. h=MD5(name)^3 (mod m);
2. (y^sn_1)*(sn_1^sn_2) (mod p - 1) ?= g^h (mod p - 1)
知道了算法,做出注册机就很简单了,详见附件。
给出一组可用的注册码:
Username:happytown
Registration Code:BD7DD4164F45497573CFE2ABD92A4B4A
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年10月16日 17:52:48
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!