好友
阅读权限 30
听众
最后登录 1970-1-1
本帖最后由 海天一色001 于 2017-5-23 15:27 编辑
第六个 CM,还是先运行一下看看:
这个程序一看又是用户 /注册码类型的,加一个“ about help ”按钮干什么?点上去看看,连着出现四个提示窗口,傻眼了!
找度娘!一个个查找单词翻译吧!
The purpose of this CrackMe v.1.00 by aLoNg3x is to MAKE INVISIBLE the buttons "OK" and "Cancella" in order to see the Ringzer0 logo. So you must insert the correct codes.
这个aLoNg3x制作的CrackMe v.1.00的目标是让"OK"和"Cancella"按键隐藏来看到Ringzer0 logo。因此你必须输入正确的注册码。
Nota: if the buttons does NOT become invisible,then you have NOT cracked this Crackme:)
注意:如果按钮没有变成隐藏,那么你没有破解 这个 Crackme!
If you need some helps e-mail me or if you solve this protection please write me your solution,Many thanks.
如果你需要帮助请给我发邮件或者如果你解决了这个保护请写信给我你的解决方法,非常感谢。
CrackMe v.1.00 written by “aLoNg3x” E-MAIL: albatro@freemail.it ; member of “Ringzer0” http://ringzer0.cjb.net
本 CrackMe v.1.00由 aLoNg3x 编写。 E-MAIL: albatro@freemail.it ; member of “Ringzer0” http://ringzer0.cjb.net
可能翻译不够准确,大致意思是说不能爆破,只允许输入正确的注册码来隐藏两个按钮。
查壳,天啊,又是这种 Delphi程序!说实话,我只是学了一个最最基本的 Basic 而已,怎么老是要破 Delphi! 太头疼了!可是头疼也没办法,想学习破解,编程语言是绕不过去的坎啊!不求精通,只求了解,先满足当前需要就行!
看着界面上的英文,怎么这么别扭呢?汉化一下先!用论坛的工具包里的好几个软件来修改标题、版权之类,总是失败,直到找到了 ResScope,才将主界面上的英文改成了汉字。
将图中所有 Caption(也就是标题或者叫名字 ) 的地方的内容全部修改成自己想改的东东,点击保存的图标,再点击运行的绿三角进行查看,我修改好后的程序界面如下:
但是这个软件没法修改弹出的消息框,再用 OD来修改:
OD导入 CM006.exe ,搜索字符串,向下查找,看到了消息框中的内容了!这里有三个,还有一个,暂时先不管它,先修改了这三个以后再找。
点击第一句提示,进入 CPU窗口,
哈哈,这里四句消息内容都有了,不需要再去找第四句了!
在 00442F51地址处右键选择数据窗口跟随 --- 立即数:
此时的数据窗口已经到了这四句消息内容的地址:
选中 ASCII行中的内容,快捷键“ Ctrl+E ”,或者右键选二进制编辑:
在 ASCII栏中输入想修改的内容,此时显示是一堆乱码,不要紧,只要确定自己修改的内容对就行,如果修改后的内容长度不够,后面可以用二进制“ 00 ”填充,下面保持大小也一定选上,保证不超出原来的内容长度即可。 编辑完成,先选中所有修改的内容,右键选择 “复制到可执行文件”,在新的窗口中仍是右键选中的内容选择“保存文件”,生成新的exe 文件。
我的新文件是 CM006_1.exe,运行一下,点击“关于 - 帮助”,嗯,成功了,消息框的内容也改了。但是还有标题和“ OK ”按键还未修改,而且修改后的内容排版很难看:
我感觉用 OD来做汉化,效率还是很低的,还容易出错,盼望哪位大神做一个插件,针对这个问题来解决一下。如果有更好的汉化方法,也请坛友们多指教,这些就这样了! 接下来就是破解了。因为已经是第三个 Delphi程序了,所以还是用 Dede:
代码复制出来,如下,图片代码太长,中间用“.........”代替了:
[Delphi] 纯文本查看 复制代码
object _Nome: TLabel
Left = 8
Top = 8
Width = 31
Height = 13
Caption = '用户名:'
end
object _Codice: TLabel
Left = 8
Top = 32
Width = 36
Height = 13
Caption = '注册码:'
end
object Pannello: TPanel
Left = 0
Top = 64
Width = 292
Height = 68
Align = alBottom
BevelOuter = bvLowered
TabOrder = 0
object Image1: TImage
Left = 1
Top = 1
Width = 290
Height = 66
Align = alClient
Picture.Data = {
07544269746D617076870000424D76870000000000003604000028000000BF01
..................................................................................................................................
0C00}
Stretch = True
end
object Ok: TButton
Left = 24
Top = 16
Width = 113
Height = 41
Caption = '确定¨'
Enabled = False
TabOrder = 0
OnClick = OkClick
end
object Cancella: TButton
Left = 152
Top = 16
Width = 113
Height = 41
Caption = '清除注册码'
TabOrder = 1
OnClick = CancellaClick
end
end
object Nome: TEdit
Left = 56
Top = 8
Width = 89
Height = 21
MaxLength = 10
TabOrder = 1
OnChange = NomeChange
end
object Codice: TEdit
Left = 56
Top = 32
Width = 89
Height = 21
TabOrder = 2
Text = '0'
OnChange = CodiceChange
end
object About: TButton
Left = 168
Top = 8
Width = 105
Height = 49
Caption = '1关于 – 帮助'
TabOrder = 3
OnClick = AboutClick
End
看到了上面的事件名称,有10个啊!结合ResScope中看到的代码,基本确定用户名最大长度为10个字符,输入字符的事件是“NomeChange”;“CodiceChange”事件是注册码输入;“CancellaClick”事件是点击“清除注册码”的代码;“确定”按钮的Enabled属性 = False,也就是灰色的不可用状态,“OkClick”是点击“确定”按钮后的事件,“AboutClick”是点击“关于-帮助”按钮的事件。其他5个目前不知道是什么东东,先放一边去。
从消息框中猜知程序正确的流程应该是要输入正确的注册码后,确定按钮变可用,点击后将自己和清除注册码这两个按键隐藏,显示出那个logo来,所以应该在判断用户名和注册码关系后不管怎样都跳到确定按钮可用的代码处,仍然能爆破,还是先试一试爆破吧!
猜测这仍是要利用用户名和用户名长度来计算注册码的,所以记下这几个事件开头的地址,在OD中Ctrl+E来查看相关代码:
NomeChange事件感觉是作者将name中的“a”拼写错误成了“o”,除了个长度限制外,在OD中没有看到其他有用的东东。
再来看CodiceChange事件:结合Darkde4中的提示,对OD中的代码进行注释:进入OD,Ctrl+E,到达地址00442C78处,注释写上“注册码输入事件段首”,再看Darkde4中在00442C88和00442C8D之间有个注释“***** TRY”,复制下来,在OD中加到哪个地址后面呢?仔细看看,觉得应该是对00442C8D地址处的注释,也不知对不对,先按照自己的想法来吧,大不了错了再改到上一句地址处!!!
就这样一路复制粘贴,CodiceChange事件很快就注释完毕,再结合自己的理解,加上段尾的注释;看到OD中下面的代码,又到了确定按钮事件的段首,干脆将这些事件全部注释一下算了!这个工作也很快完成!!!
直到这里,我才确定自己这样注释是正确的!
[Asm] 纯文本查看 复制代码
00442F4C . B8 802F4400 mov eax,CM006_1.00442F80 ; ASCII "这个由aLoNg3x制作的CM的目标是隐藏“确定和清除注册码这两个按钮使Ringzer0 logo显示出来。因此你必须输入"
00442F51 . E8 9AF6FFFF call CM006_1.004425F0 ; * Reference to: dialogs.ShowMessage(AnsiString);
00442F56 . B8 34304400 mov eax,CM006_1.00443034 ; 注意:如果这两个按钮没有被隐藏,那么你就没有破解这个Crackme
00442F5B . E8 90F6FFFF call CM006_1.004425F0 ; * Reference to: dialogs.ShowMessage(AnsiString);
00442F60 . B8 98304400 mov eax,CM006_1.00443098 ; 如果你需要帮助请给我发 e-mail 或者你解决了它的保护,请写信给我告诉你的解决方法。非常感谢。
00442F65 . E8 86F6FFFF call CM006_1.004425F0 ; * Reference to: dialogs.ShowMessage(AnsiString);
00442F6A . B8 0C314400 mov eax,CM006_1.0044310C ; CrackMe v. 1.00 由aLoNg3x编写。 我的 E-MAIL: [url=mailto:albatro@freemail.it]albatro@freemail.it[/url] ; member of "Ringzer0" URL: http://ringzer0.cjb.net
00442F6F . E8 7CF6FFFF call CM006_1.004425F0 ; * Reference to: dialogs.ShowMessage(AnsiString);
其他几个也这样复制,复制这些注释时发现,在输入用户名、输入注册码和确定按钮事件、清除注册码事件的代码段中有很多代码是一样的,说明作用也是一样。再结合上两个CM程序的练习,猜测这个程序也是在输入用户名时就开始判断注册码是否正确了。
在用户名输入代码段首00442E04处下断,F9运行程序,将“52pojie.cn”粘贴到用户名中,程序马上中断。单步运行,到00442E22处是一个比较,下一句的跳转跳过了确定按钮可用,所以nop掉,再F9运行,此时确定按钮已经是可用状态了。
先将这一步保存,另存为cm006_1_name_nop.exe,以备下一步使用;
再在注册码输入事件段首地址00442C78处F2下断,F9运行程序,在注册码文本框中刚输入“1”,程序就断下来了,此时确定按钮是可用状态,程序还未来得及判断注册码真假;直接F9运行,此时的确定按钮已变成不可用了!说明只要开始输入注册码,程序就开始判断,不正确,那么确定按钮就被设置成不可用。
因为“1”在程序中可能有很多处,一时半会可能找不到地方,在记事本中输入一串“1234567890”,复制过来作为注册码,马上断在了注册码输入事件段首处:
F8单步向下,来到00442CA1这里,指令是mov eax,[local.2] ,从信息栏中看到“堆栈 ss:[0012F3DC]=00945E48, (ASCII "1234567890") eax=0000000A”;这一句是取注册码的;继续单步到下一句00442CA7地址处,call CM006_1.00402958,从复制过来的注释(* Reference to: system.@ValLong;)猜测是一个得到整数类型变量的子程序;再单步向下,一直到本段结束,再F9一下,程序界面中没有可看出的任何变化。
本段中其他的call从Dede中的注释能看出都有明确的作用,如TLabel,Tcaption,TButton等控件或事件(过程)的名称、作用之类,只有这个0042D0B处的指令“call CM006_1.00442A3C”,Darkde4中的注释是“* Reference to : TPrincipale._PROC_00442A3C()”,看不出来有什么作用,只能说是一个子程序,很有可能就是关键call了!
来到地址00442CAE处,此处是一个比较指令cmp [local.1],0x0 ,是本代码段中第一个判断,作用是什么呢???(注册码与默认0比较,如为0时就跳过,程序进入错误的流程,确定按钮不可用),所以下一句的跳转指令可以nop掉试试:
00442CB2处je short CM006_1.00442CCC被nop掉后,F9运行程序,仍是没有变化,说明只nop这一句不行;
重新复制注册码,又断在段首,认真地分析一下本段的代码,00442CDA -00442CE2处、00442D16-00442D1E处和00442D25-00442D2D处的三段指令完全一样,注释中可以看出是关于“确定(OK)”按钮的,猜测是关于按钮enble属性的地方,前两代码处前一个指令是“mov dl,0x1”,说明edx=1,后面一处代码前一条指令是“xor edx,edx”,说明是edx=0,可能edx的值是设置按钮可用不可用的关键,就是不知道0和1哪个是可用,哪个是不可用。
复制注册码,程序断下,然后从段首单步向下,到达00442CD2处,是本段代码的第二个判断,接下来又要跳走,ds:[eax+0x47]还是不知从哪里赋的值:
00442CD6处指令为“jnz short CM006_1.00442CE7 ”,不等则跳,将指令nop掉;
F8向下,到00442CE5处,“jmp short CM006_1.00442D30”,无条件跳转:继续单步走,很快到达了段尾,于是F9,再看程序界面,确定按钮又是可用状态了,说明这两个nop爆破是正确的;
此时确定按钮可用,再将修改后的程序保存一下,命名为CM006_1_code_nop.exe备用;
下一步就应该是点击确定,如果用户名和注册码正确,则两个按钮均会隐藏起来,那么就在确定按钮事件段首下断后,点击确定:
程序断在了00442D64处。F8向下,到00442D80处,指令是“cmp byte ptr ds:[eax+0x47],0x1”,与注册码输入事件中不同之处是与1比较!
再往下观察这段代码,感觉与上面输入用户名、输入注册码事件的代码很相似,作用也应该相似,那么先不管与1或是0比较,同样在jnz处nop掉,单步继续,到00442D91处,F8,忽然又断在了注册码输入事件的段首!说明刚才的call可能是对注册码进行判断之后认为是假码,然后设注册码为默认值0。因断点都还在,所以才又断在了注册码输入事件的段首。
此时F9运行,返回到00442D96处,又一个跳转!我注释了一下,是跳过了setvisible,应该跳吗?
因为跳过了设置不可见,所以这里要先nop掉,不能跳!
继续单步向下,到地址00442DC8处,又是跳到00442DD7的指令,要跳过设置按钮不可见:
刚才已nop了到00442DD7这个地址的跳,现在也不例外,继续nop掉:
然后单步向下,来到了确定按钮事件的段尾,此时F9运行,确定按钮隐藏了,但清除注册码按钮还在,说明自己开始的想法不对,点一个确定不能隐藏两个按钮。
那么在清除注册码事件里看看:
返回OD,Ctrl+E,输入00442EA8,F2在清除注册码按钮事件段首下断:
在CM程序中点击清除注册码按钮,在OD中已经断下来了,F8走的过程中看到信息栏中有用户名和注册码的出现,说明也有什么算法要用到这两个参数,但我要爆破,就先放过去了。
来到00442EE7 处,以下代码就很熟悉了,call CM006_1.00442AF4,先是关键call进行计算,返回eax中,再比较,跳转!
把00442EEE处的跳转指令也nop掉!再单步向下,又有了一个jmp,观察了一下,不动它,继续单步到清除注册码按钮事件段尾,F9,在CM程序中的清除按钮也没有了!
赶紧将此时修改好的程序再保存一下,命名为CM006_1_nop.exe。打开CM006_1_nop.exe,试运行一下,一切正常,按两个按钮均能隐藏。而且不管在哪个文本框中输入,或是点击清除注册码按钮,确定按钮都会变得可用,不管先点击哪个按钮,哪个按钮就会隐藏,爆破成功!也有一个小问题,点击确定按钮后,注册码会重置成0,还需要下一步看看是怎么回事。
接下来追码。要找注册算法,这是太令人头疼的事!上面的爆破都费了九牛二虎之力才成功,算法???
自己认为程序流程是输入用户名、注册码,确定按钮可用;点击确定按钮,使确定按钮隐藏;再点击清除注册码按钮,则注册码按钮也隐藏,这样程序就算破解成功了。所以要先从用户名输入事件入手:
关闭程序,重新载入OD,将断点除用户名输入的段首地址外全部禁用,F9运行,将“52pojie.cn”复制入文本框中,程序中断,开始分析:
[Asm] 纯文本查看 复制代码
00442E04 /. 55 push ebp ; 用户名输入代码段首
00442E05 |. 8BEC mov ebp,esp
00442E07 |. 6A 00 push 0x0
00442E09 |. 6A 00 push 0x0
00442E0B |. 53 push ebx
00442E0C |. 8BD8 mov ebx,eax
00442E0E |. 33C0 xor eax,eax
00442E10 |. 55 push ebp
00442E11 |. 68 9B2E4400 push CM006_1.00442E9B
00442E16 |. 64:FF30 push dword ptr fs:[eax] ; ***** TRY
00442E19 |. 64:8920 mov dword ptr fs:[eax],esp
00442E1C |. 8B83 D0020000 mov eax,dword ptr ds:[ebx+0x2D0] ; 清除注册码按钮事件
00442E22 |. 8078 47 00 cmp byte ptr ds:[eax+0x47],0x0 ; ds:[eax+0x47]的值决定了跳转,那么它在什么地方被赋值的?
00442E26 75 0F jnz short CM006_1.00442E37 ; 跳到了确定按钮不可用,nop后使确定按钮可用了
00442E28 |. B2 01 mov dl,0x1 ; dl=1则确定按钮可用
00442E2A |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; 确定按钮事件
00442E30 |. 8B08 mov ecx,dword ptr ds:[eax] ; CM006_1.0044282C
00442E32 |. FF51 60 call dword ptr ds:[ecx+0x60]
00442E35 |. EB 49 jmp short CM006_1.00442E80
00442E37 |> 8D55 FC lea edx,[local.1]
00442E3A |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; * Reference to control Codice : TLabel
00442E40 |. E8 7B04FEFF call CM006_1.004232C0 ; * Reference to: controls.TControl.GetText(TControl):TCaption;
00442E45 |. 8B45 FC mov eax,[local.1] ; 取注册码
00442E48 |. 50 push eax
00442E49 |. 8D55 F8 lea edx,[local.2]
00442E4C |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; * Reference to control Nome : TLabel
00442E52 |. E8 6904FEFF call CM006_1.004232C0 ; * Reference to: controls.TControl.GetText(TControl):TCaption;
00442E57 |. 8B45 F8 mov eax,[local.2] ; 取用户名
00442E5A |. 5A pop edx ; CM006_1.0041E13E
00442E5B |. E8 DCFBFFFF call CM006_1.00442A3C ; 关键call,计算后返回al
00442E60 |. 84C0 test al,al
00442E62 |. 74 0F je short CM006_1.00442E73
00442E64 |. B2 01 mov dl,0x1 ; dl=1时,确定按钮可用
00442E66 |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; 确定按钮事件
00442E6C |. 8B08 mov ecx,dword ptr ds:[eax] ; CM006_1.0044282C
00442E6E |. FF51 60 call dword ptr ds:[ecx+0x60]
00442E71 |. EB 0D jmp short CM006_1.00442E80
00442E73 |> 33D2 xor edx,edx ; edx=0,确定按钮不可用
00442E75 |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; * Reference to control Ok : TButton确定按钮事件
00442E7B |. 8B08 mov ecx,dword ptr ds:[eax] ; CM006_1.0044282C
00442E7D |. FF51 60 call dword ptr ds:[ecx+0x60]
00442E80 |> 33C0 xor eax,eax
00442E82 |. 5A pop edx ; CM006_1.0041E13E
00442E83 |. 59 pop ecx ; CM006_1.0041E13E
00442E84 |. 59 pop ecx ; CM006_1.0041E13E
00442E85 |. 64:8910 mov dword ptr fs:[eax],edx
00442E88 |. 68 A22E4400 push CM006_1.00442EA2 ; ****** FINALLY
00442E8D |> 8D45 F8 lea eax,[local.2]
00442E90 |. BA 02000000 mov edx,0x2
00442E95 |. E8 4209FCFF call CM006_1.004037DC
00442E9A \. C3 retn ; 用户名输入事件段尾
00442E9B .^ E9 D803FCFF jmp CM006_1.00403278
00442EA0 .^ EB EB jmp short CM006_1.00442E8D
00442EA2 . 5B pop ebx ; ****** END
00442EA3 . 59 pop ecx ; CM006_1.0041E13E
00442EA4 . 59 pop ecx ; CM006_1.0041E13E
00442EA5 . 5D pop ebp ; CM006_1.0041E13E
00442EA6 . C3 retn
单步到00442E1C地址处,指令是mov eax,dword ptr ds:[ebx+0x2D0],这是清除注册码按钮事件的代码,说明输入用户名时,清除注册码按钮已经正常出现;再到00442E26地址处,在爆破时将本行跳转指令nop掉了,现在正常执行,转到了00442E37处;再继续到00442E40处,指令“call CM006_1.004232C0”经猜测验证是得到输入的字符串及字符串的长度,此处是得到默认的注册码0及长度0x1;而到00442E52处,得到的是输入的用户名及用户名长度;
再到达00442E5B处,指令是“call CM006_1.00442A3C”,爆破时猜测是关键call,返回值存入al。现在就要F7跟入:
[Asm] 纯文本查看 复制代码
00442A3C 55 push ebp ; 第一个关键子程序
00442A3D |. 8BEC mov ebp,esp
00442A3F |. 83C4 F8 add esp,-0x8
00442A42 |. 53 push ebx
00442A43 |. 56 push esi
00442A44 |. 8955 F8 mov [local.2],edx ; 注册码存入堆栈ss:[local.2]中
00442A47 |. 8945 FC mov [local.1],eax ; 用户名存入堆栈SS:[local.1]中
00442A4A |. 8B45 FC mov eax,[local.1] ; 取用户名
00442A4D |. E8 9611FCFF call CM006_1.00403BE8 ; 计数CALL,返回edx?
00442A52 |. 8B45 F8 mov eax,[local.2] ; 取注册码
00442A55 |. E8 8E11FCFF call CM006_1.00403BE8 ; edx从2变1再到2????
00442A5A |. 33C0 xor eax,eax
00442A5C |. 55 push ebp
00442A5D |. 68 E52A4400 push CM006_1.00442AE5
00442A62 |. 64:FF30 push dword ptr fs:[eax]
00442A65 |. 64:8920 mov dword ptr fs:[eax],esp
00442A68 |. 8B45 FC mov eax,[local.1] ; 取用户名
00442A6B |. E8 C40FFCFF call CM006_1.00403A34 ; @LStrLen,取字符串长度
00442A70 |. 83F8 05 cmp eax,0x5 ; 检测用户名长度是否5位以上
00442A73 |. 7E 53 jle short CM006_1.00442AC8 ; 不大于则跳,确定按钮不可用
00442A75 |. 8B45 FC mov eax,[local.1] ; 取用户名
00442A78 |. E8 B70FFCFF call CM006_1.00403A34 ; 取字符串长度
00442A7D |. 8BD8 mov ebx,eax
00442A7F |. 8B45 FC mov eax,[local.1] ; 取用户名
00442A82 |. E8 AD0FFCFF call CM006_1.00403A34
00442A87 |. 8BD0 mov edx,eax ; edx=用户名长度
00442A89 |. 4A dec edx
00442A8A |. 85D2 test edx,edx ; test是与运算,只影响标志位,执行该语句后,如edx值不等于0(zr=0),则不跳:检测edx寄存器是否为0,0则跳
00442A8C |. 7E 20 jle short CM006_1.00442AAE ; 不大于则跳
00442A8E |. B8 01000000 mov eax,0x1 ; eax=1,循环的第一个序号为1
00442A93 |> 8B4D FC /mov ecx,[local.1] ; 取用户名
00442A96 |. 0FB64C01 FF |movzx ecx,byte ptr ds:[ecx+eax-0x1] ; ecx的值是所取的1个字符的ASCII的16进制值
00442A9B |. 8B75 FC |mov esi,[local.1] ; 取用户名
00442A9E |. 0FB63406 |movzx esi,byte ptr ds:[esi+eax] ; esi的值是刚取的字符后1个字符的16进制值
00442AA2 |. 0FAFCE |imul ecx,esi ; ecx=ecx*esi
00442AA5 |. 0FAFC8 |imul ecx,eax ; ecx=ecx*eax
00442AA8 |. 03D9 |add ebx,ecx ; ebx=ebx+ecx
00442AAA |. 40 |inc eax ; eax=eax+1,eax累加1
00442AAB |. 4A |dec edx ; edx=edx-1
00442AAC |.^ 75 E5 \jnz short CM006_1.00442A93 ; 循环,对用户名进行处理
00442AAE |> 8B45 F8 mov eax,[local.2] ; 取注册码
00442AB1 |. E8 BA4BFCFF call CM006_1.00407670 ; StrToInt,字符串转成16进制数值,返回eax中
00442AB6 |. 2BD8 sub ebx,eax ; 用户名处理的结果减去注册码处理的结果是否等于0x29A
00442AB8 |. 81FB 9A020000 cmp ebx,0x29A ; 比较
00442ABE |. 75 04 jnz short CM006_1.00442AC4 ; 跳过可用,使ebx清零
00442AC0 |. B3 01 mov bl,0x1 ; 可用
00442AC2 |. EB 06 jmp short CM006_1.00442ACA ; 跳至eax清零
00442AC4 33DB xor ebx,ebx ; 不可用
00442AC6 |. EB 02 jmp short CM006_1.00442ACA
00442AC8 |> 33DB xor ebx,ebx
00442ACA |> 33C0 xor eax,eax
00442ACC |. 5A pop edx ; CM006_1.00442E60
00442ACD |. 59 pop ecx ; CM006_1.00442E60
00442ACE |. 59 pop ecx ; CM006_1.00442E60
00442ACF |. 64:8910 mov dword ptr fs:[eax],edx
00442AD2 |. 68 EC2A4400 push CM006_1.00442AEC
00442AD7 |> 8D45 F8 lea eax,[local.2]
00442ADA |. BA 02000000 mov edx,0x2
00442ADF |. E8 F80CFCFF call CM006_1.004037DC
00442AE4 \. C3 retn
00442AE5 .^ E9 8E07FCFF jmp CM006_1.00403278
00442AEA .^ EB EB jmp short CM006_1.00442AD7
00442AEC . 8BC3 mov eax,ebx
00442AEE . 5E pop esi ; CM006_1.00442E60
00442AEF . 5B pop ebx ; CM006_1.00442E60
00442AF0 . 59 pop ecx ; CM006_1.00442E60
00442AF1 . 59 pop ecx ; CM006_1.00442E60
00442AF2 . 5D pop ebp ; CM006_1.00442E60
00442AF3 . C3 retn
分析后得出结论,如果输入的注册码=用户名经循环处理后得到的一个16进制数值-16进制数值29A,将bl设为1,从而使确定按钮可用:
用VB编写从用户名计算出注册码的代码(直接将16进制数转换成10进制了)如下:
Serial= Len(name)
Serial = Serial +asc (mid(name,1,1)*asc(mid(name,2,1)*1+ asc(mid(name,2,1)*asc(mid(name,3,1)*2+...+ asc (mid(name,Len(name)-1,1)*asc (mid(name,Len(name),1)*( Len(name)-1)
Serial = Serial -666
用计算器计算了一下,用户名为“52pojie.cn”,循环处理后的结果为0x5DDD0,减去0x29A,结果为0x5DB36 ,转换成10进制数为383798。复制这个结果,继续单步走,这个关键子程序返回到00442E60处,因注册码是默认的0,所以eax=0,使确定按钮为灰色不可用状态。继续F8,直到运行到用户名输入事件结束,再F9运行,程序此时的状态如下左图:
将刚才复制的注册码粘贴入注册码文本框中,确定按钮可用了!(如上右图)
在OD中禁用用户名输入事件的断点,激活注册码输入事件断点,重新粘贴一遍注册码,程序中断:
[Asm] 纯文本查看 复制代码
00442CE7 |> 8D55 F8 lea edx,[local.2]
00442CEA |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; * Reference to control Codice : TLabel
00442CF0 |. E8 CB05FEFF call CM006_1.004232C0 ; * Reference to: controls.TControl.GetText(TControl):TCaption;
00442CF5 |. 8B45 F8 mov eax,[local.2] ; 取假码
00442CF8 |. 50 push eax
00442CF9 |. 8D55 F0 lea edx,[local.4]
00442CFC |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; * Reference to control Nome : TLabel
00442D02 |. E8 B905FEFF call CM006_1.004232C0 ; * Reference to: controls.TControl.GetText(TControl):TCaption;
00442D07 |. 8B45 F0 mov eax,[local.4] ; eax=用户名
00442D0A |. 5A pop edx ; 0012F6F4
00442D0B |. E8 2CFDFFFF call CM006_1.00442A3C ; * Reference to : TPrincipale._PROC_00442A3C(),猜测是计算并比较注册码
00442D10 |. 84C0 test al,al ; eax的值很重要!!!!
00442D12 |. 74 0F je short CM006_1.00442D23
00442D14 |. B2 01 mov dl,0x1 ; edx=1时确定按钮可用
00442D16 |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; * Reference to control Ok : TButton
00442D1C |. 8B08 mov ecx,dword ptr ds:[eax]
00442D1E |. FF51 60 call dword ptr ds:[ecx+0x60]
00442D21 |. EB 0D jmp short CM006_1.00442D30
00442D23 |> 33D2 xor edx,edx ;
单步向下,遇到的几个跳转指令时也仔细看了半天,分析流程,才让它继续下去,一直到本段代码结束,再F9运行,程序状态没有变化。
反复观察本段代码,并与用户名输入事件代码比较,看到整体流程与用户名输入事件一样,一输入字符就开始判断,而且也用了call CM006_1.00442A3C这个关键call,所以使用计算出的注册码会使确定按钮可用。
此时再禁用注册码输入事件的断点,激活确定按钮事件断点,点击确定按钮,程序又中断:
[Asm] 纯文本查看 复制代码
00442D64 /. 55 push ebp ; 确定按钮事件段首
00442D65 |. 8BEC mov ebp,esp
00442D67 |. 6A 00 push 0x0
00442D69 |. 53 push ebx
00442D6A |. 8BD8 mov ebx,eax
00442D6C |. 33C0 xor eax,eax
00442D6E |. 55 push ebp
00442D6F |. 68 ED2D4400 push CM006_1.00442DED
00442D74 |. 64:FF30 push dword ptr fs:[eax] ; ***** TRY
00442D77 |. 64:8920 mov dword ptr fs:[eax],esp
00442D7A |. 8B83 D0020000 mov eax,dword ptr ds:[ebx+0x2D0] ; * Reference to control Cancella : TButton(清除注册码按钮)
00442D80 |. 8078 47 01 cmp byte ptr ds:[eax+0x47],0x1 ; 此处与注册码输入事件中不同之处是与1比较
00442D84 75 12 jnz short CM006_1.00442D98
00442D86 |. BA 002E4400 mov edx,CM006_1.00442E00 ; 0
00442D8B |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; * Reference to control Codice : TLabel
00442D91 |. E8 5A05FEFF call CM006_1.004232F0 ; * Reference to: controls.TControl.SetText(TControl;TCaption);
00442D96 EB 3F jmp short CM006_1.00442DD7 ; 跳过了TControl.SetVisible,应该跳吗?
00442D98 8D55 FC lea edx,dword ptr ss:[ebp-0x4]
00442D9B |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; * Reference to control Codice : TLabel
00442DA1 |. E8 1A05FEFF call CM006_1.004232C0 ; * Reference to: controls.TControl.GetText(TControl):TCaption;
00442DA6 |. 8B45 FC mov eax,[local.1]
00442DA9 |. E8 C248FCFF call CM006_1.00407670 ; * Reference to: sysutils.StrToInt(S:
00442DAE |. 50 push eax
00442DAF |. 8D55 FC lea edx,[local.1]
00442DB2 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; * Reference to control Nome : TLabel
00442DB8 |. E8 0305FEFF call CM006_1.004232C0 ; * Reference to: controls.TControl.GetText(TControl):TCaption;
00442DBD |. 8B45 FC mov eax,[local.1] ; 取用户名
00442DC0 |. 5A pop edx ; 取注册码?????
00442DC1 |. E8 DAFDFFFF call CM006_1.00442BA0 ; * Reference to : TPrincipale._PROC_00442BA0()关键call
00442DC6 |. 84C0 test al,al
00442DC8 74 0D je short CM006_1.00442DD7
00442DCA |. 33D2 xor edx,edx
00442DCC |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; * Reference to control Ok : TButton
00442DD2 |. E8 D903FEFF call <CM006_1.SetVisible> ; * Reference to: controls.TControl.SetVisible(TControl;Boolean);确定按钮是否可见的call
00442DD7 |> 33C0 xor eax,eax
00442DD9 |. 5A pop edx ; 0012F24C
00442DDA |. 59 pop ecx ; 0012F24C
00442DDB |. 59 pop ecx ; 0012F24C
00442DDC |. 64:8910 mov dword ptr fs:[eax],edx
00442DDF |. 68 F42D4400 push CM006_1.00442DF4 ; ****** FINALLY
00442DE4 |> 8D45 FC lea eax,[local.1]
00442DE7 |. E8 CC09FCFF call CM006_1.004037B8
00442DEC \. C3 retn ; 确定按钮事件段尾
00442DED .^ E9 8604FCFF jmp CM006_1.00403278
00442DF2 .^ EB F0 jmp short CM006_1.00442DE4
00442DF4 . 5B pop ebx ; ****** END
00442DF5 . 59 pop ecx ; 0012F24C
00442DF6 . 5D pop ebp ; 0012F24C
00442DF7 . C3 retn
单步到00442D80处,指令是“cmp byte ptr ds:[eax+0x47],0x1”,注册码输入事件中是与0比较,此处是与1比较!这个比较应该也是一个关键的比较吧。两个事件中均有这种比较,然后跳走,说明程序在不同的流程下结果不同,就是不知道这些类似ds:[eax+0x47]、 [local.1]之类的堆栈表示什么东西,是如何赋值的?这也算是下一步学习的一个关键疑点吧!
下一句的跳转是jnz,跳过了设置按钮不可见的一个jmp指令,说明上一个算法计算出的注册码不能满足确定按钮隐藏的条件,点击确定按钮后可能会使注册码清零,确定按钮重新变成不可用状态吧!试一次,单步向下,走到本段结束处,再F9,程序状态果然和我猜想的一样了。
重新将注册码复制入文本框,确定按钮又可用了,点击它,程序中断到本段代码开头,重新F8向下走,再次来到00442D80处,ds:[eax+0x47]=1,所以仍然不会跳,程序不能正确破解,所以要在寄存器窗口双击Z标志位的数字1,让它变成0,使反汇编窗口的跳转前面的灰线变成红色,说明跳转能够执行了。
继续向下,可以很轻松地发现关键call是在00442DC1处的call CM006_1.00442BA0,F7跟进去查看:
[Asm] 纯文本查看 复制代码
00442BA0 /$ 55 push ebp ; 确定按钮事件的关键call,从注册码反算出用户名
00442BA1 |. 8BEC mov ebp,esp
00442BA3 |. 6A 00 push 0x0
00442BA5 |. 6A 00 push 0x0
00442BA7 |. 6A 00 push 0x0
00442BA9 |. 53 push ebx
00442BAA |. 56 push esi
00442BAB |. 8BF2 mov esi,edx
00442BAD |. 8945 FC mov [local.1],eax
00442BB0 |. 8B45 FC mov eax,[local.1] ; 取用户名
00442BB3 |. E8 3010FCFF call CM006_1.00403BE8 ; 计数call?edx=2
00442BB8 |. 33C0 xor eax,eax
00442BBA |. 55 push ebp
00442BBB |. 68 672C4400 push CM006_1.00442C67
00442BC0 |. 64:FF30 push dword ptr fs:[eax]
00442BC3 |. 64:8920 mov dword ptr fs:[eax],esp
00442BC6 |. 33DB xor ebx,ebx
00442BC8 |. 8D55 F8 lea edx,[local.2]
00442BCB |. 8BC6 mov eax,esi
00442BCD |. E8 6E4AFCFF call CM006_1.00407640
00442BD2 |. 8D45 F4 lea eax,[local.3]
00442BD5 |. 8B55 F8 mov edx,[local.2] ; 取注册码
00442BD8 |. E8 730CFCFF call CM006_1.00403850 ; edx=0
00442BDD |. 8B45 F8 mov eax,[local.2] ; 取注册码
00442BE0 |. E8 4F0EFCFF call CM006_1.00403A34 ; 取注册码长度
00442BE5 |. 83F8 05 cmp eax,0x5 ; 比较注册码的位数,小于5位则死
00442BE8 |. 7E 60 jle short CM006_1.00442C4A
00442BEA |. 8B45 F8 mov eax,[local.2] ; eax=注册码
00442BED |. E8 420EFCFF call CM006_1.00403A34 ; 取注册码长度
00442BF2 |. 8BF0 mov esi,eax ; esi=注册码长度
00442BF4 |. 83FE 01 cmp esi,0x1 ; 注册码长度与默认的长度1比较,不大于则死
00442BF7 |. 7C 2F jl short CM006_1.00442C28
00442BF9 |> 8D45 F4 /lea eax,[local.3]
00442BFC |. E8 0310FCFF |call CM006_1.00403C04
00442C01 |. 8D4430 FF |lea eax,dword ptr ds:[eax+esi-0x1]
00442C05 |. 50 |push eax
00442C06 |. 8B45 F8 |mov eax,[local.2] ; 取注册码
00442C09 |. 0FB64430 FF |movzx eax,byte ptr ds:[eax+esi-0x1] ; 取每个循环中的最后一个字符的ASCII值
00442C0E |. F7E8 |imul eax ; eax=eax*eax
00442C10 |. 0FBFC0 |movsx eax,ax
00442C13 |. F7EE |imul esi ; eax=eax*esi
00442C15 |. B9 19000000 |mov ecx,0x19 ; ecx==19 ???????
00442C1A |. 99 |cdq
00442C1B |. F7F9 |idiv ecx ; 有符号数除法,eax/ecx=eax...edx 结果商入eax,余数入edx
00442C1D |. 83C2 41 |add edx,0x41
00442C20 |. 58 |pop eax
00442C21 |. 8810 |mov byte ptr ds:[eax],dl ; 注册码最后一位被计算出的余数+41后的dl代表的字符代替
00442C23 |. 4E |dec esi ; esi递减,控制循环,注册码最后一位为原来的倒数第二位
00442C24 |. 85F6 |test esi,esi ; esi不为0则循环,为0向下出循环
00442C26 |.^ 75 D1 \jnz short CM006_1.00442BF9 ; 循环处理注册码
00442C28 |> 8B45 F4 mov eax,[local.3] ; 处理后的注册码存入eax中
00442C2B |. 8B55 FC mov edx,[local.1] ; 用户名
00442C2E |. E8 110FFCFF call CM006_1.00403B44 ; 计算比较的call?
00442C33 |. 75 17 jnz short CM006_1.00442C4C
00442C35 |. 8B45 FC mov eax,[local.1]
00442C38 |. 8B55 F4 mov edx,[local.3] ; user32.77D4048F
00442C3B |. E8 040FFCFF call CM006_1.00403B44
00442C40 |. 75 04 jnz short CM006_1.00442C46
00442C42 |. B3 01 mov bl,0x1
00442C44 |. EB 06 jmp short CM006_1.00442C4C
00442C46 |> 33DB xor ebx,ebx
00442C48 |. EB 02 jmp short CM006_1.00442C4C
00442C4A |> 33DB xor ebx,ebx
00442C4C |> 33C0 xor eax,eax
00442C4E |. 5A pop edx
00442C4F |. 59 pop ecx
00442C50 |. 59 pop ecx
00442C51 |. 64:8910 mov dword ptr fs:[eax],edx
00442C54 |. 68 6E2C4400 push CM006_1.00442C6E
00442C59 |> 8D45 F4 lea eax,[local.3]
00442C5C |. BA 03000000 mov edx,0x3
00442C61 |. E8 760BFCFF call CM006_1.004037DC
00442C66 \. C3 retn
00442C67 .^ E9 0C06FCFF jmp CM006_1.00403278
00442C6C .^ EB EB jmp short CM006_1.00442C59
00442C6E . 8BC3 mov eax,ebx
00442C70 . 5E pop esi
00442C71 . 5B pop ebx
00442C72 . 8BE5 mov esp,ebp
00442C74 . 5D pop ebp
00442C75 . C3 retn
从代码看,与00442A3C这个关键call类似,对照着这个call,相对应地进行注释后,发现这个关键子程序的作用是用注册码运算出一个用户名,再和输入的用户名比较,相同则确定按钮点击后隐藏,否则不成功!
算法是:
[Visual Basic] 纯文本查看 复制代码
Private Sub SerialButton_Click()
Dim Name, Lend, Code, Num, INum()
If Text1.Text <> "" Then
Name = (Text1.Text)
Lend = Len(Name) 'Lend = LenB(StrConv(Name, vbFormUnicode))’两种取字符串长度的函数
If Lend < 6 And Lend > 10 Then
MsgBox ("用户名长度必须在6到10位之间")
Text1.Text = ""
End If
Num = Asc(Mid(Name, 5, 1)) Mod 7 + 2
n = 1
For i = 1 To Num
n = n * i
Next i
ReDim INum(Lend)
For j = 1 To Lend
INum(j) = Asc(Mid(Name, j, 1)) * n
Code = Code + INum(j)
Next j
Code = Code - 31337
If Len(Code) < 6 Then MsgBox ("得到的注册码长度小于6位,请重新输入用户名进行计算!")
Text2.Text = Code
Else
MsgBox ("用户名不能为空!")
End If
End Sub
天啊,从注册码输入事件中的关键call的算法与这里的算法怎么看也不是互为反运算!用注册码383798算出的用户名是“BWDAUQ”,一看就跟“52pojie.cn”差远了!将这个反算出的用户名复制到用户名文本框中,确定按钮又不可用了!
从上面三个事件中找不到成功的希望了!最后看看清除注册码按钮事件吧:
重新将程序载入OD,将清除注册码按钮事件断点激活,其他断点禁用,F9,输入用户名“52pojie.cn”,算出的注册码“383798”,确定按钮可用了;点击清除注册码按钮,程序中断:
[Asm] 纯文本查看 复制代码
00442EA8 /. 55 push ebp ; 清除注册码按钮事件段首
00442EA9 |. 8BEC mov ebp,esp
00442EAB |. 6A 00 push 0x0
00442EAD |. 53 push ebx
00442EAE |. 8BD8 mov ebx,eax
00442EB0 |. 33C0 xor eax,eax
00442EB2 |. 55 push ebp
00442EB3 |. 68 322F4400 push CM006_1.00442F32
00442EB8 |. 64:FF30 push dword ptr fs:[eax] ; ***** TRY
00442EBB |. 64:8920 mov dword ptr fs:[eax],esp
00442EBE |. 8D55 FC lea edx,[local.1]
00442EC1 |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; * Reference to control Codice : TLabel注册码标签
00442EC7 |. E8 F403FEFF call CM006_1.004232C0 ; 取注册码字符串及字符串长度
00442ECC |. 8B45 FC mov eax,[local.1]
00442ECF |. E8 9C47FCFF call CM006_1.00407670 ; * Reference to: sysutils.StrToInt(S:字符串转换成16进制数
00442ED4 |. 50 push eax
00442ED5 |. 8D55 FC lea edx,[local.1]
00442ED8 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; * Reference to control Nome : TLabel(用户名标签)
00442EDE |. E8 DD03FEFF call CM006_1.004232C0 ; * Reference to: controls.TControl.GetText(TControl):TCaption;取用户名字符串及长度
00442EE3 |. 8B45 FC mov eax,[local.1]
00442EE6 |. 5A pop edx ; 0012F674
00442EE7 |. E8 08FCFFFF call CM006_1.00442AF4 ; * Reference to : TPrincipale._PROC_00442AF4()关键call
00442EEC |. 84C0 test al,al
00442EEE 74 1C je short CM006_1.00442F0C
00442EF0 |. 33D2 xor edx,edx
00442EF2 |. 8B83 D0020000 mov eax,dword ptr ds:[ebx+0x2D0] ; * Reference to control Cancella : TButton(清除注册码按钮)
00442EF8 |. E8 B302FEFF call CM006_1.004231B0 ; * Reference to: controls.TControl.SetVisible(TControl;Boolean);设置可见与否
00442EFD |. B2 01 mov dl,0x1 ; edx=1时,确定按钮可用
00442EFF |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; * Reference to control Ok : TButton(确定按钮)
00442F05 |. 8B08 mov ecx,dword ptr ds:[eax]
00442F07 |. FF51 60 call dword ptr ds:[ecx+0x60] ; 调用确定按钮是否可用
00442F0A |. EB 10 jmp short CM006_1.00442F1C
00442F0C |> BA 482F4400 mov edx,CM006_1.00442F48 ; 0
00442F11 |. 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0] ; * Reference to control Codice : TLabel注册码标签
00442F17 |. E8 D403FEFF call CM006_1.004232F0 ; * Reference to: controls.TControl.SetText(TControl;TCaption);设置控件文本内容
00442F1C |> 33C0 xor eax,eax
00442F1E |. 5A pop edx ; 0012F674
00442F1F |. 59 pop ecx ; 0012F674
00442F20 |. 59 pop ecx ; 0012F674
00442F21 |. 64:8910 mov dword ptr fs:[eax],edx
00442F24 |. 68 392F4400 push CM006_1.00442F39 ; ****** FINALLY
00442F29 |> 8D45 FC lea eax,[local.1]
00442F2C |. E8 8708FCFF call CM006_1.004037B8
00442F31 \. C3 retn ; 清除注册码按钮事件段尾
00442F32 .^ E9 4103FCFF jmp CM006_1.00403278
00442F37 .^ EB F0 jmp short CM006_1.00442F29
00442F39 . 5B pop ebx ; ****** END
00442F3A . 59 pop ecx ; 0012F674
00442F3B . 5D pop ebp ; 0012F674
00442F3C . C3 retn
仔细分析代码,看到00442EE7地址处的call CM006_1.00442AF4是一个关键call ,它的返回值存入al中,如果al=1,则确定按钮可用,清除注册码按钮隐藏!
单步运行一下,到00442EE7处,F7跟进去查看:
[Asm] 纯文本查看 复制代码
00442AF4 /$ 55 push ebp ; 清除注册码按钮事件的关键call
00442AF5 |. 8BEC mov ebp,esp
00442AF7 |. 83C4 F8 add esp,-0x8
00442AFA |. 53 push ebx
00442AFB |. 56 push esi
00442AFC |. 8955 F8 mov [local.2],edx ; 注册码值入【local.2】
00442AFF |. 8945 FC mov [local.1],eax ; 用户名入【local.1】
00442B02 |. 8B45 FC mov eax,[local.1] ; 取用户名
00442B05 |. E8 DE10FCFF call CM006_1.00403BE8 ; edx=2
00442B0A |. 33C0 xor eax,eax
00442B0C |. 55 push ebp
00442B0D |. 68 902B4400 push CM006_1.00442B90
00442B12 |. 64:FF30 push dword ptr fs:[eax]
00442B15 |. 64:8920 mov dword ptr fs:[eax],esp
00442B18 |. 8B45 FC mov eax,[local.1] ; 取用户名
00442B1B |. E8 140FFCFF call CM006_1.00403A34 ; 得到用户名长度
00442B20 |. 83F8 05 cmp eax,0x5 ; 如果用户名长度不大于5则死
00442B23 |. 7E 53 jle short CM006_1.00442B78
00442B25 |. 8B45 FC mov eax,[local.1] ; 取用户名
00442B28 |. 0FB640 04 movzx eax,byte ptr ds:[eax+0x4] ; 取用户名的第5个字符的ASCII值
00442B2C |. B9 07000000 mov ecx,0x7 ; ecx=7
00442B31 |. 33D2 xor edx,edx
00442B33 |. F7F1 div ecx ; eax/ecx,商入eax,余数入edx
00442B35 |. 8BC2 mov eax,edx
00442B37 |. 83C0 02 add eax,0x2
00442B3A |. E8 E1FEFFFF call CM006_1.00442A20 ; 求阶乘
00442B3F |. 8BF0 mov esi,eax
00442B41 |. 33DB xor ebx,ebx
00442B43 |. 8B45 FC mov eax,[local.1] ; 取用户名
00442B46 |. E8 E90EFCFF call CM006_1.00403A34 ; 取用户名长度
00442B4B |. 85C0 test eax,eax
00442B4D |. 7E 16 jle short CM006_1.00442B65
00442B4F |. BA 01000000 mov edx,0x1 ; edx==1,edx为循环的第一个序号
00442B54 |> 8B4D FC /mov ecx,[local.1] ; ecx=用户名
00442B57 |. 0FB64C11 FF |movzx ecx,byte ptr ds:[ecx+edx-0x1] ; 得到第一个字符的ASCII值
00442B5C |. 0FAFCE |imul ecx,esi ; ecx=ecx*esi[esi为上面call返回的eax的值]
00442B5F |. 03D9 |add ebx,ecx ; ebx=ebx+ecx
00442B61 |. 42 |inc edx ; edx递加
00442B62 |. 48 |dec eax ; eax递减
00442B63 |.^ 75 EF \jnz short CM006_1.00442B54 ; 循环得到每个用户名字符ASCII值*上一步阶乘的值的总和
00442B65 |> 2B5D F8 sub ebx,[local.2] ; 处理用户名得到的注册码与输入的注册码的16进制数值比较
00442B68 |. 81FB 697A0000 cmp ebx,0x7A69 ; 值与0x7A69比较要相等
00442B6E |. 75 04 jnz short CM006_1.00442B74 ; 不等时,ebx=0
00442B70 |. B3 01 mov bl,0x1 ; 相等时,ebx=1
00442B72 |. EB 06 jmp short CM006_1.00442B7A
00442B74 |> 33DB xor ebx,ebx
00442B76 |. EB 02 jmp short CM006_1.00442B7A
00442B78 |> 33DB xor ebx,ebx
00442B7A |> 33C0 xor eax,eax ; eax=0
00442B7C |. 5A pop edx ; CM006_1.00442B8F
00442B7D |. 59 pop ecx ; CM006_1.00442B8F
00442B7E |. 59 pop ecx ; CM006_1.00442B8F
00442B7F |. 64:8910 mov dword ptr fs:[eax],edx
00442B82 |. 68 972B4400 push CM006_1.00442B97
00442B87 |> 8D45 FC lea eax,[local.1]
00442B8A |. E8 290CFCFF call CM006_1.004037B8 ; ??????
00442B8F \. C3 retn
00442B90 .^ E9 E306FCFF jmp CM006_1.00403278
00442B95 .^ EB F0 jmp short CM006_1.00442B87
00442B97 . 8BC3 mov eax,ebx
00442B99 . 5E pop esi ; CM006_1.00442B8F
00442B9A . 5B pop ebx ; CM006_1.00442B8F
00442B9B . 59 pop ecx ; CM006_1.00442B8F
00442B9C . 59 pop ecx ; CM006_1.00442B8F
00442B9D . 5D pop ebp ; CM006_1.00442B8F
00442B9E . C3 retn
其中又有一个call CM006_1.00442A20的子程序,经研究后是取用户名的第5位对7求余然后再加2得到的数,求阶乘,最后得到的值。
这个关键call的算法其实就是取阶乘的结果再乘以用户名的每个ansi值相乘然后求和,再减去0x7A69,这个结果能够满足确定按钮可用,并且清除注册码按钮消失的要求。这样一来,就意味着程序的流程应该是输入用户名、注册码,点击清除注册码按钮,使清除注册码消失,让确定按钮可用;点击确定按钮,再将确定按钮隐藏!
试一试吧!
程序运行到00442B65处,从信息栏中看出算出的注册码值是0x14EE(5358),它减去0x7A69(31337)的值是一个负数(-25979)!负数能用吗?我用计算器算了一下,结果是“FFFFFFFFFFFF9A85”!再转换成10进制值是“18446744073709525637”这样的一组数字,和实际上将的值“-25979”相差太太太太太远了!!将这三组字符都充当注册码试了一次,前两个失败,不仅是按钮没有隐藏,而且注册码真的清0了,确定按钮也马上不可用了!第三个的负数成功了!
再点击确定按钮,没有任何反应!哦,这个注册码只能满足隐藏清除注册码的要求,确定按钮还无法隐藏啊。再将确定按钮段首断点激活,禁用其他断点,再点确定:
单步走到00442D80处,此时看到信息栏中是“ds:[00944EEF]=00”,说明ds:[eax+0x47]=0。根据前面对确定按钮事件代码的分析,我认为ds:[eax+0x47]这个堆栈中存的就是清除注册码按钮是否隐藏的属性值,隐藏为0,不隐藏为1!那么程序流程就是可以正确破解的步骤了!继续向下单步到关键call,F7跟入, 运行到0042C28处,信息栏中用户名出来了:(ASCII "AACVAT")!
F9运行,将 “AACVAT”字符串复制到用户名文本框中(见左下图),嗯,确定按钮仍然可用!
点击确定按钮,程序又中断了。F9一下,再看程序界面(右上图),成功了!!!!!
从注册码逆推出用户名的算法如下:
[Visual Basic] 纯文本查看 复制代码
Private Sub NameButton_Click()
Dim Serial, nome, lendS, Codice
If Text2.Text = "" Then
MsgBox ("注册码不能为空或者为0!")
End If
Serial = Text2.Text
lendS = Len(Serial)
If lendS < 6 Then
MsgBox ("注册码长度小于6位,请重新输入用户名进行计算!")
End If
For k = 1 To lendS
Codice = (Mid(Serial, k, 1))
Codice = Asc(Codice)
Codice = Codice * Codice * k
Codice = Codice Mod 25
Codice = Codice + 65
Codice = Chr(Codice)
nome = nome + Codice
Next k
Text3.Text = nome
End Sub
总结一下自己这些日子的学习过程,感到虽然走了不少弯路,但也在不断地加深自己对各方面知识的理解。
一种思路是先用清除注册码事件里的算法先得出一个注册码,先点击清除注册码按钮使之消失,再利用确定按钮事件里的算法反算出一个用户名,代替原来的用户名,点击确定按钮,就能够完成程序破解了。这种思路不够完美;
第二种思路则是用穷举的方法,算出满足用户名和注册码之间的三种不同的关系算法,这应该是最正确的方法,可是自己目前只实现了两种算法的有限穷举,实在无奈!
试着做成了两个注册机,一个是需要两个用户名,一个注册码来完成破解,一个是只输入一组用户名和注册码就达到目的的,但这两个注册机都不够完善,特别是关于穷举法注册机的,一是限定死了生成的用户名只有6位,7到10位的未考虑;二是其中未将用户名输入事件的算法加入比较,只符合两种算法;三是且穷举法只找到了第一组用户名和注册码后就被强行结束了。我的VB也只是初入门的水平,代码可能很啰嗦,也可能存在其他错误,敬请论坛的大神们多多指教!!
自己的006 练习附件:
006.rar
(865.22 KB, 下载次数: 52)
包括原程序,我汉化后的程序及爆破后的程序。
百度链接是: http://pan.baidu.com/s/1skMkJY9 密码: 86pm,个人学习过的crackme程序都在里面。
免费评分
参与人数 9 威望 +2
吾爱币 +18
热心值 +9
收起
理由
pk8900
+ 1
+ 1
我很赞同!速度不慢,我也是刚刚搞定了这个。
冬日安好
+ 1
+ 1
谢谢@Thanks!
385290864
+ 1
+ 1
用心讨论,共获提升!
Hmily
+ 2
+ 10
+ 1
感谢发布原创作品,吾爱破解论坛因你更精彩!
x358650
+ 1
+ 1
66666冲着排版也奶你一口。。。
那份执着
+ 1
+ 1
66666冲着排版也奶你一口。。。
rxsheng
+ 1
+ 1
用心讨论,共获提升!
钱后佛
+ 1
+ 1
666,学习学习
pwp
+ 1
+ 1
楼主,您这160个cm哪下载的,我也想练练
查看全部评分