【破解作者】大菜一号
【破解对象】一个加了壳的CrackMe
【破解目的】1、新手稍微接确一下手动脱壳;
2、算法的分析
【破解声明】
声声声声明呀``~~这个CrackMe早就有人破过了``偶也是从附件上下下来的`不过可没看一句答案`````是一句也没看呀!不相信滴下面部分就别看了`早点跳出循环吧!~汗~~~`
这个CrackMe呢加了一个很垃圾的UPX壳`所以这里我们就不用工具`就稍微加强一些手脱脱壳的技术(也到时候该勤快些哩呀``不然以后吃啥?)
参考了二哥的脱壳教程(感谢二哥~)
手动脱壳时,用Olldbg载入程序,脱壳程序里面会有有好多循环。对付循环时,只能让程序往前运行,基本不能让它往回跳,要想法跳出循环圈(也就是知道一个跳转要往回跳了,就在它下一条码处点一下,F4执行到下一句,不要让它往回跳)。不要用Peid查入口,单步跟踪,提高手动找入口能力。程序加壳段地址 应该都会有跨段跳跃,一般都是在内存中脱壳完毕,准备运行程序,手动脱壳就是要在入口将程序Dump出来。转到入口时一般都有Popad语句和开始的PUSHAD对应。我们再这里用Od的Dump插件直接脱壳。好,手动脱壳说明就到这~
下面是手动脱壳的文本过程`不懂有个动画,不过很粗糙,要了解就将就着看吧``动画在这里也声明一下:动画过程只简短加注释,详细的下面会讲到`结合一下就行了!~
程序用OD载入后提示有壳,选否不再继续分析,我们停在这里:
00406CC0 > $ 60 pushad //这个pushad,前面说了`后面会有个popad和它对应,那就要密切关注一下这个popad了
00406CC1 . BE 00604000 mov esi, 00406000
00406CC6 . 8DBE 00B0FFFF lea edi, dword ptr [esi+FFFFB000]
00406CCC . 57 push edi
00406CCD . 83CD FF or ebp, FFFFFFFF
00406CD0 . EB 10 jmp short 00406CE2
00406CD2 90 nop
00406CD3 90 nop
00406CD4 90 nop
00406CD5 90 nop
00406CD6 90 nop
00406CD7 90 nop
00406CD8 > 8A06 mov al, byte ptr [esi]
00406CDA > 46 inc esi
00406CDB . 8807 mov byte ptr [edi], al
00406CDD . 47 inc edi
00406CDE > 01DB add ebx, ebx
00406CE0 . 75 07 jnz short 00406CE9
00406CE2 > 8B1E mov ebx, dword ptr [esi]
00406CE4 . 83EE FC sub esi, -4
00406CE7 . 11DB adc ebx, ebx
00406CE9 >^ 72 ED jb short 00406CD8 //这个要往回跳了``我们就在下面点一下,F4到下一代码
00406CEB . B8 01000000 mov eax, 1
00406CF0 > 01DB add ebx, ebx
00406CF2 . 75 07 jnz short 00406CFB
00406CF4 . 8B1E mov ebx, dword ptr [esi]
00406CF6 . 83EE FC sub esi, -4
00406CF9 . 11DB adc ebx, ebx
00406CFB > 11C0 adc eax, eax
00406CFD . 01DB add ebx, ebx
00406CFF .^ 73 EF jnb short 00406CF0
00406D01 . 75 09 jnz short 00406D0C
00406D03 . 8B1E mov ebx, dword ptr [esi]
00406D05 . 83EE FC sub esi, -4
00406D08 . 11DB adc ebx, ebx
00406D0A .^ 73 E4 jnb short 00406CF0
00406D0C > 31C9 xor ecx, ecx
00406D0E . 83E8 03 sub eax, 3
00406D11 . 72 0D jb short 00406D20
00406D13 . C1E0 08 shl eax, 8
00406D16 . 8A06 mov al, byte ptr [esi]
00406D18 . 46 inc esi
00406D19 . 83F0 FF xor eax, FFFFFFFF
00406D1C . 74 74 je short 00406D92
00406D1E . 89C5 mov ebp, eax
00406D20 > 01DB add ebx, ebx
00406D22 . 75 07 jnz short 00406D2B
00406D24 . 8B1E mov ebx, dword ptr [esi]
00406D26 . 83EE FC sub esi, -4
00406D29 . 11DB adc ebx, ebx
00406D2B > 11C9 adc ecx, ecx
00406D2D . 01DB add ebx, ebx
00406D2F . 75 07 jnz short 00406D38
00406D31 . 8B1E mov ebx, dword ptr [esi]
00406D33 . 83EE FC sub esi, -4
00406D36 . 11DB adc ebx, ebx
00406D38 > 11C9 adc ecx, ecx
00406D3A . 75 20 jnz short 00406D5C
00406D3C . 41 inc ecx
00406D3D > 01DB add ebx, ebx
00406D3F . 75 07 jnz short 00406D48
00406D41 . 8B1E mov ebx, dword ptr [esi]
00406D43 . 83EE FC sub esi, -4
00406D46 . 11DB adc ebx, ebx
00406D48 > 11C9 adc ecx, ecx
00406D4A . 01DB add ebx, ebx
00406D4C .^ 73 EF jnb short 00406D3D //这个要跳了`F4到下面
00406D4E . 75 09 jnz short 00406D59
00406D50 . 8B1E mov ebx, dword ptr [esi]
00406D52 . 83EE FC sub esi, -4
00406D55 . 11DB adc ebx, ebx
00406D57 .^ 73 E4 jnb short 00406D3D
00406D59 > 83C1 02 add ecx, 2
00406D5C > 81FD 00F3FFFF cmp ebp, -0D00
00406D62 . 83D1 01 adc ecx, 1
00406D65 . 8D142F lea edx, dword ptr [edi+ebp]
00406D68 . 83FD FC cmp ebp, -4
00406D6B . 76 0F jbe short 00406D7C
00406D6D > 8A02 mov al, byte ptr [edx]
00406D6F . 42 inc edx
00406D70 . 8807 mov byte ptr [edi], al
00406D72 . 47 inc edi
00406D73 . 49 dec ecx
00406D74 .^ 75 F7 jnz short 00406D6D //看这`有两个跳转呢!没办法了`只好让它往回跳了`为啥?有人会说我们可以F4到下面,要往回跳再F4再到下面`这样行么`?你们试试看就知道`程序马上就运行起来了~不过可以发现00406d6b那个跳转可以跳过这里`所以就让它往回跳吧`待某一时刻就可以跳过这里了,F4到下面那个无条件跳转,让它跳
00406D76 .^ E9 63FFFFFF jmp 00406CDE //这个
00406D7B 90 nop
00406D7C > 8B02 mov eax, dword ptr [edx] //跳过之后会在这
00406D7E . 83C2 04 add edx, 4
00406D81 . 8907 mov dword ptr [edi], eax
00406D83 . 83C7 04 add edi, 4
00406D86 . 83E9 04 sub ecx, 4
00406D89 .^ 77 F1 ja short 00406D7C //这个要跳,F4到下面
00406D8B . 01CF add edi, ecx
00406D8D .^ E9 4CFFFFFF jmp 00406CDE //这个也要跳`F4再到下面
00406D92 > 5E pop esi
00406D93 . 89F7 mov edi, esi
00406D95 . B9 64000000 mov ecx, 64
00406D9A > 8A07 mov al, byte ptr [edi]
00406D9C . 47 inc edi
00406D9D . 2C E8 sub al, 0E8
00406D9F > 3C 01 cmp al, 1
00406DA1 .^ 77 F7 ja short 00406D9A //要跳了`再F4
00406DA3 . 803F 01 cmp byte ptr [edi], 1
00406DA6 .^ 75 F2 jnz short 00406D9A
00406DA8 . 8B07 mov eax, dword ptr [edi]
00406DAA . 8A5F 04 mov bl, byte ptr [edi+4]
00406DAD . 66:C1E8 08 shr ax, 8
00406DB1 . C1C0 10 rol eax, 10
00406DB4 . 86C4 xchg ah, al
00406DB6 . 29F8 sub eax, edi
00406DB8 . 80EB E8 sub bl, 0E8
00406DBB . 01F0 add eax, esi
00406DBD . 8907 mov dword ptr [edi], eax
00406DBF . 83C7 05 add edi, 5
00406DC2 . 89D8 mov eax, ebx
00406DC4 .^ E2 D9 loopd short 00406D9F //这是个循环`F4跳到下面去
00406DC6 . 8DBE 00400000 lea edi, dword ptr [esi+4000]
00406DCC > 8B07 mov eax, dword ptr [edi]
00406DCE . 09C0 or eax, eax
00406DD0 . 74 45 je short 00406E17
00406DD2 . 8B5F 04 mov ebx, dword ptr [edi+4]
00406DD5 . 8D8430 086900>lea eax, dword ptr [eax+esi+6908]
00406DDC . 01F3 add ebx, esi
00406DDE . 50 push eax
00406DDF . 83C7 08 add edi, 8
00406DE2 . FF96 6C690000 call dword ptr [esi+696C]
00406DE8 . 95 xchg eax, ebp
00406DE9 > 8A07 mov al, byte ptr [edi]
00406DEB . 47 inc edi
00406DEC . 08C0 or al, al
00406DEE .^ 74 DC je short 00406DCC
00406DF0 . 89F9 mov ecx, edi
00406DF2 . 79 07 jns short 00406DFB
00406DF4 . 0FB707 movzx eax, word ptr [edi]
00406DF7 . 47 inc edi
00406DF8 . 50 push eax
00406DF9 . 47 inc edi
00406DFA B9 db B9
00406DFB . 57 push edi
00406DFC . 48 dec eax
00406DFD . F2:AE repne scas byte ptr es:[edi]
00406DFF . 55 push ebp
00406E00 . FF96 70690000 call dword ptr [esi+6970]
00406E06 . 09C0 or eax, eax
00406E08 . 74 07 je short 00406E11
00406E0A . 8903 mov dword ptr [ebx], eax
00406E0C . 83C3 04 add ebx, 4
00406E0F .^ EB D8 jmp short 00406DE9 //这里,看到没`那个下面的popad,我们F4直接到popad那去
00406E11 > FF96 74690000 call dword ptr [esi+6974]
00406E17 > 61 popad //这个,够熟悉了吧~~
00406E18 .- E9 D3ABFFFF jmp 004019F0 //`夸段了`让它跳
004019F0 55 push ebp //上面就会跳到这`好了`我们已经执行过pushad和popad了,就在这里用OD的Dump插件就可以了!
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
壳脱完了`不懂?我也不懂`西西`~不懂看下动画吧!
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
算法分析:
虽然在程序点注册得不到任何错误信息`,不过正确信息总一定有吧`所以我们就
Ultra String Reference->Find ASCII->找到一个Vrey good~字眼~这分明就是成功信息嘛`呵,
往上看就整体的断在这里吧:
004015E0 6A FF push -1
004015E2 68 C81C4000 push 00401CC8
004015E7 64:A1 00000000 mov eax, dword ptr fs:[0]
004015ED 50 push eax
004015EE 64:8925 0000000>mov dword ptr fs:[0], esp
004015F5 51 push ecx
004015F6 53 push ebx
004015F7 56 push esi
004015F8 57 push edi
004015F9 8BF9 mov edi, ecx
004015FB 6A 01 push 1
004015FD E8 74030000 call <jmp.&MFC42.#6334_CWnd::UpdateDa>---->这个地方取name了
00401602 8D4C24 0C lea ecx, dword ptr [esp+C]
00401606 E8 5F030000 call <jmp.&MFC42.#540_CString::CStrin>
0040160B 8B47 68 mov eax, dword ptr [edi+68]--------->把name传给eax
0040160E C74424 18 00000>mov dword ptr [esp+18], 0
00401616 8B48 F8 mov ecx, dword ptr [eax-8]---------->name长度传给ecx了
00401619 85C9 test ecx, ecx
0040161B 7F 12 jg short 0040162F
0040161D 68 18314000 push 00403118
00401622 8D4F 60 lea ecx, dword ptr [edi+60]
00401625 E8 3A030000 call <jmp.&MFC42.#860_CString::operat>
0040162A E9 0C010000 jmp 0040173B
0040162F 8B57 64 mov edx, dword ptr [edi+64]-------->假码传给edx
00401632 8B72 F8 mov esi, dword ptr [edx-8]--------->假码长度传给esi
00401635 85F6 test esi, esi
00401637 7F 12 jg short 0040164B
00401639 68 04314000 push 00403104
0040163E 8D4F 60 lea ecx, dword ptr [edi+60]
00401641 E8 1E030000 call <jmp.&MFC42.#860_CString::operat>
00401646 E9 F0000000 jmp 0040173B
0040164B 83F9 04 cmp ecx, 4------------------------->这里把name长度和4比较,小于就完了
0040164E 7D 12 jge short 00401662------------------>大于就跳``不跳就完了
00401650 68 E0304000 push 004030E0
00401655 8D4F 60 lea ecx, dword ptr [edi+60]
00401658 E8 07030000 call <jmp.&MFC42.#860_CString::operat>
0040165D E9 D9000000 jmp 0040173B
00401662 33F6 xor esi, esi
00401664 85C9 test ecx, ecx
00401666 7E 2B jle short 00401693
00401668 0FBE0430 movsx eax, byte ptr [eax+esi]-------->取name的第一个字符了`后面循环就依次取name的各个字符
有些新手会问说为啥这里是取name字符`在用OD调试时在信息窗口可以看到了
0040166C 8D0C80 lea ecx, dword ptr [eax+eax*4]->这里eax*4+eax,eax里面是name的第一字符,那么就是把name的各个 字符*4后再加上它自己,再送到ecx
0040166F 8D04C8 lea eax, dword ptr [eax+ecx*8]->上面把结果搞到ecx里面了`那这里就是把上面的结果*8后再加上
name字符(这里把结果记作A,方便下面讲说)
00401672 B9 28000000 mov ecx, 28----------------------------->把28(十进制40)送到ecx
00401677 99 cdq------------->这个是把eax里的字的符号扩展到edx里,也就是看eax的字的符号,正则edx全0,负则edx全F
也就是-1``
00401678 F7F9 idiv ecx-->这个有符号除法`隐含的操作数是edx-eax,我们在上面知道edx里面是符号扩展的结果,eax是
0040166f那里加后的结果,所以这里就是把符号扩展结果减去A(提醒一下:这个A是40166F加后 结果,我们把它记作A),再除以十六进制的28,商送到eax,余数送edx
0040167A 8D4C24 0C lea ecx, dword ptr [esp+C]
0040167E 8A92 20304000 mov dl, byte ptr [edx+403020]->这个403020地址里面有一串东西,后面注册机会讲到.
我们知道edx里面是余数了`这里就是把余数当做是该字符串的位置,取
到dl里去。
00401684 52 push edx
00401685 E8 16030000 call <jmp.&MFC42.#940_CString::operat>
0040168A 8B47 68 mov eax, dword ptr [edi+68]------------>把name送到eax了
0040168D 46 inc esi--------------------------->esi加一
0040168E 3B70 F8 cmp esi, dword ptr [eax-8]-------->加一后和name长度比较
00401691 ^ 7C D5 jl short 00401668-------->没算完就跳上去咯`
------------------------------------------------------------------------------------>第一阶段算完`
00401693 8B47 68 mov eax, dword ptr [edi+68]
00401696 33F6 xor esi, esi
00401698 8B48 F8 mov ecx, dword ptr [eax-8]
0040169B 85C9 test ecx, ecx
0040169D 7E 2C jle short 004016CB
0040169F 0FBE0C30 movsx ecx, byte ptr [eax+esi]------->这里取name的字符了
004016A3 8BC1 mov eax, ecx---------------------->把name的字符送到eax
004016A5 C1E0 05 shl eax, 5------------------------>我们知道eax里是name的字符了`这里就是每次循环都把name各个字 符左移5位
004016A8 2BC1 sub eax, ecx---------------------->左移5位后的结果减去name的字符(根据循环,依次name的字符)
004016AA B9 28000000 mov ecx, 28----------------------->这里十六进制的28送到ecx
004016AF 99 cdq----------------------------------->又是符号扩展,不详细说了
004016B0 F7F9 idiv ecx--------------------------->又是有符号除法,根据第一阶段我们知道算法只用其余数,这里就 看作取余数到edx就行了
004016B2 8D4C24 0C lea ecx, dword ptr [esp+C]
004016B6 8A92 4C304000 mov dl, byte ptr [edx+40304C]----->40304c这里面又有一串东西`根据上面,就可以说是把余数看作是 该字符串的位置,取到dl,当然每次循环都不同
004016BC 52 push edx
004016BD E8 DE020000 call <jmp.&MFC42.#940_CString::operat>->这个call,主要起字符串连接作用,我跟过的`没啥
将每次循环结果都接到第一阶段结果屁股后面去
004016C2 8B47 68 mov eax, dword ptr [edi+68]----------->name传到eax了
004016C5 46 inc esi------------------------------->esi+1
004016C6 3B70 F8 cmp esi, dword ptr [eax-8]------------>esi和name长度比较`
004016C9 ^ 7C D4 jl short 0040169F------------->没完就跳上去`
------------------------------>好,到这里上面都算完了,两个结果都连接起来了,下面再看
004016CB 8B4424 0C mov eax, dword ptr [esp+C]
004016CF 33F6 xor esi, esi
004016D1 8B48 F8 mov ecx, dword ptr [eax-8]
004016D4 85C9 test ecx, ecx
004016D6 7E 2D jle short 00401705
004016D8 0FBE0406 movsx eax, byte ptr [esi+eax]----->我们知道上两阶段结果连在一起了`那么长度就是原name的两倍了
这里取连接后的字符到eax
004016DC 8D0C80 lea ecx, dword ptr [eax+eax*4]-->连接后的字符*4再加上它自己
004016DF 8D0448 lea eax, dword ptr [eax+ecx*2]-->上面结果*2再加上它自己
004016E2 B9 28000000 mov ecx, 28--------------------->把十六进制的28送到ecx
004016E7 99 cdq--------------------------------->又是符号扩展
004016E8 F7F9 idiv ecx------------------>又是有符号除法
004016EA 8D4C24 0C lea ecx, dword ptr [esp+C]
004016EE 8A92 78304000 mov dl, byte ptr [edx+403078]--->403078里面又有一串东西,这里就是上面取余后当作是403078地址处 字符串的位置来用`取到dl
004016F4 52 push edx
004016F5 56 push esi
004016F6 E8 9F020000 call <jmp.&MFC42.#5856_CString::SetAt>
004016FB 8B4424 0C mov eax, dword ptr [esp+C]------------->把连接后的结果传到eax
004016FF 46 inc esi-------------------------------->esi+1
00401700 3B70 F8 cmp esi, dword ptr [eax-8]------------->比较看完了没
00401703 ^ 7C D3 jl short 004016D8--------------------->没完继续
---------------------------------------------------------------->好了``第三阶段也算完,下面比较了
00401705 8B77 64 mov esi, dword ptr [edi+64]------------>假码传到esi
00401708 8B58 F8 mov ebx, dword ptr [eax-8]------------->真码长度传到ebx
0040170B 3B5E F8 cmp ebx, dword ptr [esi-8]------------->真码长度和假码长度比较,不等当然就不相同啦`就完啦
0040170E 75 34 jnz short 00401744
00401710 33C9 xor ecx, ecx
00401712 85DB test ebx, ebx
00401714 7E 0D jle short 00401723
00401716 8A1401 mov dl, byte ptr [ecx+eax]------------->如果长度相等就到这`取直码第一个字符到dl
00401719 3A140E cmp dl, byte ptr [esi+ecx]------------->和假码第一个字符比较
0040171C 75 26 jnz short 00401744--------------------->不等就不用循环了`直接错就算了
0040171E 41 inc ecx
0040171F 3BCB cmp ecx, ebx
00401721 ^ 7C F3 jl short 00401716
00401723 68 B8304000 push 004030B8 ; very good!,you are a clever student
-------------------------------------------------------------------------------->下面重覆比较部分代码
00401705 8B77 64 mov esi, dword ptr [edi+64]
00401708 8B58 F8 mov ebx, dword ptr [eax-8]
0040170B 3B5E F8 cmp ebx, dword ptr [esi-8]
0040170E 75 34 jnz short 00401744-------------->上面代码比较长度,这里nop掉让程序往下走
00401710 33C9 xor ecx, ecx
00401712 85DB test ebx, ebx
00401714 7E 0D jle short 00401723
00401716 8A1401 mov dl, byte ptr [ecx+eax]
00401719 3A140E cmp dl, byte ptr [esi+ecx]
0040171C 75 26 jnz short 00401744------------->要相等,也nop
0040171E 41 inc ecx
0040171F 3BCB cmp ecx, ebx
00401721 ^ 7C F3 jl short 00401716------------->不用再循环了,nop掉就正确了,呵~~
00401723 68 B8304000 push 004030B8 ; very good!,you are a clever student--->正确信息
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
好,算法总结一下``
上面说过了`403020 40304c 403078`这三个地址里面都有一串东东`下分别下d 403020/40304c/403078,可以看到`
403020里面->biq2jrxc-ape3*dsynhz8gto7f0uml4
40304c----->-apeoiq2jrml4xcsw6ynh7f0uv19+3/k*dbz8gt5
403078----->h7f0uv19+3/kjrml4xcsw6yn*dbz8gt5-apeoiq2
好了``经过上面算法的分析``说白了真的注册码就在403078这串东西里面,把name经过一系列算法算出一个位置,最后从403078那串东东里依次取出字符拼拼凑凑就是注册码了
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
下面注册机``偶传上的是用E语言写的`那种中文代码就不贴上来了```不过有人想看还是可以啦!:)偶用C又写了一个:
#include "stdio.h"
#include <string.h>
char bianxing1(char name[100],int n)
{
int i,j=0,a,b;
char p[]={'b','i','q','2','j','r','x','c','-','a','p','e','3','*','d','s','y','n'
,'h','z','8','g','t','5','o','7','f','0','u','m','l','4','v','1','9','w',
'6','+','/','k'};
for(i=0;i<n;i++)
{
j=name[i];
a=j*4+j;
j=a*8+j;
if(j>0)
b=0;
else
b=-1;
j=(j-b)%40;
name[i]=p[j];
}
}
char bianxing2(char name[100],int n)
{
int i,j=0,a,b;
char p[]={'-','a','p','e','o','i','q','2','j','r','m','l','4','x','c','s','w','6','y','n','h','7','f','0','u','v','1','9','+','3','/','k','*','d','b','z','8','g','t','5'};
for(i=0;i<n;i++)
{
j=name[i];
a=(j<<5)-j;
if(a>0)
b=0;
else
b=-1;
a=(a-b)%40;
name[i]=p[a];
}
}
char bianxing3(char name[100],int n)
{
int i,j=0,a,b;
char p[]={'h','7','f','0','u','v','1','9','+','3','/','k','j','r','m','l','4','x','c','s','w','6','y','n','*','d','b','z','8','g','t','5','-','a','p','e','o','i','q','2'};
for(i=0;i<n;i++)
{
j=name[i];
a=j*4+j;
a=a*2+j;
if(a>0)
b=0;
else
b=-1;
a=(a-b)%40;
name[i]=p[a];
}
}
main()
{
int i,k;
char name1[100],name2[100];
printf(" **************************\n");
printf(" XC-CrackMe~2.0[keygen]\n");
printf(" **************************\n");
printf(" name: ");
gets(name1);
k=strlen(name1);
for(i=0;i<k;i++)
name2[i]=name1[i];
bianxing1(name1,k);
bianxing2(name2,k);
strcat(name1,name2);
k=strlen(name1);
bianxing3(name1,k);
printf(" code: ");
puts(name1);
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课