【文章标题】: 一个窗口隐藏工具的注册验证流程分析
【文章作者】: 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
  
  Patch后,备份下一原文件,保存可执行文件。好了,这样系统就不会再卡死了。
  用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 &)>
  
  看到上面两个恐怖的串,估计是碰到密码学算法了。继续向下看(中间打开文件对话框 省略N行 )
  
代码:
  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>
  
  OK到此,导入许可证文件的过程的主线基本分析完成
  关键点:
  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
  
  OK总结一下启动验证
  关键点:
  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

【文章标题】: 一个窗口隐藏工具的注册验证流程分析(二)-RSA补丁与注册机
【文章作者】: FishSeeWater
【软件名称】: 为支持看雪,想研究学习的自己找找吧:)
【软件大小】: 264K
【下载地址】: 自己搜索下载
【加壳方式】: ASPack
【保护方式】: Aspack+RSA
【编写语言】: Delphi
【使用工具】: OllyIce IDA RSATool BigIntCalc
【作者声明】: 只是感兴趣研究密码学保护的程序,没有其他目的,这里也是纯代码演示。失误之处敬请诸位大侠赐教!
                    同时感谢lingyu的密码学方面大力帮助,没有lingyu的帮助,就没有这篇幅教程了:)
--------------------------------------------------------------------------------
【详细过程】
    一款窗口隐藏的工具,可定制性很高,是本人目前见过的窗口隐藏类软件中比较不错的。
(分析过程见:一个窗口隐藏工具的注册验证流程分析(一))
开工前明确一下目标:
 目标    :做出目标软件的注册机
 需要工具:Delphi7-2007,FGInt库,RSATool,大数计算器 BigIntCalc 。

预备知识:
  一个窗口隐藏工具的注册验证流程分析(一)  
   RSA保护的程序 注册机制作思路总结 
首先我们分析一下要实现我们的目标需要做哪些工作:
  1、通过前一篇文章的分析我们知道软件中用RSA算法保护的,因此 RSA算法中的n,e,d 是不可或缺的。有了这三个参数,
     我们就可以做“许可文件”了。
  2、同样,前一篇文章我们也分析出了“许可文件”中存放着用户名与授权码的密文,各占一行。我们除了要求出授权码,
     还要想办法求出一个用户名,否则通过授权码的验证后,在界面上的授权用户会显示乱码的。
整理一下:
 注册机要实现的两个功能(我们给其取个名字叫:“注册机 总纲”):
 1、根据机器码生成授权码并转换为密文的功能。
  a、找出RSA算法中的n,e,d。
  b、如果不能顺利的找出e,那还需要patch软件。
  c、用找到的n,e 写授权码生成代码。
 2、根据用户名生成用户名密文的功能。
 3、将1,2生成的密文保存到文件功能。
  
首先进行“注册机 总纲” 第1部分:
  要做RSA注册机必要的三个元素 n,e,d。通过前面的分析,我们知道在软件中已经有 n,d 的存在了,那么对于RSA的攻击我
们能不能用“2、RSA小指数攻击法”这种方法进行呢?不试试是不会知道的:),那我们先进尝试用“2、RSA小指数攻击法”
这种方法来获取做注册机所需要的e。
我们给这个方案取个名字为 :MakeRSAKeyGen 方案一
  为了工作方便,先进行一些前期工作:
  1、由于程序中的n,d都是加密后存放的,所以我们先把他自定义的加解密函数提出来。
  2、把 n,d 的加密字串提出来备用。
  我们先看一下程序的自定义加密函数:

