【文章标题】: 一个crackme的算法分析和注册机
【文章作者】: 柳州小林
【作者QQ号】: 55713720
【软件名称】: crackme
【软件大小】: 484KB
【下载地址】: 我忘了
【加壳方式】: 无
【保护方式】: 无
【编写语言】: 还没有分析得出
【使用工具】: OD
【操作平台】: XP SP3
【作者声明】: 目的是学习逆算法,写注册机(WIN32汇编)
--------------------------------------------------------------------------------
【详细过程】
用OD打开后停在这里:
0047C000 > 55 push ebp ;各系统不同,请自行计算以下地址的偏移值
0047C001 8BEC mov ebp, esp
一、找到关键代码
这时按CTRL+N看看这个crackme都用了什么API
主要是看看有没有MessageBox、GetDlgItemText、GetWindowText等API
这里看到有:
地址=0040527C 名称=user32.MessageBoxA
地址=00405290 名称=user32.GetDlgItemTextA
先看看MessageBoxA吧,点它按回车看到:
地址=004015EE 反汇编=call <jmp.&user32.MessageBoxA>
地址=00401B4C 反汇编=jmp dword ptr [<&user32.MessageBoxA>] 注释=user32.MessageBoxA
选中call <jmp.&user32.MessageBoxA>按回车就到反汇编窗口中了:
这是点了“ABout”按键后出现的信息窗口
004015E0 |. 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004015E2 |. 68 9A454000 push 0040459A ; |Title = "Crackme by_nvr !"
004015E7 |. 68 8C454000 push 0040458C ; |Text = "Good Luck Men"
004015EC |. 6A 00 push 0 ; |hOwner = NULL
004015EE |. E8 59050000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA《====回车到这里
(向上找就到了关键代码,仅限此例)
CTRL+N后选中user32.GetDlgItemTextA后回车:
004010F2 call <jmp.&user32.GetDlgItemTextA>
004014EF call <jmp.&user32.GetDlgItemTextA>
00401B2E jmp dword ptr [<&user32.GetDlgItemTe user32.GetDlgItemTextA
这个程序用了两个GetDlgItemTextA(一个取用户名,一个取注册码),在这里下断,断下后向下就是关键代码
这里要在401503下断,不然会断在window的消息循环里,无法切换到窗口
二、分析
断下GetDlgItemTextA后停在这里:
004014E0 |. 6A 10 push 10 ;取多长的字,10h=16w个
004014E2 |. 68 4C494000 push 0040494C ;存放的内存地址
004014E7 |. 68 E9030000 push 3E9 ;取哪个控件的字
004014EC |. FF75 08 push dword ptr [ebp+8] ; 上面的控件在哪个窗口
004014EF |. E8 3A060000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA 这里是
004014F4 |. 8BD8 mov ebx, eax ;存用户名长度
004014F6 |. 8B45 10 mov eax, dword ptr [ebp+10]
004014F9 |. 66:3D EB03 cmp ax, 3EB ;是否按键了"OK"按键(OD的"查看->窗口"可以看控件ID)
004014FD |. 0F85 D7000000 jnz 004015DA
00401503 |. 8BC3 mov eax, ebx ;取回用户名长度
00401505 |. 83F8 06 cmp eax, 6
00401508 |. 0F8C CA000000 jl 004015D8 ;要大于6位
0040150E |. 83F8 0A cmp eax, 0A
00401511 |. 0F8F C1000000 jg 004015D8 ;要小于10位
这个crackme的算法蛮多的,我就不一一列出只给出地址:
地址40151B--40154B:
为用户名运算后得出一个标记,后取DS里相应标记的值,存到DS:[40497C],这里记为S1
地址40154D为一个CALL:
为用户名与S1运算得一个值,记为S2
地址40155B为一个CALL:
注册码前位与DS内的一个固定值比较,所以注册码前6位也是固定的
004010DA /$ 55 push ebp
004010DB |. 8BEC mov ebp, esp
004010DD |. 83C4 FC add esp, -4
004010E0 |. 6A 20 push 20 ; /Count = 20 (32.)
004010E2 |. 68 5C494000 push 0040495C ; 存在什么地方
004010E7 |. 68 EA030000 push 3EA ; |ControlID = 3EA (1002.)
004010EC |. FF35 3C484000 push dword ptr [40483C] ; |hWnd = 000901DE ('Crack me -nvr',class='#32770')
004010F2 |. E8 370A0000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
004010F7 |. 8BD8 mov ebx, eax ; 存注册码长度
004010F9 |. 68 7C494000 push 0040497C ; 取S1的长度,实际就是取用户名长度
004010FE |. E8 CD0A0000 call <jmp.&kernel32.lstrlenA> ; \lstrlenA返回指定字符串的字节长度
00401103 |. 83C0 10 add eax, 10 ; 注册码长度=22位~26位
00401106 |. 3BC3 cmp eax, ebx ;这里我发现有一个BUG,记为BUG1以后再分析
00401108 |. 75 58 jnz short 00401162 ;所以请用6位用户名来分析
0040110A |. 0FB605 624940>movzx eax, byte ptr [404962] ; 注册码第7位是否为'-'
00401111 |. 83F8 2D cmp eax, 2D
00401114 |. 75 4C jnz short 00401162
00401116 |. 0FB605 6B4940>movzx eax, byte ptr [40496B] ; 注册码第16位是否为'-'
0040111D |. 83F8 2D cmp eax, 2D
00401120 |. 75 40 jnz short 00401162 ;这里得出注册码形式
; AAAAAA-BBBBBBBB-CCCCCC
;这时重起OD,输入6位用户名和正确形式的注册码
地址401137:是复制一份注册码前6位
地址40114C:是注册码前6位运算后与DS:[404029]比较,这里我是自己写了个穷举程序得出"NOVRAI"
地址40156D--4015D1:是注册码后6位运算后与S1比较,所以可以用S1来逆出注册后6位,40156D这一句也是BUG1
401588--4015A1是这个程序的BUG2
地址4055D3是一个CALL:是注册码中间8位运算后与S2比较,所以可以用S2逆出注册码中间8位.
对了就到40128B,之后就是美女画片了,HEHE!
三、BUG分析
BUG1:当算完S2的时候请大家看看DS,会发现S1和S2是差2个BYTE,就是说,如果用户名为8位,S1也为8位,就会和S2连在一起
形成一个长为16位的串,在004010FE取长度的时候就会得出16的结果,而不是正确的S1长度.最终用户名超过7位就无法注册
BUG2:401588--4015A1是判断注册码后6位是否是大小写字母和数字,你可以用我写的注册机输入6个9试试,注册码后6位会是6个'}'
最终有部分用户名无法注册
--------------------------------------------------------------------------------
2009年02月19日 10:41:59
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!