好友
阅读权限40
听众
最后登录1970-1-1
|
我是用户
发表于 2013-6-19 23:07
本帖最后由 我是用户 于 2013-6-23 13:46 编辑
【软件名称】: LanHelper算法分析与注册机的编写
【作者邮箱】: 2714608453@qq.com
【下载地址】: 见附件
【加壳方式】: 无
【使用工具】: OD
【操作平台】: XP SP2
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
前言:
不知不觉一个系列就完成了,前后断断续续的快一个月,这几天一直在找适合做最后一战的软件,闲来无事翻了翻以前的代码,发现以前在黑X发过的一篇文章的存稿,好吧就是它了.
今天给大家带来的是LanHelper(局域网助手)的算法分析和注册机的编写。想必这个软件大家都用过,都干过不少坏事吧,嘿嘿~~!但是这个软件有30天的试用期,超过时间就会提示注册。网上关于这个程序的破解方法有很多,但大多数是爆破的,很少涉及到算法分析的,今天就让我们来分析一下这个程序的算法。
LanHelper这个软件的算法有以下三个特点:1。注册码分段验证;2。采用了大量的浮点计算和数学运算;3。使用SEH异常处理来代替关键跳,更加隐匿。
1.定位关键代码
拿到程序,第一件要做的事是什么?当然是查壳喽。把程序载入Peid.
如图片1
从Peid上我们知道,程序是Borland Delphi 6.0 - 7.0写的。打开程序,提示“您的30天试用期已到期“。点击“输入注册号码”,在编辑框内输入试练码“hzycy“,“123456789”,注册,弹出错误提示.
如图片2
把程序载入OD,字符串搜索“您输入的注册码有误”,很遗憾的是OD并没有找到这个字符串。但是别灰心,这个程序是Borland Delphi 6.0 - 7.0写的。我们把它载入DeDe,等待程序转储分析完毕,点击DeDe中的窗体选项,在模块名中找到TForm_Reg.
见图片3
右边显示出了窗口的信息,我们可以看到TButton有很多个,经过一些偿试,我们准确定位出关键代码004DCB40。在OD上下好CC断点。开始注册。
2.算法分析与注册机的编写
输入试练码,点击确定,程序成功断下来,这说明我们定位的关键代码是正确的。很多程序都会在验证注册码时核实用户名和假码是否为空,当然这个程序也不例外.
见代码1。
[C++] 纯文本查看 复制代码 004DCB6D . E8 5616FAFF call 0047E1C8 ;获得用户名
004DCB72 . 8B45 B8 mov eax, dword ptr [ebp-48]
004DCB75 . 8D55 BC lea edx, dword ptr [ebp-44]
004DCB78 . E8 47CCF2FF call 004097C4
004DCB7D . 837D BC 00 cmp dword ptr [ebp-44], 0
004DCB81 . 74 22 je short 004DCBA5 ;判断用户名是否为空
004DCB83 . 8D55 B0 lea edx, dword ptr [ebp-50]
004DCB86 . 8B45 FC mov eax, dword ptr [ebp-4]
004DCB89 . 8B80 04030000 mov eax, dword ptr [eax+304]
004DCB8F . E8 3416FAFF call 0047E1C8 ;取假码
004DCB94 . 8B45 B0 mov eax, dword ptr [ebp-50]
004DCB97 . 8D55 B4 lea edx, dword ptr [ebp-4C]
004DCB9A . E8 25CCF2FF call 004097C4
004DCB9F . 837D B4 00 cmp dword ptr [ebp-4C], 0
004DCBA3 . 75 44 jnz short 004DCBE9 ;判断假码是否为空
0047E1C8这个Call是用来取用用户名和假码的,004097C4是用来判断用户名和假码是否为空的。如果不为空,则进入真正的算法Call 004DF3B4中,这里面才是对我们有真正价值的地方。
因为这个程序大量运用了00430F74这个Call进行验证,所以分析算法之前,我们先来分析一下这个Call,为了方便,我们把这个Call记为Call1.
见代码2。
[C++] 纯文本查看 复制代码 00430F74 /$ 55 push ebp
................................................ ;省略代码为检测参数1,2是否为0
00430FC7 |. EB 5A jmp short 00431023
00430FC9 |> DB6D 08 fld tbyte ptr [ebp+8] ;装入参数1到st(0)
00430FCC |. E8 4B1CFDFF call 00402C1C ;判断参数1的小数部分是否为0
00430FD1 |. D81D 2C104300 fcomp dword ptr [43102C] ;比较是否为0
00430FD7 |. DFE0 fstsw ax
00430FD9 |. 9E sahf
00430FDA |. 75 30 jnz short 0043100C ;通过参数1判断流程
00430FDC |. DB6D 08 fld tbyte ptr [ebp+8] ;流程2
00430FDF |. D9E1 fabs
00430FE1 |. DB2D 30104300 fld tbyte ptr [431030]
00430FE7 |. DED9 fcompp
00430FE9 |. DFE0 fstsw ax
00430FEB |. 9E sahf
00430FEC |. 72 1E jb short 0043100C
00430FEE |. 66:8B45 1C mov ax, word ptr [ebp+1C]
00430FF2 |. 50 push eax
00430FF3 |. FF75 18 push dword ptr [ebp+18]
00430FF6 |. FF75 14 push dword ptr [ebp+14]
00430FF9 |. DB6D 08 fld tbyte ptr [ebp+8]
00430FFC |. E8 4B1CFDFF call 00402C4C
00431001 |. E8 2AFFFFFF call 00430F30
00431006 |. DB7D F0 fstp tbyte ptr [ebp-10]
00431009 |. 9B wait
0043100A |. EB 17 jmp short 00431023
0043100C |> DB6D 14 fld tbyte ptr [ebp+14] ;参数2(流程1)
0043100F |. D9ED fldln2 ;将Loge2装st(0)
00431011 |. D9C9 fxch st(1) ;st(0)与st(1)交换
00431013 |. D9F1 fyl2x ;计算以2为基数的对数
00431015 |. DB6D 08 fld tbyte ptr [ebp+8] ;将参数1装入st(0)
00431018 |. DEC9 fmulp st(1), st ;st(1)与st(0)相乘
0043101A |. E8 D51BFDFF call 00402BF4 ;进入
0043101F |. DB7D F0 fstp tbyte ptr [ebp-10]
00431022 |. 9B wait
00431023 |> DB6D F0 fld tbyte ptr [ebp-10] ;计算的结果装入st(0)
00431026 |. 8BE5 mov esp, ebp
00431028 |. 5D pop ebp
00431029 \. C2 1800 retn 18
Call1有两个参数,参数1为浮点数,参数2由fstp tbyte ptr [esp]装入。通过代码2我们可知,Call1里有两个验证流程,分别记为流程1,流程2。并通过参数1的小数部分是否为0来判断流程。小数部分不为0则跳到流程1执行,为0刚跳到流程2。这里我们先分析流程1,进入流程1处的004028F4中.
见代码3。
[C++] 纯文本查看 复制代码 00402BF4 /$ D9EA fldl2e ; 将log2e装入st(0)
00402BF6 |. DEC9 fmulp st(1), st ; st(1)与st(0)相乘
00402BF8 |. D9C0 fld st ; 装入st到st(0)
00402BFA |. D9FC frndint ; 取整(四舍五入)
00402BFC |. DCE9 fsub st(1), st ; st(1)-st(0)
00402BFE |. D9C9 fxch st(1) ; st(0)与st(1)交换
00402C00 |. D9F0 f2xm1 ; 2的st(0)次幂-1
00402C02 |. D9E8 fld1 ; 将1装入(st)
00402C04 |. DEC1 faddp st(1), st ; st(1)加上st(0)相加
00402C06 |. D9FD fscale ; 2的st(0)次方
00402C08 |. DDD9 fstp st(1)
00402C0A \. C3 retn
可见Call1运用了大量的浮点运算和数学运算,是不是看的我们眼花呢?为了大家能更加直观的理解这个Call1的作用,我把它翻译成了c++代码(不包括流程2).
见图片4。
相信这样,大家就能看的懂了吧。因为决定流程2的参数是固定的,所以我们在遇见时再做具体分析。
好了,准备工作做完了,现在该进入正题了。
为了更好的说明这个软件的验证过程,我将对注册码进行分段的分析。
第一段(前6位)
见代码4。
[C++] 纯文本查看 复制代码 004DF479 .B2 01 mov dl, 1 ;dl=1
004DF47B .BE 01000000 mov esi, 1 ;esi=1
004DF480 .B8 142C5B00 mov eax, 005B2C14 ;005B2C14保存的是前6位的注册码
004DF485 >8B4D F0 mov ecx, dword ptr [ebp-10] ;假码给ecx
004DF488 .0FB64C31 FF movzx ecx, byte ptr [ecx+esi-1] ;依次取假码的前6位
004DF48D . 3B08 cmp ecx, dword ptr [eax] ;与dword ptr [eax]的值比较
004DF48F . 74 02 je short 004DF493 ; 相等就跳
004DF491 . 33D2 xor edx, edx ; 如果不等edx=0
004DF493 > 46 inc esi ; esi+1
004DF494 . 83C0 04 add eax, 4 ; eax+4(取下一位)
004DF497 . 83FE 07 cmp esi, 7 ; 比较是否取完
004DF49A .^ 75 E9 jnz short 004DF485
004DF49C . 84D2 test dl, dl ; 判断dl是否为0
004DF49E . 0F84 F8030000 je 004DF89C ; 跳向失败
由代码4可知,注册码的前6位是固定不变的。程序依次对假码的前六位进行验证,只要有一位与注册码的前6位不符,dl就会为0,导致程序验证失败。我们在数据窗口中按下CTRL+G,输入005B2C14.
见图片5。
可知注册码的前6位是LH4A8N。我们重启程序,输入LH4A8N123456,继续分析。
第二段(单独判断第9位)
见代码5。
[C++] 纯文本查看 复制代码 004DF510 . 8B45 F8 mov eax, dword ptr [ebp-8] ; eax为假码的地址
004DF513 . 8A58 08 mov bl, byte ptr [eax+8] ; bl为假码第九位
004DF516 . 33C0 xor eax, eax ; eax=0
004DF518 . 8AC3 mov al, bl ; al=bl
004DF51A . 8945 A0 mov dword ptr [ebp-60], eax
004DF51D . DB45 A0 fild dword ptr [ebp-60] ; 装入整数到st(0)(假码第九位)
004DF520 . 83C4 F4 add esp, -0C
004DF523 . DB3C24 fstp tbyte ptr [esp] ; 参数2
004DF526 . 9B wait
004DF527 . 68 FA3F0000 push 3FFA
004DF52C . 68 39052FA7 push A72F0539
004DF531 . 68 C1CB2978 push 7829CBC1 ; 参数1
004DF536 . E8 391AF5FF call 00430F74 ; 关键Call1(重点分析)
004DF53B . DD5D E8 fstp qword ptr [ebp-18] ; 运算后的结果存入[ebp-18]
004DF53E . 9B wait
004DF53F . DB2D 08F94D00 fld tbyte ptr [4DF908] ; 装入实数到st(0)
004DF545 . DC6D E8 fsubr qword ptr [ebp-18] ; 与运算后的结果相减
004DF548 . D9E1 fabs ; 求绝对值
004DF54A . 83C4 F4 add esp, -0C
004DF54D . DB3C24 fstp tbyte ptr [esp] ; 参数2
004DF550 . 9B wait
004DF551 . 68 00400000 push 4000
004DF556 . 68 F1290080 push 800029F1
004DF55B . 68 D2C6116B push 6B11C6D2 ; 参数1
004DF560 . E8 0F1AF5FF call 00430F74
004DF565 . DB2D 14F94D00 fld tbyte ptr [4DF914] ; 装入实数到st(0)
004DF56B . DED9 fcompp ; CMP来比较2个无符号数
004DF56D . DFE0 fstsw ax
004DF56F . 9E sahf
004DF570 . 0F82 26030000 jb 004DF89C ; 小于就跳向失败
通过代码5可知,程序通过两个Call1来验证注册码的第九位。第一个Call1的参数1为假码的第九位,参数2为0.0408163265306122432。因为参数2的小数部分不为0,所以跳到流程1处执行,计算后的结果记为A9。第二个Call1的参数1为A9,参数2为2.0000100000000000000,小数部分不为0,跳向流程1,计算后的结果记为A90。然后A90与tbyte ptr[4DF914](也就是6.8999999999999989760e-07)比较,如果小于,就跳向失败。知道了这个,我们就可以用穷举法来找出第九位。我们通过查ASCII码可知,第九位的取值范围是33到126。我们用一个while循环来对在这个范围的值依次进行验证,如果满足条件,则跳出提示。
具体代码见图片6。
运行这段代码,弹出提示.
见图片7。
可见第九位为一定值“#”,将假码改为LH4A8N12#456,重启程序。
第三段:(判断第7,8,10位)
见代码6。
[C++] 纯文本查看 复制代码 004DF4AC . 8B45 F8 mov eax, dword ptr [ebp-8] ; eax为假码的地址
004DF4AF . 8A58 06 mov bl, byte ptr [eax+6] ; bl为假码第七位
004DF4B2 . 33C0 xor eax, eax ; eax=0
004DF4B4 . 8AC3 mov al, bl ; al=bl
004DF4B6 . 8945 A0 mov dword ptr [ebp-60], eax
004DF4B9 . DB45 A0 fild dword ptr [ebp-60]
004DF4BC . 83C4 F4 add esp, -0C
004DF4BF . DB3C24 fstp tbyte ptr [esp] ; 参数2为假码第七位
004DF4C2 . 9B wait
004DF4C3 . 68 FE3F0000 push 3FFE
004DF4C8 . 68 BD529691 push 919652BD
004DF4CD . 68 3411363C push 3C361134 ; 参数1
004DF4D2 . E8 9D1AF5FF call 00430F74
004DF4D7 . DC45 E0 fadd qword ptr [ebp-20] ; 计算后的结果保存在[ebp-20]
004DF4DA . DD5D E0 fstp qword ptr [ebp-20] ; 存入
004DF4DD . 9B wait
004DF4DE . 8B45 F8 mov eax, dword ptr [ebp-8] ; eax为假码的地址
004DF4E1 . 8A58 07 mov bl, byte ptr [eax+7] ; bl为假码第八位
004DF4E4 . 33C0 xor eax, eax ; eax=0
004DF4E6 . 8AC3 mov al, bl ; al=bl
004DF4E8 . 8945 A0 mov dword ptr [ebp-60], eax
004DF4EB . DB45 A0 fild dword ptr [ebp-60]
004DF4EE . 83C4 F4 add esp, -0C
004DF4F1 . DB3C24 fstp tbyte ptr [esp] ; 参数2为假码第八位
004DF4F4 . 9B wait
004DF4F5 . 68 FD3F0000 push 3FFD
004DF4FA . 68 2F4CA6AA push AAA64C2F
004DF4FF . 68 234A7B83 push 837B4A23 ; 参数1
004DF504 . E8 6B1AF5FF call 00430F74 ; 相同
004DF509 . DC45 E0 fadd qword ptr [ebp-20] ; 计算后的结果加上[ebp-20]
004DF50C . DD5D E0 fstp qword ptr [ebp-20] ; 存入
004DF50F . 9B wait
................. //省略代码为判断第九位
004DF576 . 8B45 F8 mov eax, dword ptr [ebp-8] ; eax为假码的地址
004DF579 . 8A58 09 mov bl, byte ptr [eax+9] ; bl为假码第十位
004DF57C . 33C0 xor eax, eax ; eax=0
004DF57E . 8AC3 mov al, bl ; al=bl
004DF580 . 8945 A0 mov dword ptr [ebp-60], eax
004DF583 . DB45 A0 fild dword ptr [ebp-60]
004DF586 . 83C4 F4 add esp, -0C
004DF589 . DB3C24 fstp tbyte ptr [esp] ; 参数2为假码第十位
004DF58C . 9B wait
004DF58D . 68 FD3F0000 push 3FFD
004DF592 . 68 92244992 push 92492492
004DF597 . 68 49922449 push 49249249 ; 参数1
004DF59C . E8 D319F5FF call 00430F74
004DF5A1 . DC45 E0 fadd qword ptr [ebp-20] ; 计算后的结果加上[ebp-20],并保存
004DF5A4 . DD5D E0 fstp qword ptr [ebp-20]
004DF5A7 . 9B wait
004DF5A8 . DD45 E0 fld qword ptr [ebp-20]
004DF5AB . 83C4 F4 add esp, -0C
004DF5AE . DB3C24 fstp tbyte ptr [esp] ; 参数2为计算后的结果[ebp-20]
004DF5B1 . 9B wait
004DF5B2 . 68 00400000 push 4000
004DF5B7 . 68 133BB193 push 93B13B13
004DF5BC . 68 B1133BB1 push B13B13B1 ; 参数1
004DF5C1 . E8 AE19F5FF call 00430F74
004DF5C6 . DD5D E8 fstp qword ptr [ebp-18] ; 计算后的结果保存在[ebp-18]中
004DF5C9 . 9B wait
004DF5CA . DD45 E8 fld qword ptr [ebp-18]
004DF5CD . D825 20F94D00 fsub dword ptr [4DF920] ; 与dword ptr[4DF920]相减
004DF5D3 . D9E1 fabs ; 取绝对值
004DF5D5 . DB2D 24F94D00 fld tbyte ptr [4DF924] ; 装入tbyte ptr [4DF924]到st(0)
004DF5DB . DED9 fcompp ; 比较
004DF5DD . DFE0 fstsw ax
004DF5DF . 9E sahf
004DF5E0 . 0F82 B6020000 jb 004DF89C ; 小于就跳向失败
通过代码6可知,程序通过了4个Call1来验证第7,8,10位。这四个Call1的参数1的小数部分都不为0,都跳到流程1执行。前三个Call1的参数2分别第7,8,10位的ASCII码,计算后的结果分别为A7,A8,A10,第四个Call1的参数2为A7+A8+10,计算后的结果记为A101。A101减去dword ptr [4DF920](也就是685.5000),结果取绝对值。再与tbyte ptr [4DF924](也就是0.8865299999999998976)比较,小于就跳向失败。和第二段一样,我们可以用4个for循环,对第7,8,10位进行验证。
具体代码见图片8
运行这段代码,弹出提示.
见图片9。
我们把假码改成LH4A8N'w#~1234567890,重启程序。
第四段:(判断第11,12,13,14位)(SEH异常验证)
见代码7。
[C++] 纯文本查看 复制代码 004DF644 . 8945 A0 mov dword ptr [ebp-60], eax ; 第十一位
004DF647 . DB45 A0 fild dword ptr [ebp-60]
004DF64A . 83C4 F4 add esp, -0C
004DF64D . DB3C24 fstp tbyte ptr [esp] ; 第十一位
004DF650 . 9B wait
004DF651 . B8 09000000 mov eax, 9 ; eax=9
004DF656 . E8 0519F5FF call 00430F60 ; 计算[esp]的eax次方,计算结果记为A11
004DF65B . 33C0 xor eax, eax
004DF65D . 8AC3 mov al, bl ; 第十一位
004DF65F . F7E8 imul eax ; eax*eax
004DF661 . F7E8 imul eax ; eax*eax
004DF663 . 66:8945 9C mov word ptr [ebp-64], ax ; 取ax给ebp-64
004DF667 . DF45 9C fild word ptr [ebp-64] ; 将word ptr [ebp-64]装入st(0)
004DF66A . DEC9 fmulp st(1), st ; A11与st再乘
004DF66C . DC45 E0 fadd qword ptr [ebp-20] ; 加上[ebp-20](初始为0)
004DF66F . DD5D E0 fstp qword ptr [ebp-20] ; 保存在ebp-20中
004DF672 . 9B wait
004DF673 . 8B45 F8 mov eax, dword ptr [ebp-8] ; eax为假码地址
004DF676 . 8A58 0B mov bl, byte ptr [eax+B] ; bl为第十二位
004DF679 . 33C0 xor eax, eax
004DF67B . 8AC3 mov al, bl ; al=bl
004DF67D . 83C0 0B add eax, 0B ; eax+0XB
004DF680 . 71 05 jno short 004DF687 ; 不溢出就跳
004DF682 . E8 7544F2FF call 00403AFC
004DF687 > 8945 A0 mov dword ptr [ebp-60], eax
004DF68A . DB45 A0 fild dword ptr [ebp-60]
004DF68D . D9FA fsqrt ; 第十二位+0xB开方
004DF68F . E8 B835F2FF call 00402C4C ; 取开方后的整数
004DF694 . 8BF0 mov esi, eax ; esi=开方后的整数
004DF696 . C745 D8 01000>mov dword ptr [ebp-28], 1
004DF69D . C745 DC 00000>mov dword ptr [ebp-24], 0
004DF6A4 . 83FE 01 cmp esi, 1 ; 是否为1
004DF6A7 . 76 2D jbe short 004DF6D6
004DF6A9 > 8BC6 mov eax, esi ; 循环开始(算阶乘)
004DF6AB . 33D2 xor edx, edx
004DF6AD . 52 push edx
004DF6AE . 50 push eax
004DF6AF . 8B45 D8 mov eax, dword ptr [ebp-28]
004DF6B2 . 8B55 DC mov edx, dword ptr [ebp-24]
004DF6B5 . E8 2A65F2FF call 00405BE4 ; 相乘
004DF6BA . 71 05 jno short 004DF6C1
004DF6BC . E8 3B44F2FF call 00403AFC
004DF6C1 > 8945 D8 mov dword ptr [ebp-28], eax
004DF6C4 . 8955 DC mov dword ptr [ebp-24], edx
004DF6C7 . 83EE 01 sub esi, 1
004DF6CA . 73 05 jnb short 004DF6D1
004DF6CC . E8 2B44F2FF call 00403AFC
004DF6D1 > 83FE 01 cmp esi, 1
004DF6D4 .^ 77 D3 ja short 004DF6A9 ; 循环结束
004DF6D6 > FF75 DC push dword ptr [ebp-24]
004DF6D9 . FF75 D8 push dword ptr [ebp-28] ; 阶乘后的值
004DF6DC . 33C0 xor eax, eax ; eax=0
004DF6DE . 8AC3 mov al, bl ; 第十二位
004DF6E0 . F7E8 imul eax ; 相乘
004DF6E2 . F7E8 imul eax ; 相乘
004DF6E4 . 0FBFC0 movsx eax, ax ; 取ax
004DF6E7 . 33D2 xor edx, edx ; EDX=0
004DF6E9 . 8AD3 mov dl, bl ; 第十二位
004DF6EB . F7EA imul edx ; 相乘
004DF6ED . 71 05 jno short 004DF6F4
004DF6EF . E8 0844F2FF call 00403AFC
004DF6F4 > 99 cdq ; 护展成四字节
004DF6F5 . E8 EA64F2FF call 00405BE4 ; 相乘
004DF6FA . 71 05 jno short 004DF701 ; 与前面的结果相乘
004DF6FC . E8 FB43F2FF call 00403AFC
004DF701 > 8945 94 mov dword ptr [ebp-6C], eax
004DF704 . 8955 98 mov dword ptr [ebp-68], edx
004DF707 . DF6D 94 fild qword ptr [ebp-6C] ; 装入第十二位运算结果
004DF70A . DC45 E0 fadd qword ptr [ebp-20] ; 与[ebp-20]相加
004DF70D . DD5D E0 fstp qword ptr [ebp-20]
004DF710 . 9B wait
004DF711 . 8B45 F8 mov eax, dword ptr [ebp-8] ; eax为假码地址
004DF714 . 8A58 0C mov bl, byte ptr [eax+C] ; 第十三位
004DF717 . 33C0 xor eax, eax
004DF719 . 8AC3 mov al, bl ; al=bl
004DF71B . 8945 A0 mov dword ptr [ebp-60], eax
004DF71E . DB45 A0 fild dword ptr [ebp-60]
004DF721 . 83C4 F4 add esp, -0C
004DF724 . DB3C24 fstp tbyte ptr [esp] ; 参数2为假码第十三位
004DF727 . 9B wait
004DF728 . 68 01400000 push 4001
004DF72D . 68 000000C0 push C0000000
004DF732 . 6A 00 push 0 ; 参数1(此时进入流程2)
004DF734 . E8 3B18F5FF call 00430F74
004DF739 . 33C0 xor eax, eax
004DF73B . 8AC3 mov al, bl
004DF73D . 8945 A0 mov dword ptr [ebp-60], eax
004DF740 . DB45 A0 fild dword ptr [ebp-60]
004DF743 . DEC9 fmulp st(1), st ; 相乘
004DF745 . DC45 E0 fadd qword ptr [ebp-20] ; 与[ebp-20]相加
004DF748 . DD5D E0 fstp qword ptr [ebp-20]
004DF74B . 9B wait
004DF74C . 8B45 F8 mov eax, dword ptr [ebp-8]
004DF74F . 8A58 0D mov bl, byte ptr [eax+D] ; 第十四位
004DF752 . 8BC3 mov eax, ebx
004DF754 . 25 FF000000 and eax, 0FF
004DF759 . 33D2 xor edx, edx
004DF75B . 8945 D8 mov dword ptr [ebp-28], eax
004DF75E . 8955 DC mov dword ptr [ebp-24], edx
004DF761 . DF6D D8 fild qword ptr [ebp-28]
004DF764 . 83C4 F4 add esp, -0C
004DF767 . DB3C24 fstp tbyte ptr [esp] ; 参数2为假码第十四位
004DF76A . 9B wait
004DF76B . 68 00400000 push 4000
004DF770 . 68 00000080 push 80000000 ; 2(进入流程2)
004DF775 . 6A 00 push 0
004DF777 . E8 F817F5FF call 00430F74
004DF77C . 8B45 F8 mov eax, dword ptr [ebp-8]
004DF77F . 0FB640 0A movzx eax, byte ptr [eax+A] ; 第十一位
004DF783 . F7E8 imul eax ; eax*eax
004DF785 . 66:8945 9C mov word ptr [ebp-64], ax
004DF789 . DF45 9C fild word ptr [ebp-64]
004DF78C . DEC9 fmulp st(1), st ; 相乘
004DF78E . 8B45 F8 mov eax, dword ptr [ebp-8]
004DF791 . 8A40 0B mov al, byte ptr [eax+B] ; 第十二位
004DF794 . 25 FF000000 and eax, 0FF
004DF799 . 8945 A0 mov dword ptr [ebp-60], eax
004DF79C . DB45 A0 fild dword ptr [ebp-60]
004DF79F . DEC9 fmulp st(1), st ; 相乘
004DF7A1 . DC45 E0 fadd qword ptr [ebp-20] ; 与[ebp-20]相加
004DF7A4 . DD5D E0 fstp qword ptr [ebp-20]
004DF7A7 . 9B wait
004DF7A8 . 33C0 xor eax, eax
004DF7AA . 55 push ebp
004DF7AB . 68 FEF74D00 push 004DF7FE
004DF7B0 . 64:FF30 push dword ptr fs:[eax]
004DF7B3 . 64:8920 mov dword ptr fs:[eax], esp ; 安装SEH异常
004DF7B6 . DD45 E0 fld qword ptr [ebp-20] ; 压入第十一位到第十四位的运算结果
004DF7B9 . E8 8E34F2FF call 00402C4C
004DF7BE 2D 8063E7EA sub eax, EAE76380 ; 1026143249280
004DF7C3 81DA EE000000 sbb edx, 0EE
004DF7C9 . 71 05 jno short 004DF7D0
004DF7CB . E8 2C43F2FF call 00403AFC
004DF7D0 > 8945 94 mov dword ptr [ebp-6C], eax
004DF7D3 . 8955 98 mov dword ptr [ebp-68], edx
004DF7D6 . DF6D 94 fild qword ptr [ebp-6C]
004DF7D9 . DB7D 88 fstp tbyte ptr [ebp-78]
004DF7DC . 9B wait
004DF7DD . E8 06CAF2FF call 0040C1E8 ; 通过GetLocalTime计算出一个随机数,无用
004DF7E2 . DB6D 88 fld tbyte ptr [ebp-78]
004DF7E5 . DEF9 fdivp st(1), st ; st(1)%st(0)给st(1)(主要在st0)
004DF7E7 . DDD8 fstp st ; st为0,触发异常
004DF7E9 . 33C0 xor eax, eax
004DF7EB . 5A pop edx
004DF7EC . 59 pop ecx
004DF7ED . 59 pop ecx
004DF7EE . 64:8910 mov dword ptr fs:[eax], edx
004DF7F1 . E9 A6000000 jmp 004DF89C ; 如果没有触发异常,这里必跳
通过代码7可知,程序在对第13,14位进行Call1运算时,参数1分别为6.000000和2.000000。所以进入了流程2,我们写注册机时就不能利用先前写好的Call1。不过,别担心,流程2要比流程1简单.
见代码8。
[C++] 纯文本查看 复制代码 00430FDC |. DB6D 08 fld tbyte ptr [ebp+8] ; 装入参数1到st(0)(流程2)
00430FDF |. D9E1 fabs ; 取绝对值
00430FE1 |. DB2D 30104300 fld tbyte ptr [431030] ; 装入0x7FFFFFFF到st(0)
00430FE7 |. DED9 fcompp ; 比较
00430FE9 |. DFE0 fstsw ax
00430FEB |. 9E sahf
00430FEC |. 72 1E jb short 0043100C ; 小于就跳(这里死都不跳--!)
00430FEE |. 66:8B45 1C mov ax, word ptr [ebp+1C]
00430FF2 |. 50 push eax
00430FF3 |. FF75 18 push dword ptr [ebp+18]
00430FF6 |. FF75 14 push dword ptr [ebp+14]
00430FF9 |. DB6D 08 fld tbyte ptr [ebp+8] ; 装入参数1到st(0)
00430FFC |. E8 4B1CFDFF call 00402C4C ; 将参数1取整后装入eax
00431001 |. E8 2AFFFFFF call 00430F30 ; 进入
00431006 |. DB7D F0 fstp tbyte ptr [ebp-10]
00431009 |. 9B wait
0043100A |. EB 17 jmp short 00431023
由代码8可知,流程2的关键处在于Call 00430F30中,我们进入这个Call.
见代码9。
[C++] 纯文本查看 复制代码 00430F30 /$ 55 push ebp
00430F31 |. 8BEC mov ebp, esp
00430F33 |. 89C1 mov ecx, eax
00430F35 |. 99 cdq ; 扩展成四字节
00430F36 |. D9E8 fld1 ; 装入1到st(0)
00430F38 |. 31D0 xor eax, edx
00430F3A |. 29D0 sub eax, edx
00430F3C |. 74 1A je short 00430F58
00430F3E |. DB6D 08 fld tbyte ptr [ebp+8] ; 装入参数2
00430F41 |. EB 02 jmp short 00430F45
00430F43 |> D8C8 /fmul st, st ; 相乘
00430F45 |> D1E8 shr eax, 1 ; eax右移位,高位补0,低位进入CF,CF=0就跳
00430F47 |.^ 73 FA |jnb short 00430F43 ; 不小于就跳
00430F49 |. DCC9 |fmul st(1), st ; st(1)*st(0)
00430F4B |.^ 75 F6 \jnz short 00430F43 ; 不等就跳
00430F4D |. DDD8 fstp st
00430F4F |. 83F9 00 cmp ecx, 0
00430F52 |. 7D 04 jge short 00430F58
00430F54 |. D9E8 fld1
00430F56 |. DEF1 fdivrp st(1), st
00430F58 |> 9B wait
00430F59 |. 5D pop ebp
00430F5A \. C2 0C00 retn 0C
代码9的关键在于中间那个循环。shr为逻辑右移,shr eax,1的意思是将eax向右移到1位,高位补0,低位进入CF,当CF=0时,jnb跳转实现。shr eax,1也可以理解为将 eax除以2,eax为商,edx为余数。当eax不为0时,ZF=0,jnz跳转实现。是不是有点晕啊!不用担心,因为这个循环的次数是根据参数1来决定的,参数1只有两个值,分别是6和2。因此我们没有必要还原这个循环,我们只需对这两个值分别进行特定的计算就行。当参数1为6时.
c++代码见图片10。
当参数1为2时,c++代码见图片11。
Num13,Num14分别代表假码第13,14位的ASCII码,sd3,sd4分别代表最终的计算结果。是不是很简单啊!分析完流程2,我们继续看代码7,发现程序依旧还是利用Call1和四则运算分别对第11,12,13,14位的假码进行运算,然后相加得出一个具体的值(记为A14)来判断是否通过验证。只不过这种验证更加的隐蔽,它运用了SEH异常处理,我们不能像第二段或第三段直接找出这个值要满足的范围。我们现在从004DF7AA这里看起。程序在004DF7B3处安装了SEH异常,此时我们看堆栈窗口.
见图片12。
在代码窗口按下CTRL+G,输入004DF7FE,下好CC断点,继续我们的分析。在004DF7B3处,程序将A14装入st(0)中,而00402C4C这个Call的作用就是把A14取整,然后转换成16进制。因为A14之前是64位的,也就是double类型。一个寄存器是32位的,所以这里用edx和eax一起保存。然后eax减去EAE76380,edx减去0EE,然后再将eax,edx还原成一个double数(A140)。然后通过Call 0040C1E8,利用GetLocalTime计算出一个随机数。然后用这个随机数来除以A140。如果我们输入的不是正确的注册码,程序正常运行下去,并跳到004DF89C处,将全局注册标志清0。所以在这里我们必须触发了这个异常,才能进行下一步的验证。那么这个异常是什么呢?我们仔细看下代码,发现004DF7E5的代码为fdivp st(1),st。我们自然联想到了除0异常,将st(0)的值赋予0,发现程序出现异常,并跳到004DF7FE处执行,也就是我们之前下的CC断点处。限于篇幅,SEH的处理过程我就不多了,黑X论坛上有说明的贴子,大家可以上去看看,不懂的话,也可以在论坛联系我。我们在004DF803处下好CC断点,然后按下F9,发现出现异常后的程序成功的断了下来,并进行下一步验证。好了,我们现在返回去看,将004DF7BE和004DF7C3处的eax和edx组合起来就是0xEEEAE76380,转换成十进制就是1026143249280。也就是说,当最终的计算结果A14为1026143249280时,则通过验证。分析到这里是不是有点累了!没关系,加油,马上就要到达终点了。知道了验证过程,我们就可以用c++来算出这段注册码。
见图片13和图片14。
代码中的Call2(9,Num11)是用来计算Num11的9次方的,Call3是用来计算阶乘的。运行这段代码,跳出提示.
见图片15。
可知第11-14位分别为J&4T。且是定值。我们把假码改成LH4A8N'w#~J&4T12345。重启程序,进行最后一处验证。
第五段(第15,16,17,18,19位的验证)
见代码10。
[C++] 纯文本查看 复制代码 004DF803 . E8 FC4BF2FF call 00404404 ; 跳到这里执行了
004DF808 . A1 BC355B00 mov eax, dword ptr [5B35BC] ; XL[
004DF80D . 8B00 mov eax, dword ptr [eax]
004DF80F . 83C0 04 add eax, 4
004DF812 . 50 push eax
004DF813 . B9 02000000 mov ecx, 2
004DF818 . BA 14000000 mov edx, 14
004DF81D . 8B45 F8 mov eax, dword ptr [ebp-8] ; eax为假码地址
004DF820 . E8 E756F2FF call 00404F0C
004DF825 . 8B45 F8 mov eax, dword ptr [ebp-8]
004DF828 . 8A40 12 mov al, byte ptr [eax+12] ; 第十九位
004DF82B . 25 FF000000 and eax, 0FF ; 取ax低位
004DF830 . 33D2 xor edx, edx
004DF832 . 8945 B0 mov dword ptr [ebp-50], eax
004DF835 . 8955 B4 mov dword ptr [ebp-4C], edx
004DF838 . 6A 00 push 0
004DF83A . 6A 1A push 1A ; 除数1A
004DF83C . 8B45 D0 mov eax, dword ptr [ebp-30] ; 第十五位
004DF83F . 8B55 D4 mov edx, dword ptr [ebp-2C]
004DF842 . 0345 C8 add eax, dword ptr [ebp-38] ; 第十五位+第十六位
004DF845 . 1355 CC adc edx, dword ptr [ebp-34]
004DF848 . 71 05 jno short 004DF84F
004DF84A . E8 AD42F2FF call 00403AFC
004DF84F > 0345 C0 add eax, dword ptr [ebp-40] ; 再加第十七位
004DF852 . 1355 C4 adc edx, dword ptr [ebp-3C]
004DF855 . 71 05 jno short 004DF85C
004DF857 . E8 A042F2FF call 00403AFC
004DF85C > 0345 B8 add eax, dword ptr [ebp-48] ; 再加第十八位
004DF85F . 1355 BC adc edx, dword ptr [ebp-44]
004DF862 . 71 05 jno short 004DF869
004DF864 . E8 9342F2FF call 00403AFC
004DF869 > E8 CA64F2FF call 00405D38 ; 第15至18位的和除以1A,并取余数.
004DF86E . 71 05 jno short 004DF875
004DF870 . E8 8742F2FF call 00403AFC
004DF875 > 83C0 41 add eax, 41 ; 加上0x41
004DF878 . 83D2 00 adc edx, 0
004DF87B . 71 05 jno short 004DF882
004DF87D . E8 7A42F2FF call 00403AFC
004DF882 > 8945 A8 mov dword ptr [ebp-58], eax ; 计算后的结果保存在[ebp-58]中
004DF885 . 8955 AC mov dword ptr [ebp-54], edx
004DF888 . 8B45 B0 mov eax, dword ptr [ebp-50] ; eax为第十九位
004DF88B . 8B55 B4 mov edx, dword ptr [ebp-4C]
004DF88E . 3B55 AC cmp edx, dword ptr [ebp-54] ; 必为0
004DF891 . 75 03 jnz short 004DF896
004DF893 . 3B45 A8 cmp eax, dword ptr [ebp-58] ; 第十五到第十八位的计算结果要等于第十九位
004DF896 > 75 04 jnz short 004DF89C ; 不等就跳
004DF898 . C645 F7 01 mov byte ptr [ebp-9], 1 ; 全局注册标志
004DF89C > 33C0 xor eax, eax
004DF89E . 5A pop edx
004DF89F . 59 pop ecx
004DF8A0 . 59 pop ecx
由代码10可知,程序先累加第15位到18位的ASCII码,累加之和记为A19。然后用A19除以0X1A,并取其余数。结果再加上0X41,并与第十九位比较。如果相等,则在004DF898处注册全局标志。如果我们要爆破的话,可以在这里改。写注册机时,依旧是用5个for循环进行穷举.
具体代码见图片16。
运行这段代码,跳出提示.
见图17。
小结:
所以我们用注册机最后得出的注册码是LH4A8N'w#~J&4T!!!9A。重启程序,输入LH4A8N'w#~J&4T!!!9A,弹出成功注册提示.
见图片18。
由图片18可知,程序还有重启验证。当注册码通过以上验证的时候,并会将其保存在注册表中,这在一定程序上阻止了爆破。但是在整个验算过程中,用户名并没有参与验算,而且也并没有取机器特征码,导致了多机一码的情况出现,我认为这是一个失败之处。不过这个程序的用来练习算法,考验耐心是最适合不过的了,--!。至此文章就写的差不多了,由于第四段验证的代码太长,所以只说明了主要部分,详细的分析我都注释在代码上了,大家可以去看看。
C++注册机源码如下:
[C++] 纯文本查看 复制代码 void CMyDlg::OnButton1()
{
// TODO: Add your control notification handler code here
//Call1 代表00430F74
//Call2 代表00430F60
//Call3 代表00405BE4 阶乘
CString Key="LH4A8N",Key0; //注册码
double Para[10]={0.0} ; //定义参数数组并初始化为0
Para[0]=0.5686999999999998976; //第七位
Para[1]=0.3332999999999999488; //第八位
Para[2]=0.0408163265306122432; //第九位
Para[3]=2.0000100000000000000; //第九位计算结果
Para[4]=0.2857142857142856704; //第十位
Para[5]=2.3076923076923074560; //第七,八,十位计算的结果
Para[6]=6.0000000000000000000; //第十三位
Para[7]=2.0000000000000000000; //第十四位
double Para1=1.1561553999999997440; //tbyte ptr [4DF908]
double Para2=6.8999999999999989760e-07; //tbyte ptr [4DF914]
double Para3=685.5000; //dword ptr [4DF920]
double Para4=0.8865299999999998976; //tbyte ptr [4DF924]
double Para5=2147483647.0000000000; //tbyte ptr [431030]
double temp=0.0,ebp20=0.0,ebp18=0.0;
DWORD eax=0;
int Num7=0,Num8=0,Num9=0,Num10=0,Num11=0,Num12=0,Num13=0,Num14=0,Num15=0,Num16=0,Num17=0,Num18=0,Num19=0;
CString temp9="第九位为",temp7="第七位为",temp8="第八位为",temp10="第十位为",temp0="是否取其它值",
temp11="第十一位为",temp12="第十二位为",temp13="第十三位为",temp14="第十四位为",temp15="第十五位为",
temp16="第十六位为",temp17="第十七位为",temp18="第十八位为",temp19="第十九位为";
double sd1,sd2,sd3,sd4,sd;
char p;
//第九位
Num9=33;
while (Num9<127)
{
temp=Call1(Para[2],Num9);
temp=fabs(Para1-temp);
sd=Call1(Para[3],temp);
if (Para2>=sd)
{
p=(char)Num9;
temp9+=p;
AfxMessageBox(temp9);
}
Num9++;
}
//第九位
//第7位 第8位 第10位
int n=1;
for (Num7=33;Num7<127;Num7++)
{
sd1=Call1(Para[0],Num7);
ebp20+=sd1;
for (Num8=33;Num8<127;Num8++)
{
sd2=Call1(Para[1],Num8);
ebp20+=sd2;
for (Num10=33;Num10<127;Num10++)
{
sd3=Call1(Para[4],Num10);
ebp20+=sd3;
sd4=Call1(Para[5],ebp20);
temp=fabs(sd4-Para3);
if (Para4>temp)
{
p=(char)Num7;
Key0=p;
temp7+=p;
p=(char)Num8;
Key0+=p;
Key0+="#";
temp8+=p;
p=(char)Num10;
Key0+=p;
temp10+=p;
temp0=temp7+"\n"+temp8+"\n"+temp10+"\n"+"是否取其它值";
n=AfxMessageBox(temp0,MB_OKCANCEL,NULL);
if(n!=1)
{
Key+=Key0;
break;
}
temp7="第七位为";
temp8="第八位为";
temp10="第十位为";
}
ebp20-=sd3;
if(n!=1)
{
break;
}
}
ebp20-=sd2;
if(n!=1)
{
break;
}
}
ebp20-=sd1;
if(n!=1)
{
break;
}
}
ebp20=0; //清零
//第7位 第8位 第10位
//第11,12,13,14位
LONG64 eax1,eax2;
signed short ax;
signed long eax3;
double as;
for (Num11=33;Num11<127;Num11++)
{
eax=Num11*Num11;
ax=eax*eax;
as=double(ax);
sd1=as*Call2(9,Num11);
ebp20+=sd1;
for (Num12=33;Num12<127;Num12++)
{
eax=Num12+0x0B;
eax=(int)sqrt(eax);
temp=Call3(eax);
eax=Num12*Num12;
eax=eax*eax;
ax=eax;
eax=0;
eax3=ax;
eax3=eax3*Num12;
eax2=eax3*temp;
ebp20+=eax2;
for(Num13=33;Num13<127;Num13++)
{
sd3=Num13*Num13;
sd3=sd3*sd3;
temp=Num13*Num13;
sd3=sd3*temp;
sd3=sd3*Num13;
ebp20+=sd3;
for (Num14=33;Num14<127;Num14++)
{
sd4=Num14*Num14;
eax1=Num11*Num11;
eax1=eax1*sd4*Num12;
ebp20+=eax1;
if(INT64(ebp20)-1026143249280==0)
{
p=char(Num11);
temp11=temp11+p;
p=char(Num12);
temp12=temp12+p;
p=char(Num13);
temp13=temp13+p;
p=char(Num14);
temp14=temp14+p;
temp14=temp11+"\n"+temp12+"\n"+temp13+"\n"+temp14;
AfxMessageBox(temp14);
}
ebp20-=eax1;
}
ebp20-=sd3;
}
ebp20-=eax2;
}
ebp20-=sd1;
}
//第11,12,13,14位
//第15,16,17,18,19位
int Num=0,Num0;
n=1;
for (Num19=33;Num19<127;Num19++)
{
for (Num15=33;Num15<127;Num15++)
{
Num+=Num15;
for (Num16=33;Num16<127;Num16++)
{
Num+=Num16;
for (Num17=33;Num17<127;Num17++)
{
Num+=Num17;
for (Num18=33;Num18<127;Num18++)
{
Num+=Num18;
Num0=Num % 0x1A;
Num0+=0x41;
if (Num0==Num19)
{
p=(char)Num15;
Key0=p;
temp15+=p;
p=(char)Num16;
Key0+=p;
temp16+=p;
p=(char)Num17;
Key0+=p;
temp17+=p;
p=(char)Num18;
Key0+=p;
temp18+=p;
p=(char)Num19;
Key0+=p;
temp19+=p;
temp0=temp15+"\n"+temp16+"\n"+temp17+"\n"+temp18+"\n"+temp19+"\n"+"是否取其它值";
n=AfxMessageBox(temp0,MB_OKCANCEL,NULL);
if(n!=1)
{
Key+="J&4T";
Key+=Key0;
break;
}
temp15="第十五位为";
temp16="第十六位为";
temp17="第十七位为";
temp18="第十八位为";
temp19="第十九位为";
}
Num-=Num18;
if(n!=1)
{
break;
}
}
Num-=Num17;
if(n!=1)
{
break;
}
}
Num-=Num16;
if(n!=1)
{
break;
}
}
Num-=Num15;
if(n!=1)
{
break;
}
}
if(n!=1)
{
break;
}
}
m_Key.SetWindowText(Key);
}
//temp=(log(假码第七位l)/log(2)*Key[0]*参数8*log(2)(e))-int(log(假码第七位l)/log(2)*Key[0]*参数8*log(2)(e));
//temp=2的temp次方-1+1
//2的temp次方
double CMyDlg::Call1(double x, double y)
{
//x为参数1,y为参数2,dTemp1为运算的结果
double dTemp1,dTemp2;
dTemp1=log(y)/log(2)*loge2*x*log2e;
dTemp2=int(dTemp1);
dTemp1=dTemp1-dTemp2;
dTemp1=pow(2,dTemp1);
dTemp1=pow(2,dTemp2)*dTemp1;
return dTemp1;
}
double CMyDlg::Call2(double x, double y)
{
double dTemp3;
dTemp3=y*pow(2,x);
return dTemp3;
}
void CMyDlg::OnButton2()
{
// TODO: Add your control notification handler code here
}
DWORD CMyDlg::Call3(int x)
{
DWORD j=1;
for (int i=1;i<=x;i++)
{
j=i*j;
}
return j;
}
后记:
感谢支持我教程的朋友们,小菜是第一次做系列教程,有些地方表达不太清楚请见谅,因为本系列教程定位需要有一定基础的,所以下一步小菜也希望能出一些新手教程来帮助大家进步,具体下一系列的教程还没定位,希望大家能提些意见,也希望大家继续支持我,谢谢大家
=================================================================
传送门:
破解实战-第一战:http://www.52pojie.cn/thread-197281-1-1.html
破解实战-第二战:http://www.52pojie.cn/thread-197598-1-1.html
破解实战-第三站:http://www.52pojie.cn/thread-197957-1-1.html
破解实战-第四站:http://www.52pojie.cn/thread-198203-1-1.html
破解实战-第五战:http://www.52pojie.cn/thread-198365-1-1.html
破解实战-第六战:http://www.52pojie.cn/thread-198930-1-1.html
破解实战-第七战:http://www.52pojie.cn/thread-199459-1-1.html
破解实战-第八战:http://www.52pojie.cn/thread-199834-1-1.html
破解实战-第九战:http://www.52pojie.cn/thread-200655-1-1.html
破解实战-第十战:http://www.52pojie.cn/thread-200798-1-1.html
|
免费评分
-
查看全部评分
本帖被以下淘专辑推荐:
- · 破解教程|主题: 126, 订阅: 214
- · 精品收藏|主题: 9, 订阅: 6
|