代码:
00483B58 <H>/$  55            push    ebp                                                            ;  subN_222222_Encrypt
00483B59    |.  8BEC          mov     ebp, esp
00483B5B    |.  83C4 F8       add     esp, -8
00483B5E    |.  53            push    ebx
00483B5F    |.  56            push    esi
00483B60    |.  57            push    edi
00483B61    |.  894D F8       mov     [local.2], ecx
00483B64    |.  8BF2          mov     esi, edx
00483B66    |.  8945 FC       mov     [local.1], eax
00483B69    |.  8B45 FC       mov     eax, [local.1]
00483B6C    |.  E8 3310F8FF   call    <HideHelp.System::__linkproc__ LStrAddRef(void *)>
00483B71    |.  33C0          xor     eax, eax
00483B73    |.  55            push    ebp
00483B74    |.  68 EA3B4800   push    <HideHelp.loc_483BEA>
00483B79    |.  64:FF30       push    dword ptr fs:[eax]
00483B7C    |.  64:8920       mov     dword ptr fs:[eax], esp
00483B7F    |.  8B45 F8       mov     eax, [local.2]
00483B82    |.  8B55 FC       mov     edx, [local.1]
00483B85    |.  E8 A20BF8FF   call    <HideHelp.System::__linkproc__ LStrAsg(void *,void *)>
00483B8A    |.  8B45 FC       mov     eax, [local.1]
00483B8D    |.  E8 220EF8FF   call    <HideHelp.__linkproc__ LStrLen_Or_DynArrayLength(void)>
00483B92    |.  8BF8          mov     edi, eax
00483B94    |.  85FF          test    edi, edi
00483B96    |.  7E 3C         jle     short <HideHelp.loc_483BD4>
00483B98    |.  BB 01000000   mov     ebx, 1
00483B9D <H>|>  8B45 F8       /mov     eax, [local.2]                                                ;  输出
00483BA0    |.  E8 6710F8FF   |call    <HideHelp.InternalUniqueString(void &)>
00483BA5    |.  8B55 FC       |mov     edx, [local.1]                                                ;  输入
00483BA8    |.  8A541A FF     |mov     dl, byte ptr ds:[edx+ebx-1]                                   ; 取明文的ASCII码
00483BAC    |.  8BCE          |mov     ecx, esi                                                      ; ESI中存放的是一个种子数 0x59F0F3 (从参数中传进来的)
00483BAE    |.  C1E9 08       |shr     ecx, 8                                                        ; 种子数右移8位
00483BB1    |.  32D1          |xor     dl, cl                                                        ; ASCII码与右移后的值异或
00483BB3    |.  885418 FF     |mov     byte ptr ds:[eax+ebx-1], dl                                   ; 保存密文
00483BB7    |.  8B45 F8       |mov     eax, [local.2]
00483BBA    |.  8B00          |mov     eax, dword ptr ds:[eax]
00483BBC    |.  0FB64418 FF   |movzx   eax, byte ptr ds:[eax+ebx-1]                                  ; 取密文
00483BC1    |.  03F0          |add     esi, eax                                                      ; 种子数+密文
00483BC3    |.  69C6 6DCE0000 |imul    eax, esi, 0CE6D                                               ; 乘 0x58BF
00483BC9    |.  05 BF580000   |add     eax, 58BF
00483BCE    |.  8BF0          |mov     esi, eax                                                      ; 保存种子
00483BD0    |.  43            |inc     ebx
00483BD1    |.  4F            |dec     edi                                                           ; 循环
00483BD2    |.^ 75 C9         \jnz     short <HideHelp.loc_483B9D>
00483BD4 <H>|>  33C0          xor     eax, eax                                                       ;  loc_483BD4
00483BD6    |.  5A            pop     edx
00483BD7    |.  59            pop     ecx
00483BD8    |.  59            pop     ecx
00483BD9    |.  64:8910       mov     dword ptr fs:[eax], edx
00483BDC    |.  68 F13B4800   push    <HideHelp.loc_483BF1>
00483BE1 <H>|>  8D45 FC       lea     eax, [local.1]                                                 ;  loc_483BE1
00483BE4    |.  E8 EF0AF8FF   call    <HideHelp.System::__linkproc__ LStrClr(void *)>
00483BE9    \.  C3            retn
      
以上代码的关键是从0x00483B9D 到 0x00483BD2。感觉转来转去的容易出错,这里偷个懒,直接将关键代码借用:)
Delphi代码如下:
代码:
procedure Encrypt(sIn: string; sOut: PUCHAR);
label
  H3D;
var
  i: Integer;
  p1, p2: PUCHAR;
begin
  p1 := PUCHAR(sIn); 
  p2 := PUCHAR(sOut); 
  i := Length(sIn);
  asm
        mov   ebx,1;
        mov   esi,$59F0F3;
        mov   edi,dword ptr i;
      H3D:
        mov   eax,dword ptr p2;  //Out
        mov   edx,dword ptr p1;  //In
        mov   dl, byte ptr ds:[edx+ebx-1]  ;
        mov   ecx, esi;
        shr   ecx, 8;
        xor   dl, cl;
        mov   byte ptr ds:[eax+ebx-1], dl;
        mov   eax, p2;
  movzx eax, byte ptr ds:[eax+ebx-1];
        add   esi, eax;
        imul  eax, esi, $0CE6D;
        add   eax, $58BF;
        mov   esi, eax;
        inc   ebx;
        dec   edi;
        jnz   H3D;
  end;
