初练160个CrakeMe程序之007
本帖最后由 海天一色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 ,0x0 ,下句是跳过失败的jz指令。此处的指令在Dede中反编译出来的是“cmp dword ptr , +$00”,总算是知道了这个地址的由来!同时猜测的值应该是决定按钮隐藏与否的值。
向下查看代码,有三个跳转指令跳过了隐藏注册按钮,显示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地址处,可以认为栈中的值是判断注册码输入格式正确与否的,正确则跳走,不正确则跳出错误提示框00442F4E|.E8 ED02FEFF call CM007.00423240 ;controls.TControl.GetText(TControl):TCaption;得到假码和假码长度
00442F53|.8B45 F8 mov eax, ;mov eax, ,取假码
00442F56|.8D55 FC lea edx, ;lea edx,
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 ,0x0 ;cmp dword ptr , +$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, ;lea edx,
00442F73|.8B83 DC020000 mov eax,dword ptr ds: ;* Reference to control Codice : TLabel
00442F79|.E8 C202FEFF call CM007.00423240 ;controls.TControl.GetText(TControl):得到字符串及其长度
00442F7E|.8B45 F8 mov eax, ;mov eax, :eax=假码
00442F81|.E8 06FBFFFF call CM007.00442A8C ;* Reference to : TPrincipale._PROC_00442A8C()
00442F86|.A3 30584400 mov dword ptr ds:,eax
00442F8B|.BA 90304400 mov edx,CM007.00443090 ;edx为注册码,上面判断出是错误的,所以用0代替它
00442F90|.8B83 DC020000 mov eax,dword ptr ds: ;* 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, ;lea edx,
00442FA4|.8B83 D8020000 mov eax,dword ptr ds: ;* Reference to control Nome : TLabel
00442FAA|.E8 9102FEFF call CM007.00423240 ;* Reference to: controls.TControl.GetText(TControl):TCaption;
00442FAF|.8B4D F8 mov ecx, ;mov ecx, ,ecx=用户名
00442FB2|.8BD6 mov edx,esi
00442FB4|.A1 30584400 mov eax,dword ptr ds:
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: ;* 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: ;* 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: ;* Reference to control Nome : TLabel
00442FE4|.8B08 mov ecx,dword ptr ds:
00442FE6|.FF51 60 call dword ptr ds:
00442FE9|.33C0 xor eax,eax
00442FEB|.A3 30584400 mov dword ptr ds:,eax
00442FF0|.EB 1A jmp short CM007.0044300C继续来到00442FB9处,对照Dede发现此处的call没有明确的注释,可能就是关键call了!仔细分析程序代码,第三个判断处下面是隐藏注册按钮,显示Again按钮的代码,下面是关键跳,在它上面的就是关键call了。F7跟入关键call中,004429A8处下断,F8向下: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 ,ecx ;用户名入栈
004429B4|.8955 FC mov ,edx
004429B7|.8BF8 mov edi,eax
004429B9|.8B45 F8 mov eax, ;取用户名
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:
004429CC|.64:8920 mov dword ptr fs:,esp
004429CF|.8B45 F8 mov eax, ;取用户名
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, ;取用户名
004429E5|.E8 4A10FCFF call CM007.00403A34 ;取用户名长度
004429EA|.85C0 test eax,eax
004429EC|.7E 38 jle short CM007.00442A26
004429EE|.8945 F4 mov ,eax ;用户名长度入栈
004429F1|.BE 01000000 mov esi,0x1 ;esi=1,循环的起始序号
004429F6|>8B45 F8 /mov eax, ;取用户名
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, ;edx=用户名
00442A06|.0FB65432 FF ||movzx edx,byte ptr ds: ;取每次循环后用户名的第一个字符的16进制ASCII值
00442A0B|.8B4D F8 ||mov ecx, ;ecx=用户名
00442A0E|.0FB64C01 FF ||movzx ecx,byte ptr ds: ;取每次循环后用户名的最后一个字符的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
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】的值才是真正开始计算的值,是假码的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,
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 ,ecx ;ecx就是最终的结果
00442A55|.3B5D FC cmp ebx, ;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:,edx
00442A6C|.68 812A4400 push CM007.00442A81
00442A71|>8D45 F8 lea eax,
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和关键跳处:
00442BD2|.8B45 F4 mov eax,
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,”,应该是ebx中的值与注册码算出的结果比较,问题是在004429E0处“xor ebx,ebx”指令将ebx清零后下面的指令就没有给ebx赋值的语句了,所以ebx=0!我猜测可能是上面用户名算出的结果应该不为0,并存入ebx中,此时再与注册码计算出的结果比较才对呀。那么ebx清零之前又是多少?向上查看何处给ebx赋值:地址00442A35处是指令“mov ebx,edx”,再来找这个edx的值从何而来:在00442A29处和00442A2B处又有两条指令中有edx,但这不是赋值;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:的值”!)
再向上看,就到了关键call的段首,还没有其他的给eax赋值语句,说明要回溯到关键call外面去找了!
在调用这个关键call的地方,请看后面的注释,eax=ds:,再去找这个堆栈被赋值的地方!向上再向上,肯定在调用关键call前面有被赋值的地址,这次很快就找到了地方:00442F86处。但这是注册码输入错误格式后才能来到这里的啊!如果注册码格式正确,不可能来到这里。
不信邪地在程序中Ctrl+F查找“mov dword ptr ds:,eax”这条指令,Ctrl+L下一个,每找到一处都进行注释:
总共找到了八处,前四处在RegisterzClick事件中,后四处在AgainClick事件中,而且代码位置十分相似,只分析RegisterzClick事件的代码就行了。看过来看过去,反复运行程序验证,终于得出了程序流程:程序共分三步,第一步先输入用户名和非标准格式(非纯数字形式)的注册码,点击注册按钮,程序弹出注册码错误提示信息后继续向下运行,此时经call CM007.00442A8C算出结果给ds:赋值,跳过隐藏注册按钮和显示再次按钮;第二步保持用户名不动,重新输入一个格式正确(纯数字形式的)的注册码,点击注册按钮,此时关键call中的eax-edi-edx-ebx-eax终于都不再为0了,可以计算出一个正确的结果使注册按钮隐藏,再次按钮出现;第三步,保持用户名不变,再输入第一步的非标准的注册码,点击再次按钮,程序又弹出错误信息,继续运行,再将第二步的注册码输入到注册码编辑框中,再点再次按钮,再次按钮消失!最后再看CannellaClick事件,关键call是00442B30,里面的关键代码竟然是直接比较用户名和注册码,相等则弹出信息框,不等则清零!
但是不管CannellaClick事件也完全能够达到隐藏按钮的目的,不明白这个东东有什么用!
用VB编出了一个注册机,取输入的用户名和第一次的注册码,通过穷举法得出第二个注册码来。由于初学,很多问题都是一知半解的,特别是关于数值溢出的问题,困住了将近一周的时间!即使最终生成了注册机,也还可能存在溢出或是数据类型不匹配的问题,希望得到大家的帮助,共同学习提高。
最后,强迫症发作,自认为完美的程序流程是:先输入用户名(只要不是纯数字的就行),再将这个用户名作为第一次的注册码输入到注册码编辑框中,点击“清零”按钮,出现两个弹出窗;点掉后,点击“注册按钮”,再点掉错误提示,输入注册机中算出的第二次的注册码,再点击“注册”按钮;然后将用户名重新输入到注册码编辑框中,点击“再次”按钮,再点掉错误提示,再次输入第二次的注册码,点击“再次”按钮,程序破解全部完成!
自己的007练习附件:包括原程序、汉化后的程序及爆破后的程序、注册机及源码均在内。
百度链接是:http://pan.baidu.com/s/1skMkJY9 密码: 86pm,160个CM、个人学习过的前7个crackme程序都在里面。 其实楼主在分析DeDe的时候就能看出来,有一个叫Again的按钮是隐藏状态:
object Registerz: TButton
Left = 24
Top = 16
Width = 113
Height = 41
Caption = 'Register...'
TabOrder = 0
OnClick = RegisterzClick
end
object Again: TButton
Left = 160
Top = 16
Width = 113
Height = 41
Caption = '...again !!! :p'
TabOrder = 1
Visible = False
OnClick = AgainClick
end
再去分析Registerz中的代码,应该是先把隐藏按钮显示出来。再分析Registerz按钮和Again按钮,基本功能都是一样的。所以只需要分析一个按钮的功能就可以了。
* Reference to control Codice : TLabel
|
00443105 8B83DC020000 mov eax,
* Reference to: controls.TControl.GetText(TControl):TCaption;
|
0044310B E83001FEFF call 00423240
00443110 8B45F4 mov eax,
* Reference to : TPrincipale._PROC_00442A8C() //这是第一个关键点
|
00443113 E874F9FFFF call 00442A8C
00443118 A330584400 mov dword ptr [$00445830], eax
0044311D BA9C324400 mov edx, $0044329C
* Reference to control Codice : TLabel
|
00443122 8B83DC020000 mov eax,
* Reference to: controls.TControl.SetText(TControl;TCaption);
|
00443128 E84301FEFF call 00423270
0044312D E9DD000000 jmp 0044320F
00443132 85F6 test esi, esi
00443134 0F8EC4000000 jle 004431FE
0044313A 8D55F4 lea edx,
* Reference to control Nome : TLabel
|
0044313D 8B83D8020000 mov eax,
* Reference to: controls.TControl.GetText(TControl):TCaption;
|
00443143 E8F800FEFF call 00423240
00443148 8B4DF4 mov ecx,
0044314B 8BD6 mov edx, esi
0044314D A130584400 mov eax, dword ptr [$00445830]
|
00443152 E851F8FFFF call 004429A8 //这是第二个关键点
00443157 84C0 test al, al
00443159 7473 jz 004431CE
0044315B 33D2 xor edx, edx
* Reference to control Again : TButton
很显然,第一个函数是判断有没有特殊字符的,也就是说要报错的。第二个函数是计算注册码的。
而且,从代码中可以看到,第一个函数与第二个函数是互斥的,执行第一个就不会执行第二个了,执行第二个就不会执行第一个。
但是,第一个函数的结果会影响到第二个函数的注册码的计算。
那么思路就是先输入一个带有特殊字符的注册码,让程序报错,然后再输入正确的注册码。
于是,按照这个思路写注册机就行了。
注意看,我输入的第一个注册码是带空格的,这个注册码不同,最终生成的真正的注册码也不同。
另外,再和楼主说一下,生成最终的注册码时,不需要用穷举法。
只需要把前面阶段生成的(EBX-1)*0x59,然后再去与0x50的余数找平就可以了。
EAX := (EBX-1) * $59;
EDX := EAX mod $50;
EAX := EAX - EDX +1;
这个好,学习的好材料,感谢楼主 楼主是专家 panwanpeng 发表于 2017-6-3 15:17
楼主是专家
谢谢你的评分和回复!不过我还只是初学者,目标是专家,现在还远远达不到标准呢!希望能在论坛中共同学习提高!! 海天一色001 发表于 2017-6-3 15:42
谢谢你的评分和回复!不过我还只是初学者,目标是专家,现在还远远达不到标准呢!希望能在论坛中共同学习 ...
你学过汇编的对吧?看着你分析的好透彻 多谢楼主分享教程 我也想学,楼主能否加个联系方式把用到的工具发下。也想有个老司机带这学下{:1_918:} 能把CM分析地这么透彻,将来必定是大牛,先膜拜下 明天我也研究一下这个007 看到论坛里一个个厉害的程序员,真的好羡慕啊……