阳光网驿-企业信息化交流平台【DTC零售连锁全渠道解决方案】

 找回密码
 注册

QQ登录

只需一步,快速开始

扫描二维码登录本站

手机号码,快捷登录

老司机
查看: 3024|回复: 1

[转帖] 《加密与解密(第三版)》--13.5 DLL文件脱壳(重定位表修复部分)

[复制链接]

该用户从未签到

发表于 2008-5-20 16:02:55 | 显示全部楼层 |阅读模式
软件安全系列图书——《加密与解密》(第三版)

bk3.jpg


第13章 脱壳技术
13.1 基础知识
13.1.1 壳的加载过程
13.1.2 脱壳机
13.1.3 手动脱壳
13.2 寻找OEP
13.2.1 根据跨段指令寻找OEP
13.2.2 用内存访问断点找OEP
13.2.3 根据堆栈平衡原理找OEP
13.2.4 根据编译语言特点找OEP
13.3 抓取内存映像
13.3.1 Dump原理
13.3.2 反DUMP技术
13.4 重建输入表
13.4.1 输入表重建的原理
13.4.2 确定IAT的地址和大小
13.4.3 根据IAT重建输入表
13.4.4 ImportREC重建输入表
13.4.5 输入表加密概括
13.5 DLL文件脱壳
13.5.1 寻找OEP
13.5.2 Dump映像文件
13.5.3 重建DLL的输入表
13.5.4 构造重定位表
13.6 附加数据
13.7 PE文件的优化
13.8 压缩壳
13.8.1 UPX外壳
13.8.2 ASPack外壳
13.9 静态脱壳
13.9.1 外壳Loader的分析
13.9.2 编写静态脱壳器
13.10 加密壳
13.10.1 ASProtect
13.10.2 Thmedia的SDK分析


13.5 DLL文件脱壳


说明
DLL文件的脱壳与EXE文件步骤差不多,所不同的是,DLL文件多了个基址重定位表等要考虑。

在2003年出版的《加密与解密》(第二版)中以UPX,PECompact为例讲述了DLL重定位重建的方法,由于本人的思路限制,当时只是从UPX,PECompact自身特点找思路解决这问题,即先分析UPX,PECompact对重定位表处理算法,然后写工具逆算法还原重定位表,如UPXAngela.exe等工具。这种思路的通用性不好,针对不同的壳和版本,要重写工具,并且逆算法可能不完美,从而存在bug。

后来,askformore在“重建重定位表脚本”一文中,提出了一种更通用性的解决办法,利用外壳重定位相关数据时,会根据外壳转储的重定位表确定要重定位的RVA,完成代码重定位工作。将这些要重定位的RVA提取出来,再将这些RVA根据重定位表的定义重新生成一份新的重定位表。shoooo也曾提到过这个思路。于是,在第三版重写这部分时,根据这个思路写了一款工具来完成这个重建功能,详见附件的ReloREC。另外,ReloREC重构重定位表的算法代码,参考了ccfer在看雪论坛.珠海金山2007逆向分析挑战赛 第二阶段第三题 提交的代码。在此一并表示感谢!



声明:本文以第三版“13.5 DLL文件脱壳”一文和其他章节临时整理组织,稍有简化,可能有部分地方用词和描述不是太连贯。

加壳的DLL处理重定位表有以下几种情况:
1)完整的保留了原重定位表;
2)对原重定位表进行了加密处理;
等等

像ASPack,ASProtect等壳属于第1种情况,没有加密重定位表,脱壳后,只需找到重定位的地址和大小即可。
像UPX,PECompact等壳属于第2种情况,必须重建重定位表,这也是本文所要讨论的,本文以UPX为例来讲述一下重定位的重建。
用UPX v3.01将EdrLib.dll文件加壳,用PE工具查看其PE信息。
EntryPoint:E640h
ImageBase:400000h


13.5.1 寻找OEP

当DLL被初次映射到进程的地址空间中时,系统将调用DllMain函数,当卸载DLL时也会再次调用DllMain函数。也就是说,DLL文件相比EXE文件运行有一些特殊性,EXE的入口点只在开始时执行一次,而DLL的入口点在整个执行过程中至少要执行两次。一次是在开始时,用来对DLL做一些初始化。至少还有一次是在退出时,用来清理DLL再退出。所以DLL找OEP也有两条路可以走,一是载入时找,另一方法是在退出时找。而且一般来说前一种方法外壳代码较复杂,建议用第二种方法。
UPX壳比较简单,往下翻翻,就可看到跳到OEP的代码:

