@date: 2016/11/29

@author: dlive

0x00 前言

周六周日两天在打HCTF2016线上赛,没时间看书,打完比赛接着看~~

0x01 运行时压缩

对比upx压缩前后的notepad可以看到如下特点

  1. PE头的大小一样
  2. 节区名称改变(.text -> .UPX0, .data -> .UPX1)
  3. 第一个节区的SizeOfRawData=0,即第一个节区在磁盘上的大小为0,但是第一个节区的VirtualSize的值被设置为0x10000
  4. notepad_upx.exe的EP位于第二个节区,原notepad.exe的EP在第一个节区
  5. 资源节区(.rsrc)的大小几乎无变化

(对比图见书p125)

解压缩代码与压缩的源代码都在第二个节区(UPX1)。文件运行时首先执行解压缩代码,把处于压缩状态的源代码解压到第一个节区(UPX0)。解压结束后即运行源文件的EP代码。

0x02 调试UPX压缩的notepad

EP与OEP:

EP(Enter Point): Windows可执行文件的代码入口点,是执行应用程序时最先执行的代码的其实位置

OEP(Original Enter Point): 对于加壳程序,称源文件的EP为OEP

使用OD打开未加壳的notepad.exe

可以看到notepad.exe的EP为0100739D,入口处代码调用了GetModuleHandleA用来获取notepad的ImageBase

下面开始调试notepad_upx.exe,upx的解壳过程可以分为以下几个阶段:

1. 壳入口的初始化操作

用OD打开notepad_upx.exe,程序停在EP处

复习一下前面PE的知识XD : 可以验证EP地址为01015330 == Address of Entry Point + Image Base (IMAGE_OPTIONAL_HEADER32)

notepad_upx.exe在入口点进行了pushad操作(依次将EAX、ECX、EDX、EBX、ESI、EDI压栈)

然后将UPX0,UPX1节区的地址分别保存在EDI,ESI寄存器中。调试时这样同时设置EDI,ESI,就能预见从ESI所指缓冲区到EDI所指缓冲区的内存发生了复制,此时从Source(ESI)读取数据,解压缩后保存到Destination(EDI)。

之后代码通过jmp调到解压缩代码。

我们的目标是跟踪全部UPX EP代码,并最终找到原notepad的EP代码,如第一张图所示

2. 代码还原

在UPX解压缩的过程中一共有4个循环。

前里两个循环完成代码还原的功能,正式的解码循环为第二个循环。

跟踪数量庞大的代码时,请遵循如下法则:遇到循环时,先了解作用再跳出

在OD中Ctrl+F8调试第二个解码循环,同时在memory窗口观察UPX0节区的数据变化(也可以在OD CPU窗口左下方的数据窗口查看UPX0内数据变化),可以看到第二个循环将解码后的数据写入UPX0节区。

3. CALL/JMP修复

第三个循环从0101540A开始,主要用于恢复源代码的CALL/JMP指令(Opcode:E8/E9)的destination地址。

可以在memory窗口中选中UPX0区段,右键设置内存写入断点来判断第三个循环确实有对UPX0进行修改。

4. 恢复IAT

UPX压缩原notepad.exe事,会分析其IAT,提取出程序中调用的API名称列表,形成API名称字符串。

用这些API名称字符串调用GetProcAddress函数,获取API的地址。然后把API地址输入原notepad.exe的IAT区域。

最终恢复原notepad.exe的IAT

5. 将程序的控制返回到程序OEP

在notepad.exe全部解压缩完成后,将程序的控制返回到OEP处。

010154AD地址处的popad指令与notepad_upx.exe的第一条指令pushad对应,用来把寄存器状态恢复。

最终使用010154BB处的jmp指令跳转到OEP,跳转的目标地址(0100739D)就是notepad.exe的EP

可以看到OEP处的代码和源notepad.exe相同

虽然没有详细看懂upx的每条指令的含义,但是对其代码片段的功能有了基本了解

UPX详细分析可参考:

http://www.52pojie.cn/thread-294773-1-1.html

http://www.chinapyg.com/thread-76768-1-1.html

0x03 快速查找UPX OEP&UPX脱壳

1.快速查找OEP

这里用到了OD的硬件断点,关于硬件断点可以参考:

https://www.zhihu.com/question/52625624/answer/131557817

notepad_upx.exe的EP先执行了pushad,在解压完成后又使用popad恢复程序初始运行时的寄存器和堆栈状态。

我们现在知道UPX在解压完成后一定会执行popad(依次pop edi, esi, ebx, edx,ecx, eax),那么就可以在栈地址上设置硬件断点,在程序访问这个地址的数据时就会被断下。

在数据窗口找到执行完pushad后在esp对应地址,然后选择该地址开始的1字节,右键设置硬件断点(或者按书p133那样设置硬件断点均可)。

当程序执行popad时会首先从这个地址上读取数据到edi(pop edi),此时会触发硬件断点,程序断下。其下方即是跳转到OEP的JMP指令。

不过和软件断点(INT 3)断点不同的是,触发断点的指令执行(popad)完成后程序才会停止运行,即程序会断在popad下一条指令。

OD的硬件断点可以在调试->硬件断点里看到。

2.UPX脱壳

继续运行程序到OEP,然后再OD中右键选择Dump debugged process(52pojie OD中为“用Ollydump脱壳调试进程”)

修改EP为OEP(该功能会自动修改EP为EIP,此时EIP即为OEP,所以无需手动修改),点击脱壳,另存脚本为notepad_dump.exe

