【文章标题】: 一个窗口隐藏工具的注册验证流程分析
【文章作者】: FishSeeWater
【软件名称】: 为支持看雪老大,自己找找吧:)
【软件大小】: 264K
【下载地址】: 自己搜索下载
【加壳方式】: ASPack
【保护方式】: Aspack+RSA
【编写语言】: Delphi
【使用工具】: OllyIce IDA RSATool BigIntCalc
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
一款窗口隐藏的工具,可定制性很高,是本人目前见过的窗口隐藏类软件中比较不错的。
这款软件在04年就打过他的主意,不过那时功力尚浅,分析了一个通宵给爆掉了,之后就一直再没动过它,爆破之后也
不用它。最近由于工作需要(想在工作时做点休闲的事:))上网找窗口隐藏软件,又把它给找着了(有缘呀:)),
忍不住旧情复燃,重新分析一下。
好开工:
1、当然是查看软件的母亲,这容易,由于以前分析过,知道它是Delphi生的,穿ASPack衣服(好朴素呀,我喜欢:))。
2、脱衣服,5年了,现在VMP大行其道,而她还穿戴着Aspack,真是让人倍感亲切:),ASPackDie轻松搞定。
有一个form.dll文件没有加壳。
3、运行脱壳后的程序,会发现出错,连Explorer都崩了。分析了好长时间,都没有发现什么原因退出的。只好带壳调试,
OD载入,运行,这里会发现系统假死,鼠标点任何东东都不反应,但系统级按键还是有反应的,估计是钩子问题。
经过分析,在程序运行时会调用form.dll中的过程在系统中安装 Shell Mouse 等钩子。OK为了一劳永逸,先把Hook干掉:
一:Patch掉Hook
003A8718 . A1 00A83>mov eax, dword ptr ds:[3AA800] ; | 003A871D . 50 push eax ; |hWnd => 00030044 (class='Shell_TrayWnd') 003A871E . E8 F1BFF>call <jmp.&user32.GetWindowThreadProcessId> ; \GetWindowThreadProcessId 003A8723 . 8BD8 mov ebx, eax 003A8725 EB 43 jmp short form.003A876A //这里向下会安装3个HOOK,直接JMP到Hook末尾 003A8727 90 nop 003A8728 90 nop 003A8729 8C90 506>mov word ptr ds:[eax+83346850], ss 003A872F ? 3A00 cmp al, byte ptr ds:[eax] 003A8731 . 6A 0D push 0D ; |HookType = 13. 003A8733 . E8 0CC0F>call <jmp.&user32.SetWindowsHookExA> ; \SetWindowsHookExA 003A8738 . A3 F4A73>mov dword ptr ds:[3AA7F4], eax 003A873D . 6A 00 push 0 003A873F . A1 60A63>mov eax, dword ptr ds:[3AA660] 003A8744 . 50 push eax 003A8745 . 68 80843>push form.003A8480 003A874A . 0D 09E8F>or eax, BFF3E809 003A874F FF db FF 003A8750 . FFA3 F8A>jmp dword ptr ds:[ebx+3AA7F8] 003A8756 . 6A 00 push 0 ; /ThreadID = 0 003A8758 . A1 60A63>mov eax, dword ptr ds:[3AA660] ; | 003A875D . 50 push eax ; |hModule => 003A0000 (form) 003A875E . 68 34863>push form.003A8634 ; |Hookproc = form.003A8634 003A8763 . 6A 0D push 0D ; |HookType = 13. 003A8765 . E8 DABFF>call <jmp.&user32.SetWindowsHookExA> ; \SetWindowsHookExA 003A876A . A3 FCA73>mov dword ptr ds:[3AA7FC], eax 003A876F . 833D F4A>cmp dword ptr ds:[3AA7F4], 0 003A8776 . 0F95C0 setne al 003A8779 . F6D8 neg al
用DeDe找到 “导入许可文件”按钮事件的入口,在OD中下断。然后用IDA把 脱壳的程序给拆了,生成Map文件,
在OD中导入。由于按导入许可文件后,会程序会要你导入*.lic文件,所以我们先构建一个假Lic文件,在里面输入
两行文字:
UserName:FishSeeWater
LicNum:HelloWorld
二:分析“导入许可文件”事件
004949D1 |. 55 push ebp 004949D2 |. 68 064D4>push <HideHelp.loc_494D06> 004949D7 |. 64:FF30 push dword ptr fs:[eax] 004949DA |. 64:8920 mov dword ptr fs:[eax], esp 004949DD |. 8D45 F4 lea eax, [local.3] 004949E0 |. BA 1C4D4>mov edx, <HideHelp.aYw08f8h4tkwt_0> ; yw08f8h4tkwt6gotdbsnrgo3xnvgwp=qwyurhgxwcyymp5pkcu4gonsvqdbxzz10+jrspozkmpm3bv=ydi18zlynllezjbodicfln1glb28o99ulxhquaguv9m 004949E5 |. E8 86FDF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 004949EA |. 8D45 F0 lea eax, [local.4] 004949ED |. BA A04D4>mov edx, <HideHelp.aP8rvf1agdmfo_0> ; p8rvf1agdmfowqiewfdr4pj=snyqk7irbepjhi=fz+sm24be22f2ditz4ub=+bet7bmgshs8q6dmzfhwge9a+2bfmnavp2elasj1xu50+rhcy3qjjuqlx5wrgu 004949F2 |. E8 79FDF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 004949F7 |. 8D45 EC lea eax, [local.5] 004949FA |. BA 244E4>mov edx, <HideHelp.a81orb447y3he_0> ; 81orb447y3he0fnlyfswtuepqcnr1qppib83fh+jrdze5c53tzf=iyipol0xpcdpmlopz2loryh9akb=vtwfe4ojofuxgo2cgmuqndkgqp15obgznbbpgtv+au 004949FF |. E8 6CFDF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494A04 |. 8D45 E8 lea eax, [local.6] 00494A07 |. BA A84E4>mov edx, <HideHelp.aAet7r0zhomst_0> ; aet7r0zhomsteemprkk8bfdtsat2syhhxoiwnbwtj61kn2cnkf3rg7mzwbi1om5r1o++ryh5p6wss=ylweo1ija+oejg9greslomk36p3remajaw3mbwfiqbhf 00494A0C |. E8 5FFDF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494A11 |. 8D45 E4 lea eax, [local.7] 00494A14 |. BA 2C4F4>mov edx, <HideHelp.a7txt1bvikexp_0> ; 7txt1bvikexpqg+xmmjzi8khgxchsbbyozexm9m=cubh4h1i1kxroxb6dm9rbznfejjrqgscrzbid1cke4mg58=insd+wufkforjqotevz8ooc3ueg4mx7djcr=4o8cywem6ehy 00494A19 |. E8 52FDF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494A1E |. 8D45 E0 lea eax, [local.8] 00494A21 |. BA BC4F4>mov edx, <HideHelp.aHxzm402e5t5q_0> ; hxzm402e5t5qzjtoaji2vthqmk7fxcjoeadsgkry7baihvhefhgqtrwznipx0wh0gi+jk9ss9tbz8bcd4e+9bpc7ms1it9c2vqqzs1hiazkj+i04915tce0dy6wsrfzfzryvqrnou1bblzfopoaamwq=ey9sus93m5lrq 00494A26 |. E8 45FDF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494A2B |. FF75 F4 push [local.3] 00494A2E |. FF75 F0 push [local.4] 00494A31 |. FF75 EC push [local.5] 00494A34 |. FF75 E8 push [local.6] 00494A37 |. FF75 E4 push [local.7] 00494A3A |. FF75 E0 push [local.8] 00494A3D |. 8D45 DC lea eax, [local.9] ; 将上面的串连接起来 00494A40 |. BA 06000>mov edx, 6 00494A45 |. E8 2A00F>call <HideHelp.System::__linkproc__ LStrCatN(void)> 00494A4A |. BE F3F05>mov esi, 59F0F3 00494A4F |. 8D55 DC lea edx, [local.9] 00494A52 |. 8B45 DC mov eax, [local.9] 00494A55 |. E8 F272F>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> ; Base编码处理一下 00494A5A |. 8D4D 9C lea ecx, [local.25] 00494A5D |. 8BD6 mov edx, esi 00494A5F |. 8B45 DC mov eax, [local.9] 00494A62 |. E8 91F1F>call <HideHelp.subN_111111_Decrypt> ;经过这个Call后,字符串变成了10进制的串了。 00494A67 |. 8B55 9C mov edx, [local.25] 00494A6A |. 8D45 DC lea eax, [local.9] 00494A6D |. E8 FEFCF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494A72 |. 8D45 D8 lea eax, [local.10] 00494A75 |. BA 6C504>mov edx, <HideHelp.aZnznshe3uswk_0> ; znznshe3uswkxyfshxsc1vysc8ar3huo6hs4coiiametdexdsqfc+gzd5p+a5byhzn0oljin6v+olnoz060duzdrznrnws7ebikrqb6nlcjnp+mtdlk4easn3f 00494A7A |. E8 F1FCF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494A7F |. 8D45 D4 lea eax, [local.11] 00494A82 |. BA F0504>mov edx, <HideHelp.aRfvG30qI5ou6_0> ; rfv+g30q+i5ou6upkf7rbt07pxrvaupf88dyb4me6okcpoxastrrwiifci26rt82tx4bnfhxoxxum=dih5p+ga9k=cxamo5dqqwqvkvrzdw9c6n1rtwtxwryli 00494A87 |. E8 E4FCF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494A8C |. 8D45 D0 lea eax, [local.12] 00494A8F |. BA 74514>mov edx, <HideHelp.aSbaffnknyl2f_0> ; sbaffnknyl2fuqmlhpza0s40wclm5acw4smlslhajwxdgekhjf8kahzk+nqovfdk7ycapfum2=6il2prn9oogq9mrr2d+f6m+w2trena9ryhx1hv5pd+dpaaaf 00494A94 |. E8 D7FCF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494A99 |. 8D45 CC lea eax, [local.13] 00494A9C |. BA F8514>mov edx, <HideHelp.aDuqu5qwlwhx6_0> ; duqu5qwlwhx6bozkir2tp0+sxz+hkedbjpzlk+ucojgzscjtzy2m=aqf9rei1=sex4znk1aqlwmoesfn8qwd4i3jkgnri12nof=dxhfvvhmqfcixkca2z3ypbx 00494AA1 |. E8 CAFCF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494AA6 |. 8D45 C8 lea eax, [local.14] 00494AA9 |. BA 7C524>mov edx, <HideHelp.aErlkefd9pnrg_0> ; erlkefd9pnrg9awpwmoafg6zc+mqc0fcrouiattiudiuntuvt+tj+k1wevevzp0rkmx7zvch6oukkqgqile=k=htdwvejsambvfkowjoi5iq6oconwqr3rtfi=5bcxebbfto0epzk 00494AAE |. E8 BDFCF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494AB3 |. 8D45 C4 lea eax, [local.15] 00494AB6 |. BA 10534>mov edx, <HideHelp.aIma63sqrvi6C_0> ; ima63sqrvi6+ciemwfzaf0x6ebiblgz1lqwmadqekx5sfzjnwnn9dg0xsa2cb5ymea5oqb3dboyv+w=zm+iwerztyhuu2pspzfo=vdqzqwslehadgrxn4ut1lm+nzjamwk3ihzfifb8awzcofxxu=weo5yujosztkff 00494ABB |. E8 B0FCF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494AC0 |. FF75 D8 push [local.10] 00494AC3 |. FF75 D4 push [local.11] 00494AC6 |. FF75 D0 push [local.12] 00494AC9 |. FF75 CC push [local.13] 00494ACC |. FF75 C8 push [local.14] 00494ACF |. FF75 C4 push [local.15] 00494AD2 |. 8D45 C0 lea eax, [local.16] ; 处理过程同上 00494AD5 |. BA 06000>mov edx, 6 00494ADA |. E8 95FFF>call <HideHelp.System::__linkproc__ LStrCatN(void)> 00494ADF |. 8D55 C0 lea edx, [local.16] 00494AE2 |. 8B45 C0 mov eax, [local.16] 00494AE5 |. E8 6272F>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)>
00494BA7 |. /0F84 DC0>je <HideHelp.loc_494C89> 00494BAD |. |8D55 84 lea edx, [local.31] 00494BB0 |. |8BC3 mov eax, ebx 00494BB2 |. |E8 99B9F>call <HideHelp.Dialogs::TOpenDialog::GetFileName(void)> 00494BB7 |. |8B45 84 mov eax, [local.31] 00494BBA |. |8D55 A4 lea edx, [local.23] 00494BBD |. |E8 72F1F>call <HideHelp.sub_483D34> ; 读第一行:UserName.... 00494BC2 |. |837D A4 >cmp [local.23], 0 00494BC6 |. |0F84 D70>je <HideHelp.loc_494CA3> ; 失败跳走 00494BCC |. |8D55 80 lea edx, [local.32] 00494BCF |. |8BC3 mov eax, ebx 00494BD1 |. |E8 7AB9F>call <HideHelp.Dialogs::TOpenDialog::GetFileName(void)> 00494BD6 |. |8B45 80 mov eax, [local.32] 00494BD9 |. |8D55 A0 lea edx, [local.24] 00494BDC |. |E8 FBF1F>call <HideHelp.sub_483DDC> 00494BE1 |. |8D55 F8 lea edx, [local.2] 00494BE4 |. |8B45 A0 mov eax, [local.24] ; 读第二行:LicNum.... 00494BE7 |. |E8 6071F>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> ; 将用户名Base64解码 00494BEC |. |8D55 B8 lea edx, [local.18] 00494BEF |. |8B45 DC mov eax, [local.9] ; 转换第一个长串为十进制大数 00494BF2 |. |E8 4975F>call <HideHelp.FGInt_Base10StringToFGInt> 00494BF7 |. |8D55 B0 lea edx, [local.20] 00494BFA |. |8B45 C0 mov eax, [local.16] ; 转换第二个长串为十进制大数 00494BFD |. |E8 3E75F>call <HideHelp.FGInt_Base10StringToFGInt> 00494C02 |. |8D45 A8 lea eax, [local.22] 00494C05 |. |50 push eax 00494C06 |. |8D45 A8 lea eax, [local.22] 00494C09 |. |50 push eax 00494C0A |. |8D45 A8 lea eax, [local.22] 00494C0D |. |50 push eax 00494C0E |. |8D45 A8 lea eax, [local.22] 00494C11 |. |50 push eax 00494C12 |. |8D45 FC lea eax, [local.1] 00494C15 |. |50 push eax 00494C16 |. |8D4D B0 lea ecx, [local.20] ; 第二个大数N 00494C19 |. |8D55 B8 lea edx, [local.18] ; 第一个大数D 00494C1C |. |8B45 F8 mov eax, [local.2] ; 编码后的LicNum 00494C1F |. |E8 B89BF>call <HideHelp.RSADecrypt(AnsiString,TFGInt &,TFGInt &,TFGInt &,>; RSA解密函数 00494C24 |. |8D55 FC lea edx, [local.1] 00494C27 |. |8B45 FC mov eax, [local.1] 00494C2A |. |E8 1D71F>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> ; 解密结果Base64解码 00494C2F |. |8D8D 7CF>lea ecx, [local.33] 00494C35 |. |8BD6 mov edx, esi 00494C37 |. |8B45 FC mov eax, [local.1] 00494C3A |. |E8 B9EFF>call <HideHelp.subN_111111_Decrypt> ;自定义函数解密LicNum 00494C3F |. |8B95 7CF>mov edx, [local.33] 00494C45 |. |8D45 FC lea eax, [local.1] 00494C48 |. |E8 23FBF>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00494C4D |. |8D85 78F>lea eax, [local.34] 00494C53 |. |E8 D4E8F>call <HideHelp.ReadHardWare> ; 读硬盘指纹,并生成机器码。 00494C58 |. |8B85 78F>mov eax, [local.34] ; (初始 cpu 选择) 00494C5E |. |8B55 FC mov edx, [local.1] 00494C61 |. |E8 9200F>call <HideHelp.System::__linkproc__ LStrPos(void)> ; 关键比较,如果成功则保存许可文件路径到Config.ini 00494C66 |. |85C0 test eax, eax 00494C68 |. |74 1F je short <HideHelp.loc_494C89> 00494C6A |. |8D95 74F>lea edx, [local.35] 00494C70 |. |8BC3 mov eax, ebx 00494C72 |. |E8 D9B8F>call <HideHelp.Dialogs::TOpenDialog::GetFileName(void)> 00494C77 |. |8B95 74F>mov edx, [local.35] 00494C7D |. |33C9 xor ecx, ecx 00494C7F |. |B8 18544>mov eax, <HideHelp.aFilepath_0> ; filepath 00494C84 |. |E8 D7F7F>call <HideHelp.sub_484460> 00494C89 <H>|> \8B87 A00>mov eax, dword ptr ds:[edi+3A0] ; loc_494C89 00494C8F |. 8A50 38 mov dl, byte ptr ds:[eax+38] 00494C92 |. A1 B4AE4>mov eax, dword ptr ds:[<dword_49AEB4>] 00494C97 |. E8 F4F4F>call <HideHelp.sub_484190> ; 界面更新 00494C9C |. 8BC3 mov eax, ebx 00494C9E |. E8 A5ECF>call <HideHelp.System::TObject::Free(void)> 00494CA3 <H>|> 33C0 xor eax, eax ; loc_494CA3 00494CA5 |. 5A pop edx 00494CA6 |. 59 pop ecx 00494CA7 |. 59 pop ecx 00494CA8 |. 64:8910 mov dword ptr fs:[eax], edx 00494CAB |. 68 0D4D4>push <HideHelp.loc_494D0D>
关键点:
1、程序先将两个长串进行Base64解码,然后再用他自己的解密过程处理,长串被还原成两个十进制数串 D,N。
2、程序读入Lic文件中的用户名与序列号,对序列号进行Base64解码生成 M。
3、调用RSADecrypt函数 参数为 M,D ,N 对M解密。
4、对解密后的M进行Base64解码 后成 M1。
5、程序读硬盘序列号,生成机器码。
6、比较:M1字串中是否包含机器码,如果是,则将Lic文件的路径写进config.ini的filepath字段中。
导入是导入了,那么程序是不是在启动的时候还会验证呢,答案是肯定的,不然怎么限制我们使用呢:)
怎样才能快速在程序的启动验证过程中下断呢?通过上面的分析,估计上面的部分函数肯定还会被调用,
那么就在RSA的关键解密函数的入口 0x0047E7DC 上下断。
Ctrl+F2重启软件,这时会提示一些断点被禁用,Yes 。在OEP处下断,运行程序,OEP处断下后 VLT+B,
打开0x0047E7DC 的断点。F9运行,软件一会就被断下来了。哈哈,我们分析的一点没错:
三:分析启动验证流程
0048A30A . 68 E2A64>push <HideHelp.loc_48A6E2> 0048A30F . 64:FF30 push dword ptr fs:[eax] 0048A312 . 64:8920 mov dword ptr fs:[eax], esp 0048A315 . 8D4D 94 lea ecx, dword ptr ss:[ebp-6C] 0048A318 . 33D2 xor edx, edx 0048A31A . B8 F8A64>mov eax, <HideHelp.aFilepath> ; filepath 0048A31F . E8 5CA0F>call <HideHelp.sub_484380> ; 读Config.ini中的filepath字段,如果没有则跳走 0048A324 . 8B45 94 mov eax, dword ptr ss:[ebp-6C] ; filepath字段保存的就是Lic文件的路径。 0048A327 . 8D55 98 lea edx, dword ptr ss:[ebp-68] 0048A32A . E8 45E7F>call <HideHelp.Sysutils::Trim(System::AnsiString)> 0048A32F . 8B45 98 mov eax, dword ptr ss:[ebp-68] 0048A332 . BA 0CA74>mov edx, <HideHelp.aNull_5> ; null 0048A337 . E8 C4A7F>call <HideHelp.System::__linkproc__ LStrCmp(void)> 0048A33C . 74 20 je short <HideHelp.loc_48A35E> (..........省略N行 省略代码与步骤二相同) 0048A52F . 8D4D AC lea ecx, dword ptr ss:[ebp-54] 0048A532 . 8D55 B4 lea edx, dword ptr ss:[ebp-4C] 0048A535 . 8B45 F4 mov eax, dword ptr ss:[ebp-C] 0048A538 . E8 9F42F>call <HideHelp.RSADecrypt(AnsiString,TFGInt &,TFGInt &,TFGInt &,>; 同步骤二 0048A53D . 8D55 F8 lea edx, dword ptr ss:[ebp-8] 0048A540 . 8B45 F8 mov eax, dword ptr ss:[ebp-8] 0048A543 . E8 0418F>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> 0048A548 . 8D8D 78F>lea ecx, dword ptr ss:[ebp-88] 0048A54E . 8BD3 mov edx, ebx 0048A550 . 8B45 F8 mov eax, dword ptr ss:[ebp-8] 0048A553 . E8 A096F>call <HideHelp.subN_111111_Decrypt> 0048A558 . 8B95 78F>mov edx, dword ptr ss:[ebp-88] 0048A55E . 8D45 F8 lea eax, dword ptr ss:[ebp-8] 0048A561 . E8 0AA2F>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 0048A566 . 8D85 74F>lea eax, dword ptr ss:[ebp-8C] 0048A56C . E8 BB8FF>call <HideHelp.ReadHardWare> 0048A571 . 8B85 74F>mov eax, dword ptr ss:[ebp-8C] 0048A577 . 8B55 F8 mov edx, dword ptr ss:[ebp-8] 0048A57A . E8 79A7F>call <HideHelp.System::__linkproc__ LStrPos(void)> ; 同步骤二 0048A57F . 85C0 test eax, eax 0048A581 . 75 65 jnz short <HideHelp.loc_48A5E8> ; 如果序列号比较成功,跳 0048A583 . BA 38AF4>mov edx, offset <HideHelp.dword_49AF38> 0048A588 . 8B45 9C mov eax, dword ptr ss:[ebp-64] 0048A58B . E8 BC17F>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> 0048A590 . 8D8D 70F>lea ecx, dword ptr ss:[ebp-90] 0048A596 . 8BD3 mov edx, ebx 0048A598 . A1 38AF4>mov eax, dword ptr ds:[<dword_49AF38>] 0048A59D . E8 5696F>call <HideHelp.subN_111111_Decrypt> ; 解密用户名 0048A5A2 . 8B95 70F>mov edx, dword ptr ss:[ebp-90] 0048A5A8 . B8 38AF4>mov eax, offset <HideHelp.dword_49AF38> 0048A5AD . E8 7AA1F>call <HideHelp.System::__linkproc__ LStrAsg(void *,void *)> 0048A5B2 . B8 38AF4>mov eax, offset <HideHelp.dword_49AF38> 0048A5B7 . E8 1CA1F>call <HideHelp.System::__linkproc__ LStrClr(void *)> 0048A5BC . C605 B4A>mov byte ptr ds:[<byte_49AFB4>], 0 0048A5C3 . 8B45 FC mov eax, dword ptr ss:[ebp-4] 0048A5C6 . 8B90 D00>mov edx, dword ptr ds:[eax+4D0] 0048A5CC . 8B45 FC mov eax, dword ptr ss:[ebp-4] 0048A5CF . 8B80 540>mov eax, dword ptr ds:[eax+454] 0048A5D5 . E8 F66FF>call <HideHelp.Comctrls::TPageControl::SetActivePage(Comctrls::T>; 如果是试用版,则程序启动后默认显示最后一页选项卡 0048A5DA . 33D2 xor edx, edx 0048A5DC . B8 F8A64>mov eax, <HideHelp.aFilepath> ; filepath 0048A5E1 . E8 769FF>call <HideHelp.sub_48455C> 0048A5E6 . EB 4A jmp short <HideHelp.loc_48A632> 0048A5E8 <H> > BA 38AF4>mov edx, offset <HideHelp.dword_49AF38> ; loc_48A5E8 0048A5ED . 8B45 A0 mov eax, dword ptr ss:[ebp-60] 0048A5F0 . E8 5717F>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> ; 解码用户名 0048A5F5 . 8D8D 6CF>lea ecx, dword ptr ss:[ebp-94] 0048A5FB . 8BD3 mov edx, ebx 0048A5FD . A1 38AF4>mov eax, dword ptr ds:[<dword_49AF38>] 0048A602 . E8 F195F>call <HideHelp.subN_111111_Decrypt> ; 解密用户名 0048A607 . 8B95 6CF>mov edx, dword ptr ss:[ebp-94] 0048A60D . B8 38AF4>mov eax, offset <HideHelp.dword_49AF38> 0048A612 . E8 15A1F>call <HideHelp.System::__linkproc__ LStrAsg(void *,void *)> 0048A617 . 8B45 FC mov eax, dword ptr ss:[ebp-4] 0048A61A . 8B80 180>mov eax, dword ptr ds:[eax+518] 0048A620 . 8B15 B89>mov edx, dword ptr ds:[<off_4997B8>] ; <HideHelp.dword_489028> 0048A626 . E8 FDE9F>call <HideHelp.Controls::TControl::SetText(System::AnsiString)> ; 显示软件授权给“用户名” 0048A62B . C605 B4A>mov byte ptr ds:[<byte_49AFB4>], 1 0048A632 <H> > 8D85 68F>lea eax, dword ptr ss:[ebp-98] ; loc_48A632
关键点:
1、程序首先读config.ini文件中的filepath字段,如果没有则跳失败
2、从filepath指向的许可文件中读入用户名与序列号
3、进行序列号验证,验证过程同步骤二
4、如果通过序列号验证,则将读出的用户名用自定义的解密函数进行解密,将解密后的用户名显示在程序界面上,
如果没有通过验证,则设置默认选项卡为最后一页,然后运行程序。
好了,导入验证与启动验证都分析过了,但当我们关闭程序时,发现他会提示我们还有XX次试用机会,这说明还有
一个地方需要我们分析。经过测试,发现如果我们只是运行程序,然后再关闭时,并不会提示我们。只有我们在
切换选项卡后,程序才会认为我们用过一次,然后提示。也就是说,我们只要在程序运行时切换一下选项卡,那么
程序就会增加一次使用记录,好,我们继续:
四:分析试用次数限制:
我们还是在RSA的关键解密函数的入口 0x0047E7DC 上下断。OD载入,在OEP处下断,运行程序,OEP处断下后 VLT+B,
打开0x0047E7DC 的断点。F9运行,然后切换选项卡,然而我们期望中断不没有出现,这说明程序在试用次数的检查
上没有使用RSA函数。那么我们就用别的函数试试,在以上分析中多次出现过 ConvertBase64to256 那么我们就用这个
函数试试,找到ConvertBase64to256 入口点,下断,重启程序....多次中断后,程序界面出现,我们切换一下选项卡,
哈哈,断下来了:
00489254 |. 8BFA mov edi, edx 00489256 |. 8BF0 mov esi, eax 00489258 |. 33C0 xor eax, eax 0048925A |. 55 push ebp 0048925B |. 68 FA934>push <HideHelp.loc_4893FA> 00489260 |. 64:FF30 push dword ptr fs:[eax] 00489263 |. 64:8920 mov dword ptr fs:[eax], esp 00489266 |. 8D45 FC lea eax, [local.1] 00489269 |. BA 10944>mov edx, <HideHelp.aXyxue4kfs0g4sj> ; xyxue+4kfs0g4sjnsewvyx1hkk5folj8mgnghlgawqnwolumwkrpvy 0048926E |. E8 FDB4F>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> ; 键路径密文 00489273 |. 8D45 F8 lea eax, [local.2] 00489276 |. BA 50944>mov edx, <HideHelp.aS20eelxpba> ; s20eelxpba 0048927B |. E8 F0B4F>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> ; 键名密文 00489280 |. 8D55 FC lea edx, [local.1] 00489283 |. 8B45 FC mov eax, [local.1] 00489286 |. E8 C12AF>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> ; Base64解码 0048928B |. 8D4D F0 lea ecx, [local.4] 0048928E |. BA F3F05>mov edx, 59F0F3 00489293 |. 8B45 FC mov eax, [local.1] 00489296 |. E8 5DA9F>call <HideHelp.subN_111111_Decrypt> ; 解密键路径 0048929B |. 8B55 F0 mov edx, [local.4] 0048929E |. 8D45 FC lea eax, [local.1] 004892A1 |. E8 CAB4F>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 004892A6 |. 8D55 F8 lea edx, [local.2] 004892A9 |. 8B45 F8 mov eax, [local.2] 004892AC |. E8 9B2AF>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> 004892B1 |. 8D4D EC lea ecx, [local.5] 004892B4 |. BA F3F05>mov edx, 59F0F3 004892B9 |. 8B45 F8 mov eax, [local.2] 004892BC |. E8 37A9F>call <HideHelp.subN_111111_Decrypt> ; 解密键名 004892C1 |. 8B55 EC mov edx, [local.5] 004892C4 |. 8D45 F8 lea eax, [local.2] 004892C7 |. E8 A4B4F>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 004892CC |. B2 01 mov dl, 1 004892CE |. A1 EC844>mov eax, dword ptr ds:[<off_4384EC>] 004892D3 |. E8 14F3F>call <HideHelp.Registry::TRegistry::TRegistry(void)> 004892D8 |. 8BD8 mov ebx, eax 004892DA |. BA 00000>mov edx, 80000000 004892DF |. 8BC3 mov eax, ebx 004892E1 |. E8 A6F3F>call <HideHelp.Registry::TRegistry::SetRootKey(uint)> 004892E6 |. 8B55 FC mov edx, [local.1] 004892E9 |. 8BC3 mov eax, ebx 004892EB |. E8 B4F7F>call <HideHelp.Registry::TRegistry::KeyExists(System::AnsiString)> ; 判断键是否存在 004892F0 |. 84C0 test al, al 004892F2 |. 74 1B je short <HideHelp.loc_48930F> 004892F4 |. 33C9 xor ecx, ecx 004892F6 |. 8B55 FC mov edx, [local.1] 004892F9 |. 8BC3 mov eax, ebx 004892FB |. E8 F0F3F>call <HideHelp.Registry::TRegistry::OpenKey(System::AnsiString,bool)> 00489300 |. 8D4D F4 lea ecx, [local.3] 00489303 |. 8B55 F8 mov edx, [local.2] 00489306 |. 8BC3 mov eax, ebx 00489308 |. E8 ABF5F>call <HideHelp.Registry::TRegistry::ReadString(System::AnsiString)> ; 如果存在,则读出键值 0048930D |. EB 19 jmp short <HideHelp.loc_489328> 0048930F <H>|> B1 01 mov cl, 1 ; 如果不存在,则创建并设置值为"y4o" 00489311 |. 8B55 FC mov edx, [local.1] 00489314 |. 8BC3 mov eax, ebx 00489316 |. E8 D5F3F>call <HideHelp.Registry::TRegistry::OpenKey(System::AnsiString,bool)> 0048931B |. 8D45 F4 lea eax, [local.3] 0048931E |. BA 64944>mov edx, <HideHelp.dword_489464> ; y4o 00489323 |. E8 48B4F>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 00489328 <H>|> 8D55 F4 lea edx, [local.3] ; loc_489328 0048932B |. 8B45 F4 mov eax, [local.3] 0048932E |. E8 192AF>call <HideHelp.ConvertBase64to256(AnsiString,AnsiString &)> ; 读出的值解码 00489333 |. 8D4D E8 lea ecx, [local.6] 00489336 |. BA F3F05>mov edx, 59F0F3 0048933B |. 8B45 F4 mov eax, [local.3] 0048933E |. E8 B5A8F>call <HideHelp.subN_111111_Decrypt> ; 解密读出的值 00489343 |. 8B55 E8 mov edx, [local.6] 00489346 |. 8D45 F4 lea eax, [local.3] 00489349 |. E8 22B4F>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 0048934E |. 8B45 F4 mov eax, [local.3] 00489351 |. E8 C2FAF>call <HideHelp.Sysutils::StrToInt(System::AnsiString)> 00489356 |. 48 dec eax ; 值转换为int后减1,如果结果为0则 退出程序 00489357 |. 79 24 jns short <HideHelp.loc_48937D> 00489359 |. E8 DEFCF>call <HideHelp.DisableHook> ; jmp 到 form.DisableHook 0048935E |. 68 C4AE4>push offset <HideHelp.Data> ; x 00489363 |. 6A 02 push 2 00489365 |. E8 E65BF>call <HideHelp.Shell_NotifyIconA> ; jmp 到 shell32.Shell_NotifyIconA 0048936A |. 8BC6 mov eax, esi 0048936C |. E8 AFFDF>call <HideHelp.sub_489120> 00489371 |. A1 B89B4>mov eax, dword ptr ds:[<off_499BB8>] 00489376 |. 8B00 mov eax, dword ptr ds:[eax] 00489378 |. E8 4F0AF>call <HideHelp.Forms::TApplication::Terminate(void)> 0048937D <H>|> 8B45 F4 mov eax, [local.3] ; loc_48937D 00489380 |. E8 93FAF>call <HideHelp.Sysutils::StrToInt(System::AnsiString)> 00489385 |. 48 dec eax 00489386 |. 8BD7 mov edx, edi 00489388 |. E8 4FF9F>call <HideHelp.Sysutils::IntToStr(int)> 0048938D |. 8B45 F4 mov eax, [local.3] 00489390 |. E8 83FAF>call <HideHelp.Sysutils::StrToInt(System::AnsiString)> 00489395 |. 48 dec eax 00489396 |. 8D55 E0 lea edx, [local.8] 00489399 |. E8 3EF9F>call <HideHelp.Sysutils::IntToStr(int)> 0048939E |. 8B45 E0 mov eax, [local.8] 004893A1 |. 8D4D E4 lea ecx, [local.7] 004893A4 |. BA F3F05>mov edx, 59F0F3 004893A9 |. E8 AAA7F>call <HideHelp.subN_222222_Encrypt> ; 结果减1后,转为string,加密 004893AE |. 8B55 E4 mov edx, [local.7] 004893B1 |. 8D45 F4 lea eax, [local.3] 004893B4 |. E8 B7B3F>call <HideHelp.System::__linkproc__ LStrLAsg(void *,void *)> 004893B9 |. 8D55 F4 lea edx, [local.3] 004893BC |. 8B45 F4 mov eax, [local.3] 004893BF |. E8 0828F>call <HideHelp.ConvertBase256to64(AnsiString,AnsiString &)> ; 编码,保存到注册表 004893C4 |. 8B4D F4 mov ecx, [local.3] 004893C7 |. 8B55 F8 mov edx, [local.2] 004893CA |. 8BC3 mov eax, ebx 004893CC |. E8 BBF4F>call <HideHelp.Registry::TRegistry::WriteString(System::AnsiString,Syst> 004893D1 |. 8BC3 mov eax, ebx 004893D3 |. E8 84F2F>call <HideHelp.subN_RegClose_Flush> 004893D8 |. 8BC3 mov eax, ebx 004893DA |. E8 69A5F>call <HideHelp.System::TObject::Free(void)> 004893DF |. 33C0 xor eax, eax 004893E1 |. 5A pop edx
软件作者保密意识很好,在软件中没有出现任何明码信息。软件的试用次数是保存在注册表中的,然后注册表路径,
作者对其进行了加密存放,这样分析者在静态分析软件时,看到的只是一串串乱七八糟的密文。
试用次数验证的关键:
1、解码,解密注册表的路径与键名。
2、判断键名是否存在,如果没有,则新建键名,并赋值为:y4o
3、读出的键值,解码,解密,转换为数值并减1。
4、如果结果为0了,则程序退出(试用次数没啦)
5、将减1后的结果转换为字串,并加密,编码后存入注册表。
到此这款软件的注册验证流程全部分析完,如果要想去掉试用次数限制,那么只要在 004892F2 的JZ改为JMP
就可以使试用次数永远为30次。
如果想要暴破,那么可以在0048A581 JNZ改为JMP就可以实现了,但前提是要在config.ini中建一个filepath中键名,并
做一个Lic文件,在里面输入两行字串(当然也可以把这些检测全Patch掉),不过有一点就是授权用户那里为乱码。
由于先们对RSA加密方法不熟悉,费了好大劲分析,把Base64的编码差不多全逆出来时,才在网上发现老罗大侠Base算法
以及用FGint库做的Sig文件,真晕了。


国产软件,大家都不容易,补丁就不发了,如果有想用的兄弟,自己动手吧,这样也可以锻炼一下:)
好了,这次先分析到这里,在下一篇中,将为这个软件做一个注册机。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2009年11月14日 21:25:08