首页
社区
课程
招聘
[转帖]申请加入DFCG NO.2--VBIDEUtils v4.00注册算法分析
2005-10-11 18:31 8560

[转帖]申请加入DFCG NO.2--VBIDEUtils v4.00注册算法分析

2005-10-11 18:31
8560
VBIDEUtils v4.00注册算法分析
【破解作者】 winndy[FCG][PYG]
【作者邮箱】 CNwinndy@hotmail.com
【使用工具】 Driver Studio 3.2   Ollydbg v1.10 fly修改版
【破解平台】 Winxp
【软件名称】 VBIDEUtils v4.00
【官方网址】 http://www.vbdiamond.com/
【编写语言】 Microsoft VB 6.0
【软件介绍】 这是一款VB插件,里面整合了很多功能。
【破解声明】 For Study ,For Fun
【保护方式】 VB Pcode,序列号
【破解过程】 这是一个VB Addins,觉得很棘手。主要破解思路为:
  1.首先到VBIDEUtils的安装目录去看看,查看里面的ocx以及dll,以求寻得一些有用线索。
  2.采用OD以及各种反编译软件(包括Pcode的反编译工具,象VB RezQ Version 2.4a ,VB Decompiler ,vb explorer等)尝试
    得到一些敏感信息。最终锁定的目标是VBIDEUtils.dll。尽管VB Decompiler和vb explorer都是很好的Pcode反编译工具,但是
    其均不能反编译VBIDEUtils.dll。采用VB RezQ Version 2.4a则只能得到其窗体(注册窗体导出来被我利用写了注册机),并未有
    任何实质性的代码。
3.采用OD,能看到其中的导入的函数,但是也不可以动态调试(可能是我还没掌握用OD动态调试dll吧)。
4.没有办法,只好请出SoftICE。从这个时刻起,我就做好了吃苦的准备,边调试,边手工在草稿纸上作笔记,故这篇文章的代码也是从
   草稿纸上敲进来的,有影响阅读的,还望多体谅。

   现在,开始介绍具体步骤。
    拿到一个VB程序,常不知道下什么断点才是有成效的,为避免走弯路,我首先采用OD打开dll,再从其导入函数中,根据自己的敏感
   来挑选一些可能很重要的函数下断点。
   OD得到的导入函数为(在入口处,向上翻一页就可以看到):
   
   112511C0    .- FF25 BC1025>jmp dword ptr ds:[<&MSVBVM60.DllFunctionCall>]   ;  MSVBVM60.DllFunctionCall