对UPX脱壳而言,进行到这一步就可以了,但是如果是另外一些壳,可能还需要对程序的输入表进行修复操作。

脱壳后的程序可以正常运行,并且使用IDA可以正常反编译。

使用PEID查壳可以看到提示“Microsoft Visual C++ 6.0”信息,而非UPX

3.修复IAT

(为什么要修复IAT,IAT是什么原因被破坏这个我还不是很清楚,不过先有个大致概念,以后再深入研究)

打开ImportREC程序,首先在进程列表中选择正在用OD调试的那个notepad_upx.exe进程,然后在OEP中填入739D,然后点击“IAT AutoSearch”按钮,接着点击“GetImports”按钮,就可以看到程序的输入表信息了。

点击右侧的“Show Invalid”按钮,看看是否存在无效的输入表项目。无效的输入表项目前面带有问号(?),如果有可以使用右键菜单删除。这里没有无效的输入表项目,所以选择“Fix Dump”按钮,对我们的notepad_dump.exe进行修复,得到notepad_dumped_.exe程序。

至此脱壳完成。

Reverse Core 第二部分 - 14&15章 - 运行时压缩&调试UPX压缩的notepad的更多相关文章

  1. Reverse Core 第二部分 - 16&17章 - 基址重定位表&.reloc节区

    第16-17章 - 基址重定位表&.reloc节区 @date: 2016/11/31 @author: dlive 0x00 前言 这几天忙着挖邮箱漏洞,吃火锅,马上要被关禁闭,看书进度比较 ...

  2. Reverse Core 第二部分 - 13章 - PE文件格式

    @date: 2016/11/24 @author: dlive ​ PE (portable executable) ,它是微软在Unix平台的COFF(Common Object File For ...

  3. 《linux就该这么学》第十五节课:第14,15章,dhcp服务和邮件系统

    (借鉴请改动) 13章收尾 13.6.分离解析技术    1.在主配置文件中改两个any         2.编辑区域配置文件,写入acl,使用match匹配                      ...

  4. Reverse Core 第三部分 - 21章 - Windows消息钩取

    @author: dlive @date: 2016/12/19 0x01 SetWindowsHookEx() HHOOK SetWindowsHookEx( int idHook, //hook ...

  5. brew 安装的.net 运行时提示"Did you mean to run dotnet SDK commands?"

    原因未知,但有解决方案 使用 brew cask 安装的.NET Core brew cask install dotnet 结果运行时出现: 解决方案: 下载官方 .pkg 文件安装,顺便卸载掉 b ...

  6. 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化

    第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...

  7. 第15章 使用EntityFramework Core进行配置和操作数据 - Identity Server 4 中文文档(v1.0.0)

    IdentityServer旨在实现可扩展性,其中一个可扩展点是用于IdentityServer所需数据的存储机制.本快速入门展示了如何配置IdentityServer以使用EntityFramewo ...

  8. Java面向对象程序设计第14章3-8和第15章6

    Java面向对象程序设计第14章3-8和第15章6 3.完成下面方法中的代码,要求建立一个缓冲区,将字节输入流中的内容转为字符串. import java.io.*; public class tes ...

  9. Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式

    Linux就这个范儿 第15章 七种武器  linux 同步IO: sync.fsync与fdatasync   Linux中的内存大页面huge page/large page  David Cut ...

随机推荐

  1. Linux 基础操作

    根据关键字查找文件信息: cat <文件名> | grep <关键字>查询文件信息 显示100行: tail -100f easyhome.all.log |grep &quo ...

  2. Python中用format函数格式化字符串

    Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存. 1.百分号方式 语法:%[( ...

  3. Java防止SQL注入(转)

    一.SQL注入简介 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库. 二.SQL注入攻击的总体 ...

  4. Beta版本冲刺第七天

    Aruba 408 409 410 428 429 431 完成任务: 新增:完成文字导出为图片并改善画布大小 改进:适应MIUI系统相册选图 改进:调整activity间的跳转逻辑 改进:调整编辑窗 ...

  5. 隐藏NavigationBar的正确方式

    -(void)viewWillAppear:(BOOL)animated { [self.navigationController setNavigationBarHidden:YES animate ...

  6. linux下从SVN checkout目录源码下来

    需求:我们替换http请求为https协议,要查看全不全,得到源码中去检查,所以自动化扫源码查询.但是得先有源码包啊.源码包直接从SVN上checkout 1.下载 [root@v50 0.02 sr ...

  7. C#基础强化-继承与多态

    /**  特性:    单根性:一个子类只能有一个父类    传递性:爷爷类 爹类 儿子类  里氏转换    1.子类可以赋值给父类    2.如果父类装的是子类对象,则可以将这个父类转换为对应的子类 ...

  8. 【.net core 跨平台】第一步 在Ubuntu16.04 配置.net core环境

    本次使用VMware10.0.4工具安装Ubuntu16.04系统并配置.net core环境   Ubuntu 16.04 desktop下载地址:http://releases.ubuntu.co ...

  9. DX 系列之 ComboBoxEdit

    参考资料: ComboBoxEdit.Properties.Items=ComboBoxItemCollection

  10. 阿里提前批校招内推offer经历

    经过一个半月的阿里内推面试,今天终于收到了阿里的offer邮件 .阿里的内推面试一共有四轮,本人是7月19号投的内推邮件,8月28号收到了offer的邮件.首先本人谈谈内推的看法.内推是公司招聘人才的 ...