【文章标题】: DCG_3_1算法分析
【文章作者】: DCracker
【作者邮箱】: dcdevil@126.com
【软件名称】: DCG_3_1
【软件大小】: 2.50KB
【下载地址】: 自己搜索下载
【加壳方式】: 无壳
【保护方式】: name/serial
【使用工具】: OllyDbg
【操作平台】: win32
【软件介绍】: DCG考试题
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
用OllyDbg加载程序,程序停在00401000,向下查看很容易发现读取用户名的地方,在00401092下断,
输入试练码 DCracker/0123456789,点注册断下:
00401092 pushad
00401093 push 7F ; /Count = 7F (127.)
00401095 push DCG_3_1e.00401E00 ; |Buffer = DCG_3_1e.00401E00
0040109A push 3E8 ; |ControlID = 3E8 (1000.)
0040109F push dword ptr ds:[401F00] ; |hWnd = 00650140 ('Snake ',class='#32770')
004010A5 call dword ptr ds:[<&USER32.GetDlgIt>; \GetDlgItemTextA -----获得用户名DCracker
004010AB test eax,eax -----判断是否输入用户名
004010AD jnz short DCG_3_1e.004010CB
004010AF push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004010B1 push DCG_3_1e.00401400 ; |Title = "Error"
004010B6 push DCG_3_1e.00401426 ; |Text = "You forgot to enter a Name..."
004010BB push dword ptr ds:[401F00] ; |hOwner = 00650140 ('Snake ',class='#32770')
004010C1 call dword ptr ds:[<&USER32.MessageB>; \MessageBoxA -----出错提示框
004010C7 popad
004010C8 xor eax,eax
004010CA retn
004010CB push 7F ; /Count = 7F (127.)
004010CD push DCG_3_1e.00401D00 ; |Buffer = DCG_3_1e.00401D00
004010D2 push 3E9 ; |ControlID = 3E9 (1001.)
004010D7 push dword ptr ds:[401F00] ; |hWnd = 00650140 ('Snake ',class='#32770')
004010DD call dword ptr ds:[<&USER32.GetDlgIt>; \GetDlgItemTextA -----获得注册码
004010E3 test eax,eax -----判断是否输入注册码
004010E5 jnz short DCG_3_1e.00401103
004010E7 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004010E9 push DCG_3_1e.00401400 ; |Title = "Error"
004010EE push DCG_3_1e.00401444 ; |Text = "You forgot to enter a Serial..."
004010F3 push dword ptr ds:[401F00] ; |hOwner = 00650140 ('Snake ',class='#32770')
004010F9 call dword ptr ds:[<&USER32.MessageB>; \MessageBoxA -----出错提示框
004010FF popad
00401100 xor eax,eax
00401102 retn
00401103 call DCG_3_1e.004012AA -----判断输入的注册码是否符合要求!
00401108 test al,al
0040110A jnz short DCG_3_1e.00401128
0040110C push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
0040110E push DCG_3_1e.00401400 ; |Title = "Error"
00401113 push DCG_3_1e.00401464 ; |Text = "There is something wrong with your Serial..."
00401118 push dword ptr ds:[401F00] ; |hOwner = 00650140 ('Snake ',class='#32770')
0040111E call dword ptr ds:[<&USER32.MessageB>; \MessageBoxA -----出错提示框
00401124 popad
00401125 xor eax,eax
00401127 retn
00401128 call DCG_3_1e.00401173 -----开辟一块空间用来生成地图
0040112D call DCG_3_1e.0040119B -----关键子程序!!!生成地图
00401132 call DCG_3_1e.0040120C -----关键子程序!!!
00401137 test al,al
00401139 jnz short DCG_3_1e.00401157 -----爆破点!
0040113B push 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
0040113D push DCG_3_1e.00401491 ; |Title = "Too bad..."
00401142 push DCG_3_1e.0040149C ; |Text = "Common, you can do better then that! :)"
00401147 push dword ptr ds:[401F00] ; |hOwner = 00650140 ('Snake ',class='#32770')
0040114D call dword ptr ds:[<&USER32.MessageB>; \MessageBoxA -----出错提示框
00401153 popad
00401154 xor eax,eax
00401156 retn
00401157 push 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
00401159 push DCG_3_1e.004014C4 ; |Title = "Congratulations!"
0040115E push DCG_3_1e.004014D5 ; |Text = "Well done!Now for the more difficult part, try to keygen it..."
00401163 push dword ptr ds:[401F00] ; |hOwner = 00650140 ('Snake ',class='#32770')
00401169 call dword ptr ds:[<&USER32.MessageB>; \MessageBoxA -----正确提示框
0040116F popad
00401170 xor eax,eax
00401172 retn
判断注册码是否符合要求子程序:
004012AA push eax
004012AB push esi
004012AC mov esi,DCG_3_1e.00401700
004012B1 /mov eax,dword ptr ds:[esi]
004012B3 |and dword ptr ds:[esi],0
004012B6 |add esi,4
004012B9 |test eax,eax
004012BB \jnz short DCG_3_1e.004012B1
004012BD mov esi,DCG_3_1e.00401D00 ; ASCII "0123456789"
004012C2 /mov al,byte ptr ds:[esi]
004012C4 |test al,al ; Switch (cases 0..46)
004012C6 |je short DCG_3_1e.004012E0
004012C8 |cmp al,30
004012CA |jb short DCG_3_1e.004012D8 -----由此可见注册码只能由0--9,A--F构成!
004012CC |cmp al,3A
004012CE |jb short DCG_3_1e.004012DD
004012D0 |cmp al,41
004012D2 |jb short DCG_3_1e.004012D8
004012D4 |cmp al,47
004012D6 |jb short DCG_3_1e.004012DD
004012D8 |pop esi ; Default case of switch 004012C4
004012D9 |pop eax
004012DA |mov al,0
004012DC |retn
004012DD |inc esi ; Cases 30 ('0'),31 ('1'),32 ('2'), ......
004012DE \jmp short DCG_3_1e.004012C2
004012E0 pop esi ; Case 0 of switch 004012C4
004012E1 pop eax
004012E2 mov al,1
004012E4 retn
地址为00401173子程序用来在内存中生成一块空间:地址00401AF0--00401C0F,头16个字节为FF,中间256个字节为00,最后16个字节也为FF。
00401B00--00401BFF 就成了一个活动场所,两边的FF就是墙!
生成地图子程序:
0040119B push eax
0040119C push ecx
0040119D push edx
0040119E push esi
0040119F push edi
004011A0 xor ecx,ecx
004011A2 mov esi,DCG_3_1e.00401E00 ; ASCII "DCracker" -----取出用户名地址
004011A7 mov edi,DCG_3_1e.00401B00 -----取出地图首地址
004011AC push esi
004011AD mov dl,0
004011AF /mov al,byte ptr ds:[esi]
004011B1 |inc esi
004011B2 |add dl,al -----将用户名求和,结果放到dl中
004011B4 |test al,al
004011B6 \jnz short DCG_3_1e.004011AF
004011B8 pop esi
004011B9 /movzx eax,byte ptr ds:[esi] -----取出用户名字符
004011BC |xor al,dl -----与dl结果作xor运算
004011BE |/sub dl,al -----dl-al-->dl
004011C0 ||or byte ptr ds:[eax+edi],0CC -----原程序此处有Bug! 应为xor! 用来在地图中该位置放入CC,同时判断
004011C4 ||jnz short DCG_3_1e.004011CA 此处是否已经为CC,若是CC则dl-1,换个位置放入CC,否则CC个数将
004011C6 ||dec dl 不等于用户名个数,有背作者原意!
004011C8 |\jmp short DCG_3_1e.004011BE
004011CA |inc ecx
004011CB |inc esi
004011CC |cmp byte ptr ds:[esi],0 -----判断是否处理完
004011CF \jnz short DCG_3_1e.004011B9
004011D1 mov dword ptr ds:[401F04],ecx
004011D7 xor dl,al
004011D9 /sub al,dl
004011DB |cmp byte ptr ds:[edi+eax],0CC -----判断此处是否为CC
004011DF |jnz short DCG_3_1e.004011E5
004011E1 |dec dl
004011E3 \jmp short DCG_3_1e.004011D9
004011E5 mov byte ptr ds:[edi+eax],0DD -----不为CC则放入DD
004011E9 mov al,dl
004011EB /cmp byte ptr ds:[edi+eax],0CC -----判断此处是否为CC
004011EF |je short DCG_3_1e.004011F7
004011F1 |cmp byte ptr ds:[edi+eax],0DD -----判断此处是否为DD
004011F5 |jnz short DCG_3_1e.004011FB
004011F7 |dec al
004011F9 \jmp short DCG_3_1e.004011EB
004011FB lea eax,dword ptr ds:[edi+eax]
004011FE mov byte ptr ds:[eax],99 -----不为CC和DD则放入99
00401201 mov dword ptr ds:[401700],eax
00401206 pop edi
00401207 pop esi
00401208 pop edx
00401209 pop ecx
0040120A pop eax
0040120B retn
核心运算子程序:
0040120C pushad
0040120D mov esi,DCG_3_1e.00401D00 ; ASCII "0123456789" -----取出注册码地址
00401212 mov edi,DCG_3_1e.00401700 -----取出99所在位置
00401217 /movzx eax,byte ptr ds:[esi] -----取出一个注册码
0040121A |test al,al ; Switch (cases 0..39) -----是否为零
0040121C |je short DCG_3_1e.00401260
0040121E |sub al,30 -----减30,即:30->0,31->1,32->2......
00401220 |cmp al,0A -----是否为大写字母
00401222 |jb short DCG_3_1e.00401226
00401224 |sub al,7 ; Default case of switch 0040121A
00401226 |mov ecx,eax ; Cases 30 ('0'),31 ('1'),......eax->ecx
00401228 |and al,3 -----这里开始计算移动99的距离!
0040122A |shr cl,2
0040122D |mov edx,10
00401232 |test al,al
00401234 |je short DCG_3_1e.00401241
00401236 |dec eax
00401237 |je short DCG_3_1e.0040123F
00401239 |shr edx,4
0040123C |dec eax
0040123D |jnz short DCG_3_1e.00401241
0040123F |neg edx
00401241 |mov eax,dword ptr ds:[edi]
00401243 |add eax,edx
00401245 |mov al,byte ptr ds:[eax]
00401247 |test al,al ; Switch (cases 0..DD) -----99的新位置是否为0,
00401249 |je short DCG_3_1e.0040127A 若是则到0040127A处处理
0040124B |cmp al,99 -----99的新位置是否为99,
0040124D |je short DCG_3_1e.00401260 -----若是则Over!
0040124F |cmp al,0CC -----99的新位置是否为CC,
00401251 |je short DCG_3_1e.00401268 -----若是则到00401268处处理
00401253 |cmp al,0DD -----判断是否是DD
00401255 |jnz short DCG_3_1e.00401260
00401257 |cmp dword ptr ds:[401F04],0 ; Case DD of switch 00401247 -----判断剩余CC是否为零
0040125E |je short DCG_3_1e.00401264
00401260 |popad ; Default case of switch 00401247
00401261 |mov al,0
00401263 |retn
00401264 |popad
00401265 |mov al,1 -----al被置1,注册码正确!
00401267 |retn
00401268 |dec dword ptr ds:[401F04] ; Case CC of switch 00401247 -----CC剩余个数减1
0040126E |call DCG_3_1e.00401289 -----处理CC
00401273 |mov dword ptr ds:[eax],ebx -----取出99原位置
00401275 |mov byte ptr ds:[ebx],99 -----放入99
00401278 |jmp short DCG_3_1e.0040127F
0040127A |call DCG_3_1e.00401289 ; Case 0 of switch 00401247 -----处理00
0040127F |test ecx,ecx -----移动次数是否为零,为零返回
00401281 |je short DCG_3_1e.00401286
00401283 |dec ecx -----移动次数减1,返回继续移动99
00401284 |jmp short DCG_3_1e.00401241
00401286 |inc esi -----esi加1处理注册码下一位
00401287 \jmp short DCG_3_1e.00401217
00401289 push edi
0040128A mov eax,dword ptr ds:[edi]
0040128C mov ebx,eax
0040128E add eax,edx -----计算新位置
00401290 /mov dword ptr ds:[edi],eax
00401292 |mov byte ptr ds:[eax],99 -----在新位置放入99
00401295 |mov byte ptr ds:[ebx],0 -----99原位置清零
00401298 |add edi,4
0040129B |cmp dword ptr ds:[edi],0 -----判断所有99是否移动完,未完继续
0040129E |je short DCG_3_1e.004012A6 否则返回
004012A0 |mov eax,ebx
004012A2 |mov ebx,dword ptr ds:[edi]
004012A4 \jmp short DCG_3_1e.00401290
004012A6 mov eax,edi
004012A8 pop edi
004012A9 retn
通过以上算法分析可知:该程序注册算法为贪吃蛇游戏!99为蛇,CC为要吃的东西,吃完所有的CC后吃到DD,即注册成功!
程序根据用户名初始化地图,即初始化99,CC,DD的位置,CC的个数等于用户名的个数。根据输入的注册码来移动99,即贪吃蛇。
每吃到一个CC后,就会长出一个99,移动99不能撞到自身(即99),也不能撞到墙(即FF),也不能提前吃到DD!注册码不同字符,
对应不同的移动距离,详见下表:
注册码 移动次数 步长(字节) 移动总距离(步长x次数) 备注
0 1 16 16 “-”表示向低址移动
1 1 -16 -16
2 1 -1 -1
3 1 1 1
4 2 16 32
5 2 -16 -32
6 2 -1 -2
7 2 1 2
8 3 16 48
9 3 -16 -48
A 3 -1 -3
B 3 1 3
C 4 16 64
D 4 -16 -64
E 4 -1 -4
F 4 1 4
由此表即可算出移动路线及长度,从而得到注册码,由于同一用户名对应很多种移动方案,故注册码不唯一!
例如:DCracker 注册码:DD9EE68F3FFFFB444FFFB1
--------------------------------------------------------------------------------
【经验总结】
这个程序的注册算法难度一般,不是很复杂,但是注册机编写有些麻烦!实际上,分析注册算法并没有花很长时间,但注册
机却写了两天!而且还没有完全写好,注册机只对部分用户名适用,可能是因为本人VC是自学的,学得不好......
文章写得仓促,不当之处还望指教!
多谢观看!
--------------------------------------------------------------------------------
【版权声明】: 本文原创DCracker, 转载请注明作者并保持文章的完整, 谢谢!
2006年08月07日 AM 11:25:44
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课