第五节 脱壳高级篇
5、ASProtect保护1、ASProtect v0.95保护
教程写作: 看雪
技术指导:D.boy 和RuFeng
写作日期:2000年5月30日
目标程序:ShowDep 4.0 beta 1
程序大小:Showdep.exe 为177K
程序下载:ShowDep 4.0
使用工具:Softice 4.05; ProcDump 1.6.2 Final; FrogsICE v0.43;Icedump 6.016
首先忠心感谢D.boy 和RuFeng的热心帮助,是在他们的帮助下,我才对PE文件有一定程度的了解。我把从他们那里吸取的经验总结了一篇文章,希望对大家有所帮助。为了使初学者能更好理解这文章,本人尽量写的仔细点。
一、Import表的一些理论知识
在你看这篇文章之前,你应对PE文件的结构有一定了解,否则请先看看PE介绍.
1、现在不少软件脱壳后Import表被损坏或地址发生改变,需要我们手动找到正确的Import表来替换被破坏的Import表,并修正Import表的地址,程序才能正确执行。
程序装载时,需要装载很多函数和DLL文件,这时程序需要判定目标函数的地址并将该函数插补到该执行文件的映像中,所需要的信息都是放在PE文件的Import表,PE文件中的每一个输入函数都明确的列于该表中。
一般来说Import表是存放在程序的.idata块,它一般包含其他外来DLL的函数及数据信息。但也有不少程序的Import表不存在idata块中,给我们判断Import表的地址造成困难,但不要着急,只要了解Import表的结构你就能迅速定位Import表的地址。
2、Import表以一个IMAGE_IMPORT_DESCRIPTOR数组开始。每一个被PE文件隐式连结进来的DLL都有一个IMAGE_IMPORT_DESCRIPTOR。在这个数组中,没有字段指出该结构数组的项数,但它的最后一个单元是NULL,可以由此计数算出该数组的项数。IMAGE_IMPORT_DESCRIPTOR的格式如下:
image_import_descriptors结构:
| IMAGE_IMPORT_DESCRIPTOR struct | ||
| riginalFirstThunk | dd 0 | 该字段是一个指针数组的RVA偏移。其中每一个指针都指向一IMAGE_IMPORT_BY_NAME结构 |
| TimeDateStamp | dd 0 | 时间及日期标志,在这可以忽略 |
| ForwarderChain | dd 0 | 正向链结索引,在这可以忽略 |
| Name | dd 0 | 以NULL结尾的ASCII字符的RVA地址,该字符串包含输入的DLL名,比如"Kernel32.dll" 或"USER32.DLL" (关键!,我们定位Import表的依据) |
| FirstThunk | dd 0 | 该字段是在Image_thunk_data联合结构中的RVA偏移。大多数情况下,Image_thunk_data是指IMAGE_IMPORT_BY_NAME结构的指针。如果不是一个指针的话,那它就是该功能在DLL中的序号。 |
| IMAGE_IMPORT_DESCRIPTOR ends | ||
3、为了使大家更好理解,我们以ShowDep 4.0 beta的Import表为例,ShowDep 4.0用 ASProtect 加壳,用prodump 分析,发现没有idata段。 脱壳大师D.boy很成功分析出该软件的Import表:import rav 0042D104,size 00001470 ;Image Base(基址)=00400000。
该软件的Import表的image_import_descriptors结构如下:
| -----SHOWDEP!.rdata+1104--------------------------dword-------------PROT---(0)-- |
| 0030:0042D104 ①0002D23C ②00000000 ③00000000 ④0002DA8A <............... ^ |
| 0030:0042D114 ⑤0002C070 ⑥0002D43C ⑦00000000 ⑦00000000 p...<........... v |
为了解释方便,我在每个数据前加了序号。上图就是一个典型的Import表开始处的image_import_descriptors结构,Import表就是以这个数组开始的一段连续内存空间,在这里大小是1470的连续空间。各项数据含义如下:
| IMAGE_IMPORT_DESCRIPTOR struct | ||
| riginalFirstThunk | dd 0 | ①0002D23C |
| TimeDateStamp | dd 0 | ②00000000 |
| ForwarderChain | dd 0 | ③00000000 |
| Name | dd 0 | ④0002DA8A(关键!,我们定位Import表的依据) |
| FirstThunk | dd 0 | ⑤0002C070 |
| IMAGE_IMPORT_DESCRIPTOR ends | ||
现在我们将image_import_descriptors结构中每项的地址均显示分析一下:
① Name选项(我们定位Import表地址就是以此为依据的)
在这例Name项值为:④0002DA8A
下命令DD 42DA8A (显示内存地址42DA8A的数据,其中42DA8A=④0002DA8A+Image Base(基址))
| -----SHOWDEP!.rdata+1A8A--------------------------dword-------------PROT---(0)-- |
| 0030:0042DA8A 4E52454B 32334C45 4C4C442E 019A0000 KERNEL32.DLL.... ^ |
| 0030:0042DA9A 64616F4C 73727543 0041726F 6F4C01AB LoadCursorA...Lo v |
想必大家己睁大眼睛了,发现什么有价值的东西?对,就是KERNEL32.DLL!就是我们的突破口。
Import表装载的基本原理是:根据Import表的指示找到外部模块的文件名,再使用Win32 API函数GetModuleHandleA获得该模块在内存中的句柄。如果没在内存中就使用LoadLibraryA API调用装入该模块。随后使用获得的模块句柄调用Win32 API函数GetProcAddress 获得该模块中Import表指定功能的实际地址,加上装入基址,并且填入Import表的FirstThunk所指的IMAGE_IMPORT_BY_NAME结构指针数组中,完成该模块的一个功能的人工装入填写。循环调用函数GetProcAddress以获得其他功能调用的地址,加上装入基址,并填入之,以完成一个外部模块的装入。再循环上述过程对其他模块进行装入。
因此我们可以从函数LoadLibraryA入手,该函数会装入外部模块,我们监视这函数的入口参数是否为KERNEL32.DLL,以此来确定Import表的状况。即确定 image_import_descriptors结构中的name选项。
函数LoadLibrary:
| HINSTANCE LoadLibrary( LPCTSTR lpLibFileName // 执行模块的文件名和地址 ); |
只要函数LoadLibrary参数的模块名为KERNEL32.DLL,就会出现图二的情况,这时KERNEL32.DLL地址为0042DA8A。因此image_import_descriptors结构中name为0042DA8A—400000=2DA8A,这时利用S命令在内存中查找字条串002DA8A,就可确定import表的地址。
(到这里我们就能确定Import表的地址了,下面几项可帮助我们大家更好理解Import表)
②riginalFirstThunk项
在这里riginalFirstThunk项值为:①0002D23C
下命令DD 42D23C (显示内存地址42D23C的数据,其中42DA8A=①0002D23C+Image Base(基址))
| -----SHOWDEP!.rdata+123C--------------------------dword-------------PROT---(0)-- |
| 0030:0042D23C 0002D798 0002D7A8 0002D7BE 0002D782 ................ ^ |
| 0030:0042D24C 0002D7CC 0002D7E0 0002D7F6 0002D80A ................ ^ |
| 0030:0042D25C 0002D81C 0002D830 0002D840 0002D854 ....0...@...T... v |
| 0030:0042D26C 0002D868 0002D87C 0002D890 0002D8A0 h...|........... v |
图三是一个指针数组,其中每一个指针都指向一IMAGE_IMPORT_BY_NAME结构。
我们以第一个指针0002D798为例,显示它所指的IMAGE_IMPORT_BY_NAME结构.
下命令:dd 42d798(注意:0002D798+Image Base(基址))
| -----SHOWDEP!.rdata+1798--------------------------dword-------------PROT---(0)-- |
| 0030:0042D798 6547011B 636F4C74 69546C61 0000656D ..GetLocalTime.. ^ |
| 0030:0042D7A8 6F430025 6E69746E 65446575 45677562 %.ContinueDebugE ^ |
| 0030:0042D7B8 746E6576 022B0000 65736552 65764574 vent..+.ResetEve v |
| 0030:0042D7C8 0000746E 615702CB 6F467469 62654472 nt....WaitForDeb v |
③FirstThunk项
在这例,FirstThunk项的值为:⑤0002C070
下命令dd 42c070 (注意:0002c070+Image Base(基址)):
| -----SHOWDEP!.rdata+0070--------------------------dword-------------PROT---(0)-- |
| 0030:0042C070 0002D798 0002D7A8 0002D7BE 0002D782 ................ ^ |
| 0030:0042C080 0002D7CC 0002D7E0 0002D7F6 0002D80A ................ ^ |
| 0030:0042C090 0002D81C 0002D830 0002D840 0002D854 ....0...@...T... v |
| 0030:0042C0A0 0002D868 0002D87C 0002D890 0002D8A0 h...|........... v |
图五是一个指针数组(Image_thunk_data联合结构),大多数情况下,Image_thunk_data是指IMAGE_IMPORT_BY_NAME结构的指针。如果不是一个指针的话,那它就是该功能在DLL中的序号。不知你发现没有,图五的数据和图三完全相同,原因就是这个。
二、确定Import表的地址和大小并修正Import表
通过上面的讲解,想必大家对Import表己比较熟悉了吧,现在以脱ShowDep 4.0 beta 1的壳为例,讲解定位Import表的位置和大小的几种方法。
方法一:通过idata来确定Import表的位置
大部分加壳程序的块表中都有.idata这一项,.idata包含其他外来DLL的函数及数据信息,也就是说Import表的起始地址就是.idata的地址。如是这种情况,脱壳就简单多了。
先dump取正确的Import表(假设取名为idata.bin),然后在解压入口点full dump取整个程序(假设取名为dump.exe),用 Procdump打开dump.exe文件, 察看.idata Section. 记下Raw Size和 Raw Offset的值。用HexWorkshop打开dump.exe, 将idata.bin拷贝/粘贴到 dump.exe(粘贴位置为Raw Offset, 大小为Raw Size). 再修正Entry Point和Import表的地址(此值就是.idata的RVA)
具体操作参考后面几节的脱壳教学实例。
方法二:没.idata一项,利用bpx loadlibrarya来判断Import表的位置
由于ShowDep 4.0 beta 1没.idata一项,因此要确定Import表的位置和大小较困难,步骤如下:
①分析ShowDep 4.0 beta 1的文件PE头
运行 Procdump,点击pe-editor按钮,选中ShowDep.exe文件:
Size of image : 000A0000 ; 这个PE文件执行时分配的内存空间。
Image Base : 00400000 ; 基址
②确定Import表的地址
a.先装载Icedump
在这用Icedump 6.016版本,其命令操作形式完全和以前的版本不同。先在Icedump目录里运行相应SOFTICE版本的icedump.exe(我用的SOFTICE是4.05版,因此在win9x/405目录下运行icedump.exe),如Icedump装载成功,Icedump会返回如下信息:
| icedump v6.0.1.6 for winice v4.05 loader icedump unloaded icedump loaded ←出现这句话表示Icedump装载成功 C:> |
b.再装载FrogsICE
由于ShowDep能检测到SOFTICE的存在,因此装载Frogsice就可躲过。在我机子里:FrogsICE 1.00 Final和Icedump不能很好兼容工作,因此我将FrogsICE换成版本v0.43,hehe..它们配合的很好。双击FPloader.exe文件即可装载成功。
OK,到此你的SOFTICE的功能己大大加强里。
这时试试运行ShowDep,这时屏幕将蓝屏给你一菜单选项,告知ShowDep发现了SOFTICE,是否欺骗它,这时你按ESC按钮,程序即可正常运行。(注:我的SOFTICE用icepath打过补丁)
c、拦截函数loadlibararya
下命令:bpx loadlibrarya do "dd esp->4"
(注:在TRW2000下实现同样功能的命令是: bpx loadlibrarya do "dd *(esp+4)"
这个命令就是当拦截loadlibararya函数时,显示其入口参数的在内存的值,如:
0137:00710242 PUSH EAX
0137:00710243 CALL [loadlibarary] ;当调用此函数将中断,并显示push参数的值,在这里即:d eax
ok,断点设置好后,运行ShowDep程序,将中断,此时按F5一直到数据窗口显示为:KERNEL32.DLL字符。在我win97系统下,只要按两下F5,即可看到如下情况:
| -----SHOWDEP!.rdata+1A8A--------------------------dword-------------PROT---(0)-- |
| 0030:0042DA8A 4E52454B 32334C45 4C4C442E 019A0000 KERNEL32.DLL.... ^ |
| 0030:0042DA9A 64616F4C 73727543 0041726F 6F4C01AB LoadCursorA...Lo v |
hehe...看看图六和图三是不是一样啊!其中前面的地址0042DA8A就是关键。
0042DA8A就是image_import_descriptors结构中的name项的值,因为该结构如下:
0030:0042D104 0002D23C 00000000 00000000 0002DA8A <............... ^
0030:0042D114 0002C070 0002D43C 00000000 00000000 p...<........... v
因此这时我要在数据窗口向前查找字符串0002DA8A(0002DA8A=0042DA8A-基址):
下命令:S DS:400000 L FFFFFFFF 8A DA 02 00
(注:在TRW2000下实现同样功能的命令是: S 30:0 L FFFFFFFF 8A DA 02 00)(用上面的好象不行,但愿新版能改进)
结果如下:
| -----SHOWDEP!.rdata+1100--------------------------dword-------------PROT---(0)-- |
| 013F:0042D100 0042B385 0002D23C 00000000 00000000 ..B.<........... ^ |
| 013F:0042D110 0002DA8A 0002C070 0002D43C 00000000 ....p...<....... v |
仔细比较图七和图一,发现这就是Import表的IMAGE_IMPORT_DESCRIPTOR数组。
如你看不习惯,可再下命令: D 42D110-C 这样就可显示和图一一样的画面了。
这样就可确定Import表的地址是2D104=0042D104—400000(基址)
③确定Import表的大小
现己将Import表的起始地址确定了:RVA=42D104。只要确定Import表的尾部就可计算出其大小,Import表在内存里是连续存放的一段数据,其一般结尾处一段内存空间都是0,在此例,你开始先定位来到Import表的起始处,按ALT+↓向下翻页(或ALT+PageDown),直到看到如下情况:
| 013F:0042E54A 65530262 766E4574 6E6F7269 746E656D b.SetEnvironment |
| 013F:0042E55A 69726156 656C6261 011D0041 4C746547 VariableA...GetL |
| 013F:0042E56A 6C61636F 666E4965 0000576F 00000000 ocaleInfoW...... |
| 013F:0042E57A 00000000 00000000 00000000 00000000 ................ v |
| 013F:0042E58A 00000000 00000000 00000000 00000000 ................ v |
字符串0000576F就是Import表的最后一项,其后面一位000000的地址为:42e574(其边界就是上面红色的W,如你在SOFTICE不能确定可DUMP后在十六进制工具Hexworkshop很方便知边界地址)
因此Import表的大小=42E574-42D104=1470
④、找入口点
在SOFTICE你会来到如下:
0137:00710B35 MOV EDX,[EAX]
0137:00710B37 MOV EAX,[EBP+08]
0137:00710B3A ADD EDX,[EAX+18]
0137:00710B3D MOV EAX,[EBP+08]
0137:00710B40 MOV EAX,[EAX+1C]
0137:00710B43 CALL 007104C8 ←按F8进去
0137:00710B48 POP EDI
0137:00710B49 POP ESI
0137:00710B4A POP EBX
0137:00710B4B POP ECX
按F8进去来到:
| 0137:007104C6 8BC0 MOV EAX,EAX |
| 0137:007104C8 89C4 MOV ESP,EAX |
| 0137:007104CA 89D0 MOV EAX,EDX |
| 0137:007104CC 8B1D34567100 MOV EBX,[00715634] |
| 137:007104D2 89041C MOV [EBX+ESP],EAX |
| 0137:007104D5 61 POPAD |
| 0137:007104D6 50 PUSH EAX ←此处EAX=422c3a即入口点的值 |
| 0137:007104D7 C3 RET ←返回到入口点 |
此时程序己完全解压准备运行了。记下程序入口点:00422c3a 在dump前,清除所有的断点:bc *. 因此你可在SOFTICE下用命令:
:/dump 400000 A0000 c:\temp\dump.exe
(如你是用Icedump 6.016以前版本用此命令:pagein d 400000 A0000 c:\temp\dump.exe)
⑤修正PE文件头
用 Procdump打开刚建好的 dump.exe文件,点击pe-editor按钮,然后再点击SECTIONS按钮,在每个section点击右键,选中Edit section,把所有的 section 的PSize == VSize offset == RVA (即让物理地址和大小等于虚拟地址和大小)。如你是用Procdump脱的壳,可省去这一步。
在改完所有的sections后,按OK,存盘后,你在资源管理器中刷新一下,就会发现dumped.exe的图标回来了,但还不能运行,你还要修正入口点和import表。
将入口点(Entry Point)改为:00422c3a(记着:00422c3a-imagebase=22c3a)
再点击Directory按钮,将Import Table改为 RVA (2D104 );而其选项Size只要比0大就可;(这程序DUMP后improt并没被破坏,只要把import rav/size 填上就OK了!) 在这例中,Import表的尺寸没用上,但方法要掌握,如碰到Import表损坏的程序,就要替换Import表程了,这时需要Import表的大小了。
然后点击OK,退出Procdump,再运行 dumped.exe ,程序成功运行!
方法三:利用S命令查找字串KERNEL32.DLL来确定Import表的位置
如果软件没.idata项,用方法二bpx loadlibrarya do "dd esp->4"也不能看到KERNEL32.DLL,这种情况就要用S命令来协助了。
先bpx loadlibrarya do "dd esp->4"
中断后,凭经验来判断Import表完全触压时机,一般中断在第一次或第二次(某些情况要几次)Import表就基本解压结束了。这时下命令:S DS:400000 l FFFFFFFF 'KERNEL32.DLL'
会在数据区找到KERNEL32.DLL ,但这不一定是image_import_descriptors结构中对应的KERNEL32.DLL项,这要跟据具体情况来分析了,一般我们要找到的KERNEL32.DLL是在xxx:4xxxxxx的地址形式处。
如不能确定何处是关键的KERNEL32.DLL,只好dump后,用十六进制工具来分析了,这样较直观,打开后查找image_import_descriptors类似结构。
当然Import表确定方法多种,这里将本人常用的几种方法列出供参考!也欢迎你将自己的经验告诉大家,互相提高。
2、ASProtect v0.94b保护
英文原作:r!sc 《Almost Manual Unpacking (using Softice and Icedump)》
原作日期:6th febuary 2000
教程翻译: 看雪
翻译日期:2000年5月26日
声 明: 本文以r!sc的教程为基础,以自己的观点补充调整。
目标程序:aspack.exe . 231,424 . v 2.001
程序下载:AsPack
使用工具:Softice 4.05; ProcDump 1.6.2 Final; FrogsICE v0.43;Icedump 6.016
part1 . 理论知识
part2 . 分析原文件的PE头
part3 . 抓取import table
part4 . Dump整个程序并修正文件头
=part1===part1===part1===part1===part1===part1===part1===part1===part1===part1=
理论知识
这种被压缩或加密的PE文件,执行时,在内存中将会完全解压。其中import表在装载中也会被完全解压或解密(—攻击点)。其中抓取import表就很关键了,其具体位置,可用 Procdump分析文件头得到。然后,要跟踪程序完全解压后的跳到程序处的入口点,然后在内存里dump取程序的整个部分。将刚dump取的正确import表用十六进制工具粘贴到完全dump的程序,再 修正文件头,这样程序就可正常运行。
=part2===part2===part2===part2===part2===part2===part2===part2===part2===part2=
分析原文件的PE头
运行 Procdump,点击pe-editor按钮,选中ASPack.exe文件,我们想要得到程序解压后的尺寸,import表的地址和大小...幸运的是这个文件的每个块(section )都存在。
Size of image : 00079000 ; 这个PE文件执行时分配的内存空间。
Image Base : 00400000 ; 基址
.idata ;.idata包含其他外来DLL的函数及数据信息
Virtual Size : 00002000 ; idata在内存的尺寸
Virtual Offset : 00046000 ; idata的地址(+imagebase == 00446000)
.rdata
Virtual Size : 00001000
Virtual Offset : 00049000
import表有可能在idata块或rdata块,到底在哪部分?看看它们的尺寸,我将马压在.idata ...
=part3===part3===part3===part3===part3===part3===part3===part3===part3===part3=
抓取import table
1、先装载Icedump
在这用Icedump 6.016版本,其命令操作形式完全和以前的版本不同。先在Icedump目录里运行相应SOFTICE版本的icedump.exe(我用的SOFTICE是4.05版,因此在win9x/405目录下运行icedump.exe),如Icedump装载成功,Icedump会返回如下信息:
| icedump v6.0.1.6 for winice v4.05 loader icedump unloaded icedump loaded ←出现这句话表示Icedump装载成功 C:> |
2、再装载FrogsICE
由于aspack能检测到SOFTICE的存在,因此装载Frogsice就可躲过。在我机子里:FrogsICE 1.00 Final和Icedump不能很好兼容工作,因此我将FrogsICE换成版本v0.43,hehe..它们配合的很好。双击FPloader.exe文件即可装载成功。
OK,到此你的SOFTICE的功能己大大加强里。
这时试试运行aspack,这时屏幕将蓝屏给你一菜单选项,告知aspack发现了SOFTICE,是否欺骗它,这时你按ESC按钮,程序即可正常运行。
3、 记住我们的第一步是抓取import table,它在内存的446000到449000处,因此程序运行时注意这段内存代码的解压情况。
由于SOFTICE不能LOAD aspack.exe,我们用:bpx loadlibrarya命令来拦断。
loadlibrarya命令解释:如果import表没在内存中就使用LoadLibraryA API调用装入该模块,因此我们可以拦截此函数来观察import表。
:bpx loadlibrarya 然后运行aspack将中断如下:
Break due to BPX KERNEL32!LoadLibraryA
:dd 446000 l 40 (下此命令观察内存446000到449000处的数据)
.
-------SPACK!.idata--------------------dword----------ROT--?(0)--
0030:00446000 ???????? ???????? ???????? ???????? ................
0030:00446010 ???????? ???????? ???????? ???????? ................
0030:00446020 ???????? ???????? ???????? ???????? ................
0030:00446030 ???????? ???????? ???????? ???????? ................
上图是SOFTICE的数据窗口,显示 ??...说明import表在内存中没解压。
再按F5一下,程序将中断如下:(记着:在这例中只能要按一下F5,否则将不能抓取正确的import表)
0030:00446000 00000000 00000000 00000000 0004669C .............f..
0030:00446010 0004612C 00000000 00000000 00000000 ,a..............
0030:00446020 000468B6 000461AC 00000000 00000000 .h...a..........
0030:00446030 00000000 000468D0 000461B4 00000000 .....h...a......
这时446000处不是?? ?? ?? ??,意味着import表己被解压了。
.import表以一个IMAGE_IMPORT_DESCRIPTOR数组开始。 image_import_descriptors数据有5组dwords组成。
image_import_descriptors结构:
①dd offset original_first_thunk
②dd timedatestamp 时间及日期标志
③dd forwardchain 正向链结索引
④dd offset library name以NULL结尾的ASCII字符的RVA地址,该字符串包含输入的DLL名,
比如"Kernel32.dll"或"USER32.DLL"。
⑤dd offset first_thunk 该字段是在Image_thunk_data联合结构中的RVA偏移
其中timedatestamp和forwardchain通常设置为00000000, original first thunk 选项不是必须的.
:dd 446000 l 40
0030:00446000 00000000 00000000 00000000 0004669C .............f..
0030:00446010 0004612C 00000000 00000000 00000000 ,a..............
地址4669c 指向 LibraryName (RVA, 你需要加上基址imagebase+400000)
:db 44669c l 10
0030:0044669C 4B 45 52 4E 45 4C 33 32-2E 44 4C 4C 00 00 00 00 KERNEL32.DLL....
地址612c指向 first_thunk 库。
:dd 44612c l 10
0030:0044612C 000466AA 000466C2 000466DA 000466F2 .f...f...f...f..
这些是以NULL结尾的ASCII字符的RVA地址, . . 466aa 是第一个API函数的地址,466c2是第二个API函数地址...它们以以NULL结尾。
:db 0004466aa l 20
0030:004466AA 00 00 44 65 6C 65 74 65-43 72 69 74 69 63 61 6C ..DeleteCritical
0030:004466BA 53 65 63 74 69 6F 6E 00-00 00 4C 65 61 76 65 43 Section...LeaveC
通过上面的分析可知,这就是原始的.import表,快dump it!!(看看上文的image_import_descriptors地址)
:/dump 446000 2000 c:\aspack.idata.bin
(如你是用Icedump 6.016以前版本用此命令:pagein d 446000 2000 c:\aspack.idata.bin)
为了方便大家对比,特将dump正确的import表放在此下载。
=part4===part4===part4===part4===part4===part4===part4===part4===part4===part4=
Dump整个程序并修正文件头
1、现在我们要找程序的入口点,下命令:bpx loadlibrarya ,然后按14下F5,然后按F10一步一步跟踪来到如下代码:
| 0137:00C1150E 8B4508 MOV EAX,[EBP+08] 0137:00C11511 8B10 MOV EDX,[EAX] DS:004664FC=00400000 0137:00C11513 8B4508 MOV EAX,[EBP+08] 0137:00C11516 035018 ADD EDX,[EAX+18] 0137:00C11519 8B4508 MOV EAX,[EBP+08] 0137:00C1151C 8B401C MOV EAX,[EAX+1C] 0137:00C1151F E874F9FFFF CALL 00C10E98 ←在此按F8进入 0137:00C11524 5F POP EDI 0137:00C11525 5E POP ESI 0137:00C11526 5B POP EBX 0137:00C11527 59 POP ECX 0137:00C11528 59 POP ECX 0137:00C11529 5D POP EBP 0137:00C1152A C20400 RET 0004 |
F8进入后来到如下:
| 0137:00C10E96 8BC0 MOV EAX,EAX 0137:00C10E98 89C4 MOV ESP,EAX 0137:00C10E9A 89D0 MOV EAX,EDX 0137:00C10E9C 8B1D6C66C100 MOV EBX,[00C1666C] 0137:00C10EA2 89041C MOV [EBX+ESP],EAX 0137:00C10EA5 61 POPAD 0137:00C10EA6 50 PUSH EAX ;push 442b98 即为入口点 0137:00C10EA7 C3 RET ;返回到己完全解压的代码处,即入口点处。 0137:00C10EA8 C3 RET |
来到入口点:
| 0167:00442B98 55 PUSH EBP ←此处为入口点 0167:00442B99 8BEC MOV EBP,ESP 0167:00442B9B 83C4F4 ADD ESP,-0C |
在0167:00442B98 处就可dump整个内存数据了,此时程序己完全解压准备运行了。记下程序入口点:00442B98
在dump前,清除所有的断点:bc *.
./dump 400000 79000 c:\aspack.dumped.exe
(如你是用Icedump 6.016以前版本用此命令:pagein d 400000 79000 c:\aspack.dumped.exe)
2、替换正确的import表
用Hexworkshop打开aspack.dumped.exe和aspack.idata.bin. Goto到exe文件的46000偏移处,Select Block大小为2000. 拷贝aspack.idata.bin文件的同样大小(2000)的Block,粘贴到exe文件中以替换掉不正确的.idata section,然后存盘。(注意:以上所有数据都是十六进制)
3、修正PE文件头
用 Procdump打开刚建好的 aspack.dumped.exe文件,点击pe-editor按钮,然后再点击SECTIONS按钮,在每个section点击右键,选中Edit section,把所有的 section 的PSize = VSize offset = RVA 。
如:CODE 的PSize=0001E000; VSize=00042000;offset =00000400;RVA=00001000;
改成:PSize = VSize= 00042000;offset = RVA =00001000;
在改完所有的sections后,按OK,存盘后,你在资源管理器中刷新一下,就会发现aspack.dumped.exe的图标回来了,但还不能运行,你还要修正入口点和import表。
将入口点(Entry Point)改为:00042B98(记着:00442B98-imagebase=42B98)
再点击Directory按钮,将Import Table改为 RVA (46000 );而其选项Size只要比0大就可;
然后点击OK,退出Procdump,再运行 aspack.dumped.exe ,程序运行的很甜美!
这时你用W32DASM不能反汇编,你可用 Procdump编辑第一个section characteristics:
将其 c0000060 (data, writable)改为: 60000040 (code, executable)或 e0000060 (code, data, etc etc)
注:大家抓取屏幕可在Icedump 6.016中,用:/Screendump抓取。
不加参数命令:/Screendump 选取模式,重复执行,会在0、1、2、3、4五种模式下转换。
模式1(默认)是以文本方式存盘,模式2是以HTML文件存盘。其它的请参考其readme.
模式选好后,就可用命令: /SCREENDUMP [
3、ASProtect v0.9x保护
Advanced Zip Password Recovery 3.0的脱壳
教程写作: 冰毒
作者信箱: break_ice@hotmail.com
写作日期: 2000年3月25日
版权声明: 本文没有版权,允许任意转贴和修改. 但如果只引用文中部分内容时,请最好注明原文出处,以表示对一位Cracker同行的劳动的尊重.
使用工具:
TRW2000 1.03
ProcDump 1.6.2
Hexworkshop 3.02
下载:Advanced Zip Password Recovery 3.0
比起其Beta版来,AZPR 3.0正式版的保护更为加强. 1. 对Softice的多处Check,用FrogsICE不能完全骗过; 2. CRC的校验; 3. 动态地址(好象是这个名吧?); 4.对Loader的防范,这回用Process Patch不行了.
用Softice跟踪它会是一种痛苦 (当然完全可以用Softice,只是你要了解程序的Anti-debugger技巧,在关键的Check后改变跳转方向). 这次祭出我们中国人的骄傲TRW2000来对付它.
先用Procdump的PE Editor查看一下程序的.idata section记下数值. Virtual size=2000, Virtual offset=21000. 另外,记下程序的Image Base: 400000.
1. 运行TRW, Load程序. 程序中断在入口处. 但接下来无论是否BPX设断只要G继续运行程序,便会出错. 下Faults off, G, OK程序退出.
2. 试BPM 421000 (.idata的offset), 再Load程序. G, 程序可以被正常中断, COOL!
3. G,当程序第三次(? 记不清了. 原则是d 421000显示的data区见到XXXX0200 000000字样)中断时,BD*,F10直到下面的语句
*********************************
注意: 程序每次Load时Offset都不一样,你的机器中的XXXX:YYYYYYYY肯定和下面的不同. 下Code on指令,这样才好参照下面的代码. 另外,下面所摘的几段代码是引用高手tiamath的.因为我不清楚在TRW下如何dump屏幕 (谁能指教一下:);而用ICEDump的pagein n命令来Dump Softice的屏幕,我的机器会死机. :(
*********************************
0167:004F34CD 50 PUSH EAX
0167:004F34CE B890274F00 MOV EAX,004F2790
0167:004F34D3 50 PUSH EAX
0167:004F34D4 B8A4274F00 MOV EAX,004F27A4
0167:004F34D9 50 PUSH EAX
0167:004F34DA B8A0284F00 MOV EAX,004F28A0
0167:004F34DF 50 PUSH EAX
0167:004F34E0 B8AC274F00 MOV EAX,004F27AC
0167:004F34E5 50 PUSH EAX
0167:004F34E6 B8646C4E00 MOV EAX,004E6C64
0167:004F34EB 50 PUSH EAX
0167:004F34EC 8B4508 MOV EAX,[EBP+08]
0167:004F34EF 8D4824 LEA ECX,[EAX+24]
0167:004F34F2 8B4508 MOV EAX,[EBP+08]
0167:004F34F5 8B500C MOV EDX,[EAX+0C]
0167:004F34F8 8B4508 MOV EAX,[EBP+08]
0167:004F34FB 8B4008 MOV EAX,[EAX+08]
0167:004F34FE E899F4FFFF CALL 004F299C
0167:004F3503 33C0 XOR EAX,EAX <--在这里Dump .idata
下指令 W 421000 L 2000 azprdata.bin
OK. 再往下,直到
0167:004F3637 8D4818 LEA ECX,[EAX+18]
0167:004F363A 8B4508 MOV EAX,[EBP+08]
0167:004F363D 8B10 MOV EDX,[EAX]
0167:004F363F 8B4508 MOV EAX,[EBP+08]
0167:004F3642 8B401C MOV EAX,[EAX+1C]
0167:004F3645 E8EAF6FFFF CALL 004F2D34 <--F8进入
0167:004F364A 5F POP EDI
0167:004F364B 5E POP ESI
到了
0167:004F2D72 E9148B1DA8 JMP A86CB88B
0167:004F2D77 8E4F00 MOV CS,[EDI+00]
0167:004F2D7A EB01 JMP 004F2D7D
0167:004F2D7C EB89 JMP 004F2D07
0167:004F2D7E 041C ADD AL,1C
0167:004F2D80 EB02 JMP 004F2D84
0167:004F2D82 EBE8 JMP 004F2D6C
0167:004F2D84 61 POPAD
0167:004F2D85 EB01 JMP 004F2D88
0167:004F2D87 E850EB02E9 CALL E95218DC
0167:004F2D8C 17 POP SS
0167:004F2D8D E802000000 CALL 004F2D94
0167:004F2D92 E91758C35E JMP 5F1285AE
0167:004F2D97 5B POP EBX
0167:004F2D98 59 POP ECX
此时小心地跟踪,碰到JMP时按F8而不要按F10
直到显示变成
0167:004F2D72 E9148B1DA8 JMP A86CB88B
0167:004F2D77 8E4F00 MOV CS,[EDI+00]
0167:004F2D7A EB01 JMP 004F2D7D
0167:004F2D7C EB89 JMP 004F2D07
0167:004F2D7E 041C ADD AL,1C
0167:004F2D80 EB02 JMP 004F2D84
0167:004F2D82 EBE8 JMP 004F2D6C
0167:004F2D84 61 POPAD
0167:004F2D85 EB01 JMP 004F2D88
0167:004F2D87 E850EB02E9 CALL E95218DC
0167:004F2D8C 17 POP SS
0167:004F2D8D E802000000 CALL 004F2D94
0167:004F2D92 E91758C35E JMP 5F1285AE
0167:004F2D94 58 POP EAX <-- 光标位于此行时, EAX=401000
0167:004F2D95 C3 RET <--这里
下Suspend指令. 回到Windows. 用ProcDump来Dump(full)脱壳的程序,得到azprdump.exe. 你也可以用TRW的PEDUMP命令来得到脱壳程序,但我个人的经验很容易死机,所以我宁愿用ProcDump来做.
4. 用PE Editor修改程序的Entry Point为1000. 并查看脱壳后程序的.idata section. 此时Raw size=1670, Raw offset=1FC00. 修改Directory中Import Table的RVA=21000,SIZE=1670.
5. 用Hexworkshop打开azprdump.exe和azprdata.bin. Goto到exe文件的1FC00偏移处,Select Block大小为1670. 拷贝.bin文件的同样大小(1670)的Block,粘贴到exe文件中以替换掉不正确的.idata section.
现在,再试着运行程序,应该可以正常运行了. 如果程序出错,再做一件事: 把 .bss section的raw size值改为00000000 (高手tiamath的建议).
有了脱壳的程序,大家应该会Patch它成为注册版了吧. 只需改一个字节.
结语: 用本文所描述的方法,可以对绝大多数Asprotect+Aspack保护的程序进行成功的脱壳. 大家读完这篇教程,不妨找几个程序开刀. 比方说,The Bat! 1.39现在不应该再难住大家了.
好了,这次就到这儿了,下回见.
致谢:
感谢SV, Hobgoblin, tiamath. 没有他们的帮助,我不可能做到这一步.
因为创e 成就无限! ┆ 创e下载:下载绿色!