end;
经过测试,代码正确。然后是解密函数:
代码:
00483BF8 <H>/$  55            push    ebp                                                            ;  subN_111111_Decrypt
00483BF9    |.  8BEC          mov     ebp, esp
00483BFB    |.  83C4 F8       add     esp, -8
00483BFE    |.  53            push    ebx
00483BFF    |.  56            push    esi
00483C00    |.  57            push    edi
00483C01    |.  894D F8       mov     [local.2], ecx
00483C04    |.  8BF2          mov     esi, edx
00483C06    |.  8945 FC       mov     [local.1], eax
00483C09    |.  8B45 FC       mov     eax, [local.1]
00483C0C    |.  E8 930FF8FF   call    <HideHelp.System::__linkproc__ LStrAddRef(void *)>
00483C11    |.  33C0          xor     eax, eax
00483C13    |.  55            push    ebp
00483C14    |.  68 883C4800   push    <HideHelp.loc_483C88>
00483C19    |.  64:FF30       push    dword ptr fs:[eax]
00483C1C    |.  64:8920       mov     dword ptr fs:[eax], esp
00483C1F    |.  8B45 F8       mov     eax, [local.2]
00483C22    |.  8B55 FC       mov     edx, [local.1]
00483C25    |.  E8 020BF8FF   call    <HideHelp.System::__linkproc__ LStrAsg(void *,void *)>
00483C2A    |.  8B45 FC       mov     eax, [local.1]
00483C2D    |.  E8 820DF8FF   call    <HideHelp.__linkproc__ LStrLen_Or_DynArrayLength(void)>
00483C32    |.  8BF8          mov     edi, eax
00483C34    |.  85FF          test    edi, edi
00483C36    |.  7E 3A         jle     short <HideHelp.loc_483C72>
00483C38    |.  BB 01000000   mov     ebx, 1
00483C3D <H>|>  8B45 F8       /mov     eax, [local.2]                                                ;  loc_483C3D
00483C40    |.  E8 C70FF8FF   |call    <HideHelp.InternalUniqueString(void &)>
00483C45    |.  8B55 FC       |mov     edx, [local.1]
00483C48    |.  8A541A FF     |mov     dl, byte ptr ds:[edx+ebx-1]
00483C4C    |.  8BCE          |mov     ecx, esi
00483C4E    |.  C1E9 08       |shr     ecx, 8
00483C51    |.  32D1          |xor     dl, cl
00483C53    |.  885418 FF     |mov     byte ptr ds:[eax+ebx-1], dl
00483C57    |.  8B45 FC       |mov     eax, [local.1]
00483C5A    |.  0FB64418 FF   |movzx   eax, byte ptr ds:[eax+ebx-1]
00483C5F    |.  03F0          |add     esi, eax
00483C61    |.  69C6 6DCE0000 |imul    eax, esi, 0CE6D
00483C67    |.  05 BF580000   |add     eax, 58BF
00483C6C    |.  8BF0          |mov     esi, eax
00483C6E    |.  43            |inc     ebx
00483C6F    |.  4F            |dec     edi
00483C70    |.^ 75 CB         \jnz     short <HideHelp.loc_483C3D>
00483C72 <H>|>  33C0          xor     eax, eax                                                       ;  loc_483C72
00483C74    |.  5A            pop     edx
00483C75    |.  59            pop     ecx
00483C76    |.  59            pop     ecx
00483C77    |.  64:8910       mov     dword ptr fs:[eax], edx
00483C7A    |.  68 8F3C4800   push    <HideHelp.loc_483C8F>
00483C7F <H>|>  8D45 FC       lea     eax, [local.1]                                                 ;  loc_483C7F
00483C82    |.  E8 510AF8FF   call    <HideHelp.System::__linkproc__ LStrClr(void *)>
00483C87    \.  C3            retn
转换成Delphi代码 方法同加密函数:
代码:
procedure Decrypt(sIn: string; sOut: PUCHAR);
label
  H3D;
var
  i: Integer;
  p1, p2: PUCHAR;
begin
  p1 := PUCHAR(sIn); 
  p2 := PUCHAR(sOut); 
  i := Length(sIn);

  asm
        mov ebx,1;
        mov esi,$59F0F3;
        mov edi,dword ptr i;
      H3D:
        mov   eax,dword ptr p2;  //Out
        mov   edx,dword ptr p1;  //In
        mov   dl, byte ptr ds:[edx+ebx-1];
        mov   ecx, esi;
        shr   ecx, 8;
        xor   dl, cl;
        mov   byte ptr ds:[eax+ebx-1], dl;
        mov   eax,dword ptr p1;   // [local.1]            ;
        movzx eax, byte ptr ds:[eax+ebx-1];
        add   esi, eax;
        imul  eax, esi, $0CE6D;
        add   eax, $58BF;
        mov   esi, eax;
        inc   ebx;
        dec   edi;
        jnz   H3D;
  end;