112511C6    .- FF25 501025>jmp dword ptr ds:[<&MSVBVM60.#594>]              ;  MSVBVM60.rtcRandomize
112511CC    .- FF25 481025>jmp dword ptr ds:[<&MSVBVM60.#593>]              ;  MSVBVM60.rtcRandomNext
112511D2    .- FF25 401125>jmp dword ptr ds:[<&MSVBVM60.#572>]              ;  MSVBVM60.rtcHexBstrFromVar
112511D8    .- FF25 581025>jmp dword ptr ds:[<&MSVBVM60.#595>]              ;  MSVBVM60.rtcMsgBox
112511DE    .- FF25 941025>jmp dword ptr ds:[<&MSVBVM60.#631>]              ;  MSVBVM60.rtcMidCharBstr
....
....
为了阅读的方便,省略若干...

   

   
我选择了这几个:

rtcHexBstrFromVar,rtcMidcharBstr,rtcR8ValfromBstr,rtcMidcharVar,rtcAnsivalueBstr.
奇怪的是我并没有看到__vbastrcomp,尽管这样,我还是下了这个断点。

仅仅下msvbvm60!__vbastrcomp这个断点的时候,第二次中断的时候在调用msvbvm60!__vbastrcomp前,可以在堆栈里看到输入的
假注册码以及真注册码。但这不是目的,最终的目标是分析出其算法。

我输入的用户名是winndy,假码是1234567890,看到的真注册码是2E6D-BDFD-FCF9-6279。

下面把msvbvm60!__vbastrcomp断点清除,下上面选择的4个断点。
首先中断在rtcMidcharVar,单步跟踪下去,可以发现,rtcMidcharVar调用了rtcMidcharBstr(这也是我选择的断点之一,这样我清除了rtcMidcharBstr,于是又少了一个断点,尽可能简化破解过程),而rtcMidcharBstr又调用了rtcMidBstr。从rtcMidcharVar返回后,发现是call eax 来引发的这个调用。代码为:
001B:7348E7A8:cmp BYTE PTR [734A01A4],00
            AF:JNZ 734965AD
          E7B5:call EAX=====>这里进去,调用了rtcMidcharVar。
          E7B7:cmp EDI,ESP
          E7B9:jnz 73496595
.......
Ctrl+D,中断rtcAnsivalueBstr,单步跟踪,发现其调用了Kernel32!WideCharToMultiByte
当单步跟踪到001B:7347A6D0:movzx AX,BYTE PTR[EBP+0A]
时,发现AX="77",这正是用户名winndy的w的ascii码,OK,这这一行下断,则rtcMidcharVar和rtcAnsivalueBstr都可以清除了,
因为程序先midchar取出用户名的一个字符,然后调用rtcAnsivalueBstr算出字符的ascii码,最终结果都可以从这一句看到,所以
再减少两个断点吧!
        001B:7347A6D0后一个jmp语句:jmp 7347A6CB,单步下去,在001B:73495191处:pop eax;弹出了77。
001B:73495191:pop eax               ;eax=0077  
         5192:add [esp],AX          ;[esp]=0
         6196: jo  73494062
................
................
001B:7349549E pop eax         ;eax=12,每次都是这个值,常数?yes
         549F xor [esp],eax   ;77 xor 12=65
......(pcode的控制语句(也许不是很确切),把代码取出来,再跳转,注意到pcode程序,往往一小段代码中只有一句有用的,其余全是控制代码)

Ctrl+D,在001B:7347A6D0:movzx AX,BYTE PTR[EBP+0A]处可以看到全部的字符串的ascii码,
出了用户名,我还看到一个长16的常数串,"36BRI4TQ1WJ83ET6",这个常数串的操作与用户名有区别。
用户名每次都与12xor,而常数串则每次与19xor。

由于破解是零星的进行的,笔记也记得不是很全。
在与12或19异或后,还有个求和的过程,即:用户名与12异或后再加起来,winndy得到的结果是024E。

16个字符也有求和的过程,但又稍微有变化。前面8个字符与19XOR后加起来,后面8个字符要加上前面8个字符与19XOR后相应(9对应1...,16对应8)的结果,然后再与19XOR,最后累加和。常数串的累加和是066C。

截取的代码为:
001B:73492B08 pop ecx               ;ecx=65,别忘了77 xor 12=65
         2B09 mov [eax*2+ebx],cx    ;cx=65,这里保存65,我电脑上ebx是001D1180,eax=1。
......
     73492A3E mov BX,[eax*2+ebx]   ;这里取出了65
         2A42 push ebx              ;压入堆栈
.....
         5191 pop eax               ;取出65
              add [esp],AX          ;累加和
......
001B:7348E2FA pop ebx              ;bx=65
          E2FB mov [ebp+eax],bx     ;保存和,ebp=0012DFF4,eax=FFFFFECC,ebp+eax=0012DEC0
                                    ;下命令 d ebp+eax,可以看到数据。




求完和以后,这两个不同的和还要再相加,求总和。
代码为:
001B:7348E2B6 movsx eax,WORD PTR [esi]
         E2B9 mov    ax,[ebp+eax]            ;取出和
         E2BD push eax                       ;压入堆栈
......
     73495191 pop eax                        ;把和弹出来
         5192 add [esp],ax                   ;累加两个和,[esp]=02B9,ax=066C,sum=0925



得到和0925以后,再除以0200,得到的余数,留作后续使用。
001B:7349505E movsx eax,WORD PTR[ESI]        ;eax=0200,这也是常数
         5061 push eax
.....
         5327 pop ecx                         ;ecx=0200
         5328 pop eax                         ;eax=0925,sum
         5329 CWD
         532B IDIV CX                         ;
         532E push edx                        ;dx=0125,保存余数,后续使用


前面提到过一个长为16的常数串,其中后面8个字符经过处理(Xor 19)后得到:
42 9F BC 9A 9A 6B B8 67

用户名经过处理(Xor 19)后为:
65 7B 7C 7C 76 6B

还有花样,代码为:

001B:73482A3E mov bx,[eax*2+ebx]     ;eax=1,ebx=002089A2
;d eax*2+ebx
;002089A2 BC 00 9A 00 9A 00 6B 00 B8 00 67 00 42 00 9F 00

这不是后面8个字符处理后的结果吗?
但是顺序却不一样了。
但通过观察,可以发现现在的第一个字节BC是原来的倒数第6个字节,而6正好是用户名的长度。

后来发现,BC 与2E异或,后来又与65异或,
BC和65都知道是怎么得来的,但2E就不知道了,
后来发现这是常数,数据为:
001CF30A 2E 00 59 00 8E 00 3F 00-E7 00 20 00 81 00 33 00
         1C 00 61 00 F8 00 29 00-88 00 35 00 4E 00 A4 00
其实只用得上前面8个。

(BC xor 2E) xor 65=F7

得到得F7再减去上面得到得余数0125,然后进一步处理:
001B:734951FD pop eax           ;ax=0125
         51FE sub [esp],ax      ;[esp]=F7,F7-0125=FFD2
         5202 jo 73494062
......
         5345 cmp WORD PTR [esp],00
         534A jge 73495446
         543C NEG WORD PTR [esp]  ;取补,FFD2取反加1等于2E,发现没有,正好是注册码的前两个字符。
         5440 jo  73495446
         5446 xor eax,eax
         5448 mov al,[esi]
         544a INC esi
         544B jmp [eax*4+7348EA58]




后面还有验证,看此结果是否大于10。
001B:73496EA7 pop edx         ;edx=0010
         6EA8 pop ecx         ;ecx=2E
         6EA9 cmp cx,dx
         6EAC mov eax,0
         6EB1 SETL AL
         6EB4 NEG EAX
....


上面这段代码的功能我还不是很清除。
然后我就开始写注册机,通过比较各种不同长度用户名的注册码与注册机算出的注册码,
我知道后面我没有跟踪下去的代码的意思,
若取补后长度(不计前面的0)大于2,则取高两位;
若取补后的长度等于2,则就取这两位;
若取补后的长度为1,则在注册码前面补0。

另外,在写注册机时,

"长为16的常数串,其中后面8个字符经过处理(Xor 19)后得到:
42 9F BC 9A 9A 6B B8 67
"
从BC开始,即这样排列:BC 9A 9A 6B B8 67 42 9F 。
一开始,我没有将BC的位置与用户名长度联系起来,后来怀疑是用来除和的常数0200错了,
取不同长度用户名再次跟踪后发现,这的确是常数。
于是我将其看作一个循环链表,最终将其与用户名长度联系起来。
具体规则就是,如果用户名长度对8取模后为0,则从起始位置开始。
若长度对8取模后(n)大于0,则从倒数第n个位置开始。

具体算法可见注册机(VB 6编写,未优化)。

注册信息保存在注册表中。
我的:
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\CLSID\{28D51C00-5D2A-4CFC-0078-000000350000}]
@="09200511"
"A"="winndy"
"B"="2E6D-BDFD-FCF9-6279"

【破解总结】 SoftICE真不愧为SoftICE。算法比较复杂,由于是pcode语句,一小段代码往往只有一句真正有用的代码,
             所以容易跟丢,往往要不断地重复。
             下断点有技巧,不能乱下,要找准。
             在大致知道程序地意思后,可先尝试写出注册机,可辅助调试,象最后地那段代码,完全是猜出来的。
              有错之处,还望指教。
           
【Greetings】  看雪论坛,FCG论坛,DFCG论坛,PYG论坛等
               
【完稿时间等】2005.10.11,晚上18:19,天气:晴
              武汉

【VB注册机源码】
附件:vbideutils_src.rar

[培训]《安卓高级研修班(网课)》月薪三万计划

收藏
点赞7
打赏
分享
最新回复 (8)
雪    币: 227
活跃值: (370)
能力值: ( LV9,RANK:530 )
在线值:
发帖
回帖
粉丝
lnn1123 13 2005-10-11 19:50
2
0
雪    币: 443
活跃值: (200)
能力值: ( LV9,RANK:1140 )
在线值:
发帖
回帖
粉丝
冷血书生 28 2005-10-11 20:20
3
0
没坐上沙发!

板凳也好啊!

支持+学习!!
雪    币: 50
活跃值: (145)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
zhaoocn 7 2005-10-11 23:40
4
0
有毅力佩服!!!!!!!!!!!
雪    币: 367
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DarkNess0ut 2005-10-12 13:06
5
0
有些用户名得到的注册码不对哦:)
雪    币: 449
活跃值: (552)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
winndy 17 2005-10-12 13:22
6
0
最初由 DarkNess0ut 发布
有些用户名得到的注册码不对哦:)


谢谢黑老大指点
把不对的用户名拿出来
我再看看。
雪    币: 449
活跃值: (552)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
winndy 17 2005-10-13 17:47
7
0
最初由 DarkNess0ut 发布
有些用户名得到的注册码不对哦:)

老大,你说的是不是中文用户名啊?
这个我在注册机里确实没有处理。
以前破PYG的crackme时,刚开始作出的注册机也是不完美,不支持汉字,
后来加入了,才算通过PYG的关卡。
雪    币: 367
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DarkNess0ut 2005-10-13 18:35
8
0
比如我用DarkNess0ut,得到的注册码不能注册
汉字没有试过
雪    币: 449
活跃值: (552)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
winndy 17 2005-10-13 18:45
9
0
最初由 DarkNess0ut 发布
比如我用DarkNess0ut,得到的注册码不能注册
汉字没有试过

好,谢了
我有时间再看看
游客
登录 | 注册 方可回帖
返回