好友
阅读权限30
听众
最后登录1970-1-1
|
本帖最后由 海天一色001 于 2017-6-3 13:53 编辑
第7个CM,还是先打开看看是什么情况:
仍然是用户名/注册码形式的,随意输入用户名和注册码,点Register键,没任何反应;再点Cancella键,注册码清零;继续点击Register键,出现信息框要求注册码大于0。再点About-Help,四个信息窗口出来了。大概意思是最好不爆破打补丁,要找出对应的用户名和注册码,隐藏下面的按钮。与上一个有些类似,肯定是更难了!老规矩,先查壳:
没壳,Delphi程序,还是先汉化一下吧,目的仍然是对各种软件工具的熟悉和掌握。具体过程和上一个基本一致,利用ResScope和OD基本上完成了,汉化程序另存为cm007.exe;给个界面:
点击注册按钮后见右上图,有点小问题:如果注册码输入非纯数字,如下图:
点击关于-帮助按钮后:
后面还有两条提示信息,和上一个CM中的提示一样,因为没什么有用信息,我没有对它们汉化,直接又练习了一次去掉弹出窗口:如下图将地址00442CDC至00442CEB处的代码全部nop掉,再保存到可执行文件后,就去除了后两个弹出信息窗口。
在保存到可执行文件时提示文件基址有变化,是否保存,我还是保存了,保存后发现文件中地址确实有了变化,但功能没有什么影响,对比图如下:
上一张图是原程序的代码,先将字符串赋值给eax,再把eax作为call的参数,运行call来显示提示信息,下一张是修改汉化后的代码,可以看出是使用直接显示字符串的方法。经测试,修改后的程序能够正常运行,所以就这样保存下来了。好了,虽然程序要求找算法,但为了练习,我还是先爆破再追码:Delphi程序有IDA等软件进行反汇编,可以进行更有效的分析,可是我不会!哪位大神有中文版的使用教程啊?还是请出DeDe出场,看到了这些熟悉的过程:
AboutClick事件就不用跟踪了,其他三个事件都要进去分析。还是对照Dede中的注释在OD中去分析各个事件的代码:
第一次爆破:OD载入CM007.exe,Ctrl+E,粘贴入Dede中复制来的RAV(内存地址)“ 00442F28”,到达RegisterzClick事件段首,F2下断点:F9运行程序,输入用户名“52pojie.cn”,注册码“1111111111”,点击注册按钮,程序中断,F8单步向下:到达地址00442F60处,指令为cmp [local.1],0x0 ,下句是跳过失败的jz指令。此处的指令在Dede中反编译出来的是“cmp dword ptr [ebp-$04], +$00”,总算是知道了这个地址的由来!同时猜测[local.1]的值应该是决定按钮隐藏与否的值。
向下查看代码,有三个跳转指令跳过了隐藏注册按钮,显示Again按钮,走向失败,而本句的指令跳过了第一个走向失败的jmp指令,所以此处可以不动;此时关键call不再执行,所以要继续向下到达00442F9B地址处,因为怕上一个跳过本条指令的je指令失效,所以将这里nop掉!一直单步向下,同样将两个跳到失败的语句nop掉,一条在紧挨着第一个跳转到失败指令的00442F9F处,一条在00442FC0处。
这三个跳转失效,继续单步到段尾处,F9,程序果然将注册按钮隐藏了,不过又显出了一个“再次”的按钮!(见左下图)
其实到这里,根据上一个CM和这里的实际情况,再在00442FCF地址处将mov dl,0x1改成mov dl 0x0,就可以让再次按钮根本不显示!修改之后,将全部修改保存到可执行文件CM007_Register_nop.exe中!运行CM007_Register_nop.exe,果然点击注册按钮,自身隐藏起来了,“再次”按钮也没有出现!
第二次爆破:虽然爆破法很暴力,但在00442FCF地址处将mov dl,0x1改成mov dl 0x0也有点太粗暴了,毕竟程序还要显示出来一个“再次”按钮的,那么撤消此处的修改,继续爆破:在004430BC处下断,点击再次按钮,程序断在了AgainClick事件段首。观察本段代码,与注册按钮事件很相似,那么先按照上一个思路,同样将三个跳转(0044312D处的jmp,00443134处的jle,00443159处的je)nop掉。因为此时按钮已全部隐藏了,干脆将全部修改复制到可执行文件CM007_again_nop.exe中,打开CM007_again_nop.exe,如下左图:点击注册,如下中图:再点击再次,如下右图,爆破成功!有点不舒服的是隐藏再次按钮后程序标题有点小乱,其实是点击再次按钮后,标题栏要加上用户名,因为爆破不完全,使字符看起来不正确了。
这种爆破方法要修改好几个跳转语句,有点太过于直接,还是应该找到关键call和关键跳,直接修改关键call的返回值,使之符合关键跳成功的条件。这样分析起来可能要费劲些,但修改的地方就少了,可能修改一个地方或者两个地方就能爆破成功了。第三次爆破:重载程序,F9,输入用户名“52pojie.cn”,注册码“1234567890”,点击注册按钮,程序中断。F8向下,走到00442F59处,一时不注意F7跟入00402958这个call了。既然进来了,就单步看看是干什么的吧。仔细分析,发现这是对输入的注册码进行判断和处理的子程序,如果注册码字符不符合程序内含条件,则跳出失败的提示;如果首字符是“-+Xx$”等特殊值时进行新的计算,最后得到处理注册码后的数值存入eax中。再来到00442F60地址处,可以认为栈[local.1]中的值是判断注册码输入格式正确与否的,正确则跳走,不正确则跳出错误提示框[Asm] 纯文本查看 复制代码 00442F4E |. E8 ED02FEFF call CM007.00423240 ; controls.TControl.GetText(TControl):TCaption;得到假码和假码长度
00442F53 |. 8B45 F8 mov eax,[local.2] ; mov eax, [ebp-$08],取假码
00442F56 |. 8D55 FC lea edx,[local.1] ; lea edx, [ebp-$04]
00442F59 |. E8 FAF9FBFF call CM007.00402958 ; * Reference to: system.@ValLong;判断假码第一个字符是不是空格、+、-、X、x、$等特殊字符,最后得到假码的16进制数值存入eax中
00442F5E |. 8BF0 mov esi,eax ; esi=假码的16进制数值
00442F60 |. 837D FC 00 cmp [local.1],0x0 ; cmp dword ptr [ebp-$04], +$00:第一个判断:此处的地址存入的值应该是判断注册码输入正误的值
00442F64 74 37 je short CM007.00442F9D ; 假码输入格式正确则跳,错误不跳,不用动
00442F66 |. B8 38304400 mov eax,CM007.00443038 ; eax为在注册码编辑栏中输入一个有效数值的提示内容
00442F6B |. E8 00F6FFFF call CM007.00442570 ; dialogs.ShowMessage(AnsiString);显示提示信息框
00442F70 |. 8D55 F8 lea edx,[local.2] ; lea edx, [ebp-$08]
00442F73 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; * Reference to control Codice : TLabel
00442F79 |. E8 C202FEFF call CM007.00423240 ; controls.TControl.GetText(TControl):得到字符串及其长度
00442F7E |. 8B45 F8 mov eax,[local.2] ; mov eax, [ebp-$08]:eax=假码
00442F81 |. E8 06FBFFFF call CM007.00442A8C ; * Reference to : TPrincipale._PROC_00442A8C()
00442F86 |. A3 30584400 mov dword ptr ds:[0x445830],eax
00442F8B |. BA 90304400 mov edx,CM007.00443090 ; edx为注册码,上面判断出是错误的,所以用0代替它
00442F90 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; * Reference to control Codice : TLabel
00442F96 |. E8 D502FEFF call CM007.00423270 ; * Reference to: controls.TControl.SetText(TControl;TCaption);
00442F9B EB 6F jmp short CM007.0044300C ; 跳过了隐藏注册按钮,显示Again按钮,走向失败
00442F9D |> 85F6 test esi,esi ; 第二个判断处,看假码是否小于等于0
00442F9F 7E 5A jle short CM007.00442FFB ; 跳过了隐藏注册按钮,显示Again按钮,走向失败
00442FA1 |. 8D55 F8 lea edx,[local.2] ; lea edx, [ebp-$08]
00442FA4 |. 8B83 D8020000 mov eax,dword ptr ds:[ebx+0x2D8] ; * Reference to control Nome : TLabel
00442FAA |. E8 9102FEFF call CM007.00423240 ; * Reference to: controls.TControl.GetText(TControl):TCaption;
00442FAF |. 8B4D F8 mov ecx,[local.2] ; mov ecx, [ebp-$08],ecx=用户名
00442FB2 |. 8BD6 mov edx,esi
00442FB4 |. A1 30584400 mov eax,dword ptr ds:[0x445830]
00442FB9 |. E8 EAF9FFFF call CM007.004429A8 ; 关键Call,参数为eax=0?,edx=假码的16进制值,ecx=用户名
00442FBE |. 84C0 test al,al ; 第三个判断
00442FC0 74 30 je short CM007.00442FF2 ; 跳过了隐藏注册按钮,显示Again按钮,走向失败
00442FC2 |. 33D2 xor edx,edx
00442FC4 |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC] ; * Reference to control Registerz : TButton
00442FCA |. E8 6101FEFF call CM007.00423130 ; * Reference to: controls.TControl.SetVisible(TControl;Boolean);
00442FCF B2 01 mov dl,0x1
00442FD1 |. 8B83 E8020000 mov eax,dword ptr ds:[ebx+0x2E8] ; * Reference to control Again : TButton
00442FD7 |. E8 5401FEFF call CM007.00423130 ; * Reference to: controls.TControl.SetVisible(TControl;Boolean);
00442FDC |. 33D2 xor edx,edx
00442FDE |. 8B83 D8020000 mov eax,dword ptr ds:[ebx+0x2D8] ; * Reference to control Nome : TLabel
00442FE4 |. 8B08 mov ecx,dword ptr ds:[eax]
00442FE6 |. FF51 60 call dword ptr ds:[ecx+0x60]
00442FE9 |. 33C0 xor eax,eax
00442FEB |. A3 30584400 mov dword ptr ds:[0x445830],eax
00442FF0 |. EB 1A jmp short CM007.0044300C 继续来到00442FB9处,对照Dede发现此处的call没有明确的注释,可能就是关键call了!仔细分析程序代码,第三个判断处下面是隐藏注册按钮,显示Again按钮的代码,下面是关键跳,在它上面的就是关键call了。F7跟入关键call中,004429A8处下断,F8向下:[Asm] 纯文本查看 复制代码 004429A8 /$ 55 push ebp ; 关键Call,计算与判断注册码
004429A9 |. 8BEC mov ebp,esp
004429AB |. 83C4 F4 add esp,-0xC
004429AE |. 53 push ebx
004429AF |. 56 push esi
004429B0 |. 57 push edi
004429B1 |. 894D F8 mov [local.2],ecx ; 用户名入栈
004429B4 |. 8955 FC mov [local.1],edx
004429B7 |. 8BF8 mov edi,eax
004429B9 |. 8B45 F8 mov eax,[local.2] ; 取用户名
004429BC |. E8 2712FCFF call CM007.00403BE8 ; 结果是edx=2????
004429C1 |. 33C0 xor eax,eax
004429C3 |. 55 push ebp
004429C4 |. 68 7A2A4400 push CM007.00442A7A
004429C9 |. 64:FF30 push dword ptr fs:[eax]
004429CC |. 64:8920 mov dword ptr fs:[eax],esp
004429CF |. 8B45 F8 mov eax,[local.2] ; 取用户名
004429D2 |. E8 5D10FCFF call CM007.00403A34 ; 取用户名长度,返回eax中
004429D7 |. 83F8 04 cmp eax,0x4 ; 用户名长度不能小于4位
004429DA |. 0F8E 82000000 jle CM007.00442A62 ; 跳则死
004429E0 |. 33DB xor ebx,ebx ; ebx=0
004429E2 |. 8B45 F8 mov eax,[local.2] ; 取用户名
004429E5 |. E8 4A10FCFF call CM007.00403A34 ; 取用户名长度
004429EA |. 85C0 test eax,eax
004429EC |. 7E 38 jle short CM007.00442A26
004429EE |. 8945 F4 mov [local.3],eax ; 用户名长度入栈
004429F1 |. BE 01000000 mov esi,0x1 ; esi=1,循环的起始序号
004429F6 |> 8B45 F8 /mov eax,[local.2] ; 取用户名
004429F9 |. E8 3610FCFF |call CM007.00403A34 ; 取用户名长度
004429FE |. 83F8 01 |cmp eax,0x1 ; eax>1
00442A01 |. 7C 1D |jl short CM007.00442A20
00442A03 |> 8B55 F8 |/mov edx,[local.2] ; edx=用户名
00442A06 |. 0FB65432 FF ||movzx edx,byte ptr ds:[edx+esi-0x1] ; 取每次循环后用户名的第一个字符的16进制ASCII值
00442A0B |. 8B4D F8 ||mov ecx,[local.2] ; ecx=用户名
00442A0E |. 0FB64C01 FF ||movzx ecx,byte ptr ds:[ecx+eax-0x1] ; 取每次循环后用户名的最后一个字符的16进制ASCII值
00442A13 |. 0FAFD1 ||imul edx,ecx ; edx=edx*ecx;第一位乘以最后一位
00442A16 |. 0FAFD7 ||imul edx,edi ; edx=edx*edi edi为0,得edx=0
00442A19 |. 03DA ||add ebx,edx ; ebx=ebx+edx;;可是ebx也是0,结果还是0
00442A1B |. 48 ||dec eax ; eax递减
00442A1C |. 85C0 ||test eax,eax ; 检测eax是否为0
00442A1E |.^ 75 E3 |\jnz short CM007.00442A03 ; 循环结果是eax=0,ecx=用户名第一位字符的16进制ASCII值,edx=0
00442A20 |> 46 |inc esi
00442A21 |. FF4D F4 |dec [local.3]
00442A24 |.^ 75 D0 \jnz short CM007.004429F6 ; 循环结果是eax=0,ecx=用户名第一位字符的16进制ASCII值,esi=用户名长度+1
00442A26 |> 8BC3 mov eax,ebx
00442A28 |. 99 cdq
00442A29 |. 33C2 xor eax,edx
00442A2B |. 2BC2 sub eax,edx
00442A2D |. B9 2A2C0A00 mov ecx,0xA2C2A
00442A32 |. 99 cdq
00442A33 |. F7F9 idiv ecx ; 结果是eax=0!!!!!
00442A35 |. 8BDA mov ebx,edx
00442A37 |. 8B45 FC mov eax,[local.1] ; 【local.1】的值才是真正开始计算的值,是假码的16进制数值
00442A3A |. B9 59000000 mov ecx,0x59
00442A3F |. 99 cdq
00442A40 |. F7F9 idiv ecx ; eax/0x59,商入eax,余数入edx
00442A42 |. 8BC8 mov ecx,eax ; ecx=eax,即上面算式结果取整
00442A44 |. 8B45 FC mov eax,[local.1]
00442A47 |. BE 50000000 mov esi,0x50
00442A4C |. 99 cdq
00442A4D |. F7FE idiv esi ; eax/esi,商入eax,余数入edx
00442A4F |. 03CA add ecx,edx ; 【local.1】/0x59取整后的数+【local.1】/0x50取余的数
00442A51 |. 41 inc ecx ; ecx=ecx+1
00442A52 |. 894D FC mov [local.1],ecx ; ecx就是最终的结果
00442A55 |. 3B5D FC cmp ebx,[local.1] ; ebx与注册码比较
00442A58 /75 04 jnz short CM007.00442A5E ; 也可以在这里nop,其他地方就不用nop了
00442A5A |. B3 01 mov bl,0x1 ; bl=1,正确
00442A5C |. EB 06 jmp short CM007.00442A64
00442A5E |> 33DB xor ebx,ebx ; bl=0,错误
00442A60 |. EB 02 jmp short CM007.00442A64
00442A62 |> 33DB xor ebx,ebx
00442A64 |> 33C0 xor eax,eax
00442A66 |. 5A pop edx ; 0012F6A4
00442A67 |. 59 pop ecx ; 0012F6A4
00442A68 |. 59 pop ecx ; 0012F6A4
00442A69 |. 64:8910 mov dword ptr fs:[eax],edx
00442A6C |. 68 812A4400 push CM007.00442A81
00442A71 |> 8D45 F8 lea eax,[local.2]
00442A74 |. E8 3F0DFCFF call CM007.004037B8
00442A79 \. C3 retn
00442A7A .^ E9 F907FCFF jmp CM007.00403278
00442A7F .^ EB F0 jmp short CM007.00442A71
00442A81 . 8BC3 mov eax,ebx
00442A83 . 5F pop edi ; 0012F6A4
00442A84 . 5E pop esi ; 0012F6A4
00442A85 . 5B pop ebx ; 0012F6A4
00442A86 . 8BE5 mov esp,ebp
00442A88 . 5D pop ebp ; 0012F6A4
00442A89 . C3 retn 从整个call运行的结果看,返回值是al=1或者0,而al=0则死,那么干脆在call的第一句就赋值:mov al,0x1,第二句就retn!运行程序,果然注册按钮隐藏起来,再次按钮显示了。点击再次按钮,程序中断到AgainClick事件段首,F8继续,运行到00443152处,出现了“call CM007.004429A8”这个指令,和RegisterzClick事件调用的同一个关键Call!那么就不用再单步走了,直接F9,再次按钮也隐藏了!将这个修改保存到CM007_call_retn.exe中,试运行一下,成功!这种爆破将关键call里的运算全部屏蔽掉了,而且修改的地方很少,此时程序可以不用输入用户名;还可以在关键call的00442A58地址处将跳转语句nop掉,这时bl=1,继续运行后又将ebx的值传给了eax,同样使整个call的返回值变成al=0,这样的修改保存为CM007_call_nop.exe,这样爆破掉整个程序,必须输入用户名和注册码。在我们改好以上之后,可以看到注册按钮和再次按钮都消失了。不过对照上个CM,CannellaClick事件中还有很重要的算法,也不能掉以轻心!在OD中给CannellaClick事件段首下断,点击清零按钮,程序中断,F8向下分析,很快来到关键call和关键跳处:
[Asm] 纯文本查看 复制代码 00442BD2 |. 8B45 F4 mov eax,[local.3]
00442BD5 |. 5A pop edx
00442BD6 |. E8 55FFFFFF call aLoNg3x_.00442B30 ;关键call
00442BDB |. 84C0 test al,al
00442BDD |. 0F84 86000000 je aLoNg3x_.00442C69
关键跳00442BD6处的call追入查看,得知仍是利用用户名和注册码计算后,然后返回al是否为0,不为0则完成破解,为0时则将注册码清零。所以在这里仍然可以将00442BDD处nop掉,
或者进入00442B30的call中,用retn大法或者nop掉00442B66处的跳,也可以将00442B6C处的指令改为“mov bl,0x1”,最终达到的效果是点击清零按钮,出现GREAT和LAMER两个信息框,完全爆破程序!此处爆破后我是nop掉00442B66处的跳,最终保存为CM007_end_nop.exe。其他几种请大家自行测试吧。在爆破过程中发现,第二个信息框的字符“LAMER”是利用“GREAT”变换而来的,大家也可以试着改改,不过用途不是很大。爆破成功,接下来是追码,尽量编写出注册机吧!重新载入CM007.exe,F9运行,因为注册和再次按钮都调用了同一个关键call,所以先禁用其他断点,激活关键call的段首处断点,输入用户名和注册码,点注册按钮,程序中断到004429A8处。单步运行了好几次,一遍遍地分析代码,唯恐看错了哪里。但是不得不说,这段代码有点莫名其妙的! 00442A35地址以前的计算,看上去很热闹,循环处理用户名的每一个字符,又是乘法又是加法,结果是0!很怀疑是不是作者哪个地方弄错了。从00442A37地址开始才是有效的判断注册码正误的算法!只是利用了注册码然后就判断了吗?具体的算法是:先取假码的16进制值,除以0x59,商取整,再加上假码的16进制值除以0x50的余数,最后加上0x1,结果=0!!!!程序到00442A55处指令是“cmp ebx,[local.1]”,应该是ebx中的值与注册码算出的结果比较,问题是在004429E0处“xor ebx,ebx”指令将ebx清零后下面的指令就没有给ebx赋值的语句了,所以ebx=0!我猜测可能是上面用户名算出的结果应该不为0,并存入ebx中,此时再与注册码计算出的结果比较才对呀。那么ebx清零之前又是多少?向上查看何处给ebx赋值:地址00442A35处是指令“mov ebx,edx”,再来找这个edx的值从何而来:在00442A29处和00442A2B处又有两条指令中有edx,但这不是赋值;[Asm] 纯文本查看 复制代码 00442A29 |. 33C2 xor eax,edx
00442A2B |. 2BC2 sub eax,edx 继续向上找,到00442A16处,这里是带符号数的乘法,“imul edx,edi”,在这一句的上一条指令中,edx的值是用户名第一个字符与最后一个字符的16进制ASCII值的积,已有了具体的值,但在这一句中因为edi=0所以edx结果始终为0!
问题又转到了edi的0从何而来了:继续向上查看:到地址004429B7处,“mov edi,eax”指令又将问题交给了eax!(反复查找了几次,此处注释为:“edi=ds:[0x445830]的值”!)
再向上看,就到了关键call的段首,还没有其他的给eax赋值语句,说明要回溯到关键call外面去找了!
在调用这个关键call的地方,请看后面的注释,eax=ds:[0x445830],再去找这个堆栈被赋值的地方!向上再向上,肯定在调用关键call前面有被赋值的地址,这次很快就找到了地方:00442F86处。但这是注册码输入错误格式后才能来到这里的啊!如果注册码格式正确,不可能来到这里。
不信邪地在程序中Ctrl+F查找“mov dword ptr ds:[0x445830],eax”这条指令,Ctrl+L下一个,每找到一处都进行注释:
总共找到了八处,前四处在RegisterzClick事件中,后四处在AgainClick事件中,而且代码位置十分相似,只分析RegisterzClick事件的代码就行了。看过来看过去,反复运行程序验证,终于得出了程序流程:程序共分三步,第一步先输入用户名和非标准格式(非纯数字形式)的注册码,点击注册按钮,程序弹出注册码错误提示信息后继续向下运行,此时经call CM007.00442A8C算出结果给ds:[0x445830]赋值,跳过隐藏注册按钮和显示再次按钮;第二步保持用户名不动,重新输入一个格式正确(纯数字形式的)的注册码,点击注册按钮,此时关键call中的eax-edi-edx-ebx-eax终于都不再为0了,可以计算出一个正确的结果使注册按钮隐藏,再次按钮出现;第三步,保持用户名不变,再输入第一步的非标准的注册码,点击再次按钮,程序又弹出错误信息,继续运行,再将第二步的注册码输入到注册码编辑框中,再点再次按钮,再次按钮消失!最后再看CannellaClick事件,关键call是00442B30,里面的关键代码竟然是直接比较用户名和注册码,相等则弹出信息框,不等则清零!
但是不管CannellaClick事件也完全能够达到隐藏按钮的目的,不明白这个东东有什么用!
用VB编出了一个注册机,取输入的用户名和第一次的注册码,通过穷举法得出第二个注册码来。由于初学,很多问题都是一知半解的,特别是关于数值溢出的问题,困住了将近一周的时间!即使最终生成了注册机,也还可能存在溢出或是数据类型不匹配的问题,希望得到大家的帮助,共同学习提高。
最后,强迫症发作,自认为完美的程序流程是:先输入用户名(只要不是纯数字的就行),再将这个用户名作为第一次的注册码输入到注册码编辑框中,点击“清零”按钮,出现两个弹出窗;点掉后,点击“注册按钮”,再点掉错误提示,输入注册机中算出的第二次的注册码,再点击“注册”按钮;然后将用户名重新输入到注册码编辑框中,点击“再次”按钮,再点掉错误提示,再次输入第二次的注册码,点击“再次”按钮,程序破解全部完成!
自己的007练习附件:
007-1.7z
(587.62 KB, 下载次数: 69)
包括原程序、汉化后的程序及爆破后的程序、注册机及源码均在内。
百度链接是:http://pan.baidu.com/s/1skMkJY9 密码: 86pm,160个CM、个人学习过的前7个crackme程序都在里面。 |
免费评分
-
查看全部评分
|