end;
经过测试,代码同样正确:)。
下面我们把程序中的 n,d 的密文提出来
代码:
var //这里做几个全局变量备用
 gStrN , gStrE , gStrD , gStrUserName , gStrUserLicNum: string;

var
  strD, strN, str: string;
 retPD, retPN : array[0..2048] of UCHAR;

  strD := 'yW08F8H4TKwt6GOtDBSNRGO3XnVgWp=QwyuRhgxWcYyMP5pkcU4GoNSvqDbXZZ10+JRSPozkMPm3BV=yDI18ZlYnLlEZjBodicFLn1GLB28O99ulXhQuaGUV9M' +
    'P8rVf1agDMfOWQIewfDR4pJ=SnyqK7IRbEPjHI=fZ+SM24bE22f2diTZ4uB=+BEt7BMGsHS8q6DMZfhwgE9a+2BfmNAvP2eLASj1XU50+rhCy3QJjuqLX5WRgu' +
    '81oRB447Y3he0FnLyfSwTUEpQcNR1QppIB83Fh+jRdze5C53TZF=iYIpoL0xPCdPmLOpz2lorYh9aKB=vtWfE4OJOFuxGO2cgMuqndkgqp15OBgZNBBPGtV+au' +
    'AEt7r0ZHOmSTeEmPRkk8BFdTSat2sYhHXOiwNBwtJ61KN2CNKF3rG7mZwBI1oM5r1o++ryh5p6wsS=yLWeO1IjA+OEjG9gresLOmK36P3REMaJaw3MbwfiqbhF' +
    '7TxT1BVIkeXpQG+xmmjZi8KHGXCHSbByoZExm9m=CUBH4H1i1kXroxB6dm9RbZnfEjjRQgscRzBid1cke4mg58=InsD+WufkFORJqoteVZ8oOC3UEG4mX7dJCr=4O8CYwEm6eHY' +
    'HxzM402e5T5QZjToaJi2vThQmK7FXcJoeaDsgKRY7baihvhefHGqTrWZnIPx0wh0GI+jK9sS9tBZ8bcd4E+9bPc7Ms1IT9c2vqqZs1HIAzkj+I04915tCE0dY6WsrfZfzryVqrNoU1BBLZfOPoaAmWq=eY9sus93M5LrQ';
  //========================
  strN := 'zNznSHe3UsWKxYFSHxsC1vYSC8AR3hUO6hS4COiIaMetdexdSQFc+GZD5p+A5byHZn0oLJIN6V+OLnoz060duZdrZNRNWS7EBIkrqB6nLCjNP+mTDlk4eASn3f' +
    'Rfv+g30q+i5ou6upkF7RbT07PxrvAUPF88Dyb4ME6okCPoXAStRrwIIFCi26rt82TX4bNfHXoXxUm=dIh5P+gA9k=CxAmo5DqQwQvKVrZDW9C6n1rTWtXWRyLI' +
    'sbafFNKnYL2FUqMLhPzA0S40wcLm5acW4smLSlhAjwXdGekhJf8kaHZK+nqOVFdk7YCAPfUm2=6iL2pRn9OOgQ9mrr2d+F6M+w2TReNA9RyHX1hv5pD+DPAAAf' +
    'dUQu5QwLwHX6bOzkIr2TP0+SXZ+HKedbJpZLK+UcoJGZsCJtZy2M=AqF9REI1=SEX4znk1AqLWmoEsFN8QWD4i3JKgnrI12nOF=DxhfVvhMQfCIxkCa2Z3ypbx' +
    'erlKeFD9pnrg9Awpwmoafg6ZC+MqC0fCROUiaTtiUdIuntUvt+Tj+k1WEveVZp0RKMX7zvCh6OUkKQGQILE=k=htdWVejSAmBvFKOWjOi5iQ6OCOnwqR3rtFi=5bCxeBbFto0EpZk' +
    'ima63sQrvI6+cIEmWfZaf0x6eBiBlGz1LqwMADqeKX5SFZjNWNn9dg0xSA2cB5yMea5OqB3DboYV+w=zM+IwErztyHuU2PsPZfo=vDQZQWsLEhADGRXn4UT1lm+NZJamWK3IHzfIfb8awzCofXXU=weO5yuJOSztkFf';
