[系统安全25]脱壳技术

1、寻找OEP

想要比较快速精准地寻找OEP,要熟悉不同语言、不同编译器的OEP特征。

1.1、利用内存断点

通过在.text段设断点的方式来找出是哪段代码正在操作此区段中的数据。一般情况下壳的Stub部分与宿主程序的代码段往往不在一个区段中,因此当我们在Stub部分所在的区段中发现了指向宿主程序代码段的跨段跳转时,就代表我们已经找到OEP处了。

实践脱壳:A1Pack Base壳

Demo13-1.exe脱壳测试:测试的是A1Pack Base壳。

脱壳环境

  • 操作系统:Windows 10
  • 反调试工具:x32dbg
  • 加壳程序名:Demo13-1.exe

加壳区段

x32dbg_内存断点脱壳

加壳后的程序多了一个名为.A1Pass的区段,OEP在0x00411200。指向.A1Pass段,可推断.A1Pass段保存有Stub部分。而默认情况下OEP只会只想.text段。

使用x32dbg加载Demo13-1.exe后,打开内存布局窗口(ALT+M)。在其中选择.text段设置内存写入断点。

在.text段的所有位置上设定了写入断点,按F9键运行程序,此时程序中断到了如下位置:

004113E8 | 8810  | mov byte ptr ds:[eax],dl |   ; 我们停在了这里
004113EA | 40    | inc eax                  |
004113EB | 3BC1  | cmp eax,ecx              |
004113ED | 72 F1 | jb demo13-1.4113E0       |
004113EF | C3    | ret                      |

通过反汇编代码可知这是一个循环解密操作,一次解密仅一个字节的数据。在结尾的0x004113EF地址处下一个断点,ALT+M转到内存页面取消先前对.text段设定的写入断点,F9运行程序。直达0x004113EF处后,经过一段单步跟踪调试。来到一个大的跨区段跳转,如下位置:

004111F4 | FF25 24174100            | jmp dword ptr ds:[411724]               |

从跳转的地址可知这是一个跨区段跳转,且跳转目标位于.text区段。再次单步一次即可运行到宿主程序的OEP处。

00401357 | E8 9F2C0000       | call demo13-1.403FFB            |   ;OEP处
0040135C | E9 95FEFFFF       | jmp demo13-1.4011F6             |
00401361 | 8BFF              | mov edi,edi                     |
00401363 | 55                | push ebp                        |   ;VS2010的OEP特征
00401364 | 8BEC              | mov ebp,esp                     |
00401366 | 81EC 28030000     | sub esp,328                     |
0040136C | A3 F8DE4000       | mov dword ptr ds:[40DEF8],eax   |
00401371 | 890D F4DE4000     | mov dword ptr ds:[40DEF4],ecx   |
00401377 | 8915 F0DE4000     | mov dword ptr ds:[40DEF0],edx   |
0040137D | 891D ECDE4000     | mov dword ptr ds:[40DEEC],ebx   |
00401383 | 8935 E8DE4000     | mov dword ptr ds:[40DEE8],esi   |
00401389 | 893D E4DE4000     | mov dword ptr ds:[40DEE4],edi   | edi:".text"
0040138F | 66:8C15 10DF4000  | mov word ptr ds:[40DF10],ss     |
00401396 | 66:8C0D 04DF4000  | mov word ptr ds:[40DF04],cs     |
0040139D | 66:8C1D E0DE4000  | mov word ptr ds:[40DEE0],ds     |
004013A4 | 66:8C05 DCDE4000  | mov word ptr ds:[40DEDC],es     |
004013AB | 66:8C25 D8DE4000  | mov word ptr ds:[40DED8],fs     |
004013B2 | 66:8C2D D4DE4000  | mov word ptr ds:[40DED4],gs     |

脱壳效果

脱壳使用x32dbg的插件Scylla,dump即可。脱壳后的宿主程序.A1Pass的区段还在,但是已经可以使用PEID识别出编译器版本。

1.2、利用堆栈平衡

ESP寄存器在绝大部分的情况下保存的是当前栈顶位置的地址,因此如果加壳程序严格遵守堆栈平衡原则的话,那么在其执行完Stub子程序后打算跳转到宿主程序执行时,ESP指向的栈顶位置就应该与程序刚开始时的一样。

实践脱壳:ASPack壳

Demo13-2.exe脱壳测试:测试的是ASPack壳。

脱壳环境

  • 操作系统:Windows 10
  • 反调试工具:x32dbg
  • 加壳程序名:Demo13-2.exe

加壳区段

加壳特征

00411001 >  60              pushad
00411002    E8 03000000     call Demo13-2.0041100A
00411007  - E9 EB045D45     jmp 459E14F7
0041100C    55              push ebp
0041100D    C3              retn

x32dbg_利用堆栈平衡脱壳

在ESP处设下硬件访问断点,当Stub程序做完所有工作后准备执行宿主程序时,此处的硬件断点就会被触发。

无视首次异常(shift+F9)运行程序到如下位置:

