汇X考轻松里有一套职称考试测试的软件,一个朋友问我能不能帮忙破解一下,正好是新手,就拿来练手了。不过破解过程真的好辛苦。。。
废话不多说,先查壳:ASProtect 2.3 SKE build 04.26 Beta [3],直接用脚本或者脱壳机都能很顺利地脱掉。之后遇到第一个问题:明明指针全部有效,可是脱壳文件运行到启动界面就停住了。开始猜原因,补区段?自校验?折腾一圈,最后OD载入脱壳后的文件,运行后F12暂停,调用堆栈:
猜测是脱壳后可能修改了一些代码,直接给Sleep了,那好,直接来到调用的地方:
00530E5E . /74 18 je Xde_Hzkqs.00530E78
00530E60 > |68 7F969800 push 0x98967F ; /Timeout = 9999999. ms
00530E65 . |E8 3ADDEDFF call <jmp.&kernel32.Sleep> ; \Sleep
00530E6A .^|EB F4 jmp Xde_Hzkqs.00530E60
00530E6C . |A1 98F65300 mov eax,dword ptr ds:[0x53F698]
00530E71 . |8B00 mov eax,dword ptr ds:[eax]
00530E73 . |E8 F45CF4FF call de_Hzkqs.00476B6C
00530E78 > \33C0 xor eax,eax
把je改成jmp,ok,程序可以跑起来了。
第二步就是破解了,这个软件提供了多种注册方式:
试了两种注册方式,都是ini文件重启验证的,本来想输入假码看能不能跟出注册码,但是peid一查加了n种加密算法,跟了一阵后内存里出现的都是加密后的字符串,该方法看来实现不了。
于是想直接将重启验证过程给清除掉,最常用的当然是下断点BP GetPrivateProfileStringA了,不过跟踪的时候发现软件启动起来以后会带上好几个线程,也不止读取一个ini文件,比较杂乱,所以就采用了下面的跟踪方法:
1、启动软件,,弹出正在使用的为试用版本的提示对话框,这个时候F12暂停,堆栈窗口找到调用的MessageBox函数的地方并返回到该段的段首,提示:
本地调用来自 00476E0D, 0052E8FE, 0052EA6C, 0052EA91, 0052F97D, 0052FAF9, 00530D34, 00530FB8, 00531042, 00531D54, 00531DDE, 00532210, 0053229A, 00532F55, 00533AC0, 00534B24, 00534F34, 00535F83, 0053604B, 00536534, 00536685, 005366D0, 0053711A
接着Ctrl+R在所有地址上都下断,重新运行程序后停留在00534B24处。
来到00534B24所处段的段首,又有提示:
本
地调用来自 0053199E, 005319DC, 00533A20, 00533A5F
同上面一样的方法,全部下断后重新运行,停留在005319DC处(这里其实也是天草高级版第15课里的思路)。
005319CF . 8B55 9C mov edx,dword ptr ss:[ebp-0x64]
005319D2 . E8 A533EDFF call paper.00404D7C
[COLOR="red"]005319D7 . 74 15 je Xpaper.005319EE ; 这里必须跳[/COLOR]
005319D9 . 8B45 FC mov eax,dword ptr ss:[ebp-0x4]
[COLOR="red"]005319DC . E8 AB300000 call paper.00534A8C [/COLOR] ; 这里调用使用试用版的提示
005319E1 . 33C0 xor eax,eax
005319E3 . 5A pop edx
005319E4 . 59 pop ecx
005319E5 . 59 pop ecx
005319E6 . 64:8910 mov dword ptr fs:[eax],edx
005319E9 . E9 DC200000 jmp paper.00533ACA
005319EE > 8D85 78FAFFFF lea eax,dword ptr ss:[ebp-0x588]
005319F4 . 50 push eax
005319F5 . B9 01000000 mov ecx,0x1
005319FA . BA 14000000 mov edx,0x14
005319FF . 8B45 CC mov eax,dword ptr ss:[ebp-0x34]
00531A02 . E8 8934EDFF call paper.00404E90
00531A07 . 8B85 78FAFFFF mov eax,dword ptr ss:[ebp-0x588]
00531A0D . 8B55 98 mov edx,dword ptr ss:[ebp-0x68]
00531A10 . E8 6733EDFF call paper.00404D7C
[COLOR="red"]00531A15 . 0F85 41200000 jnz paper.00533A5C[/COLOR] ; 不能让它跳
00531A1B . 6A 07 push 0x7
00531A1D . B9 06000000 mov ecx,0x6
00531A22 . BA 09000000 mov edx,0x9
00531A27 . 8B45 E0 mov eax,dword ptr ss:[ebp-0x20]
00531A2A . E8 45F4F9FF call paper.004D0E74
00531A2F . 33D2 xor edx,edx
00531A31 . 52 push edx
可以发现在005319D7处有个je的判断,我们把它改成jmp。
然后单步调试,在00531A15有一个很大的跳转,如果跳的话就直接失败了,所以要NOP掉。
接下去还是手工单步调试,调试的时候失败了好几回,总结出了两条:
1、凡是遇到Sleep的都要跳过去。
[COLOR="red"]00531E72[/COLOR] . /74 63 je Xpaper.00531ED7
00531E74 . |A1 FCF65300 mov eax,dword ptr ds:[0x53F6FC]
00531E79 . |FF30 push dword ptr ds:[eax]
00531E7B . |68 74445300 push paper.00534474 ; ASCII "hz"
00531E80 . |8D85 04FAFFFF lea eax,dword ptr ss:[ebp-0x5FC]
00531E86 . |50 push eax
00531E87 . |B9 05000000 mov ecx,0x5
00531E8C . |BA 03000000 mov edx,0x3
00531E91 . |8B45 D8 mov eax,dword ptr ss:[ebp-0x28]
00531E94 . |E8 F72FEDFF call paper.00404E90
00531E99 . |FFB5 04FAFFFF push dword ptr ss:[ebp-0x5FC]
00531E9F . |68 80445300 push paper.00534480 ; ASCII ".dll"
00531EA4 . |8D85 08FAFFFF lea eax,dword ptr ss:[ebp-0x5F8]
00531EAA . |BA 04000000 mov edx,0x4
00531EAF . |E8 3C2EEDFF call paper.00404CF0
00531EB4 . |8B85 08FAFFFF mov eax,dword ptr ss:[ebp-0x5F8]
00531EBA . |E8 697DEDFF call paper.00409C28
00531EBF > |68 7F969800 push 0x98967F ; /Timeout = 9999999. ms
[COLOR="Red"]00531EC4 [/COLOR] . |E8 DBCCEDFF call <jmp.&kernel32.Sleep> ; \Sleep
2、凡是遇到LoadLibraryA的都要跳过去。
[COLOR="red"]00531EFC . /0F87 2F010000 ja paper.00532031[/COLOR]
00531F02 . |A1 FCF65300 mov eax,dword ptr ds:[0x53F6FC]
00531F07 . |FF30 push dword ptr ds:[eax]
00531F09 . |68 74445300 push paper.00534474 ; ASCII "hz"
00531F0E . |8D85 FCF9FFFF lea eax,dword ptr ss:[ebp-0x604]
00531F14 . |50 push eax
00531F15 . |B9 05000000 mov ecx,0x5
00531F1A . |BA 03000000 mov edx,0x3
00531F1F . |8B45 D8 mov eax,dword ptr ss:[ebp-0x28]
00531F22 . |E8 692FEDFF call paper.00404E90
00531F27 . |FFB5 FCF9FFFF push dword ptr ss:[ebp-0x604]
00531F2D . |68 80445300 push paper.00534480 ; ASCII ".dll"
00531F32 . |8D85 00FAFFFF lea eax,dword ptr ss:[ebp-0x600]
00531F38 . |BA 04000000 mov edx,0x4
00531F3D . |E8 AE2DEDFF call paper.00404CF0
00531F42 . |8B85 00FAFFFF mov eax,dword ptr ss:[ebp-0x600]
00531F48 . |E8 E32EEDFF call paper.00404E30
00531F4D . |50 push eax ; /FileName
[COLOR="red"]00531F4E . |E8 2554EDFF call <jmp.&kernel32.LoadLibraryA> ; \LoadLibraryA[/COLOR]
00531F53 . |A3 F4245400 mov dword ptr ds:[0x5424F4],eax
这里手工调试需要的就只是耐心和细心了,代码很多,慢慢跳过上面的两个陷阱,最终就运行成功了!
运行修改后的程序没有验证过程了,点击注册直接提示注册成功:
呵呵,第一回练手,碰到如此复杂的一个程序,办法虽笨也算是搞定了。如果能有更好的方法规避Sleep和LoadLibraryA这两个函数的话,也请大家指教!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)