代码:

  1. 003DE7F5   .  58              pop     eax
  2. 003DE7F6   .  61              popad
  3. 003DE7F7   .  8D4424 80       lea     eax, dword ptr [esp-80]
  4. 003DE7FB   >  6A 00           push    0
  5. 003DE7FD   .  39C4            cmp     esp, eax
  6. 003DE7FF   .^ 75 FA           jnz     short 003DE7FB
  7. 003DE801   .  83EC 80         sub     esp, -80
  8. 003DE804   >- E9 372AFFFF     jmp     003D1240  //跳到OEP
复制代码


13.5.2 Dump映像文件

停在OEP后,运行LordPE,在进程窗口选择loaddll.exe进程,在下方窗口中的EdrLib.dll模块上单击右键,执行“dump full”菜单命令,将文件抓取并保存到文件里,如图13.43所示。
dll.gif

图13.45 抓取DLL内存映像

对于DLL文件来说,Windows系统没有办法保证每一次运行时提供相同的基地址。如果DLL基址所在内存空间被占用或该区域不够大,系统会寻找另一个地址空间的区域来映射DLL,此时外壳将对DLL执行某些重定位操作。从图13.43得知,此时DLL被映射到内存的地址是03D000h,与EdrLib.dll默认的基址400000h不同,被重定位项所指向的地方是已经重定位了的代码数据。
例如这句:
代码:
003D1266     A1 58B43D00        mov     eax, dword ptr [3DB458]
为了得到与加壳前一样的文件,必须找到重定位的代码,跳过它,让其不被重定位。重新加载DLL,对上句重定位的地址3D1267h下内存写断点,中断几下,就可来到重定位的处理代码。

代码:


  1. 003DE79E  mov     al, byte ptr [edi]              ;指向UPX自己加密过的重定位表
  2. 003DE7A0  inc     edi                                  ;指针移向下一位
  3. 003DE7A1  or      eax, eax                            ;EAX=0?结束标志
  4. 003DE7A3  je      short 003DE7C7               
  5. 003DE7A5  cmp     al, 0EF
  6. 003DE7A7  ja      short 003DE7BA                  
  7. 003DE7A9  add     ebx, eax                          ;EBX的初值为(0xFFC+基址)
  8. 003DE7AB  mov     eax, dword ptr [ebx]             ;EBX指向需要重定位的数据,取出放到EAX
  9. 003DE7AD  xchg    ah, al
  10. 003DE7AF  rol     eax, 10
  11. 003DE7B2  xchg    ah, al
  12. 003DE7B4  add     eax, esi                          ; ESI指向UPX0区块的VA,本例=3D1000
  13. 003DE7B6  mov     dword ptr [ebx], eax           ;重定位
  14. 003DE7B8  jmp     short 003DE79C              
  15. 003DE7BA  and     al, 0F
  16. 003DE7BC  shl     eax, 10
  17. 003DE7BF  mov     ax, word ptr [edi]
  18. 003DE7C2  add     edi, 2
  19. 003DE7C5  jmp     short 003DE7A9            
  20. 003DE7C7  mov     ebp, dword ptr [esi+E044]        ;改好ESI为401000后,按F4到这里
复制代码


UPX壳己将原基址重定位表清零,重定位操作时,使用其自己的重定位表。地址3DE7B4h处ESI指向UPX0区块的VA,本例为3D1000h,为了让代码以默认ImageBase的值400000h重定位代码,可以在这句强制将ESI的值改为401000h。来到这句后,双击ESI寄存器,改成401000h,然后按F4来到3DE7C7h这时。此时代码段的数据没被重定位,可以Dump了。
代码:

  1. 003D1253     833D 68AD4000 00     cmp     dword ptr [40AD68], 0
复制代码


运行LordPE将DLL映像抓取,并保存为upx_dumped.dll。

13.5.3 重建DLL的输入表

ImportREC能很好地支持DLL的输入表的重建,首先,在Options里将“Use PE Header From Disk”默认的选项去除。这是因为ImportREC需要获得基址计算RVA值,DLL如果重定位了,从磁盘取默认基址计算会导致结果错误。

