【文章标题】: 新手补给之脱壳+破解 —— 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
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)