好友
阅读权限30
听众
最后登录1970-1-1
|
CM是什么?Crackme是什么?这是什么东西?楼主发的什么?
他们都是一些公开给别人尝试破解的小程序,制作 Crackme 的人可能是程序员,想测试一下自己的软件保护技术,也可能是一位 Cracker,想挑战一下其它 Cracker 的破解实力,也可能是一些正在学习破解的人,自己编一些小程序给自己破解,KeyGenMe是要求别人做出它的 keygen (序号产生器), ReverseMe 要求别人把它的算法做出逆向分析, UnpackMe 是要求别人把它成功脱壳,本版块禁止回复非技术无关水贴。
【文章标题】: 一个简单CM的简单算法分析
【文章作者】: JoyChou
【软件名称】: Game
【软件大小】: 176KB
【下载地址】: 见附件
【加壳方式】: 无壳
【保护方式】: 注册码保护
【编写语言】: Microsoft Visual C++ 5.0 [Debug]
【使用工具】: PEiD,OD,IDA
【操作平台】: XP SP3
【软件介绍】: 一个简单的猜数字游戏
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
首先查壳无壳,VC++的程序,运行后,ALT+E查看模块没有MFC42模块,不是MFC的程序。
OD载入后,ctrl+n,查看输入表
发现MessageBox,GetDlgItemText暴露无遗,然后搜索字符串也OK,
所以这个程序是拿给大家玩的,并无什么难度。
——————————————————————————————————
菜鸟试着从程序入口点分析这个CM,不直接着手关键点
在编写Win32应用程序的适合,都必须在源码里实现一个Winmain函数,
但Windows程序并不是从WinMain函数开始的,首先被执行的是启动函数相关代码,这段
代码由编译器自动生成,然后才是WinMain函数,
所以程序的OEP并不是WinMain函数,我们OD载入向下看,一直到这,00401682 |. 52 push edx
00401683 |. 6A 00 push 0
00401685 |. 6A 00 push 0 ; /pModule = NULL
00401687 |. FF15 B8A14200 call dword ptr ds:[<&KERNEL32.GetModule>; \GetModuleHandleA
0040168D |. 50 push eax
0040168E |. E8 81F9FFFF call Game.00401014 //程序WinMain函数的入口点
00401693 |. 8945 A0 mov dword ptr ss:[ebp-60],eax
00401696 |. 8B45 A0 mov eax,dword ptr ss:[ebp-60]
00401699 |. 50 push eax
0040169A |. E8 91080000 call Game.00401F30 // 退出程序
此时ctrl+G 输入401014来到程序入口点
00401014 $ /E9 17000000 jmp Game.00401030
来到00401030
00401030 > \55 push ebp
00401031 . 8BEC mov ebp,esp
00401033 . 83EC 40 sub esp,40
00401036 . 53 push ebx
00401037 . 56 push esi
00401038 . 57 push edi
00401039 . 8D7D C0 lea edi,dword ptr ss:[ebp-40]
0040103C . B9 10000000 mov ecx,10
00401041 . B8 CCCCCCCC mov eax,CCCCCCCC
00401046 . F3:AB rep stos dword ptr es:[edi]
00401048 . 8B45 08 mov eax,dword ptr ss:[ebp+8]
0040104B . A3 507C4200 mov dword ptr ds:[427C50],eax
00401050 . 8BF4 mov esi,esp
00401052 . 6A 00 push 0 ; /lParam = NULL
00401054 . 68 0A104000 push Game.0040100A ; |DlgProc = Game.0040100A
00401059 . 6A 00 push 0 ; |hOwner = NULL
0040105B . 6A 65 push 65 ; |pTemplate = 65
0040105D . 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; |
00401060 . 51 push ecx ; |hInst
00401061 . FF15 D0A24200 call dword ptr ds:[<&USER32.DialogBoxParamA>] ; \DialogBoxParamA// 弹出对话框
00401067 . 3BF4 cmp esi,esp
00401069 . E8 52040000 call Game.004014C0 // 无用call
0040106E . 33C0 xor eax,eax
00401070 . 5F pop edi
00401071 . 5E pop esi
00401072 . 5B pop ebx
00401073 . 83C4 40 add esp,40
00401076 . 3BEC cmp ebp,esp
00401078 . E8 43040000 call Game.004014C0 // 无用call
0040107D . 8BE5 mov esp,ebp
0040107F . 5D pop ebp
00401080 . C2 1000 retn 10
到这后,我们再向下看,就看到了程序的主要部分
004010B8 |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
004010BB |. 8945 E8 mov dword ptr ss:[ebp-18],eax
004010BE |. 837D E8 10 cmp dword ptr ss:[ebp-18],10
004010C2 |. 0F84 B9000000 je Game.00401181
004010C8 |. 817D E8 10010>cmp dword ptr ss:[ebp-18],110
004010CF |. 74 0E je short Game.004010DF
004010D1 |. 817D E8 11010>cmp dword ptr ss:[ebp-18],111
004010D8 |. 74 26 je short Game.00401100
004010DA |. E9 B9000000 jmp Game.00401198
004010DF |> 8BF4 mov esi,esp
004010E1 |. 6A 00 push 0 ; /lParam = 0
004010E3 |. 6A 0E push 0E ; |wParam = E
004010E5 |. 68 C5000000 push 0C5 ; |Message = EM_LIMITTEXT
004010EA |. 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; |
004010ED |. 51 push ecx ; |hWnd
004010EE |. FF15 C0A24200 call dword ptr ds:[<&USER32.SendMessageA>] ; \SendMessageA // 使对话框的输入字符最大长度为0E,不过程序没有执行到这
00401113 |. 6A 10 push 10 ; /Count = 10 (16.)
00401115 |. 8D45 EC lea eax,dword ptr ss:[ebp-14] ; |
00401118 |. 50 push eax ; |Buffer
00401119 |. 68 E9030000 push 3E9 ; |ControlID = 3E9 (1001.)
0040111E |. 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; |
00401121 |. 51 push ecx ; |hWnd
00401122 |. FF15 C4A24200 call dword ptr ds:[<&USER32.GetDlgItemTextA>] ; \GetDlgItemTextA //获取对话框控件的字符串,在这下断就好了
00401128 |. 3BF4 cmp esi,esp
0040112A |. E8 91030000 call Game.004014C0
0040112F |. 8D55 EC lea edx,dword ptr ss:[ebp-14]
00401132 |. 52 push edx //获得的字符串压栈
00401133 |. E8 D7FEFFFF call Game.0040100F // 处理获取的字符串
F7进入00401133 这个关键call
00401320 /> \55 push ebp
00401321 |. 8BEC mov ebp,esp
00401323 |. 83EC 58 sub esp,58
00401326 |. 53 push ebx
00401327 |. 56 push esi
00401328 |. 57 push edi
00401329 |. 8D7D A8 lea edi,dword ptr ss:[ebp-58] ; HDg
0040132C |. B9 16000000 mov ecx,16
00401331 |. B8 CCCCCCCC mov eax,CCCCCCCC
00401336 |. F3:AB rep stos dword ptr es:[edi]
00401338 |. C645 EC 00 mov byte ptr ss:[ebp-14],0
0040133C |. 33C0 xor eax,eax
0040133E |. 8945 ED mov dword ptr ss:[ebp-13],eax
00401341 |. 8945 F1 mov dword ptr ss:[ebp-F],eax
00401344 |. 8945 F5 mov dword ptr ss:[ebp-B],eax
00401347 |. 66:8945 F9 mov word ptr ss:[ebp-7],ax
0040134B |. 8845 FB mov byte ptr ss:[ebp-5],al
0040134E |. 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; 假码
00401351 |. 51 push ecx ; 假码入栈
00401352 |. E8 A9010000 call Game.00401500 ; 计算假码长度返回给eax,这里就不进去了
00401357 |. 83C4 04 add esp,4
0040135A |. 8945 FC mov dword ptr ss:[ebp-4],eax ; ebp-4为假码长度
0040135D |. C745 E8 01000>mov dword ptr ss:[ebp-18],1 ; i=i+2,i初始化为1
00401364 |. EB 09 jmp short Game.0040136F
00401366 |> 8B55 E8 /mov edx,dword ptr ss:[ebp-18]
00401369 |. 83C2 02 |add edx,2
0040136C |. 8955 E8 |mov dword ptr ss:[ebp-18],edx
0040136F |> 8B45 E8 mov eax,dword ptr ss:[ebp-18]
00401372 |. 3B45 FC |cmp eax,dword ptr ss:[ebp-4]
00401375 |. 7D 17 |jge short Game.0040138E ; eax大于等于假码长度就跳出循环
00401377 |. 8B4D 08 |mov ecx,dword ptr ss:[ebp+8] ; ebp+8指向假码
0040137A |. 034D E8 |add ecx,dword ptr ss:[ebp-18] ; 取偶数位,也就是s[1],s[3],s[5]数组表示
0040137D |. 0FBE11 |movsx edx,byte ptr ds:[ecx] ; 依次给edx
00401380 |. 83FA 32 |cmp edx,32 ; 和2比较
00401383 |. 74 07 |je short Game.0040138C ; 相等就跳
00401385 |. 33C0 |xor eax,eax
00401387 |. E9 B4000000 |jmp Game.00401440 ; 跳向错误
0040138C |>^ EB D8 \jmp short Game.00401366
所以程序先处理偶数位,要求就是每位偶数位都是2,下面就是处理奇数位,但是处理奇数位的时候用了一个反汇编代码比较多的表示方法
a % 2 == 0,当然0,2,4,6等余数都是0,所以表示的是奇数。
if(a % 2 == 0)
{
//do something
}
对应的汇编指令比较多些
Assembly code
00A31395 mov eax,dword ptr [a]
00A31398 and eax,80000001h
00A3139D jns main+34h (0A313A4h)
00A3139F dec eax
00A313A0 or eax,0FFFFFFFEh
00A313A3 inc eax
00A313A4 text eax,eax
00A313A7 jne main+40h (0A313B0h)
如果这样处理
if(a & 0x1 == 1)
{
//do something
}
对应的汇编指令好少啊~
Assembly code
002D1395 mov eax,dword ptr [a]
002D1398 and eax,1
002D139B je main+34h (2D13A4h)
相比之下,我想出题者肯定是想增加下难度而已。菜鸟再次飘过~~下面继续分析奇数位~~
接下来的代码就是将奇数位放在ebp-14,这里菜鸟就不仔细分析了,有时候可以直接步过看结果,猜测算法,大大减少了分析代码的时间
0040138E |> \C745 E8 00000>mov dword ptr ss:[ebp-18],0 ; i=0
00401395 |. EB 09 jmp short Game.004013A0
00401397 |> 8B45 E8 /mov eax,dword ptr ss:[ebp-18]
0040139A |. 83C0 01 |add eax,1 ; i++
0040139D |. 8945 E8 |mov dword ptr ss:[ebp-18],eax
004013A0 |> 8B4D E8 mov ecx,dword ptr ss:[ebp-18]
004013A3 |. 3B4D FC |cmp ecx,dword ptr ss:[ebp-4] ; i和假码长度比较
004013A6 |. 7D 2A |jge short Game.004013D2 ; 大于等于就跳
004013A8 |. 8B55 E8 |mov edx,dword ptr ss:[ebp-18]
004013AB |. 81E2 01000080 |and edx,80000001 ; edx=i^80000001
004013B1 |. 79 05 |jns short Game.004013B8 ; SF=0就跳
004013B3 |. 4A |dec edx
004013B4 |. 83CA FE |or edx,FFFFFFFE
004013B7 |. 42 |inc edx
004013B8 |> 85D2 |test edx,edx
004013BA |. 75 14 |jnz short Game.004013D0
004013BC |. 8B45 E8 |mov eax,dword ptr ss:[ebp-18]
004013BF |. 99 |cdq
004013C0 |. 2BC2 |sub eax,edx
004013C2 |. D1F8 |sar eax,1
004013C4 |. 8B4D 08 |mov ecx,dword ptr ss:[ebp+8]
004013C7 |. 034D E8 |add ecx,dword ptr ss:[ebp-18]
004013CA |. 8A11 |mov dl,byte ptr ds:[ecx]
004013CC |. 885405 EC |mov byte ptr ss:[ebp+eax-14],dl
004013D0 |>^ EB C5 \jmp short Game.00401397
004013D2 |> 8D45 EC lea eax,dword ptr ss:[ebp-14]
下面就是处理奇数位~~
004013D2 |> \8D45 EC lea eax,dword ptr ss:[ebp-14]
004013D5 |. 50 push eax ; 奇数位入栈处理
004013D6 |. E8 2AFCFFFF call Game.00401005 ; 这个call很重要,进去看看
004013DB |. 83C4 04 add esp,4
004013DE |. 85C0 test eax,eax ; eax返回应该不为0
004013E0 |. 74 2D je short Game.0040140F ; 跳向失败,不让它跳
004013E2 |. 8D4D EC lea ecx,dword ptr ss:[ebp-14]
004013E5 |. 51 push ecx
004013E6 |. E8 1AFCFFFF call Game.00401005 ; 和上面call一样,也就是处理两次
004013EB |. 83C4 04 add esp,4
004013EE |. 85C0 test eax,eax ; eax返回应该不为0
004013F0 |. 74 17 je short Game.00401409 ; 跳向失败,不让它跳
004013F2 |. 8D55 EC lea edx,dword ptr ss:[ebp-14]
004013F5 |. 52 push edx
004013F6 |. E8 05010000 call Game.00401500 ; 计算ebp-14指向字符串的长度,再IDA里可以直接看到strlen这个函数名字
004013FB |. 83C4 04 add esp,4
004013FE |. 83F8 04 cmp eax,4 ; 和4比较
00401401 |. 74 04 je short Game.00401407 ; 不相等就跳向失败
00401403 |. 33C0 xor eax,eax
00401405 |. EB 39 jmp short Game.00401440 ; 跳向失败
00401407 |> EB 04 jmp short Game.0040140D ; 长度等于4就跳向这
00401409 |> 33C0 xor eax,eax
0040140B |. EB 33 jmp short Game.00401440
0040140D |> EB 04 jmp short Game.00401413 ; 再跳到这
0040140F |> 33C0 xor eax,eax
00401411 |. EB 2D jmp short Game.00401440
00401413 |> 0FBE45 EC movsx eax,byte ptr ss:[ebp-14] ; 再到这,取奇数第一位
00401417 |. 0FBE4D ED movsx ecx,byte ptr ss:[ebp-13] ; 奇数第二位
0040141B |. 3BC1 cmp eax,ecx
0040141D |. 7E 1F jle short Game.0040143E ; 跳向失败,故第一位应该大于第二位
0040141F |. 0FBE55 ED movsx edx,byte ptr ss:[ebp-13]
00401423 |. 0FBE45 EE movsx eax,byte ptr ss:[ebp-12] ; 奇数第三位
00401427 |. 3BD0 cmp edx,eax
00401429 |. 7D 13 jge short Game.0040143E ; 跳向失败,故第二位应该小于第三位
0040142B |. 0FBE4D EE movsx ecx,byte ptr ss:[ebp-12]
0040142F |. 0FBE55 EF movsx edx,byte ptr ss:[ebp-11] ; 奇数位第四位
00401433 |. 3BCA cmp ecx,edx
00401435 |. 75 07 jnz short Game.0040143E ; 跳向失败,故第三位应该等于第四位
00401437 |. B8 01000000 mov eax,1
0040143C |. EB 02 jmp short Game.00401440
0040143E |> 33C0 xor eax,eax ; 目的就是跳过这代码,使eax不等于0
00401440 |> 5F pop edi
00401441 |. 5E pop esi
00401442 |. 5B pop ebx
00401443 |. 83C4 58 add esp,58
00401446 |. 3BEC cmp ebp,esp
00401448 |. E8 73000000 call Game.004014C0 ; 没用call
0040144D |. 8BE5 mov esp,ebp
0040144F |. 5D pop ebp
00401450 \. C3 retn
这里F7进入004013D6 这个call,很关键的call,
也就是输入的要求:首先输入的数字必须满足
假码的第三位=第三位-第二位,结果加上0x30,最后的结果必须和第一位相等,然后依次循环,即a[3]-a[2]=a[1]
等价于第三位=第一位+第二位
运行一次,减少一位
要运行2次,最后和4比较,故奇数位是6位
比如:
1235 call一次 变成112 ,就是后者减去前者
如果第二次就是 01
00401200 /> \55 push ebp
00401201 |. 8BEC mov ebp,esp
00401203 |. 83EC 44 sub esp,44
00401206 |. 53 push ebx
00401207 |. 56 push esi
00401208 |. 57 push edi
00401209 |. 8D7D BC lea edi,dword ptr ss:[ebp-44]
0040120C |. B9 11000000 mov ecx,11
00401211 |. B8 CCCCCCCC mov eax,CCCCCCCC
00401216 |. F3:AB rep stos dword ptr es:[edi]
00401218 |. C745 FC 00000>mov dword ptr ss:[ebp-4],0
0040121F |. EB 09 jmp short Game.0040122A
00401221 |> 8B45 FC /mov eax,dword ptr ss:[ebp-4]
00401224 |. 83C0 01 |add eax,1
00401227 |. 8945 FC |mov dword ptr ss:[ebp-4],eax
0040122A |> 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; 指向奇数位假码
0040122D |. 034D FC |add ecx,dword ptr ss:[ebp-4] ; 循环变量
00401230 |. 0FBE51 02 |movsx edx,byte ptr ds:[ecx+2]
00401234 |. 85D2 |test edx,edx ; 读取前4个
00401236 |. 74 2C |je short Game.00401264
00401238 |. 8B45 08 |mov eax,dword ptr ss:[ebp+8]
0040123B |. 0345 FC |add eax,dword ptr ss:[ebp-4]
0040123E |. 0FBE08 |movsx ecx,byte ptr ds:[eax] ; 取s[0]
00401241 |. 8B55 08 |mov edx,dword ptr ss:[ebp+8]
00401244 |. 0355 FC |add edx,dword ptr ss:[ebp-4]
00401247 |. 0FBE42 02 |movsx eax,byte ptr ds:[edx+2] ; 取s[2]
0040124B |. 8B55 08 |mov edx,dword ptr ss:[ebp+8]
0040124E |. 0355 FC |add edx,dword ptr ss:[ebp-4]
00401251 |. 0FBE52 01 |movsx edx,byte ptr ds:[edx+1] ; 取s[1]
00401255 |. 2BC2 |sub eax,edx ; s[2] = s[2]-s[1]
00401257 |. 83C0 30 |add eax,30 ; s[2]+0x30
0040125A |. 3BC8 |cmp ecx,eax ; ecx必须等于eax,以后依次循环
0040125C |. 74 04 |je short Game.00401262
0040125E |. 33C0 |xor eax,eax
00401260 |. EB 7E |jmp short Game.004012E0
00401262 |>^ EB BD \jmp short Game.00401221
00401264 |> 8B45 08 mov eax,dword ptr ss:[ebp+8] ; 假码
00401267 |. 0345 FC add eax,dword ptr ss:[ebp-4]
0040126A |. 0FBE48 01 movsx ecx,byte ptr ds:[eax+1] ; s[3]
0040126E |. 8B55 08 mov edx,dword ptr ss:[ebp+8]
00401271 |. 0355 FC add edx,dword ptr ss:[ebp-4]
00401274 |. 0FBE02 movsx eax,byte ptr ds:[edx] ; s[2]
00401277 |. 2BC8 sub ecx,eax
00401279 |. 83C1 30 add ecx,30
0040127C |. 8B55 08 mov edx,dword ptr ss:[ebp+8]
0040127F |. 0355 FC add edx,dword ptr ss:[ebp-4]
00401282 |. 0FBE42 FF movsx eax,byte ptr ds:[edx-1]
00401286 |. 2BC8 sub ecx,eax ; 使得ecx=0
00401288 |. 85C9 test ecx,ecx
0040128A |. 74 04 je short Game.00401290 ; 不跳就错误了
0040128C |. 33C0 xor eax,eax
0040128E |. EB 50 jmp short Game.004012E0
00401290 |> C745 FC 00000>mov dword ptr ss:[ebp-4],0
00401297 |. EB 09 jmp short Game.004012A2
00401299 |> 8B4D FC /mov ecx,dword ptr ss:[ebp-4]
0040129C |. 83C1 01 |add ecx,1
0040129F |. 894D FC |mov dword ptr ss:[ebp-4],ecx
004012A2 |> 8B55 08 mov edx,dword ptr ss:[ebp+8]
004012A5 |. 0355 FC |add edx,dword ptr ss:[ebp-4]
004012A8 |. 0FBE42 01 |movsx eax,byte ptr ds:[edx+1]
004012AC |. 85C0 |test eax,eax
004012AE |. 74 22 |je short Game.004012D2
004012B0 |. 8B4D 08 |mov ecx,dword ptr ss:[ebp+8]
004012B3 |. 034D FC |add ecx,dword ptr ss:[ebp-4]
004012B6 |. 0FBE51 01 |movsx edx,byte ptr ds:[ecx+1] ; s[1]
004012BA |. 8B45 08 |mov eax,dword ptr ss:[ebp+8]
004012BD |. 0345 FC |add eax,dword ptr ss:[ebp-4]
004012C0 |. 0FBE08 |movsx ecx,byte ptr ds:[eax] ; s[0]
004012C3 |. 2BD1 |sub edx,ecx ; s[1]-s[0]
004012C5 |. 83C2 30 |add edx,30 ; s[1]+0x30
004012C8 |. 8B45 08 |mov eax,dword ptr ss:[ebp+8]
004012CB |. 0345 FC |add eax,dword ptr ss:[ebp-4]
004012CE |. 8810 |mov byte ptr ds:[eax],dl ; 将计算结果覆盖之前的奇数
004012D0 |.^ EB C7 \jmp short Game.00401299
004012D2 |> 8B4D 08 mov ecx,dword ptr ss:[ebp+8]
004012D5 |. 034D FC add ecx,dword ptr ss:[ebp-4]
004012D8 |. C601 00 mov byte ptr ds:[ecx],0 ; 最后一位置为0,即少一位
004012DB |. B8 01000000 mov eax,1
004012E0 |> 5F pop edi
004012E1 |. 5E pop esi
004012E2 |. 5B pop ebx
004012E3 |. 8BE5 mov esp,ebp
004012E5 |. 5D pop ebp
004012E6 \. C3 retn
最后就是算法总结:
首先输入的数字必须满足
假码的第三位=第三位-第二位,结果加上0x30,最后的结果必须和第一位相等,然后依次循环,即a[3]-a[2]=a[1]
即第三位-第二位=第一位,依次循环 等价于第三位=第一位+第二位
call sub_401005看懂这个函数很关键啊,运行一次,减少一位
要运行2次,最后和4比较,故奇数位6位
1235 call一次 变成112 ,就是后者减去前者
如果第二次就是 01
最后覆盖后的结果(奇数是4位)必须满足:
第一位应该大于第二位
第二位应该小于第三位
第三位应该等于第四位
看懂所有算法后,我们就可以试着猜了
第一次肯定从0开始,011235 一次10112 再一次就不对了
继续猜~~从1开始,112358 依次01123 再一次 就是1011,刚好符合题意。运行下ok。搞定~~
然后注册码就是——121222325282
--------------------------------------------------------------------------------
【经验总结】
菜鸟第一次写破文,写得很乱,自己也不怎么会,只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
经验就是需要耐心吧,OD结合到IDA用效果会更好,对有些简单算法的反汇编代码应该熟悉。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于JoyChou 转载请注明作者并保持文章的完整, 谢谢!
|
免费评分
-
查看全部评分
|