在“一个窗口隐藏工具的注册验证流程分析(一)”中我们知道 密文转换为明文的过程是:
  先Base64解码,然后进行解密。
代码:
  ZeroMemory(@retPD[0], 2048);
  ZeroMemory(@retPN[0], 2048);

  ConvertBase64to256(strD, str);
  Decrypt(str, @retPD[0]);
  gStrD := StrPas(@retPD[0]);

  ConvertBase64to256(strn, str);
  Decrypt(str, @retPN[0]);
  gStrN := StrPas(@retPN[0]);
在OD中对比一下转换结果,完全正确:),至此准备工作结束。
下一步就是碰运气,穷举e 
根据 “RSA保护的程序 注册机制作思路总结”中的“2、RSA小指数攻击法”做如下代码:
代码:
procedure TForm1.Button1Click(Sender: TObject);
var
  C, D, N, t: TFGInt;
  i: Int64;
  CC, MM: string;
begin

  i := 1;
  CC := '244';
  MM := '244'; //C=M=随机数,这里取244

  Base10StringToFGInt(CC, C);
  Base10StringToFGInt(gStrD , D); //'113'      
  Base10StringToFGInt(gstrN, N);  // '253'{用于测试算法的数据,能正确得到e 为37}   
  FGIntModExp(C, d, n, t);
  base10stringtofgint('1', C);
  while (true) do
  begin
    FGIntMulMod(t, c, n, c);
    FGIntToBase10String(C, CC);
    if CompareText(cc, MM) = 0 then
      Break;
    Inc(i);
    if (i mod 1000) = 0 then
    begin  //每1000次输出一次记录,这样知道程序还活着:)
      Application.ProcessMessages;
      Memo1.Lines.Add(IntToStr(i - 1) + ':' + cc);
    end;
  end;
  ShowMessage('Find e is:'IntToStr(i));

  FGIntDestroy(c);
  FGIntDestroy(D);
  FGIntDestroy(N);
  fgintdestroy(t);
end;
??  点击 Button1 开始烤机:),大约 烤了一整天 跑了大约 26209999 圈(软件作者要是看见到这儿一定大笑),无果。
(低指数的方法不是用穷举的,如果已知d,e<N^0.25时可以用连分数的方法求得e,e<N^0.292时可以用格的方法求解)
  这条路行不通了,那么我们只能patch程序了,选用“RSA保护的程序 注册机制作思路总结”中的“ 5、Patch 软件中
的 n 方法  方法(2):patch  n”
同样我们给这个方案取个名字为 :MakeRSAKeyGen 方案二
  要用这个方案我们需要做以下工作:
  1、修改 软件中的 n -> n1 使其能被分解,这一步我作最笨的方法,将 n 复制到“RSA-Tool 2 by tE!”中,从最后一
位开始修改,每修改一个数,就试着分解一次,一直试到能被分解。(有好方法的大侠,指点一下:))
  2、根据1的结果n1求出e1。
  3、由于软件中的 n是加密存放的,我们要将 n1 进行加密,然后找出与软件中 n 的密文的差异。
  4、由于软件是加了壳,脱壳后运行失败(花了半天时间也没找到校验点),所以要写SMC代码进行patch。

好,先进行第1步:
  用“MakeKeyGen 方案一”中的gStrN在“RSA-Tool 2 by tE!”中不断的尝试,最终得到一个可以分解的
n1=
'9109676641797590455299325381557970644094332447294568208169542387328200377287362660196248240302975224' +
'36162594303063564362304511537109338351673819598252874100284451724272462199810305492930902303077645209189' +
'639525370608072447987764975873583010657111705896345281041457722837371963022245340260618592689309888097782' +
'3442801287098800457558159160536083128478704452534715956427194741462551268300601677737506330013838312757199' +
'9919264087947284128073111362166647095439931544594741052894988598611281470600469348227362381705777084290741' +
'3044075101173359128336872576132362291767751578567571474102475300622053';
(修改最后的两位 22089 -> 22053 )
分解结果如下:
PRIME FACTOR: 13    
PRIME FACTOR: 21617    21616  
PRIME FACTOR: 353867    353866
PRIME FACTOR: 8400157    8400156
PRIME FACTOR: 1958009509  1958009508
PRIME FACTOR: 556957328254410864802964489413134314407698235229222186968662049226849815313872520449463765519551386442930930845379505343311000298619593291615546427403836090993159409058393687665472968939858310714756327219732522065051448037707929830996761382116689674353568406392955829417532497743804413764200713235026801886093865255095296119037365963007860579072945983141163857894944053537289903161397415187272027703093973830226023001793130418738276225868832257160195521007402441294716393203271657860544397278586971521738598471905875861897447476708590705829135480911805796104499883
 求φ(N)=(13-1)(21617-1)(353867-1)(8400157-1)(1958009509-1)(55695732....6104499883-1)=
