目录
第一章逆向基础知识
第一节 软件逆向工程
01 软件逆向工程概念
逆向工程 Reverse Engineering 原本是指通过拆解机器装置并观察其运行情况来推导其制造方法、工作原理和原始设计的行为,但在软件领域,逆向工程主要指的是阅读反汇编(将机器语言代码转换成汇编语言代码)后的代码,以及使用调试器分析软件行为等工作。
编译与反编译不一定生成汇编代码,一般生成某种设计好的中间语言。但反编译的二进制解码过程中,首先会生成一种类汇编或汇编代码因此二进制解码也可称为反汇编。
软件工程与软件逆向工程的区别
对于软件工程而言,软件的设计讲究封装,将各个模块进行封装,将具体的实现进行隐藏,只暴露一个接口给使用者。对于模块的使用者而言,封装好的块相当于一个“黑盒子”,使用者使用“盒子”时,无需关心“盒子”的内部实现,只需要按照模块预留的接口进行使用即可。
软件逆向工程对于软件工程而言,却是正好相反的。对软件进行逆向工程时要查看软件的行为,即软件的输入与输出的情况;要查看软件的文件列表,即软件使用了哪些动态链接 库,有哪些配置文件,甚至还要通过一系列的工具查看软件的文件结构、反汇编代码等。
对比软件工程与软件逆向工程可以发现,软件工程是在封装、实现一个具备某种功能的 “黑盒子”,而软件逆向工程则是在分析“黑盒子”并尝试还原封装的实现与设计。后者对于前者而言是一个相反的过程,因此称为“软件逆向工程”。
02 软件逆向工程的应用
(1)代码恢复
(2)密码算法识别
(3)漏洞挖掘和利用
(4)代码检测:比较代码,查找恶意软件,查找软件漏洞
03 逆向分析方法
静态分析法:在不执行代码文件的情况下,对代码进行静态分析的一种方法静态分析时并不会执行代码,而是观察代码文件的外部特征,获取文件的类型(EXE、DLI、DOC、ZIP等)、大小、PE头信息、Import/Export API、内部字符串、是否运行时解压缩、注册信息、调试信息、数字证书等多种信息。此外,使用反汇编工具(Disassembler)查看内部代码、分析代码结构也属于静态分析的范畴。
动态分析法:是在程序文件的执行过程中对代码进行动态分析的一种方法,通过调试来分析代码流,获得内存的状态等。
04 逆向分析工具
静态分析工具:
IDA Pro、c32asm、BeaEngine 、Udis86 、AsmJit
动态分析工具:
OllyDbg 、x64dbg、Mdebug、WinDb g
PE工具:
Exeinfope 、LordPE
十六进制编辑工具:
010Editor 、c32asm、WinHex
05 涉及的代码类型
代码逆向分析的主要对象是可执行文件,没有源代码的情况下分析可执行文件的二进制代码。因此,掌握程序源代码与二进制代码之间的关系有助于理解代码的逆向分析过程。
源代码: Source Code 。对于一般的 C++代码,在 Visual C++ 集成开发环境中编写完源代码之后,执行菜单栏中的“生成”命令,就可以将程序代码编译成exe 可执行文件。
十六进制代码: HexCode。把程序源代码编译成0和1组成的二进制可执行文件后,计算机就能够很好地识别它,但是对于逆向分析而言,十六进制代码比二进制代码更好理解一些。
汇编代码: Assembly Code 。可执行文件反汇编后的代码。
第二节进制及进制的转换
在计算机中,无论是文本文件、图片文件,还是音频文件、视频文件、可执行文件等,都是以二进制形式存储的。进制及进制的转换是软件逆向工程的基础,因为计算机使用的进制是二进制,它不同于人们现实生活中使用的十进制,所以需要学习不同的进制及进制之间的转换。
01 现实生活中的进制与计算机的二进制
常用的:十进制,二进制,八进制,十六进制(表达:*进制,逢*进一)
计算机中是二进制。二进制就是逢二进一。在计算机中使用二进制是因为计算机用高电平和低电平来表示1和0最为方便和稳定,高电平被认为是1,低电平被认为是0,这就是所谓的二进制的来源。由于二进制在阅读上不方便,计算机又引入了十六进制来直观地表示二进制。所谓的十六进制,就是逢十六进一。因此在计算机中,常见的数据表示方法有二进制、十进制和十六进制。
02 进制的定义
十进制::由0到9十个数字组成,且逢十进一。(0123456789)
二进制:是由0到1两个数字组成,且逢二进一。(01)
十六进制:是由0到9十个数字和A到F六个字母组成,且逢十六进一。由此,衍生出N进制的定义:由N个符号组成,逢N进一。(0123456789ABCDEF)
03 进制的转换
二进制转十进制
十六进制转二进制
由于一个简单的数值用二进制表示需要很长的位数,阅读起来很不方便,因此汇编和调试器常用十六进制表示二进制。十六进制的每一位可以代表4个二进制位因为2的4次方刚好是16。二进制与十六进制的对应关系。
(二进制对应的十六进制与十进制数)
十六进制转二进制
将二进制转换为十六进制,例如,二进制数10010011转换成十六进制,转换过程如下:
第一步,把 10010011从最低位开始按每4位分为一组,不足4位前面补0,划分结果为 10010011;
第二步,把划分好的组进行查表,1001对应十六进制是9,0011对应的十六进制是3。那么,二进制数10010011转换成十六进制数后就是93。
第三节 数据宽度,字节序和ASCll码
01 数据的宽度
数据的宽度是指数据在存储器中存储的尺寸。在计算机中,数据的基本存储单位是字节(byte,B),每字节占8个位(位是计算机存储的最小单位,而不是基本单位,因为在存储数据时几乎没有按位进行存储的,一位就是存储二进制的0或1)。其他的存储单位还有字(word)、双字(dword)和八字节(qword )。
1字节=8位;1字=2字节=16位:1双字=4字节=32位,1八字节=8字节=64 位。
在计算机编程中,常用的3个重要数据存储单位分别是 byte、word 和dword .
02 数值的表示范围
在计算机中存储数值时,也是依据数据宽度进行存储的,在存储数据时由于存储数据的宽度限制,数值的表示也是有范围限制的。byte、word 和 dword 三种数据存储宽度能表示的数据的范围如1表所示,1位可以存0或1两个数,1字节可以存0到2的8次方-1,其余类似。
这里只给出了无符号整数的表示范围。数值分为有符号数和无符号数,有符号数又分正数和负数,而无符号数只有正数没有负数。计算机中表示有符号数时借助最高位来实现,如果最高位是0,那么表示正数,如果最高位是1则表示负数。关于有符号数和无符号数不必过多地纠结,因为计算机表示数据是不区分有符号还是无符号的,有符号还是无符号是人在进行区分。
03 字节序
字节序,即多字节数据存放顺序,随着系统架构的不同而不同。字节序对于逆向工程是一项基础知识,在动态分析程序的时候,往往需要观察内存数据的变化情况,这就需要我们在掌握数据的存储宽度、范围之后,进一步了解字节序。
数值在内存的存放顺序有正序( Big-也称大端存储)和逆序Endian(Little-Endian,也称小端存储)之分。通常情况下,Windows 操作系统兼容的 CPU 采用小端存储方式,而Unix 操作系统兼容的 CPU 多采用大端存储方式。网络中传输数据时采用大端方式。
04 ASCII
计算机智能存储二进制数据,那么计算机是如何存储字符的呢?为了存储字符,计算机必须支持特定的字符集,字符集的作用是将字符映射为整数。早期字符集仅仅使用8个二进制数据位进行存储,即美国标准信息交换码(American Standard Code for Information
Interchange ,ASCII)。后来,由于全世界语言的种类繁多,又产生了新的字符集Unicode 字符编码。
在 ASCI 字符集中,每个字符由唯一的7位整数表示。ASCI仅使用了每个字节的低7位,最高位被不同计算机用来创建私有字符集。由于标准 ASCI仅使用7位,因此十进制表示范围是0 ~127共128 个字符。
Unicode 编码是为了使字符编码更进一步符合国际化而对 ASCII 字符编码进行的扩展,Unicode 使用一个字(也就是两个字节,即 16 位)来表示一个字符。 Unicode 字符编码的编码范围是:0-65535,它包含三套编码方式如:UTF-8,UTF-16和UTF-32。
在 Unicode 中,所有字符都是 16 位的,其中所有的7位 ASCII 码都被扩充为 16 位,高位扩充的是0。例如,字符串“Happy”的 ASCII 码是:
第四节 在OD中查看
在逆向分析中,调试工具非常重要。调试器能够跟踪一个进程的运行时状态在逆向分析中称为动态分析工具。调试工具应用十分广泛,如漏洞的挖掘、游戏外挂的分析、软件加密解密等。本节介绍应用层下最流行的调试工具 0llyDbg。
OllyDbg简称 OD,是一款具有可视化界面的运行在应用层的 32 位反汇编逆向调试分析工具。 OD 具有操作简单、参考文档丰富、支持插件功能等特点,OD真正的精髓在于配合逆向的思路来达到逆向者的目的。
01 OD 的选型
OD 的主流版本是 1.10,但是拥有修改版。所谓修改版,就是由用户自己对OD 进行修改而产生的版本,类似于病毒的免杀。软件制作者为了防止软件被 OD调试,加入了很多专门针对 OD 调试的反调试功能来保护自己的软件不被调试,从而不被破解;而破解者为了能够继续使用 0D 来破解软件,不得不对0D 进行修改从而达到反反调试的效果。简单来说,反调试是阻止使用 0D进行调试,而反反调试是突破反调试继续进行调试。 OD的修改版本目的就是为了能够更好地突破软件的反调试功能。
注意:修改版本的 OD 可以叫着千奇百怪的名字,比如 OllyICE、OllySeX等
02 OD 主界面
OD 主界面如图1所示,工作区大致可以分为6个部分分别是反汇编窗口、信息提示窗口、数据窗口、寄存器窗口、栈窗口和命令窗口。下面分别介绍它们的功能。
反汇编窗口:显示反汇编代码
信息提示窗口:与反汇编窗口中上下文环境相关的内存、寄存器或跳转、调用来源等信息;
数据窗口:以多种格式显示内存中的内容;
寄存器窗口:显示各个寄存器的内容
栈窗口:显示栈内容、栈帧。
命令窗口:用于输入命令来简化调试分析工作。
03 在 OD 的数据窗口中查看数据
为了能够直观地观察内存中的数据,我们通过 RadAsm 创建一个无资源汇编工程,编写如下汇编代码,编译连接后用 OD 运行观察数据窗口。
调试RadAsm语言
RadASM 中设置 masm32 的路径
第五节 编程判断主机字符序
01 字节序相关函数
在 TCP/IP 网络编程中会涉及字节序相关函数。在 TCP/IP 协议中,数据是按照网络字节序进行传输的,网络字节序指网络传输相关协议所规定的字节传输的顺序TCP/IP 协议所使用的网络字节序与大端方式相同。而主机字节序包含大端方式与小端方式,因此在进行网络传输时会进行相应的判断,如果主机字节序是大端方式,则无需进行转换即可传输,如果主机字节序是小端方式,则需要转换成网络字节序(也就是转换成大端方式)后再进行传输。
常用的字节序涉及的函数有4个:
u_short htons(u_short hostshort):
u_long htonl(u_long hostlong);
u_short ntohs(u_short netshort);
u_long ntohl(u_long netlong);
在这4个函数中,前两个函数用于将主机字节序转换成网络字节序,后两个函数用于将网络字节序转换为主机字节序。
02 编程判断主机字节序
编程判断主机字节序”是很多杀毒软件公司或者安全开发职位的一道基础面试题,这里给出实现题目的两种方法,
第一种是“取值比较法”
所谓取值比较法,是首先定义一个4字节的十六进制数(这是因为使用调试器查看内存最直观的就是十六进制),而后通过指针方式取出这个十六进制数在“内存”中的某字节,最后与实际数值中相对应的数进行比较。由于字节序的原因,内存中的某字节与实际数值中对应的字节可能不相同,这样即可确定采用的是哪种字节序。
第二种是“直接转换比较法”
所谓直接转换比较法,是指利用字节序转换函数将所定义的值进行转换,然后用转换后的值与原值进行比较。这种方式比较直接,如果原值与转换后的值相同说明主机字节序采用大端方式,否则采用小端方式。
第六节Windows操作系统
01 Win32 API
Win API 就是 windows 应用程序接口,是针对 microsoft windows 操作系统家族的系统编程接口·它是 API函数以函数库的形式组织在一起形成的,API函数提供应用程序运行所需要的窗口管理、图形设备管理、内存管理等服务功能。
它被设计为各种语言的程序调用也是应用软件与 Windows系统最直接的交互方式。
常用的 Win 32 API 函数:
int GetWindowText:将指定窗口的标题条文本(如果存在)拷贝到一个缓存区内。
HWND GetDlgItem:返回窗口中指定参数ID 的控件的句柄。
UINT GetDlgItemText:获得与对话框中的控件相关的标题或文本。
int MessageBox:用于显示一个模态对话框,其中包含一个系统图标、一组按钮和一个简短的特定于应用程序消息,如状态或错误的信息。
在NT架构下,Win32 API 能接受 Unicode和ASCI两种字符集,而其内核只能使用Unicode 字符集。
在Win32 API数字符集中,“A”表示 ANSI(美国国家标准学“w”表示 Widechars会的标准码,单字节方式),(即 Unicode ,双字节方式)
例如在编程时使用MessageBox数,32位函数入口有两个,分别是MessageBoxA(ANSI版)、MessageBoxW(宽字符版)
02 动态链接库
Windows运作的核心是动态链接。所谓动态链接,就是把一些经常会共用的代码(静态链接的 0BJ程序库)制作成动态链接库(DLL)文件。
当可执行文件调用到 DLL 文件内的函数时,windows操作系统才会把 DLL 文件加载存储器内。
DLL文件本身的结构就是可执行文件(PE),当程序需求数才进行链接。通过动态链接方式,存储器浪费的情形将可大幅降低
(Windows7中,DLL通常位于系统安装目录下\WindowsSystem32 )
三个主要的动态链接库:
kernel32.d:控制系统的内存管理、数据的输入输出操作和中断处理,当 Windows启动时就驻留在内存的特定的写保护区域,使别的程序无法占有这个内存区域。
user32.dl:Windows用户界面相关应用程序接口
gdi32.dll:Windows GDI图形界面。
03 Windows消息机制
Windows 系统与应用程序之间、应用程序与应用程序之间的通讯就是通过消息来触发,并靠对消息的响应和处理来完成。
Windows 系统中有两种消息队列:一种是系统消息队列,另一种是应用程序消息队列。
由于 Windows 本身是由消息驱动,所以我们调试程序时跟踪一个消息会得到相当底层的答案。
Windows为当前执行的每个 Windows程序维护一个消息队列。在发生事件之后,系统将该事件转换为一个消息,并将消息投放入程序的消息队列中。程序通过执行一块称之为消息循环的程序代码从消息队列中取出消息。
04 虚拟内存
虚拟内存并不是真正的内存,它是通过映射(Map)的方法,使可用的虚拟地址(VirtualAddress)达到4GB(因为EIP32位索引的最大范围是 4GB)。
每个应用程序可以被分配到 2GB的虚拟地址,剩下的2GB留给操作系统自用。(WindowsNT中,应用程序甚至有3GB的虚拟地址。)
Windows 是一个分时的多任务操作系统,CPU的时间被分割成-个个的时间片后分配给各个不同的应用程序,在一个时间片里,和这个应用程序执行无关的东西并不会又映射到线性地址中。因此可以做到每个程序都拥有自己独立的 4GB 寻址空间,互不干扰。
虚拟内存的实现方法和过程:
1、当一个应用程序被启动时,操作系统创建一个新的进程,并给这个进程分配 2GB的虚拟地址(不是内存,只是地址)
2、虚拟内存管理器将应用程序的代码映射到哪个程序的虚拟地址中的某个位置,并把当前所需要的代码读取到内存物理地址中;3、如果使用动态链接库 DLL,那么DLL程序也会被映射到进程的虚拟地址空间,在需要的时候才被读入物理内存;
4、其他项目(像数据、堆栈等)的空间是首先从物理内存中分配再反向映射到虚拟地址空间中的;
5、应用程序通过使用的他的虚拟地址空间中的地址开始执行,然后由虚拟内存管理器把每次的内存访问映射到物理位置去。
在保护模式下
应用程序不会直接访问物理地址;
虚拟内存管理器通过虚拟地址的访问请求,控制所有的物理地址访问;
每个应用程序都有独立的 4GB寻址空间,不同应用程序的地址空间是彼此隔离的;
DLL程序没有自己的“私有”空间,它们称之为动态链接库文件它们总是被映射到其他应用程序的地址空间中的,作为其他应用程序的一部分运行。