脱壳存根(stub)

脱壳存根执行了以下三个步骤:

(1)将原始程序脱壳到内存中

(2)解析原始可执行文件的所有导入函数

(3)将可执行程序转移到原始的程序入口点(OEP)

手动查找OEP

查找尾部跳转指令

  1. 最简单的手动查找策略就是查找尾部跳转指令,当脱壳存根开始运行时,尾部跳转指令跳转的地址不包含有效指令,但是一旦原程序运行就肯定包含有效的指令。

  2. 另一种查找尾部跳转的方法是在栈上设置读断点。要设置读断点,必须使用硬件断点,或一个OllyDbg内存断点来实现。

  这种方法应用在ESP定律法中:

  (1) 首先step-over,直到esp的值产生变化,记录下esp值

  (2)在命令中用dd,在数据窗口中跟随记录下的地址

  (3)然后在该地址设置硬件断点,可以用命令hr xxxxxx

  (4)F9执行程序,然后step-over到达程序的OEP

  1. 另一种查找尾部跳转的策略是在GetProcAddress函数设置断点。多数脱壳器会使用GetProcess函数来解析原始函数的导出表。在该函数上设置断点可以使你绕过脱壳存根的开头代码。

  2. 在原始程序调用且继续向后工作的函数上设置断点。

    查找OEP

  3. 一种手动查找OEP的策略是在代码中每个循环后面设置断点。

  4. 调用GetModuleHandleAGetVersion函数的开始地址就是OEP。

  5. 使用OllyDbgRunTrace选项

手动修复导入表

导入表在内存中实际上有两个表:

  1. 函数名称或者序号列表,其中包含加载器或脱壳存根所需要的函数名称或者序号
  2. 所有导入函数的地址列表。

常见的壳

UPX、ASPack、Petite、WinUpack(Upack)、Themida

脱壳exe和dll的区别

  • DLL中的OEP是DllMain原始函数的开始地址,加壳DLL列出的开始地址是脱壳存根中的一个地址。 打开PE文件,定位到IMAGE_FILE_HEADER节的特征标志域,如果IMAGE_FILE_HEADER节0x2000处的比特位为1,则表示这个PE文件是DLL,为0表示是一个可执行文件。

  • exe的入口点只在开始时执行一次,而dLL的入口点在整个执行过程中至少执行两次,一次是在开始,另一次是在退出时,用来清理dll再退出。

OllyDump为什么能通过跨段找到OEP:
  通常,脱壳存根在一个节里,而可执行程序被打包到另一节中,使用step-over或者step-into方法,当程序从一个节跳转到另一个节运行时,OllyDbg可以探测到这种转移,并且在那里进行中断。step-over方法跳过所有call指令,然而如果一个call函数没有返回,那么OllyDbg将会定位到OEP。

pushA指令:用来存在寄存器的栈地址,在这些地址上设置硬件断点
popAD指令:调用该指令时会触发设置的硬件断点,此时就能在不远的地方找到OEP(在尾部跳转之前会有一个popad或者popfd指令)

课后习题

Lab18-1:直接找到尾部跳转(JMP),然后找到OEP
Lab18-2:利用插件(find oep by section hop(Trace Over))
Lab18-3:插件无法利用,查找可能的尾部跳转(查找指令retn),设置硬件断点,但是没有命中断点。找到程序开头处pushFD和pushAD,在esp第一次改变的地址上设下硬件访问断点。
Lab18-4:和实验三一样的思路
Lab18-5:用ctrl+G定位,在LoadLibrary和GetProcAddress上设置断点,单步执行直到找到OEP