00411416 | 75 08         | jne demo13-2.411420   |
00411418 | B8 01000000   | mov eax,1             |
0041141D | C2 0C00       | ret C                 |
00411420 | 68 57134000   | push demo13-2.401357  |
00411425 | C3            | ret                   |  ; 结束后跳转到OEP

单步步过F8到达OEP位置:

00401357 | E8 9F2C0000       | call demo13-2.403FFB           | OEP处
0040135C | E9 95FEFFFF       | jmp demo13-2.4011F6            |
00401361 | 8BFF              | mov edi,edi                    |
00401363 | 55                | push ebp                       |
00401364 | 8BEC              | mov ebp,esp                    |
00401366 | 81EC 28030000     | sub esp,328                    |
0040136C | A3 F8DE4000       | mov dword ptr ds:[40DEF8],eax  |
00401371 | 890D F4DE4000     | mov dword ptr ds:[40DEF4],ecx  | ecx:EntryPoint
00401377 | 8915 F0DE4000     | mov dword ptr ds:[40DEF0],edx  | edx:EntryPoint
0040137D | 891D ECDE4000     | mov dword ptr ds:[40DEEC],ebx  |
00401383 | 8935 E8DE4000     | mov dword ptr ds:[40DEE8],esi  | esi:EntryPoint
00401389 | 893D E4DE4000     | mov dword ptr ds:[40DEE4],edi  | edi:EntryPoint
0040138F | 66:8C15 10DF4000  | mov word ptr ds:[40DF10],ss    |
00401396 | 66:8C0D 04DF4000  | mov word ptr ds:[40DF04],cs    |
0040139D | 66:8C1D E0DE4000  | mov word ptr ds:[40DEE0],ds    |
004013A4 | 66:8C05 DCDE4000  | mov word ptr ds:[40DEDC],es    |
004013AB | 66:8C25 D8DE4000  | mov word ptr ds:[40DED8],fs    |
004013B2 | 66:8C2D D4DE4000  | mov word ptr ds:[40DED4],gs    |

脱Aspack要修复导入表,否则程序会无法运行。用x32dbg插件Scylla即可很方便的修复IAT。OEP处Dump出程序,然后IAT Autosearch自动搜索导入表,Get Imports获取导入表。Fix Dump修复程序。

1.3、利用编译语言特点

  • 1.利用特征码快速定位OEP

利用特征码搜索功能搜索特征码,第一步就是提取特征码。以VS2010为例,取其前5行反汇编代码作为特征码。

00401357 | E8 9F2C0000       | call demo13-2.403FFB           | OEP处
0040135C | E9 95FEFFFF       | jmp demo13-2.4011F6            |
00401361 | 8BFF              | mov edi,edi                    |
00401363 | 55                | push ebp                       |
00401364 | 8BEC              | mov ebp,esp                    |

得到反汇编后,将其HEX部分的地址常量替换为"?",因为在不同的程序中这些地址会有变动,而通配符?在搜索中可以代表任何字符。替换后结果如下:

E8 ????????
E9 ????????
8BFF
55
8BEC

Ctrl+B搜索:

当加壳的宿主程序解压解密运行完毕后,就可以得到OEP的位置了,如下所示:

两个地址,判断哪个为函数即可推断出OEP在那个地址中。这种方法的前提是已经知道程序为什么语言编写。

  • 2.利用关键API快速定位OEP

不同语言、不同编译器的OEP处所调用的第一个API函数是有所不同的,但是基本上都是一些初始化、获取系统环境信息的API,而这些API在普通壳的Stub部分是比较罕见的。

编译环境 关键API
VC6 GetVersion
VC7.1(VS2003) msvcrt.__set_app_type
VC8.0(VS2005) GetSystemTimeAsFileTime
VC9.0(VS2008) GetSystemTimeAsFileTime
VC10.0(VS2010) GetSystemTimeAsFileTime
Delphi7 GetModuleHandleA
BC++ GetModuleHandleA

2、转储内存映像

Dump操作是将映射在内存中的映像文件按照PE区段表的信息保存到一个文件中。

执行Dump操作的软件:LordPE、PETools、OD中的OllyDump...

将程序运行到真实OEP处,然后选择调试的目标软件进程,在弹出的快捷菜单中选择dump full即可。

3、重建导入表

导入表中起关键作用的结构是IAT(Import Address Table,导入地址表)。由于IAT在数据目录中是导入表的一部分,所以将重建导入表直接称为修复IAT。

3.1、导入表重建原理

重建导入表就是一个由IAT信息逆向推导出导入表的过程,由于IAT中保存有相应API的地址。因此只需要遍历进程空间内所有已加载模块的导出表信息即可获得重建导入表所需的模块名称与API函数名称等信息,有了这些信息后就已经具备了重建导入表所需要的数据。

3.2、导入表重建工具

  • ImportREC
  • Scylla

4、参考

[分享] 手脱ASPack v2.12变形壳2 [复制链接]
https://www.52pojie.cn/thread-433460-1-1.html
OD里alt+F9和Ctrl+F9和shift+F9的区别
https://blog.csdn.net/u011672712/article/details/52136245

posted @ 2018-07-15 00:38  17bdw  阅读(1350)  评论(0编辑  收藏  举报