1)在ImportREC下拉列表框中选择DLL装载器的进程,此处为loaddll.exe进程。
2)单击“Pick DLL”按钮,在DLL进程列表中选择EdrLib.dll进程(见图13.47)。
dll2.gif

图13.47 选择DLL进程
3)在OEP处,填上DLL入口的RVA值1240h,单击IAT AutoSearch按钮获取IAT地址。如果失败,必须手工判断DLL的IAT位置和大小,其RVA为7000h,Size为E8h。
4)单击“Get Import”按钮,让其分析IAT结构重建输入表。
5)勾选Add new section,单击“Fix Dump”按钮,并选择刚抓取的映像文件dumped.dll,它将创建一个dumped_.dll文件。

13.5.4 构造重定位表

原理请参考本文开始处的说明。
先来回顾一个重定位表的结构:

代码:
IMAGE_BASE_RELOCATION STRUCT    VirtualAddress    dd    0    SizeOfBlock        dd    0    Type1            dw    0; 其中:Bit15—Bit12为类型 type,Bit11--Bit0 为ItemOffsetIMAGE_RELOCATION ENDS
重定位表以1000h大小为一个段,因为ItemOffset最长为12位,即刚好为1000h。如果还有更多段,将重复上面数据结构,直到VirtualAddress为NULL,表示结束。
ReloREC工具可以根据一组重定位的RVA,重新构造一个新的重定位表。首先要做的工作是将UPX外壳这些要重定位的RVA提取出来。
在处理重定位代码语句中,下面这句就是对代码重定位,其中EBX保存的就是要重定位的地址。

代码:
003DE7B6     mov     dword ptr [ebx], eax      ;EBX指向要重定位的RVA
补丁的思路是找块代码空间,跳过去执行补丁代码,将重定位的地址转成RVA,并保存下来。如下语句跳到补丁代码处:

代码:
003DE7B8   jmp     short 003DE80A我们键入的补丁代码:003DE80A   pushad003DE80B   mov     edx, dword ptr [3E0000]    ;从全局变量3E0000h取一地址指针003DE811   sub     ebx, 3D0000                  ;减外壳基址,将ebx中的地址转成RVA003DE817   mov     dword ptr [edx], ebx       ;将获得的RVA保存下来003DE819   add     edx, 4                        ;指向下一个DWRD地址003DE81C   mov     dword ptr [3E0000], edx  ;将指针保存到全局变量中003DE822   popad003DE823   jmp     003DE79C                       ;跳回外壳代码

3E0000h这个地址是OllyDbg的插件HideOD临时分配的,其初始值设为3E0010h,如图13.71。
dll3.gif

补丁代码键入完成后,外壳在处理重定位相关代码时,这段补丁代码将需要重定位的RVA全部提取出来。执行完补丁代码,数据窗口将保存需要重定位的RVA,
将需要重定位的RVA复制出来(选取数据时,最后一个DWORD数据是0),操作时单击鼠标右键,执行菜单Binary/Binary copy(二进制复制)功能,再运行WinHex,新建一文档,将这段二进制数据粘贴进去,粘贴时,选择ASCII Hex模式(图13.52),然后将提取的数据保存为Relo.bin。

Relo.bin中保存的就是需要重定位的地址,以RVA表示。部分数据如下:

代码:
0000101D000010310000106E0000108D000010A1000010DE000010FB000011090000110F……
ReloREC这款工具,就是根据这些RVA重新生成一份新的重定位表


准备工作完成,运行ReloREC,将Relo.bin拖放到ReloREC主界面上可打开此文件,如图13.53。然后在dumped_.dll里找一块空白代码处保存重定位表(一般在UPX1或UPX2区块里找),在这选择C000h处。在Relocation's RVA域里填上原始重定位表的RVA地址,本例为C000h,最后单击“Fix Dump”按钮,打开上节刚修复输入表的dumped_.dll文件,即可完成重定位表的修复。
dll4.gif






脱壳实例.rar

42.1 KB, 下载次数: 18

ReloREC 1.0.rar

36.97 KB, 下载次数: 7

楼主热帖
启用邀请码注册,提高发帖质量,建设交流社区
  • TA的每日心情
    开心
    2018-3-30 20:27
  • 签到天数: 227 天

    [LV.7]常住居民III

    发表于 2017-8-4 16:26:11 | 显示全部楼层
    介绍很详细                                                        
    启用邀请码注册,提高发帖质量,建设交流社区
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    快速回复 返回顶部 返回列表