【破文标题】十万个为什么 - 知识问答2006 破解分析
【破文作者】ssfighter@newsmth
【作者邮箱】ssfighter@gmail.com
【作者主页】
【破解工具】OllyICE PEiD0.94 VBExplorer
【破解平台】WinXP
【软件名称】十万个为什么 - 知识问答2006语言版
【软件大小】4.7M
【原版下载】
【保护方式】AsPack加壳,未注册有30次使用限制
【软件简介】一个知识问答的软件,有语言提示。
【破解声明】只是感兴趣,没有任何目的,如有错误之处还请大侠指出
------------------------------------------------------------------------
在天空软件站上找到的这个软件,题库还是很大的,没事的时候休闲一下还是很不错的选择,当然,我们的目的是破解这个软件。首先用PEiD侦壳,显示为 ASPack 2.12 -> Alexey Solodovnikov。用AsPackDie脱壳不表。脱壳之后程序无法启动,估计是有文件大小的检验,侦壳发现是用VB编写的。顺便说一句,这个程序防破解的限制还是比较多的,因此要有点耐心。
用Ollydbg载入,直接下API断点bp rtcFileLen,Ctrl+F9返回后来到这里:
004F0619 . FF15 98114000 call near ds:[<&MSVBVM60.#578>] ; MSVBVM60.rtcFileLen
004F061F . 3D 70190800 cmp eax, 81970 ; 比较文件大小
004F0624 . 7E 12 jle short unpacked.004F0638
004F0626 . C745 FC 05000>mov dword ptr ss:[ebp-4], 5
004F062D . FF15 34104000 call near ds:[<&MSVBVM60.__vbaEnd>] ; MSVBVM60.__vbaEnd
若要这里跳过文件大小校验,只要把jle改成jmp强制跳转即可,这样程序就可以启动了。
但是如果你是用Ollydbg等调试器载入的,程序会出错并自动关机,说明程序检验了调试器,虽然我是用odbg的修改版OllyICE载入的,可以正常启动不会出问题,但是还是先把检验去掉好些。用Ultra String Reference插件搜索所有UNICODE字符串,找Ollydbg,发现了两处参考,先到第一处看看,来到这里:
0055B668 . 68 58804400 push unpacked.00448058 ; ollydbg
0055B66D . 8D4D CC lea ecx, ss:[ebp-34]
0055B670 . 51 push ecx
0055B671 . FF15 B8114000 call near ds:[<&MSVBVM60.__vbaStrToAn>; MSVBVM60.__vbaStrToAnsi
0055B677 . 50 push eax
0055B678 . 6A 00 push 0
0055B67A . 6A 00 push 0
0055B67C . E8 0BC9EEFF call unpacked.00447F8C
0055B681 . 8945 A4 mov ss:[ebp-5C], eax
0055B684 . FF15 5C104000 call near ds:[<&MSVBVM60.__vbaSetSyst>; MSVBVM60.__vbaSetSystemError
0055B68A . 8B55 A4 mov edx, ss:[ebp-5C]
0055B68D . 8955 D8 mov ss:[ebp-28], edx
0055B690 . 8D4D CC lea ecx, ss:[ebp-34]
0055B693 . FF15 10124000 call near ds:[<&MSVBVM60.__vbaFreeStr>; MSVBVM60.__vbaFreeStr
0055B699 . C745 FC 10000>mov dword ptr ss:[ebp-4], 10
0055B6A0 . 837D D8 00 cmp dword ptr ss:[ebp-28], 0
0055B6A4 . 0F84 93000000 je unpacked.0055B73D
注意这个跳转,如果不跳的话会来到这里:
0055CBD7 . C745 FC 11000>mov dword ptr ss:[ebp-4], 11
0055CBDE . C745 B4 F0834>mov dword ptr ss:[ebp-4C], unpacked.>; rundll32 user.exe,exitwindows
0055CBE5 . C745 AC 08000>mov dword ptr ss:[ebp-54], 8
0055CBEC . 8D55 AC lea edx, ss:[ebp-54]
0055CBEF . 8D4D BC lea ecx, ss:[ebp-44]
0055CBF2 . FF15 BC114000 call near ds:[<&MSVBVM60.__vbaVarDup>>; MSVBVM60.__vbaVarDup
0055CBF8 . 6A 00 push 0
0055CBFA . 8D55 BC lea edx, ss:[ebp-44]
0055CBFD . 52 push edx
0055CBFE . FF15 14114000 call near ds:[<&MSVBVM60.#600>] ; MSVBVM60.rtcShell
0055CC04 . DD5D A4 fstp qword ptr ss:[ebp-5C]
0055CC07 . 8D4D BC lea ecx, ss:[ebp-44]
0055CC0A . FF15 20104000 call near ds:[<&MSVBVM60.__vbaFreeVar>; MSVBVM60.__vbaFreeVar
0055CC10 . C745 FC 12000>mov dword ptr ss:[ebp-4], 12
0055CC17 . C745 B4 30844>mov dword ptr ss:[ebp-4C], unpacked.>; shutdown -s
0055CC1E . C745 AC 08000>mov dword ptr ss:[ebp-54], 8
0055CC25 . 8D55 AC lea edx, ss:[ebp-54]
0055CC28 . 8D4D BC lea ecx, ss:[ebp-44]
0055CC2B . FF15 BC114000 call near ds:[<&MSVBVM60.__vbaVarDup>>; MSVBVM60.__vbaVarDup
0055CC31 . 6A 00 push 0
0055CC33 . 8D45 BC lea eax, ss:[ebp-44]
0055CC36 . 50 push eax
0055CC37 . FF15 14114000 call near ds:[<&MSVBVM60.#600>] ; MSVBVM60.rtcShell
呵呵,看到上面的东西了吧,当程序检测到有调试器在运行的时候就会运行shutdown -s命令关机,因此把55B6A4处的je改成jmp强制跳转即可。当然,这还没完,程序还检测了一次调试器,记得刚才的ollydbg有两处参考吧,来到第二处,在这里:
0055E2A4 . 68 08854400 push unpacked.00448508 ; ollydbg
0055E2A9 . FF15 D8104000 call near ds:[<&MSVBVM60.__vbaStrCmp>>; MSVBVM60.__vbaStrCmp
0055E2AF . F7D8 neg eax
0055E2B1 . 1BC0 sbb eax, eax
0055E2B3 . 40 inc eax
0055E2B4 . F7D8 neg eax
0055E2B6 . 66:0BF0 or si, ax
往前后翻翻看一看,这里简直汇集了众多破解工具,程序当检测到任何一种破解工具就会自动关机。来到下面的判断的地方:
0055E65A . 83C4 64 add esp, 64
0055E65D . 0FBF8D ECFDFF>movsx ecx, word ptr ss:[ebp-214]
0055E664 . 85C9 test ecx, ecx
0055E666 . 0F84 E6000000 je unpacked.0055E752
把这里的跳转也改成jmp即可,到此为止,放破解的限制基本解除,但是程序还是不允许Smart Check调试,我不知道他还有什么地方还有限制了,而且这个程序一启动的时候是启动一个fpj.frm的窗口,这个窗口先检查有没有调试器正在跟踪,如果没有的话就运行,否则自动关机。
到这里的时候我已经运行了20多次了,因此必须赶紧知道程序对30次使用限制的东西在哪儿,用 RegSnap可以看到程序在每次运行在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\QQPP里面修改了 jiv1b这个键值,这里记录的就是已经使用的次数。注意,如果前面没有把放破解的限制去掉的话,是不能在程序运行的时候使用RegSnap的,否则就会关机,当然最简单的办法是在程序运行前执行一次扫描,程序运行后再执行一次扫描,呵呵,这属于小trick了,本不属于本文的范围的。
言归正传,来到选择帮助-注册菜单,程序又退出了,看来作者也真够狡猾的,这里居然又验了一次文件大小,仍然下API断点bp rtcFileLen,停在这里:
00566A6B FF15 98114000 call near ds:[<&MSVBVM60.#578>] ; MSVBVM60.rtcFileLen
00566A71 3D 52FA0800 cmp eax, 8FA52
00566A76 7E 0D jle short unpacked.00566A85
仍然是把这里对jle改成jmp强制跳转即可,至此所有限制都已取消了,就剩下分析注册码的计算过程了。
用VBExplorer查看一下确定按钮的地址00566B30,在这里下断点,odbg载入,运行,填入用户名ssfighter,注册码12345,确定后断在这里:
00566B30 > \55 push ebp
00566B31 . 8BEC mov ebp, esp
00566B33 . 83EC 18 sub esp, 18
00566B36 . 68 D6474000 push <jmp.&MSVBVM60.__vbaExceptHandle>; SE 处理程序安装
00566B3B . 64:A1 0000000>mov eax, fs:[0]
00566B41 . 50 push eax
由于VB的程序比较长,全写下来太占地方了,我们一点点跟踪,略过前面一堆代码,来到这里:
00566FEF . 50 push eax ; 用户名
00566FF0 . FF15 24104000 call near ds:[<&MSVBVM60.__vbaLenBstr>] ; 取用户名长度
00566FF6 . 8BF0 mov esi, eax
00566FF8 . F7DE neg esi
00566FFA . 1BF6 sbb esi, esi
00566FFC . 46 inc esi
00566FFD . F7DE neg esi
00566FFF . 8B4D B0 mov ecx, ss:[ebp-50]
00567002 . 51 push ecx ; 注册码
00567003 . FF15 24104000 call near ds:[<&MSVBVM60.__vbaLenBstr>] ; 取注册码长度
00567009 . F7D8 neg eax
0056700B . 1BC0 sbb eax, eax
0056700D . 40 inc eax
0056700E . F7D8 neg eax
00567010 . 66:0BF0 or si, ax
00567013 . 66:89B5 68FEF>mov ss:[ebp-198], si
0056701A . 8D55 B0 lea edx, ss:[ebp-50]
0056701D . 52 push edx
0056701E . 8D45 B4 lea eax, ss:[ebp-4C]
00567021 . 50 push eax
00567022 . 6A 02 push 2
00567024 . FF15 8C114000 call near ds:[<&MSVBVM60.__vbaFreeStrList>; MSVBVM60.__vbaFreeStrList
0056702A . 83C4 0C add esp, 0C
0056702D . 8D4D 90 lea ecx, ss:[ebp-70]
00567030 . 51 push ecx
00567031 . 8D55 94 lea edx, ss:[ebp-6C]
00567034 . 52 push edx
00567035 . 6A 02 push 2
00567037 . FF15 40104000 call near ds:[<&MSVBVM60.__vbaFreeObjList>; MSVBVM60.__vbaFreeObjList
0056703D . 83C4 0C add esp, 0C
00567040 . 0FBF85 68FEFF>movsx eax, word ptr ss:[ebp-198]
00567047 . 85C0 test eax, eax
00567049 . 0F84 CB000000 je unpacked.0056711A
这里只要用户名和注册码都不是空的即可,再往下跟,来到这里:
0056719B > \6A 01 push 1
0056719D . 8B4D B4 mov ecx, ss:[ebp-4C]
005671A0 . 51 push ecx
005671A1 . FF15 D8114000 call near ds:[<&MSVBVM60.#616>] ; MSVBVM60.rtcLeftCharBstr
这里是取用户名最左边一位,再往后看
00567245 > \6A 01 push 1
00567247 . 8B45 B4 mov eax, ss:[ebp-4C]
0056724A . 50 push eax
0056724B . FF15 E4114000 call near ds:[<&MSVBVM60.#618>] ; MSVBVM60.rtcRightCharBstr
这里是取用户名最后一位
00567251 . 8BD0 mov edx, eax
00567253 . 8D4D DC lea ecx, ss:[ebp-24]
00567256 . FF15 E8114000 call near ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
0056725C . 8D4D B4 lea ecx, ss:[ebp-4C]
0056725F . FF15 10124000 call near ds:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr
00567265 . 8D4D 94 lea ecx, ss:[ebp-6C]
00567268 . FF15 14124000 call near ds:[<&MSVBVM60.__vbaFreeObj>] ; MSVBVM60.__vbaFreeObj
0056726E . C745 FC 0E000>mov dword ptr ss:[ebp-4], 0E
00567275 . 8B4D BC mov ecx, ss:[ebp-44]
00567278 . 51 push ecx
00567279 . FF15 44104000 call near ds:[<&MSVBVM60.#516>] ; MSVBVM60.rtcAnsiValueBstr
0056727F . 66:6BC0 0B imul ax, ax, 0B ; 用户名第一位ascii码乘以0B, 记作a
00567283 . 0F80 17110000 jo unpacked.005683A0
00567289 . 66:8945 B8 mov ss:[ebp-48], ax
0056728D . C745 FC 0F000>mov dword ptr ss:[ebp-4], 0F
00567294 . 8B55 DC mov edx, ss:[ebp-24]
00567297 . 52 push edx
00567298 . FF15 44104000 call near ds:[<&MSVBVM60.#516>] ; MSVBVM60.rtcAnsiValueBstr
0056729E . 66:2D 4E00 sub ax, 4E ; 用户名最后一位ascii码减去4E,记作b
005672A2 . 0F80 F8100000 jo unpacked.005683A0
005672A8 . 66:8945 D0 mov ss:[ebp-30], ax
005672AC . C745 FC 10000>mov dword ptr ss:[ebp-4], 10
005672B3 . 66:8B45 B8 mov ax, ss:[ebp-48]
005672B7 . 66:0345 D0 add ax, ss:[ebp-30] ; a+b,记作c
005672BB . 0F80 DF100000 jo unpacked.005683A0
005672C1 . 66:8945 D8 mov ss:[ebp-28], ax
005672C5 . C745 FC 11000>mov dword ptr ss:[ebp-4], 11
005672CC . 6A 04 push 4
005672CE . 66:8B4D D8 mov cx, ss:[ebp-28]
005672D2 . 51 push ecx
005672D3 . FF15 08104000 call near ds:[<&MSVBVM60.__vbaStrI2>] ; c转化成字符串(10进制)
005672D9 . 8BD0 mov edx, eax
005672DB . 8D4D B4 lea ecx, ss:[ebp-4C]
005672DE . FF15 E8114000 call near ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
005672E4 . 50 push eax
005672E5 . FF15 E4114000 call near ds:[<&MSVBVM60.#618>] ; 取c的最右边4位
005672EB . 8BD0 mov edx, eax
005672ED . 8D4D D4 lea ecx, ss:[ebp-2C]
005672F0 . FF15 E8114000 call near ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
005672F6 . 8D4D B4 lea ecx, ss:[ebp-4C]
005672F9 . FF15 10124000 call near ds:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr
005672FF . C745 FC 12000>mov dword ptr ss:[ebp-4], 12
00567306 . 8B55 D4 mov edx, ss:[ebp-2C]
00567309 . 52 push edx
0056730A . 68 90644400 push unpacked.00446490
0056730F . FF15 D8104000 call near ds:[<&MSVBVM60.__vbaStrCmp>] ; c和"0"比较
00567315 . 8BF0 mov esi, eax
00567317 . F7DE neg esi
00567319 . 1BF6 sbb esi, esi
0056731B . F7DE neg esi
0056731D . 8B45 D4 mov eax, ss:[ebp-2C]
00567320 . 50 push eax
00567321 . 68 F0614400 push unpacked.004461F0
00567326 . FF15 D8104000 call near ds:[<&MSVBVM60.__vbaStrCmp>] ; c和""比较
0056732C . F7D8 neg eax
0056732E . 1BC0 sbb eax, eax
00567330 . F7D8 neg eax
00567332 . 23F0 and esi, eax
00567334 . 85F6 test esi, esi
00567336 . 75 15 jnz short unpacked.0056734D ; 前面的两个比较取与,都不等则跳
00567338 . C745 FC 13000>mov dword ptr ss:[ebp-4], 13
0056733F . BA 8C8F4400 mov edx, unpacked.00448F8C ; UNICODE "8723"
00567344 . 8D4D D4 lea ecx, ss:[ebp-2C]
00567347 . FF15 80114000 call near ds:[<&MSVBVM60.__vbaStrCopy>] ; MSVBVM60.__vbaStrCopy
0056734D > \C745 FC 15000>mov dword ptr ss:[ebp-4], 15
注意这里,如果计算出来的c是"0"或者是空串则令c为"8723"。
再往下跳过一堆看不太懂的代码,来到这里:
005674D9 . 51 push ecx
005674DA . 6A 02 push 2
005674DC . 8B55 B4 mov edx, ss:[ebp-4C]
005674DF . 52 push edx
005674E0 . FF15 B4104000 call near ds:[<&MSVBVM60.#631>] ; 取用户名第二位
005674E6 . 8BD0 mov edx, eax
005674E8 . 8D4D B0 lea ecx, ss:[ebp-50]
005674EB . FF15 E8114000 call near ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
005674F1 . 50 push eax
005674F2 . 68 08694400 push unpacked.00446908
005674F7 . FF15 D8104000 call near ds:[<&MSVBVM60.__vbaStrCmp>] ; 和"4"比较
005674FD . 8BF0 mov esi, eax
005674FF . F7DE neg esi
00567501 . 1BF6 sbb esi, esi
00567503 . 46 inc esi
00567504 . F7DE neg esi
00567506 . 8B45 AC mov eax, ss:[ebp-54]
00567509 . 50 push eax
0056750A . FF15 24104000 call near ds:[<&MSVBVM60.__vbaLenBstr>] ; 取用户名位数
00567510 . 33C9 xor ecx, ecx
00567512 . 83F8 07 cmp eax, 7 ; 用户名位数和7比较
00567515 . 0F94C1 sete cl ; 如果不等则cl=0,相等则cl=1
00567518 . F7D9 neg ecx
0056751A . 66:23F1 and si, cx
0056751D . 8B55 A8 mov edx, ss:[ebp-58]
00567520 . 52 push edx
00567521 . 6A 02 push 2
00567523 . 8B45 D4 mov eax, ss:[ebp-2C]
00567526 . 50 push eax
00567527 . FF15 D8114000 call near ds:[<&MSVBVM60.#616>] ; 取c的前两位
0056752D . 8BD0 mov edx, eax
0056752F . 8D4D A4 lea ecx, ss:[ebp-5C]
00567532 . FF15 E8114000 call near ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
00567538 . 50 push eax
00567539 . 68 9C8F4400 push unpacked.00448F9C ; UNICODE "0735"
0056753E . FF15 54104000 call near ds:[<&MSVBVM60.__vbaStrCat>] ; c的前两位和0735连成一串
00567544 . 8BD0 mov edx, eax
00567546 . 8D4D A0 lea ecx, ss:[ebp-60]
00567549 . FF15 E8114000 call near ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
0056754F . 50 push eax
00567550 . 6A 02 push 2
00567552 . 8B4D D4 mov ecx, ss:[ebp-2C]
00567555 . 51 push ecx
00567556 . FF15 E4114000 call near ds:[<&MSVBVM60.#618>] ; 取c的后两位
0056755C . 8BD0 mov edx, eax
0056755E . 8D4D 9C lea ecx, ss:[ebp-64]
00567561 . FF15 E8114000 call near ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
00567567 . 50 push eax
00567568 . FF15 54104000 call near ds:[<&MSVBVM60.__vbaStrCat>] ; 把刚才得到的串和c的后两位再串起来
0056756E . 8BD0 mov edx, eax
00567570 . 8D4D 98 lea ecx, ss:[ebp-68]
00567573 . FF15 E8114000 call near ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
00567579 . 50 push eax
0056757A . FF15 D8104000 call near ds:[<&MSVBVM60.__vbaStrCmp>] ; 真假码比较
00567580 . F7D8 neg eax
00567582 . 1BC0 sbb eax, eax
00567584 . 40 inc eax
00567585 . F7D8 neg eax
00567587 . 66:23F0 and si, ax
注意,这里真实的注册码已经计算出来,就是把0735插入到c的前两位与后两位之间,但是仅仅注册码对了还不行。再看下面:
005675DF . 0FBF8D 60FEFF>movsx ecx, word ptr ss:[ebp-1A0]
005675E6 . 85C9 test ecx, ecx
005675E8 . 0F84 77090000 je unpacked.00567F65
往前翻翻,这里其实是进行了三次判断,只有这三次判断都满足才能算注册通过,这三次分别是用户名第二位是否是4,用户名位数是否为7位,注册码是否正确,只有这三个都正确才能注册通过。当然如果想爆破都话把这里nop掉也就可以了,这个程序启动时不会验证用户名和注册码的关系,注册通过只是在注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\QQPP这里加上一个jiv3a的键,键值为5,启动时只判断这个是否正确来决定是否是注册版。
顺便写上启动时候的判断,在这里:
004F2252 . BA 84714400 mov edx, unpacked.00447184 ; jiv3a
004F2257 . 8D4D C8 lea ecx, ss:[ebp-38]
004F225A . FF15 80114000 call near ds:[<&MSVBVM60.__vbaStrCopy>] ; MSVBVM60.__vbaStrCopy
004F2260 . BA 50714400 mov edx, unpacked.00447150 ; software\microsoft\qqpp
004F2265 . 8D4D CC lea ecx, ss:[ebp-34]
004F2268 . FF15 80114000 call near ds:[<&MSVBVM60.__vbaStrCopy>] ; MSVBVM60.__vbaStrCopy
004F226E . C785 E4FEFFFF>mov dword ptr ss:[ebp-11C], 80000002
004F2278 . 8D85 1CFFFFFF lea eax, ss:[ebp-E4]
004F227E . 50 push eax
004F227F . 8D4D C8 lea ecx, ss:[ebp-38]
004F2282 . 51 push ecx
004F2283 . 8D55 CC lea edx, ss:[ebp-34]
004F2286 . 52 push edx
004F2287 . 8D85 E4FEFFFF lea eax, ss:[ebp-11C]
004F228D . 50 push eax
004F228E . E8 CDC60600 call unpacked.0055E960
004F2293 . 8D4D C8 lea ecx, ss:[ebp-38]
004F2296 . 51 push ecx
004F2297 . 8D55 CC lea edx, ss:[ebp-34]
004F229A . 52 push edx
004F229B . 6A 02 push 2
004F229D . FF15 8C114000 call near ds:[<&MSVBVM60.__vbaFreeStrList>; MSVBVM60.__vbaFreeStrList
004F22A3 . 83C4 0C add esp, 0C
004F22A6 . C745 FC 2B000>mov dword ptr ss:[ebp-4], 2B
004F22AD . 8B45 D4 mov eax, ss:[ebp-2C]
004F22B0 . 50 push eax
004F22B1 . 68 94714400 push unpacked.00447194 ; 5
004F22B6 . FF15 D8104000 call near ds:[<&MSVBVM60.__vbaStrCmp>] ; MSVBVM60.__vbaStrCmp
004F22BC . 85C0 test eax, eax
004F22BE . 0F85 51010000 jnz unpacked.004F2415
当然,把这里nop掉也可以实现爆破。
------------------------------------------------------------------------
总结一下,程序注册的规律是:用户名必须是7位,用户名第二位必须是4。
注册码的计算方法(以下均为10进制):取用户名第一位,ascii码乘以11,再取用户名最后一位,ascii码减去78,两者求和,如果这个和大于4位则取最后4位,如果结果是0或者不存在则令其是8723,并在这个数的前两位和后两位之间插入0735,这八位数字就是注册码。
顺便说一下,这个程序作者好像花了很多功夫在防破解上面,但是最后验证程序是否注册却只是单纯地检验了一个键值是否正确,不免显得有点头重脚轻,这样做可以很容易被爆破,希望作者能够将这里改进一下。
------------------------------------------------------------------------
【版权声明】转载请保持文章的完整性,并注明原作者的信息,谢谢:)
评论