840851852130554789386360834770038046146243589926884630332660878723473191579514160278173491167783191340745431740863390195295740520825132465904574291912138799850040725334972587948805868120174672471067858676478799970903128216202731902770875332211662480599192762238113013776410209671122213864394924440647121629814866210646282987015481596381203240374013653319925428883097279145067837237786175314658230451280454878964799308103999221634110531206639630758070786712252068951774592860333476259172862238813876995082526940496198800408450553099204807965530051201457128423989978627851529220296326192443392
2步:
  用大数计算器“Big Integer Calculator v1.13”的"Ans =Y/X MO&D Z"功能:Y=1,X=d,Z=φ(N) 求
e1=
665423198598457135611823655533941559861421578435393300083137353370825608526185673338792379252683689631580106781144591826731820827597966918680104972683892041696413889849467245531128821039527456216630119251985079814477181479640223324877964129768459685847162264422751613043753280968338514199435853919515955358722275569000025933531058497319934065287860726139989849471769211082057086080072991201071542619256467364206306659592811415765154755486076174831848935459978974754170224555305159478325395912611904314893470083999319328907782821874131226034241090139813403525535702340219479896302134850932059
一并附上原始 d=
251093920138545922957428345425986187018379391680658325597208001586587082336209006483792733611868783274894824108564516616502650285636441545385283114632057693558700630149188666293115105220397417877877043140397229482871062720925330665155131419980275911241140161960933672256473450313022833904050367704043644185145463952219275014803282716883335677667469114089112041993223733319724281471769399761823629551042365626333225281691643131618313998088254354745362544245336698857997708190064878066129080140447829183188806927780437695837077042541778002957481446192994806259985210143179008015138950407379667
3步:
  在“一个窗口隐藏工具的注册验证流程分析(一)”中我们知道 密文转换为明文的过程是:
    先Base64解码,然后进行解密。
  我们只要逆向上述过程就可以得到密文:
代码:
var
 retPN :array[0..2048] of UCHAR;
 b64: string;

  ZeroMemory(@retPN[0], 2048);
  Encrypt(n1, @retPN[0]);
  //ConvertBase256to64(StrPas(@retPN[0]), b64);
  //在这里,我们会发现加密字串没有完全转换,原因是在n1经过加密后存在 ASCII #0 。好办,我们改造一个
  // ConvertBase256to64。见下一代码块:(不知道软件中是怎么实现的,有经验的大虾指点一下:))
  MyConvertBase256to64(@retPN, Length(n1) - 1, b64);
  /////////////////
  /////////////////
  procedure MyConvertBase256to64(str256: PUChar; Len: integer; var str64: string); overload;
  const
    chr64: array[1..64] of char = ('a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F',
      'g', 'G', 'h', 'H', 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p',
      'P', 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X', 'y', 'Y',
      'z', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '=');
    PGPchr64: array[1..64] of char = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
      'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
      'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
      'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/');
  var
    temp: string;
    trans: array[0..255] of string;
    i, j, len6: longint;
    g: integer;
    str: array[0..2048] of UCHAR;
  begin
    CopyMemory(@str, str256, 2048);
    initialize8(trans);
    temp := '';
    for i := 0 to Len do temp := temp + trans[ord(str[i])];
    while (length(temp) mod 6) <> 0 do temp := temp + '0';
    len6 := length(temp) div 6;
    str64 := '';
    for i := 1 to len6 do
    begin
      zeronetochar6(g, copy(temp, 1, 6));
      str64 := str64 + chr64[g];
      delete(temp, 1, 6);
    end;
  end;
