能力值:
( LV6,RANK:90 )
2 楼
破解对象:sunson Crackme(Crack-No.1.exe)
详见:本论坛【原创】本人写的第一个Crackme(http://bbs.pediy.com/showthread.php?s=&threadid=17508)
站内下载:http://bbs.pediy.com/upload/2005/37/files/crackme.rar_196.rar
相关信息:VB程序,没加壳
破解工具:Smartcheck 6.20, OllyDbg 1.10聆风听雨汉化第二版
------------------------------------------------------------------
这个sunson Crackme我10来天前也试过1个多钟,找不到要点。我想既然是他的第一个Crackme,我想难度应该可以作为我的练习吧,加上我自己对VB编程还有些经验和信心,于是决定从这个程序入手。
我破解这个程序的方法比较笨(既然是菜鸟,我多花点时间我也没话说),希望高手能够指点更好些的方法。
先用Smartcheck打开,F5运行,用户名键入hud,注册码键入123123,点击“注册”按钮。返回SmartCheck中查看Event,可以看到在Form_Load中,调入一个常数字串sunson,在Click事件中:
- 取得试练码及长度
- 取得用户名及长度
- 逐位和常数字串"POJE"比较
好!我们现在用Ollydbg打开,F9运行,输入同样的用户名hud和试练码123123,试验了几个常用断点都没成功,于是准备用笨办法:在OD中对“注册按钮”下消息断点(让大虾见笑了)!
在OD中选择察看窗口,在“注册”那一行点右键,选择“消息断点设置在ClassProc”,跳出消息选择对话框,在消息下拉框中,选择“202 WM_LBUTTONUP”,也就是在注册按钮上点击左键,当左键弹起时的消息上下断点。
能力值:
( LV6,RANK:90 )
3 楼
返回sunson Crackme。点击“注册”按钮,OD中断在下面地址:
7343FF3F > $ 5>push ebp ; 这里是ProcClass的入口
这里是消息处理函数,所以不要步入,否则掉进无穷无尽的循环中难以脱身。一路按F8步过,密切观察OD中央的信息窗口和右上的奇存器窗口,重点要关注以下几个字串:hud, 123123, POJE,我们输入的用户名、试练码以及SmartCheck侦查出来的对照字符串,然后来到这里(到这里多少都需要点耐心):
......
0040289F > \8>mov ecx,dword ptr ss:[ebp-20] ; 试练码出来了
004028A2 . 5>push ecx ; 下面取得试练码长度
004028A3 . F>call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaLenBstr
004028A9 . 8>mov edx,dword ptr ss:[ebp-1C] ; 用户名出来了
004028AC . 3>xor ebx,ebx
004028AE . 8>test eax,eax
004028B0 . 0>setle bl
004028B3 . 5>push edx
004028B4 . F>neg ebx ; 下面取得用户名长度
004028B6 . F>call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaLenBstr
上面代码意义不大,但我们通过一些蛛丝马迹可以知道:猎物已经不远了!继续按F8,来到这里:
00402937 . F>call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaStrMove
0040293D . 8>mov ecx,dword ptr ds:[esi]
0040293F . 8>lea edx,dword ptr ss:[ebp-19C]
00402945 . 8>lea eax,dword ptr ss:[ebp-20]
00402948 . 5>push edx
00402949 . 5>push eax
0040294A . 5>push esi
0040294B . F>call dword ptr ds:[ecx+6FC] ; Crack-No.00402453,这里F7进入
在这里先下一个断点,以便有需要时重新再试时断下,然后按F7步入Call内。
如果有和我水平差不多的初学者或许会问:为什么这里要步进呢?
注意右边有OD提示的这个Call的名称:Crack-No.00402453,这个名称是第一个以我们调试的程序名开始的,而上面其它的Call注释的名称都是以MSVBVM60、USER32之类开始的系统函数的调用。既然是用户程序的代码,我们当然要进去看看。
以下也是一样,前面可以先用F8全部走一遍摸摸大体的情况。然后回头再跟,如果是系统的Call,一般用F8步过,如果发现有异常(比如:hud, 123123, POJE等字串),看情况决定是否步入;如果是用户代码,一般要进去看看。
进入Call内部代码后,继续按F8,密切观察OD中央的信息窗口和右上的奇存器窗口,继续关注hud, 123123, POJE字串及其变形字串。
00403303 . >call dword ptr ds:[<&MSVBVM60.__>; MSVBVM60.__vbaStrVarMove
00403309 . >mov ebx,dword ptr ds:[<&MSVBVM60>; 这里字符串常数POJE出现在了eax中
继续按F8,下面就是关键的第一部分注册码验证了:
0040335B > >cmp esi,dword ptr ss:[ebp-104] ; 从1到4("POJE"长度)逐位循环比较,ESI是当前比较的位数
00403361 . >jg Crack-No.004034F0 ; 小于或等于4则继续
00403367 . >mov edi,dword ptr ss:[ebp+C]
0040336A . >mov eax,dword ptr ds:[edi] ; 载入用户名hud到eax
0040336C . >push eax
0040336D . >call dword ptr ds:[<&MSVBVM60.__vba>; MSVBVM60.__vbaLenBstr
00403373 . >mov ecx,eax ; 上面取得用户名长度到ecx
00403375 . >mov eax,esi ; eax=当前循环到的位
00403377 . >cdq
00403378 . >idiv ecx ; 除以用户名长度
0040337A . >mov dword ptr ss:[ebp-64],3
00403381 . >mov dword ptr ss:[ebp-4C],1
00403388 . >mov dword ptr ss:[ebp-54],2
0040338F . >mov dword ptr ss:[ebp-5C],edx
00403392 . >mov edx,dword ptr ds:[edi]
00403394 . >push edx
00403395 . >call dword ptr ds:[<&MSVBVM60.__vba>; MSVBVM60.__vbaLenBstr
0040339B . >mov ecx,eax
0040339D . >mov eax,esi
0040339F . >cdq
004033A0 . >idiv ecx ; 当前位除以用户名长度,取其余数
......
004033F7 . >call dword ptr ds:[<&MSVBVM60.__vba>; MSVBVM60.__vbaI4Var
004033FD . >mov edx,dword ptr ds:[edi] ; |将用户名(hud)放入edx
004033FF . >push eax ; |Arg2
00403400 . >push edx ; |Arg1
00403401 . >call dword ptr ds:[<&MSVBVM60.#631>>; \rtcMidCharBstr
00403407 . >mov edx,eax
00403409 . >lea ecx,dword ptr ss:[ebp-30]
0040340C . >call ebx
0040340E . >push eax ; /Arg1
0040340F . >call dword ptr ds:[<&MSVBVM60.#516>>; \从用户名中取出一位字符
00403415 . >mov ecx,dword ptr ss:[ebp-28] ; "POJE"放入eax
00403418 . >movsx edi,ax ; 将刚才取到的用户名字符放到edi
0040341B . >lea eax,dword ptr ss:[ebp-44]
0040341E . >push eax ; /Arg3
0040341F . >push esi ; |Arg2
00403420 . >push ecx ; |Arg1
00403421 . >call dword ptr ds:[<&MSVBVM60.#631>>; \rtcMidCharBstr
00403427 . >mov edx,eax
00403429 . >lea ecx,dword ptr ss:[ebp-2C]
0040342C . >call ebx
0040342E . >push eax ; /Arg1
0040342F . >call dword ptr ds:[<&MSVBVM60.#516>>; \从POJE中取一字符
00403435 . >movsx edx,ax ; 将POJE中取到的字符放到edx
00403438 . >lea eax,dword ptr ss:[ebp-30]
0040343B . >lea ecx,dword ptr ss:[ebp-2C]
0040343E . >push eax
0040343F . >push ecx
00403440 . >push 2
00403442 . >xor edi,edx ; 用户名中字符和POJE中逐字符XOR
00403444 . >call dword ptr ds:[<&MSVBVM60.__vba>; MSVBVM60.__vbaFreeStrList
0040344A . >lea edx,dword ptr ss:[ebp-84]
00403450 . >lea eax,dword ptr ss:[ebp-74]
00403453 . >push edx
00403454 . >lea ecx,dword ptr ss:[ebp-64]
00403457 . >push eax
00403458 . >push ecx
00403459 . >lea edx,dword ptr ss:[ebp-54]
0040345C . >lea eax,dword ptr ss:[ebp-C4]
00403462 . >push edx
00403463 . >lea ecx,dword ptr ss:[ebp-44]
00403466 . >push eax
00403467 . >push ecx
00403468 . >push 6
0040346A . >call dword ptr ds:[<&MSVBVM60.__vba>; MSVBVM60.__vbaFreeVarList
00403470 . >mov edx,dword ptr ss:[ebp-20]
00403473 . >mov eax,edi
00403475 . >mov dword ptr ss:[ebp-AC],edx
0040347B . >mov ecx,1A
00403480 . >cdq
00403481 . >idiv ecx ; XOR后的结果除以1A
00403483 . >add esp,28
00403486 . >mov dword ptr ss:[ebp-B4],8
00403490 . >add edx,41 ; edx(除后余数)+41
00403493 . >jo Crack-No.00403600
00403499 . >push edx ; /Arg2
0040349A . >lea edx,dword ptr ss:[ebp-44] ; |
0040349D . >push edx ; |Arg1
0040349E . >call dword ptr ds:[<&MSVBVM60.#608>>; \rtcVarBstrFromAnsi
004034A4 . >lea eax,dword ptr ss:[ebp-B4]
004034AA . >lea ecx,dword ptr ss:[ebp-44]
004034AD . >push eax ; /Arg3
004034AE . >lea edx,dword ptr ss:[ebp-54] ; |
004034B1 . >push ecx ; |Arg2
004034B2 . >push edx ; |Arg1
004034B3 . >call dword ptr ds:[<&MSVBVM60.__vba>; \__vbaVarCat
004034B9 . >push eax ; 将结果字符相连
004034BA . >call dword ptr ds:[<&MSVBVM60.__vba>; MSVBVM60.__vbaStrVarMove
004034C0 . >mov edx,eax ; edx是连接后的字符
004034C2 . >lea ecx,dword ptr ss:[ebp-20]
004034C5 . >call ebx
004034C7 . >lea eax,dword ptr ss:[ebp-54]
004034CA . >lea ecx,dword ptr ss:[ebp-44]
004034CD . >push eax
004034CE . >push ecx
004034CF . >push 2
004034D1 . >call dword ptr ds:[<&MSVBVM60.__vba>; MSVBVM60.__vbaFreeVarList
004034D7 . >mov eax,1
004034DC . >add esp,0C
004034DF . >add eax,esi
004034E1 . >jo Crack-No.00403600
004034E7 . >mov esi,eax
004034E9 . >xor edi,edi
004034EB .^>jmp Crack-No.0040335B ; 循环回去
上面一段的算法是:
1. 从1到4循环(设为i),用i除用户名长,商如果大于0就取商,否则取余数(以便应付用户名不足4位时的情况),设这里所取数为j;
2. 依次取POJE(i)和用户名(j)进行XOR,设结果为x;
3. x 除以1A后取余数,设为y;
4. y + 0x41就是前4位注册码的每位。
用户名为hud时前4位正确的注册码是EGIT,在这里设个断点,按F9,然后回到Crackme,将试练码改为EGIT,按F9一直到刚才设下的断点处,然后F8单步跳转到这里:
0040352E > >mov eax,dword ptr ss:[ebp-20] ; 这里是前面根据用户名算出来的字符串EGIT
00403531 . >mov ecx,dword ptr ss:[ebp-2C] ; 这是是输入的试练码
00403534 . 50 push eax
00403535 . 6A 04 push 4
00403537 . 51 push ecx
00403538 . FF15 >call dword ptr ds:[<&MSVBVM60.#616>; MSVBVM60.rtcLeftCharBstr
0040353E . 8BD0 mov edx,eax ; 上面取试练码前4位
00403540 . 8D4D >lea ecx,dword ptr ss:[ebp-30]
00403543 . FFD3 call ebx
00403545 . 50 push eax
00403546 . FF15 >call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaStrCmp
0040354C . F7D8 neg eax ; 上面比较前4位,如果相同则eax=0
0040354E . 1BC0 sbb eax,eax
00403550 . 8D55 >lea edx,dword ptr ss:[ebp-30]
00403553 . 40 inc eax
00403554 . 52 push edx
00403555 . F7D8 neg eax
00403557 . 8945 >mov dword ptr ss:[ebp-24],eax
0040355A . 8D45 >lea eax,dword ptr ss:[ebp-2C]
0040355D . 50 push eax
0040355E . 6A 02 push 2
00403560 . FF15 >call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaFreeStrList
00403566 . 83C4 >add esp,0C
00403569 . 8D4D >lea ecx,dword ptr ss:[ebp-34]
0040356C . FF15 >call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaFreeObj
00403572 . 68 D7>push Crack-No.004035D7
00403577 . EB 4D jmp short Crack-No.004035C6
从这里一直退出当前Call(第一部分比较),一路F8下去:
......
00402981 . 66:3B>cmp bx,di ; 如果前4位相等,这里为0
如果不相等时di=0000, bx=FFFF,就会跳过第二部分的验证,要爆破这里可以作为第一个爆破点。
00402984 . /0F85 >jnz Crack-No.00402D22 ; 这里不能跳,跳就over了
能力值:
( LV6,RANK:90 )
4 楼
一路F8下去:
.......
004029C5 > |8B45 >mov eax,dword ptr ss:[ebp-1C] ; 这里再次取出用户名,开始处理后面部分
004029C8 . 8D55 >lea edx,dword ptr ss:[ebp-38]
004029CB . 52 push edx ; /Arg3
004029CC . 6A 05 push 5 ; |Arg2 = 00000005,表示从第5位开始取
004029CE . 50 push eax ; |Arg1
004029CF . C745 >mov dword ptr ss:[ebp-30],80020004 ; |
004029D6 . C745 >mov dword ptr ss:[ebp-38],0A ; |
004029DD . 8D5E >lea ebx,dword ptr ds:[esi+38] ; |
004029E0 . FF15 >call dword ptr ds:[<&MSVBVM60.#631>; \rtcMidCharBstr
004029E6 . 8BD0 mov edx,eax ; 上面取出用户名第5位以后的字符
一路F8下去:
.......
00402A55 . FF15 >call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaHresultCheckObj
00402A5B > 8B55 >mov edx,dword ptr ss:[ebp-1C] ; 这里取出试练码
00402A5E . 8D4D >lea ecx,dword ptr ss:[ebp-38]
00402A61 . 51 push ecx ; /Arg3
00402A62 . 6A 05 push 5 ; |Arg2 = 00000005
00402A64 . 52 push edx ; |Arg1
00402A65 . C745 >mov dword ptr ss:[ebp-30],80020004 ; |
00402A6C . C745 >mov dword ptr ss:[ebp-38],0A ; |
00402A73 . FF15 >call dword ptr ds:[<&MSVBVM60.#631>; \rtcMidCharBstr
00402A79 . 8BD0 mov edx,eax ; 上面将第5位度练码以后部分取出
一路F8下去:
......
00402AB0 . FF91 >call dword ptr ds:[ecx+700] ; Crack-No.00402460,这里要F7步入
进去后:
0040373B . FF15 >call dword ptr ds:[<&MSVBVM60.__vb> ; 这里出现一串字符zfzhui
00403741 . 8BD0 mov edx,eax
......
0040378F . 6A 0B push 0B
00403791 . FF15 >call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaFreeVarList
00403797 . 83C4 >add esp,30
0040379A . 6A 01 push 1
0040379C . FF15 >call dword ptr ds:[<&MSVBVM60.__vb>; MSVBVM60.__vbaOnError
004037A2 . 8B75 >mov esi,dword ptr ss:[ebp+10]
004037A5 . 8B0E mov ecx,dword ptr ds:[esi]
004037A7 . 51 push ecx
004037A8 . 8B1D >mov ebx,dword ptr ds:[<&MSVBVM60._>; MSVBVM60.__vbaLenBstr
004037AE . FFD3 call ebx ; <&MSVBVM60.__vbaLenBstr>
004037B0 . 83F8 >cmp eax,4 ; 上面得到第5位后试练码的长度,和4比较
004037B3 . /0F8C >jl Crack-No.00403C50 ; 小于4位则跳,好像不能跳,一跳就结束了
在这里先下个断点,以便等下回来。如果小于4位就到下面:
00403C50 > \6>push 0 ; /Arg1 = 00000000
00403C52 . F>call dword ptr ds:[<&MSVBVM60.__v>; \__vbaStrBool
00403C58 . 8>mov edx,eax ; 这里将Bool转为字符,如果是False就Over了
所以刚才的试练码不行,除前4位(EGIT)的后面至少还要4位,所以将试练码改为EGIT1234(用1234方便跟踪位置)。为了说明简便,下面将用户名第5位以后部分简称"后用户名",将试练(注册)码第5位以后部分简称"后试练(注册)码"(有些别扭,请原谅)。
按F9一直到上边断点(004037B3)处,然后F8单步:
004037B9 . 6>push -1 ; /Arg1 = FFFFFFFF
004037BB . F>call dword ptr ds:[<&MSVBVM60.__v>; \__vbaStrBool
004037C1 . 8>mov edx,eax ; 先载入-1,化为字符"True"
004037C3 . 8>lea ecx,dword ptr ss:[ebp-28]
004037C6 . F>call edi
004037C8 . 8>mov edx,dword ptr ds:[esi]
004037CA . 5>push edx ; 1234,后试练码
004037CB . F>call ebx
004037CD . 9>cdq
004037CE . 2>sub eax,edx
004037D0 . D>sar eax,1
004037D2 . 8>mov dword ptr ss:[ebp-168],eax
004037D8 . B>mov esi,1
004037DD > 3>cmp esi,eax ; 循环位数大过"后试练码"就停止循环
004037DF . 0>jg Crack-No.00403C63
004037E5 . 8>mov eax,dword ptr ss:[ebp+8]
004037E8 . 8>mov ecx,dword ptr ds:[eax+38] ; 取出(第5位)后用户名
004037EB . 5>push ecx
004037EC . F>call ebx ; 取得后用户名位长
004037EE . 8>test eax,eax
004037F0 . C>mov dword ptr ss:[ebp-50],1
004037F7 . C>mov dword ptr ss:[ebp-58],2
004037FE . 8>lea edx,dword ptr ss:[ebp-58]
00403801 . 5>push edx ; /Arg3
00403802 . 8>mov eax,esi ; |
00403804 . 0>jg Crack-No.00403A1D ; |
0040380A . 6>imul eax,eax,2 ; |eax*2
0040380D . 0>jo Crack-No.00403D1E ; |
00403813 . 8>sub eax,1 ; |eax -= 1;
00403816 . 0>jo Crack-No.00403D1E ; |
0040381C . 5>push eax ; |Arg2
0040381D . 8>mov ecx,dword ptr ss:[ebp+10] ; |
00403820 . 8>mov edx,dword ptr ds:[ecx] ; |
00403822 . 5>push edx ; |Arg1
00403823 . F>call dword ptr ds:[<&MSVBVM60.#63>; \rtcMidCharBstr
00403829 . 8>mov edx,eax
0040382B . 8>lea ecx,dword ptr ss:[ebp-34]
0040382E . F>call edi
00403830 . 5>push eax
00403831 . F>call dword ptr ds:[<&MSVBVM60.#58>; \
00403837 . D>fstp qword ptr ss:[ebp-160] ; 取后试练码循环位字符,转化为浮点
0040383D . 8>mov eax,dword ptr ss:[ebp-24] ; 载入常数字串"zfzhui"
如果用户名第5位到第10位(共6位)不足6位的部分就用常数字串"zfzhui"相应位填充,用户名固定为10位长。
00403840 . 5>push eax
00403841 . F>call ebx ; "zfzhui"长度=6
00403843 . 8>mov ecx,eax
F8单步......
004038B6 . B>mov eax,1 ; 这里eax先置为1(True)
004038BB . 8>mov dword ptr ss:[ebp-40], eax ; Bool = True
通过前面00403C58、004037C1类似情况,我们可以推测,堆栈中应该是VB中的一个Boolean变量,在注册码各个位长的判断中,如果不符合条件,就会设为False,也就game over了,有编程经验的朋友应该都容易了解。这里先设置为True。
004038BE . B>mov ecx,2
004038C3 . 8>mov dword ptr ss:[ebp-48],ecx
004038C6 . 8>mov dword ptr ss:[ebp-90],eax
004038CC . 8>mov dword ptr ss:[ebp-98],ecx
004038D2 . 8>lea ecx,dword ptr ss:[ebp-48]
004038D5 . 5>push ecx ; /Arg3
004038D6 . 8>mov edx,esi ; |esi为循环变量,移入edx
004038D8 . 6>imul edx,edx,2 ; |edx*2, edx循环位,这是在"后试练码"中取字符的位置
004038DB . 0>jo Crack-No.00403D1E ; |
004038E1 . 5>push edx ; |Arg2
004038E2 . 8>mov eax,dword ptr ss:[ebp+10] ; |
004038E5 . 8>mov ecx,dword ptr ds:[eax] ; |后试练码
004038E7 . 5>push ecx ; |Arg1
004038E8 . F>call dword ptr ds:[<&MSVBVM60.#63>; \rtcMidCharBstr
004038EE . 8>mov edx,eax
004038F0 . 8>lea ecx,dword ptr ss:[ebp-30]
004038F3 . F>call edi
004038F5 . 5>push eax ; /Arg1
004038F6 . F>call dword ptr ds:[<&MSVBVM60.#51>; \rtcAnsiValueBstr,取“后试练码”的循环位+1位
004038FC . 6>sub ax,41 ; 减41
取“后试练码”的循环位+1位ASCII码值,然后减0x41。
00403900 . /0>jo Crack-No.00403D1E
00403906 . |0>movsx edx,ax
00403909 . |8>mov dword ptr ss:[ebp-17C],edx
0040390F . |D>fild dword ptr ss:[ebp-17C] ; “后试练码”的(循环位+1)位 - 0x41 的结果
00403915 . |D>fstp qword ptr ss:[ebp-184]
0040391B . |D>fld qword ptr ss:[ebp-160] ; 循环位,这里决定要乘几个26
00403921 . |D>fmul qword ptr ds:[401160] ; 循环位*26.
00403927 . |D>fadd qword ptr ss:[ebp-184] ; -15. + 循环位*26. = 11.
0040392D . |D>fstsw ax
0040392F . |A>test al,0D
00403931 . |0>jnz Crack-No.00403D19
00403937 . |F>call dword ptr ds:[<&MSVBVM60.__v>; MSVBVM60.__vbaFpI4
0040393D . |8>mov ebx,eax ; 化为整数
0040393F . |8>lea eax,dword ptr ss:[ebp-98]
00403945 . |5>push eax
00403946 . |8>lea ecx,dword ptr ss:[ebp-88]
0040394C . |5>push ecx
0040394D . |F>call dword ptr ds:[<&MSVBVM60.__v>; MSVBVM60.__vbaI4Var
00403953 . |5>push eax ; |Arg2
00403954 . |8>mov edx,dword ptr ss:[ebp-24] ; |
00403957 . |5>push edx ; |"zfzhui"入栈
00403958 . |F>call dword ptr ds:[<&MSVBVM60.#63>; \rtcMidCharBstr
0040395E . |8>mov edx,eax
00403960 . |8>lea ecx,dword ptr ss:[ebp-38]
00403963 . |F>call edi
00403965 . |5>push eax ; /Arg1
00403966 . |F>call dword ptr ds:[<&MSVBVM60.#51>; \rtcAnsiValueBstr
0040396C . |0>movsx eax,ax ; 取zfzhui的第一位z
0040396F . |3>xor ebx,eax ; 7A('z') XOR 0B = 71('q')
这一段取从1到"后试练码"长度循环,设循环位为i,i * 26 + "后试练码"[i+1]位 - 0x41,与"后用户名"[i]进行XOR,得到结果字符Target string。
继续F8 ......
004039FF . 8>mov eax,dword ptr ds:[edx+34] ; 载入常数字串"sunson"
00403A02 . 5>push eax
00403A03 . 8>mov ecx,dword ptr ss:[ebp-2C]
00403A06 . 5>push ecx
00403A07 . 6>push 0
00403A09 . F>call dword ptr ds:[<&MSVBVM60.__v>; MSVBVM60.__vbaInStr
00403A0F . 8>cmp eax,1 ; 上面函数看Target string是否是"sunson"前面相应位
00403A12 . 0>jnz Crack-No.00403C50 ; 这里eax要=1,否则跳转就over了
我们可以再次用SmartCheck打开Crackme No.1,输入hud, EGIT1234,F5运行,然后点击Event中的Click,可以看到下面的反编译:
Double(11)--> Long(11) ; CLong
Mid$(String str = 00153A4C
= "zfzhui"
Long start = 1 0x00000001
ASC returns integer: 122
String string = 0014DB3C
= "z"
Chr
Integer charcode = 113 0x0071
Instr returns LONG: 0
Long start = 1 0x00000001
String string1 = 0014DB14
= "sunson"
String string2 = 0014DAD4
= "q"
Integer compare = 0 0x0000
Boolean(False)--> String("False")
String("False")--> Boolean(False)
上面这一段SmartCheck的反编译代码应该很清楚,和上面是一致的,可以帮助我们更加清楚地了解上面代码。
先在上面(00403A12这里)下个断点,等会还要回来。F8继续就跳转了:
00403C50 > \6>push 0 ; /Arg1 = 00000000
00403C52 . F>call dword ptr ds:[<&MSVBVM60.__v>; \__vbaStrBool
00403C58 . 8>mov edx,eax ; 这里将Bool转为字符,如果是False就Over了
上面一段判断计算出来的'q'在不是字串'sunson'第1个字母,翻译成VB,应该类似于:
str = "sq"
If Instr("sunson", str)<>1 Then
Bool = True
Continue!
Else
Bool = False
Game over!
End If
第二部分的算法到这里已经明了:
1. 用户名Name共10位后6位不足的用"zfzhui"填充;
2. 注册码最少8位;
3. 循环变量(设为 i )从1循环到(注册码长度-4);
4. 每次循环取2位,前面1位化为浮点,后面一位是取"后注册码"字符的位置;
5. SNo[i+1] - 0x41 + i * 0x26,设为x;
5. Name[i] XOR x, 设为y;
6. 如果y[1]y[2]y[x]等于"sunson"前x位就继续,否则over。
后面我们做注册机时就取最小位数4位。
能力值:
( LV6,RANK:90 )
5 楼
下面是算法总结及C语言注册机: /*********************************************************************
sunson's Crackme No.1 C语言注册机 (Win-TC)
By hud, November 12, 2005
-----------------------------------------------------------------------
算法:
前4位算法是:
1. 从1到4循环(设为i);
2. 用i除用户名长;
3. 商如果大于0就取商,否则取余数(如果用户名小于4位),设这个数为j;
4. 依次取POJE(i)和用户名(j)进行XOR,设结果为x;
3. x 除以1A后取余数,设为y;
4. 依次得到的y+41相连即是前4位注册码,设为Z。
后6位算法是
第二部分的算法到这里已经明了:
1. 用户名Name共10位后6位不足的用zfzhui填充;
2. 注册码最少8位;
3. 变量(设为 i )从1循环到(注册码长度-4);
4. 每次循环取2位,前面1位化为浮点,后面一位是取"后注册码的位数";
5. SNo[i+1] - 0x41 + i * 0x26,设为x;
5. (后6位)Name[i/2] XOR x, 设为y;
6. 如果各位相连的y在"sunson"中,继续,否则over。
后面我们就取最小位数4位。
*********************************************************************/
#include <stdio.h>
void get_num(char sn[9], int i, char name)
{
char c, s1[] = "sunson";
int k=33, pos, x; /* k从显示字符开始 */
for (pos=0; pos<10; ++pos) /* pos确定乘几个26 */
{
while (k<127)
{
x = k - 0x41 + pos * 26;
c = x ^ name;
if (s1[i/2]==c)
{
sn[4+i] = pos + 0x30;
sn[4+i+1] = k;
return;
}
else
++k;
}
}
}
void main()
{
char n1[11], sn1[9]; /* 用户名,注册码 */
char n2[7]; /* 用户名后6位 */
char s1[] = "POJE",
s3[] = "zfzhui"; /* 几个常数 */
int len, pos;
int i;
printf("Input name:\n");
scanf("%s", n1);
len = strlen(n1);
if (len==0)
return;
for (i=0; i<4; ++i) /* 前4位判断 */
{
pos = i/(len-1) > 0 ? i/(len-1)-1 : i%len;
sn1[i] = (n1[pos] ^ s1[i]) % 0x1A + 0x41;
}
for (i=0; i<6; ++i) /* 如不足10位填充s3 */
if (i+4>=len)
n2[i] = s3[i];
else
n2[i] = n1[i+4];
n2[i] = '\0';
for (i=0; i<4; ++i) /* 后面最小4位判断 */
{
get_num(sn1, i, n2[i/2]);
++i;
}
sn1[8] = '\0';
printf("SN is: %s\n", sn1);
getch();
} ???????????????????????????????????????? 终于破解完成并且写完破文,好累!
也许有初学者想问问,我总共花了多少时间(嗨,真是那壶不开提那壶)?
不过,不多,5个晚上,每晚5个钟左右!(晕倒,这还不多)
比起一些高手来,十几分钟破解一个软件的算法,那是太多了!不过我是在菜鸟阶段,我知道不比别人多花些时间是很难进步的,我原来准备用一个月的时间攻下来,还好,只用了6分之一,对我来说自然不多。
想到像我这样的初学者,破解一个软件如此辛苦,我尽量详细一些把这个过程写下来,如果对部分初学者能有所帮助,我也没算白熬几个晚上了。
还有很多地方不尽人意的地方,比如开始部分,下消息断点效率实在太低,按了好长时间的F8也没动静,又怕有什么重要的地方漏掉了。中间也是笨办法,一步步地走,应该有更有效率的方法吧。如果朋友们有对付VB程序的好办法,请多多指教。
还有个感觉就是:汇编基础偏弱,很多地方似是而非,浪费了不少时间。
另外,用户名如果超出10位,注册码超出8位等情况没去验证。
能力值:
( LV2,RANK:10 )
6 楼
顶下慢慢看..........
能力值:
( LV9,RANK:530 )
7 楼
能力值:
(RANK:410 )
8 楼
不错,写得很详细。
能力值:
( LV4,RANK:50 )
9 楼
不错,写得很详细。非常适合俺等菜鸟学习
能力值:
( LV2,RANK:10 )
10 楼
好好学习。
楼主让我感动,奋战了5个晚上
想我弄个vb cracke me才三个钟头就没信心了,惭愧~~~
能力值:
( LV6,RANK:90 )
11 楼
多谢朋友们的支持!既然有朋友们的鼓励,我正准备再写个续篇出来。
能力值:
( LV2,RANK:10 )
12 楼
请问LZ,他的CRACK NO 2你研究出来了吗?比这个难啊
能力值:
( LV2,RANK:10 )
13 楼
最初由 hud 发布 多谢朋友们的支持!既然有朋友们的鼓励,我正准备再写个续篇出来。
双手双脚支持
能力值:
( LV2,RANK:10 )
14 楼
算法: 前4位算法是: 1. 从1到4循环(设为i); 2. 用i除用户名长; 3. 商如果大于0就取商,否则取余数(如果用户名小于4位),设这个数为j; 4. 依次取POJE(i)和用户名(j)进行XOR,设结果为x; 3. x 除以1A后取余数,设为y; 4. 依次得到的y+41相连即是前4位注册码,设为Z。 后6位算法是 第二部分的算法到这里已经明了: 1. 用户名Name共10位后6位不足的用zfzhui填充; 2. 注册码最少8位; 3. 变量(设为 i )从1循环到(注册码长度-4); 4. 每次循环取2位,前面1位化为浮点,后面一位是取"后注册码的位数"; 5. SNo[i+1] - 0x41 + i * 0x26,设为x; 5. (后6位)Name[i/2] XOR x, 设为y; 6. 如果各位相连的y在"sunson"中,继续,否则over。 后面我们就取最小位数4位。 *********************************************************************/
总的来说分析的很不错,只是第二部分存在一点点错误
第一,用户名没有长度限制
第二,如果不足四位用户名,不是用zfzhui填充,而是用sunson填充,与zfzhui异或
第三,如果用户名大于四位,则是与sunson异或,得到的结果与用户名四位以后的字符进行比较
能力值:
( LV6,RANK:90 )
15 楼
谢谢sunson的点评!
因为是我的第1个VB破解练习,又花了不少时间,当时知道还有些地方还没有验证完毕,也没去进一步深究。
这两天太忙,有时间我修正一下。再次感谢你,给我一个很好的Crackme练习!
能力值:
( LV6,RANK:90 )
16 楼
现在做一些修改,将上次的错误修正并将上次没判断的情况补上,还有上次的注册机也有些问题,一并改过。
断点修改一下,下到vbaLenBStr,再输入1位Name和试练码(根据前面的注册机做):
Name: h
Serial: ENIT0J0T
断在下面:
004028A3 . F>call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>; MSVBVM60.__vbaLenBstr
发现寄存器中已经有试练码,改换到前面2句下断就行了:
0040289F > \8>mov ecx,dword ptr ss:[ebp-20] ; 试练码出来了
004028A2 . 5>push ecx ; 下面取得试练码长度
004028A3 . F>call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>; MSVBVM60.__vbaLenBstr
004028A9 . 8>mov edx,dword ptr ss:[ebp-1C]
后面的和前面分析差不多,只是Name长度是小于5和5位以上有不同的判断,一并在下面的算法和注册机中一次性改过。
/*********************************************************************
sunson's Crackme No.1 C语言注册机 (Win-TC)
By hud, November 12, 2005 (Rev-A)
By hud, November 15, 2005 (Rev-B)
-----------------------------------------------------------------------
算法:
前4位算法是:
1. 从1到4循环(设为i);
2. 如果Name只有1位,1到4的循环都取这1位;
3. 如果Name多于1位,取 i Mod len位。设3、4两步取的位为j;
4. POJE(i) XOR Name(j),设结果为x;
3. x Mod 1A,设为y;
4. 依次得到的y+41相连即是前4位注册码,设为Z。
后6位算法是(用户名Name小于5位情况):
1. 用户名至少1位,Name小于5位的后6位用"sunson"填充;
2. 注册码后面部分长度最少4位,多的不计;
3. 变量(设为 i )从1循环到4;
4. 每次循环注册码后面部分取2位,前面1位化为浮点,后面一位化为ASCII值;
5. SNo[i+1] - 0x41 + i * 0x26,设为x;
5. 如果Name小于5位,x和"zfzhui"前2位XOR, 5位以上则和"sunson"前2位XOR。
设为y;
6. 如果Name小于5位,[y1][y2] 应是"su"; Name5位以上[y1][y2]则应是"on";
假如不是作练习,这个算法有些缺陷:
1. 用户名实际只取前3位,在小于5位的情况下,hud0, hud1, hud...z是一样
的;
2. 所有大于4位的用户名,也是实际检测前4位,只不过后面用sonsun填充,
也就是说,hud01, hud02, hud03..., hud0abcde...,只要前4位一样,注册
码全是一样的。
*********************************************************************/
#include <stdio.h>
int len;
void get_num(char sn[9], int i, char name)
{
char c, s1[] = "zf", s2[]="on";
int k=33, pos, x; /* k从显示字符开始 */
for (pos=0; pos<10; ++pos) /* pos确定乘几个26 */
{
while (k<127)
{
x = k - 0x41 + pos * 26;
c = x ^ name;
if ((len<5 && s1[i/2]==c) |
(len>4 && s2[i/2]==c))
{
sn[4+i] = pos + 0x30;
sn[4+i+1] = k;
return;
}
else
++k;
}
}
}
void main()
{
char n1[11], sn1[9]; /* 用户名,注册码 */
char n2[7]; /* 用户名后6位 */
char s1[] = "POJE",
s2[] = "sunson"; /* 几个常数 */
int pos;
int i;
printf("Input name:\n");
scanf("%s", n1);
len = strlen(n1);
if (len==0)
return;
for (i=0; i<4; ++i) /* 前4位判断 */
{
if (len==1) /* 之前没加这句,len=1时要出错 */
pos = 0; /* 固定取第1位 */
else
pos = i % ( len - 1 );
sn1[i] = (n1[pos] ^ s1[i]) % 0x1A + 0x41;
}
/* 前4位已经确定,下面计算后面4位 */
for (i=0; i<6; ++i) /* 如不足10位填充s2 */
if (i+4>=len)
n2[i] = s2[i];
else
n2[i] = n1[i+4];
n2[i] = '\0';
for (i=0; i<4; ++i) /* 后面最小4位判断 */
{
if (len<5) /* 根据位长作不同选择 */
get_num(sn1, i, n2[i/2]);
else
get_num(sn1, i, s2[i/2]);
++i;
}
sn1[8] = '\0';
printf("SN is: %s\n", sn1);
getch();
}
附C语言注册机:
附件:keygen.zip
能力值:
( LV2,RANK:10 )
17 楼
楼主是我们学习的榜样。
能力值:
( LV2,RANK:10 )
18 楼
最初由 hud 发布 返回sunson Crackme。点击“注册”按钮,OD中断在下面地址: 7343FF3F > $ 5>push ebp ; 这里是ProcClass的入口 ........
我有一事不明,我按照楼主的方法,中断是在
6605F626 [> 55 push ebp
不解,请问为什么?
6605F626 是系统的地址吗?
能力值:
( LV6,RANK:90 )
19 楼
不会啊,因为你提到的地址在程序中应该没有分配。
你再看看,是不是消息选得不对?应该是"202 WM_LBUTTONUP"。在选择消息时,左下角有个选项,将它勾上,再到消息框里去找可以按消息的字母顺序查找,就会方便些了。
另外,照我在16楼更新的,用__vbaStrCopy下断更好些。
能力值:
( LV2,RANK:10 )
20 楼
我试了很多次了,还是 6605F626 ,郁闷了
能力值:
( LV2,RANK:10 )
21 楼
谢谢指点
在本论坛发表留言
能力值:
( LV6,RANK:90 )
22 楼
能力值:
( LV2,RANK:10 )
23 楼
谢谢热心的hud
可能是其它的什么原因吧
代码都是一样的(除了地址),
6605F626 W> 55 push ebp
6605F627 8BEC mov ebp,esp
6605F629 83EC 30 sub esp,30
6605F62C 53 push ebx
6605F62D 8B5D 08 mov ebx,dword ptr ss:[ebp+8]
6605F630 56 push esi
6605F631 57 push edi
6605F632 53 push ebx
6605F633 E8 CCBCF>call MSVBVM60.6600B304
6605F638 8B7D 0C mov edi,dword ptr ss:[ebp+C]
6605F63B 8BF0 mov esi,eax
6605F63D 85F6 test esi,esi
6605F63F 75 40 jnz short MSVBVM60.6605F681
6605F641 83FF 24 cmp edi,24
能力值:
( LV6,RANK:90 )
24 楼
哦,会不会是系统不同呢?
我只在XP下试过,可能其它系统地址会不同吧。
能力值:
( LV13,RANK:330 )
25 楼
不错,写得很详细,向楼主学习。。。