【文章标题】: 新手补给之脱壳+破解  HwndSpy v1.9(Armadillo)
【文章作者】: rocktx
【软件名称】: HwndSpy
【软件大小】: 920KB
【下载地址】: http://www.highplains.net/public/HPSHwndSpy.exe
【加壳方式】: Armadillo
【保护方式】: Armadillo
【编写语言】: VC6
【操作平台】: WinXP SP3
【软件介绍】: 简直就是 SpyXX 加强版
【作者声明】: 拙作一篇,献给像我一样的精品菜鸟,好好学习,天天向上!
--------------------------------------------------------------------------------
【详细过程】
  
  
  
  一、脱壳
  
  保护方式:
  !- Protected Armadillo
  Protection system (Professional)
  !- <Protection Options>
  Debug-Blocker
  CopyMem-II
  Enable Import Table Elimination
  Enable Memory-Patching Protections
  !- <Backup Key Options>
  Variable Backup Keys
  !- <Compression Options>
  Better/Slower Compression
  !- <Other Options>
  !- 版本号: 4.10 08Apr2005
  !- Elapsed Time 00h 00m 12s 734ms
  
  重点是前三项保护方式:
  Debug-Blocker
  CopyMem-II
  Enable Import Table Elimination
  
  准备工具:
  OD (with HideOD 插件)  插件用于 AntiAntiDebug
  ArmaDetach 1.3   主要功臣,大大加快脱壳进度,有时候在处理 Debug-Blocker 时,1.1 版会比较稳定
  LordPE   处理 PE 模块
  ImportRec 1.6 or 1.7     处理IAT
  ArmInline 0.96   处理 IAT 乱序
  FixRes   处理资源
  ODbgScript plugin v1.47  OD脚本插件,一般使用 1.47 或以上的版本
  CFF Explorer     又一款 PE 编辑器,可以不用,纯粹个人喜好
  
  准备脚本(将下面的文本保存为 .osc 或者 .txt 文件):
  ////////////////////////////////////////////////////////////////////////////////////////////////////
  //转单进程脚本
  msg "请忽略所有异常,并添加忽略C000001E异常,然后运行本脚本!"
  gpa "OpenMutexA","kernel32.dll"
  bp $RESULT
  esto
  exec
  pushad
  pushfd
  push edx
  xor eax,eax
  push eax
  push eax
  call kernel32.CreateMutexA
  popfd
  popad
  jmp kernel32.OpenMutexA
  ende
  bc eip
  msg "现已转换成单进程!"
  ret
  ////////////////////////////////////////////////////////////////////////////////////////////////////
  
  首先用 HideOD 隐藏好OD,然后忽略所有异常,如果实在搞不定,可以用看雪的 OllyICE;
  
  下面开始流水作业(考虑到篇幅和精力,本人未启用完全手脱功能);
  
  
  1、寻找OEP
  
  打开 ArmaDetach v1.3,选择 CopyMem-II 模式,拖入 HwndSpy.exe 文件,显示信息
  -------------------------------------
  Filename: HwndSpy.exe
  Parent process iD:  [000004BC]
  Processing...
  [PROTECTiON SYSTEM]
  Professional Edition
  [PROTECTiON OPTiONS]
  Debug-Blocker protection detected
  CopyMem-II protection detected
  Memory-Patching Protections enabled
  Import Table Elimination enabled
  [CHiLD iNFO]
  Crypto call found:  [0049BA76]
  Child process iD:   [00000D9C]      // 进程ID
  Entry point:        [0042A2C6]      // OEP
  Original bytes:     [558BEC6A]      // OEP 处4个字节值
  Detached successfully :)
  -------------------------------------
  打开OD,附加进程 00000D9C 后 Alt+F9,会停在 OEP,还原上面的4个字节值后:
  0042A2C6    55              push ebp
  0042A2C7    8BEC            mov ebp,esp
  0042A2C9    6A FF           push -1
  0042A2CB    68 F8424500     push HwndSpy.004542F8
  0042A2D0    68 B8914200     push HwndSpy.004291B8
  0042A2D5    64:A1 00000000  mov eax,dword ptr fs:[0]
  0042A2DB    50              push eax
  
  
  2、处理IAT
  
  为避免功亏一篑,这里推荐使用 ArmaDetach v1.1,拖入 HwndSpy.exe 文件后显示信息
  -------------------------------------
  DONE!
  Child process ID: 0000092C          // 进程ID
  Entry point: 004BD000               // 壳的入口
  Original bytes: 60E8                // 壳的入口处头2个字节值
  -------------------------------------
  再打开一个OD,附加进程 00000B54,Alt+F9返回并还原上面的2个字节到入口:
  004BD000 >  60              pushad
  004BD001    E8 00000000     call HwndSpy.004BD006
  004BD006    5D              pop ebp
  004BD007    50              push eax
  004BD008    51              push ecx
  004BD009    0FCA            bswap edx
  004BD00B    F7D2            not edx
  004BD00D    9C              pushfd
  
  用 ODbgScript v1.47 载入上面保存的脚本文件,程序会自动执行,将进程由双变单;
  然后 he GetModuleHandleA,Shift + F9一直运行,注意观察堆栈窗口,直到依次出现:
  00127A6C   00D752BA  /CALL 到 GetModuleHandleA 来自 00D752B4
  00127A70   00D88BAC  \pModule = "kernel32.dll"
  00127A74   00D89CC4  ASCII "VirtualAlloc"
  ...
  00127A6C   00D752D7  /CALL 到 GetModuleHandleA 来自 00D752D1
  00127A70   00D88BAC  \pModule = "kernel32.dll"
  00127A74   00D89CB8  ASCII "VirtualFree"
  ...
  001277D0   00D64F0D  /CALL 到 GetModuleHandleA 来自 00D64F07
  001277D4   00127920  \pModule = "kernel32.dll"
  001277D8   00000000
  
  取消断点,Alt+F9返回到
  00D64F0D    8B0D AC0DD900   mov ecx,dword ptr ds:[D90DAC]
  00D64F13    89040E          mov dword ptr ds:[esi+ecx],eax
  00D64F16    A1 AC0DD900     mov eax,dword ptr ds:[D90DAC]
  00D64F1B    391C06          cmp dword ptr ds:[esi+eax],ebx
  00D64F1E    75 16           jnz short 00D64F36
  00D64F20    8D85 B4FEFFFF   lea eax,dword ptr ss:[ebp-14C]
  00D64F26    50              push eax
  00D64F27    FF15 B432D800   call dword ptr ds:[D832B4]          ; kernel32.LoadLibraryA
  00D64F2D    8B0D AC0DD900   mov ecx,dword ptr ds:[D90DAC]
  00D64F33    89040E          mov dword ptr ds:[esi+ecx],eax
  00D64F36    A1 AC0DD900     mov eax,dword ptr ds:[D90DAC]
  00D64F3B    391C06          cmp dword ptr ds:[esi+eax],ebx
  00D64F3E    0F84 2F010000   je 00D65073                         ; Magic jump,改成 jmp 后,Enter 跟随
  
  来到
  00D65073    83C7 0C         add edi,0C
  00D65076    89BD 78FDFFFF   mov dword ptr ss:[ebp-288],edi
  00D6507C    83C6 04         add esi,4
  00D6507F    395F FC         cmp dword ptr ds:[edi-4],ebx
  00D65082  ^ 0F85 49FEFFFF   jnz 00D64ED1
  00D65088    EB 03           jmp short 00D6508D                  ; 这里F4
  00D6508A    D6              salc
  00D6508B    D6              salc
  00D6508C    8F              ???                                 ; 未知命令
  
  恢复上面的修改,在内存镜像的 .text 段F2下断,Shift+F9运行后断下,IAT解码完毕;
  
  现在回到前一个OD,右键查找模块调用(Search for all intermodular calls),在结果中随便点一个已解码函数调用,回车后跟随IAT地址,比如到
  00F21748  7C80E87C  kernel32.FileTimeToSystemTime
  00F2174C  7C80E8F6  kernel32.FileTimeToLocalFileTime
  00F21750  773D7E70  COMCTL32.CreatePropertySheetPageA
  
  随便复制几个字节的二进制码,到后一个 OD 的内存镜像中查找该值,会找到
  00F312CC  020C0119
  00F312D0  77F18BEE  GDI32.SaveDC
  00F312D4  7E41945D  USER32.GetWindowLongA
  00F312D8  00D6623E
  00F312DC  7E42D312  USER32.DestroyIcon
  00F312E0  7E42D312  USER32.DestroyIcon
  ...
  00F31BF0  77F1E9BE  GDI32.Rectangle
  00F31BF4  7E4186C7  USER32.GetDC
  00F31BF8  77F1DCFF  GDI32.GetTextExtentPointA
  00F31BFC  00000000
  
  将这些已完全解码的 IAT 指针值,用二进制方式复制并粘贴到前一个OD的IAT中,注意不要错位;
  
  然后打开 ArmInline,载入前一个OD,在输入表乱序选项卡中填写:
  New base va of IAT = 0046D000
  Length of existing IAT = 1000       (AmrInline 提供的值通常会偏小)
  
  最后点 Rebase IAT,输入表就移动到 0046D000 处了;
  当然,如果不想用 ArmInline,可以试试 ImportRec的重建IAT功能;
  
  3、dump 内存镜像,用 ImportRec 恢复一下 IAT,脱壳完毕;
  
  
  
  二、破解:
  Armadillo 壳的破解起来比较简单,主要用到两个函数:LoadLibrary、GetEnvironmentVariable,对于某些版本可以
  直接修改验证段的返回值,但是如果程序在多处有检测,逐一修改不仅麻烦,还有可能会错过暗桩的处理;
  
  
  1、bp LoadLibraryA
  
  Armadillo 在验证时,一般会先 LoadLibrary("Armaccess.dll"),凭该函数断点,很容易找到关键代码:
  004145A6    8D85 30FFFFFF             lea eax,dword ptr ss:[ebp-D0]
  004145AC    50                        push eax
  004145AD    FF15 14D24600             call dword ptr ds:[<&kernel32.LoadLibraryA>]    ; kernel32.LoadLibraryA
  004145B3    8BF8                      mov edi,eax
  004145B5    3BFB                      cmp edi,ebx
  004145B7    897D F8                   mov dword ptr ss:[ebp-8],edi
  004145BA    75 09                     jnz short 004145C5                              ; 成功载入就会跳走
  004145BC    5F                        pop edi
  004145BD    5E                        pop esi
  004145BE    33C0                      xor eax,eax                                     ; 否则返回0,所以要让它返回1
  004145C0    5B                        pop ebx
  004145C1    8BE5                      mov esp,ebp
  004145C3    5D                        pop ebp
  004145C4    C3                        retn
  
  往上到段首
  00414550    55                        push ebp
  00414551    8BEC                      mov ebp,esp
  00414553    81EC 98010000             sub esp,198
  00414559    53                        push ebx
  0041455A    56                        push esi
  0041455B    57                        push edi
  
  改为
  00414550    33C0                      xor eax,eax
  00414552    40                        inc eax
  00414553    C3                        retn
  
  
  2、bp GetEnvironmentVariableA 
  
  Armadillo 会使用该函数获取一些环境变量,也就是注册信息,对于本程序,要检测的变量为:
  
  HPSKEYTYPE、USESLEFT、HPSMAXDAYS、DAYSINSTALLED、USERKEY、USERNAME、HPSVERSION、HPSPRODUCTID
  
  当然前面还有个 FIRSTRUN,这些变量名是用 GetEnvironmentVariableA 断点跟踪获得的,本程序的调用方式如下:
  -----------------------------------------------------------
  typedef DWORD (WINAPI * PGENV)(LPCTSTR, LPTSTR, DWORD);
  HMODULE hDLL = LoadLibrary("kernel32.dll");
  // hDLL = GetModuleHandle("kernel32.dll");
  if (hDLL)
  {
    PGENV getenv = (PGENV)GetProcAddress(hDLL, "GetEnvironmentVariableA");
    if (getenv)
    {
      getenv("USERKEY", szKey, sizeof(szKey) / sizeof(TCHAR));
      // 检查 szKey 值
    }
  }
  -----------------------------------------------------------
  下面是找到的验证过程函数之一:
  00401A50    64:A1 00000000            mov eax,dword ptr fs:[0]
  00401A56    6A FF                     push -1
  00401A58    68 2DA04400               push 0044A02D
  00401A5D    50                        push eax
  00401A5E    64:8925 00000000          mov dword ptr fs:[0],esp
  00401A65    81EC E0030000             sub esp,3E0
  00401A6B    8D4424 18                 lea eax,dword ptr ss:[esp+18]
  00401A6F    50                        push eax
  00401A70    E8 4BEB0000               call 004105C0                             ; 这里开始Patch,改成 jmp 0044D150
  00401A75    83C4 04                   add esp,4
  00401A78    85C0                      test eax,eax
  00401A7A    0F84 47090000             je 004023C7
  
  Patch代码:
  0044D150    60                        pushad
  0044D151    E8 00000000               call 0044D156
  0044D156    5F                        pop edi
  0044D157    83EF 06                   sub edi,6
  0044D15A    83C7 50                   add edi,50
  0044D15D    33C0                      xor eax,eax
  0044D15F    66:8B07                   mov ax,word ptr ds:[edi]
  0044D162    66:85C0                   test ax,ax
  0044D165    74 13                     je short 0044D17A
  0044D167    03C7                      add eax,edi
  0044D169    50                        push eax
  0044D16A    66:0347 02                add ax,word ptr ds:[edi+2]
  0044D16E    50                        push eax
  0044D16F    FF15 C8D34600             call dword ptr ds:[46D3C8]                ; kernel32.SetEnvironmentVariableA
  0044D175    83C7 04                   add edi,4
  0044D178  ^ EB E3                     jmp short 0044D15D
  0044D17A    61                        popad
  0044D17B    E8 4034FCFF               call 004105C0
  0044D180  ^ E9 F048FBFF               jmp 00401A75
  
  二进制:
  60 E8 00 00 00 00 5F 83 EF 06 83 C7 50 33 C0 66 8B 07 66 85 C0 74 13 03 C7 50 66 03 47 02 50 FF
  15 C8 D3 46 00 83 C7 04 EB E3 61 E8 40 34 FC FF E9 F0 48 FB FF
  
  数据部分:
  0044D1A0  00040060
  0044D1A4  0004006C
  0044D1A8  00040078
  0044D1AC  00040084
  0044D1B0  00380094
  0044D1B4  000800D4
  0044D1B8  000400E8
  0044D1BC  000800F4
  
  二进制:
  60 00 04 00 6C 00 04 00 78 00 04 00 84 00 04 00 94 00 38 00 D4 00 08 00 E8 00 04 00 F4 00 08 00
  
  ...
  0044D200  00000034  4...
  0044D204  4B535048  HPSK
  0044D208  59545945  EYTY
  0044D20C  00004550  PE..
  0044D210  00000031  1...
  0044D214  53455355  USES
  0044D218  5446454C  LEFT
  0044D21C  00000000  ....
  0044D220  00000032  2...
  0044D224  4D535048  HPSM
  0044D228  41445841  AXDA
  0044D22C  00005359  YS..
  0044D230  00000030  0...
  0044D234  53594144  DAYS
  0044D238  54534E49  INST
  0044D23C  454C4C41  ALLE
  0044D240  00000044  D...
  0044D244  31313131  1111
  0044D248  31313131  1111
  0044D24C  3232322D  -222
  0044D250  32323232  2222
  0044D254  33332D32  2-33
  0044D258  33333333  3333
  0044D25C  342D3333  33-4
  0044D260  34343434  4444
  0044D264  2D343434  444-
  0044D268  35353535  5555
  0044D26C  35353535  5555
  0044D270  3636362D  -666
  0044D274  36363636  6666
  0044D278  00000036  6...
  0044D27C  52455355  USER
  0044D280  0059454B  KEY.
  0044D284  00000000  ....
  0044D288  6B636F72  rock
  0044D28C  00007874  tx..
  0044D290  52455355  USER
  0044D294  454D414E  NAME
  0044D298  00000000  ....
  0044D29C  00000000  ....
  0044D2A0  00000031  1...
  0044D2A4  56535048  HPSV
  0044D2A8  49535245  ERSI
  0044D2AC  00004E4F  ON..
  0044D2B0  31303546  F501
  0044D2B4  00000000  ....
  0044D2B8  50535048  HPSP
  0044D2BC  55444F52  RODU
  0044D2C0  44495443  CTID
  
  二进制:
  34 00 00 00 48 50 53 4B 45 59 54 59 50 45 00 00 31 00 00 00 55 53 45 53 4C 45 46 54 00 00 00 00
  32 00 00 00 48 50 53 4D 41 58 44 41 59 53 00 00 30 00 00 00 44 41 59 53 49 4E 53 54 41 4C 4C 45
  44 00 00 00 31 31 31 31 31 31 31 31 2D 32 32 32 32 32 32 32 32 2D 33 33 33 33 33 33 33 33 2D 34
  34 34 34 34 34 34 34 2D 35 35 35 35 35 35 35 35 2D 36 36 36 36 36 36 36 36 00 00 00 55 53 45 52
  4B 45 59 00 00 00 00 00 72 6F 63 6B 74 78 00 00 55 53 45 52 4E 41 4D 45 00 00 00 00 00 00 00 00
  31 00 00 00 48 50 53 56 45 52 53 49 4F 4E 00 00 46 35 30 31 00 00 00 00 48 50 53 50 52 4F 44 55
  43 54 49 44
  
  原理是调用 SetEnvironmentVariableA 设置环境变量:
  --------------------------------------------------------
  HPSKEYTYPE = "4"        (注册码类型,必须为4)
  USESLEFT = "1"
  HPSMAXDAYS = "2"        (日期限制)
  DAYSINSTALLED = "0"
  USERKEY = "11111111-22222222-33333333-44444444-55555555-66666666"     (注册码任意,长度区间[41, 100])
  USERNAME = "rocktx"   (用户名任意,但不能是"DEFAULT",长度不大于100)
  HPSVERSION = "1"      (版本ID,即:1.9版,是程序之前用 GetFileVersion 获得的)
  HPSPRODUCTID = "F501"
  --------------------------------------------------------
  
  最后要导入注册表(要和环境变量相匹配):
  --------------------------------------------------------
  Windows Registry Editor Version 5.00
  
  [HKEY_CURRENT_USER\Software\High Plains Software\HPS HwndSpy\User]
  "UserName"="rocktx"
  "User"="rocktx"
  "RegistrationKey"="11111111-22222222-33333333-44444444-55555555-66666666"
  --------------------------------------------------------
  
  
  
  三、优化
  
  1、挪动IAT (这一步可以合并到脱壳阶段)
  
  对于一般的 VC 程序,IAT是在 .rdata 区段,以 Notepad98.exe 为例,用 LordPE 查看输入表在 00406000 处,在OD中:
  00406000  000061B4
  00406004  FFFFFFFF
  00406008  FFFFFFFF
  0040600C  00006592      // 这里就是第一个模块 shell32.dll 的偏移,查看 00406592 就会看到
  00406010  000063F8
  
  现在回到 HwndSpy 的.rdata段,右键查找字符串:kernel32.dll,找到
  0045D1BE  4E52454B  KERN
  0045D1C2  32334C45  EL32
  0045D1C6  6C6C642E  .dll
  
  显然,该模块的偏移值为 0005D1BE,继续查找这个常量,来到
  0045BFA8  00000000      // 这里就是IAT的原始位置,RVA = 0005BFA8
  0045BFAC  00000000
  0045BFB0  00000000
  0045BFB4  0005D1BE      // 这里指向 kernel32.dll
  0045BFB8  00000000
  
  当然,kernel32.dll 不一定是第一个模块,数据窗口中上下翻翻,自己确定一下。
  
  从 0045BFA8 到 .rdata 段尾用 00 清空(如果程序有导出表,注意不要一并清空了),将程序保存为一个副本 0001.exe,以后的操作
  都针对这个副本进行,打开 ImportRec,选项中只勾选 Create New IAT (重建IAT) ,获取 IAT 后还原到 RVA 0005BFA8 处即可;
  
  
  2、挪动资源
  
  LordPE 查看 .text1 区段的偏移为 0006D000,下面的步骤就是将资源放到这里:
  a、用 FixRes 打开0001.exe,NewRVA = 0006D000,FileAlignment = 1000,选择导出路径后,点 Dump Resource;
  b、用 CFF Explorer 打开0001.exe,除了前三个区段,将后面的垃圾区段全部删除(Delete Section (Header and Data))后保存;
  c、用 LordPE 打开0001.exe,查看区段表,右键 Load section from disk...(从磁盘中载入区段),载入刚才 Dump 的资源文件;
  如果想要完美,可以将区段名改为 .rsrc,区段属性改为 40000040;
  d、修正资源的 RVA 为 0006D000,重建一下PE;
  e、最后修正可选头中的编译器链接版本为 0600,以免 Peid 认不出来,将 Base of Code 改成 00001000,以免 OD 分析不了代码结构;
  
  3、去掉多余的菜单项( 这些菜单项的功能纯粹是增加右键菜单长度 ):
  00405CB4    8B55 04                   mov edx,dword ptr ss:[ebp+4]                      ; 改成 jmp 00405D05
  00405CB7    6A 00                     push 0
  00405CB9    6A 00                     push 0
  00405CBB    68 00080000               push 800
  00405CC0    52                        push edx
  00405CC1    FFD7                      call edi
  00405CC3    8B45 04                   mov eax,dword ptr ss:[ebp+4]
  00405CC6    68 B8FF4500               push 0045FFB8                                     ; ASCII "Re&gister..."
  
  至此,程序处理完毕,大小 568 kb,可以用UPX压缩;
  
  
  
  
--------------------------------------------------------------------------------
【经验总结】
  全在上面了,好累,以后注意劳逸结合,Armageddon处理一下就可以了。
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2010年06月03日 22:05:12