n1的密文=
zNznSHe3UsWKxYFSHxsC1vYSC8AR3hUO6hS4COiIaMetdexdSQFc+GZD5p+A5byHZn0oLJIN6V+OLnoz060duZdrZNRNWS7EBIkrqB6nLCjNP+mTDlk4eASn3fRfv+g30q+i5ou6upkF7RbT07PxrvAUPF88Dyb4ME6okCPoXAStRrwIIFCi26rt82TX4bNfHXoXxUm=dIh5P+gA9k=CxAmo5DqQwQvKVrZDW9C6n1rTWtXWRyLIsbafFNKnYL2FUqMLhPzA0S40wcLm5acW4smLSlhAjwXdGekhJf8kaHZK+nqOVFdk7YCAPfUm2=6iL2pRn9OOgQ9mrr2d+F6M+w2TReNA9RyHX1hv5pD+DPAAAfdUQu5QwLwHX6bOzkIr2TP0+SXZ+HKedbJpZLK+UcoJGZsCJtZy2M=AqF9REI1=SEX4znk1AqLWmoEsFN8QWD4i3JKgnrI12nOF=DxhfVvhMQfCIxkCa2Z3ypbxerlKeFD9pnrg9Awpwmoafg6ZC+MqC0fCROUiaTtiUdIuntUvt+Tj+k1WEveVZp0RKMX7zvCh6OUkKQGQILE=k=htdWVejSAmBvFKOWjOi5iQ6OCOnwqR3rtFi=5bCxeBbFto0EpZkima63sQrvI6+cIEmWfZaf0x6eBiBlGz1LqwMADqeKX5SFZjNWNn9dg0xSA2cB5yMea5OqB3DboYV+w=zM+IwErztyHuU2PsPZfo=vDQZQWsLEhADGRXn4UT1lm+NZJamWK3IHzfIfb8awzCofXXU=weO5yuJOSztk9O
与软件中的原始n密文串比较,我们会发现只是在字串的最后两位9O(HEX 0x39,0x4F)有变化:),注意最后一位是字母'O',
不是数字0,这一个字母费了我一天的时间找原因,原来错看成是数字了,晕!OK第3步完成!
4步:
  在软件中找个地方写SMC代码,将密文差异写入到n密文中,最好的地方就是在壳完全解压资源后进行修补。
注意:在程序中有两处 n的密文,一个是在程序启动时用的,差异字节地址在0x48ADB1处,一个是在导入授权时调用的,差异字节地址在0x4953B1处)。
修补代码为:
mov     dword ptr ds:[48ADB1], 4F39
mov     dword ptr ds:[4953B1], 4F39
我们来看一下壳解压完成准备跳入OEP处的代码:
代码:
004BE384      8906            mov     dword ptr ds:[esi], eax
004BE386      8946 0C         mov     dword ptr ds:[esi+C], eax
004BE389      8946 10         mov     dword ptr ds:[esi+10], eax
004BE38C      83C6 14         add     esi, 14
004BE38F      8B95 22040000   mov     edx, dword ptr ss:[ebp+422]
004BE395    ^ E9 EBFEFFFF     jmp     HideHelp.004BE285
004BE5C6      B8 54710900     mov     eax, 97154
004BE39F      50              push    eax
004BE3A0      0385 22040000   add     eax, dword ptr ss:[ebp+422]
004BE3A6      59              pop     ecx
004BE3A7      0BC9            or      ecx, ecx
004BE3A9      8985 A8030000   mov     dword ptr ss:[ebp+3A8], eax
004BE3AF      61              popad
004BE3B0      75 08           jnz     short HideHelp.004BE3BA
004BE3B2      B8 01000000     mov     eax, 1
004BE3B7      C2 0C00         retn    0C
004BE3BA      68 54714900     push    <HideHelp.start>
004BE3BF      C3              retn
004BE3C0      8B85 26040000   mov     eax, dword ptr ss:[ebp+426]
004BE3C6      8D8D 3B040000   lea     ecx, dword ptr ss:[ebp+43B]
004BE3CC      51              push    ecx
? 在0x004BE3BF处程序就进入入口了,我们就选 0x004BE5C6这里做跳板吧,注意,不要选0x004BE3BA,因为这一行在程序运行时会
被修改。
 修补的代码应该放在哪里呢?书上说是是找节的空隙,这里呢我们用个简单的方法,OD载入程序,运行到OEP处,然后用Winhex
打开程序的内存,在0x004BE5C6 向后的地方找一块全0的地方,这里我们找的是 0x004BE5B0,好就在这里写入修补代码:
代码:
004BE39A     /E9 11020000     jmp     HideHelp.004BE5B0  //跳板,跳到修补代码
////
004BE5B0      60              pushad    //保护现场
004BE5B1      C705 B1AD4800 3>mov     dword ptr ds:[48ADB1], 4F39  //修补
004BE5BB      C705 B1534900 3>mov     dword ptr ds:[4953B1], 4F39
004BE5C5      61              popad     //恢复现场
004BE5C6      B8 54710900     mov     eax, 97154  //将原来的代码补上
004BE5CB    ^ E9 CFFDFFFF     jmp     HideHelp.004BE39F  //跳回去
? 修改完后,右键“复制到可执行文件->再右键->保存文件”。
 现在我们用n1,e1来写授权码生成代码(要用FGint库了):
 在“一个窗口隐藏工具的注册验证流程分析(一)”中我们知道 授权码转换为明文的过程:
   LicNum密文->Base64-256->RSA解密->Base64-256->自定义函数解密->LicNum明文。
 要得到LicNum密文,我们逆向上述过程就可,如下:
   LicNum明文->自定义函数加密->Base256-64->RSA加密->Base256-64->LicNum密文。
 这里的LicNum明文从哪来呢?上篇我们分析过,软件最终比较是否注册成功的关键点是:“LicNum明文中是否含有机器码,
如果有,则注册成功!”所以明文就是软件提供的“机器码”(根据判断规则,只要含有就注册成功,所以机器码的头与尾
都可以插入其他字串)。
代码:
var
  strMachineID ,strUserLicence: string;
  retPMachineID : array[0..2048] of UCHAR;
  e, n : tfgint;


  gStrN := n1;//n1 内容上面找 
  gStrE := e1;//e1 内容上面找
  strMachineID :='软件中显示的机器码';
 
  ZeroMemory(@retPMachineID[0], 2048);
  Encrypt(strMachineID , @retPMachineID[0]);
  MyConvertBase256to64(@retPMachineID, Length(strMachineID) - 1, strMachineID); 

  Base10StringToFGInt(gStrE, e);
  Base10StringToFGInt(gStrN, n);
  RSAEncrypt(strMachineID, e, n, strUserLicence);

  ConvertBase256to64(strUserLicence, gStrUserLicNum);

  FGIntDestroy(e);
  FGIntDestroy(n);
OK 至此,我们的“注册机 总纲”的第1部分工作全部完成!
中场休息一下:) 

接着我们开始处理“注册机 总纲”第2部分:
有了上面的基础,这一步工作比较容易,同样接上篇 我们知道 用户名密文转换为明文的过程是:
    先Base64解码,然后进行解密。
  我们只要逆向上述过程就可以得到密文:
代码:
var
 retPUserName : array[0..2048] of UCHAR;
 strUserName : string; 

  //生成授权用户
  strUserName:= 'FishSeeWater';
  ZeroMemory(@retPUserName[0], 2048);
  Encrypt(strUserName, @retPUserName[0]);
  ConvertBase256to64(StrPas(@retPUserName[0]), gStrUserName);
测试一下代码,ok没问题。
接下来,我们来实现 “注册机 总纲”的第3部分工作:
比较简单,直接上代码:
代码:
  var
    sF : TextFile;
  begin
    sF := TSaveDialog.Create(nil);
    sf.Filter:='我的RSA注册机 许可文件 *.lc|*.lc|所有文件 *.*|*.*||';
    if sF.Execute then
    begin
      AssignFile(F, sF.FileName);
      Rewrite(F);
      Writeln(F, gStrUserName);
      Writeln(F, gStrUserLicNum);
      CloseFile(F)
    end;
  end;
  好了,至此,我们的“做出软件的注册机”全部实现!经测试,KeyGen是可用的:)。当然,patch文件 可用补丁工具生成
一个文件补丁,这样就更小巧了:)!
 当年这个软件费了好大精力爆破,这次又费了好大精力,上篇教程以做总结:)。
 前后花了一个周的时间来分析这个软件的注册算法,中间走了好多圈圈,不过总的来就收获颇丰,用一个周时间搞定了36元,
从时间上看效率奇低,从知识上收获还是蛮高的,有了这一次,从此再无“密码学恐惧症”了:),并且还学会了FGInt库的使
用有机会再用这技术去蹂躏别人去,嘎嘎嘎嘎......:)
 
 是该说再见的时候了,国产软件,想用的兄弟在以上代码中分离注册机吧,不过做出来不要散发呀,大家都不容易:)

把用到的库文件传上来以后备用:)


--------------------------------------------------------------------------------
【经验总结】
   本文从两个方面详细的描述了对于RSA算法保护的程序如何过行攻击,并给出了切实可行的攻击代码。

--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2009年11月18日 11:38:56
上传的附件 FGInt_Library.zip