恶意样本分析-6-IDA使用

XT 2020-09-08 13:15:00

使用IDA反汇编

代码分析常用语了解恶意样本内部源码不可见时使用。

1. 代码分析工具

代码分析工具可以根据他们的功能、描述、数量进行分类。
反汇编程序是一个可以将机器语言转汇编代码;并且可以静态代码分析。静态代码分析可以在不执行二进制程序的时候让你了解到程序的行为。

一个调试器是个应用程序同时也是可以反汇编代码;除此之外也可以执行控制汇编二进制执行。使用调试工具,你不仅可以执行单条指令,或选择函数,或执行整个程序。调试工具可以动态分析,还可以在程序执行的过程中检查可疑的二进制。

反编译器是一个将机器码转成更高级语言的程序(伪代码)。反编译器能够很好辅助反推工程进程并能够简化工作。

2. 静态代码分析(使用IDA反汇编)

Hex-Rays IDA pro
https://www.hex-rays.com/products/ida/
IDA是最有影响力且流行的商业反编译调试工具;常被用于逆向工程,恶意病毒分析以及脆弱性研究。IDA可以运行在不同平台(macOS、Linux和windows)支持分析不同的文件类型(PE/ELF/Macho-O)。除商业版本之外,IDA还提供2个其他版本:IDA demo版本(评估版本)和IDA免费版本;两个版本都有一定的限制,都可以反编译32和64位windows程序,但是免费版无法调试二进制,demo版本无法调试64位二进制,demo版本也无法保存数据库,并且demo版本和免费版都无法支持IDApython。

本部分和下一部分将会看下IDA pro的特征,并且使用IDA施行静态代码分析。这一部分仅包含与恶意代码分析相关的功能。

IDA相关深入了解图书推荐《The IDA Pro Book》by Chris Eagle

2.1 在IDA中加载二进制

IDA会像windows一样加载文件到内存中。IDA可以通过判断文件头确定最可能适合的加载器。在选择文件后IDA会加载对话框,用于确认合适的加载起和进程类型。文件设置(file option)选项是用于加载未识别的文件,一般使用该选项处理shellcode。默认情况下IDA不会在反编译中加载PE头和源部分。通过使用手动加载checkbox选项,可以手动选择加载基址和加载位置,IDA将会在加载的每个部分包括PE头给予相应的提示。点击OK,IDA将文件加载到内存,并且开始反编译相关代码。

2.2 扩展IDA显示

IDA桌面版结合了很多静态分析工具的特征到一个单独特窗口中。下面将对IDA卓敏啊版和它不同窗口进行介绍。其包含多个不同的标签(IDA View-A,Hex View-1,等等),也可以通过点击添加标签按钮或者点击View/open subviews菜单进行添加。

2.2.1 反汇编窗口

当二进制文件被加载,IDA展示的窗口就是反汇编编辑窗口(也叫做IDA-view窗口),这是个主要窗口,用于分析和展示反汇编代码,并且可以用于分析反汇编二进制。
IDA可以使用两个模式展示反编译的代码:Graph view(graph diassembly view)和Text view(实际应该叫text diassembly view),默认进入的是graph view,这里可以使用空格快捷键进行切换。

在graph view模式下,IDA一次只显示一个函数,在一个流程图的窗口中函数在基本块区中断。这个模式可以快速识别分支和循环生命。在Graph view模式下,颜色和箭头的指示方向都是根据判断显示的。条件跳转使用红色和绿色的箭头,true条件用绿色箭头表示,false使用红色箭头表示。蓝色的箭头是被用来表示无条件跳转,循环使用的是向上的蓝色的箭头表示。在graph view中虚拟地址默认不显示(每个基础块仅显示最基本的信息展示)。如果需要显示虚拟地址信息,需要点击Options/general然后点击Line prefixes以启用。

下图中可以观察到条件跳转中,绿色箭头(条件true)进行跳转,对应的虚拟地址也是跳转,而红色箭头指向正常的数据流,虚拟地址为连续。

在text view模式中,整个反编译目前处于线性方式展示。整个虚拟地址默认展示,<section name>:<virtual address>格式。在text view窗口中最左边的部分被称为箭头窗口,用于展示程序的非线性流。虚线箭头代表条件跳转,实线箭头表示无条件跳转,加粗的箭头表示循环。

2.2.2 函数窗口function widnow

函数窗口显示所有IDA识别出来的函数,该床扣同时也显示每个函数可以被找到的虚拟地址,每个函数的大小,以及其他函数相关信息。双击可以定位跳转到对应函数的位置。每个函数与大量的标志相关联(例如R、F、L等等标志)。通过F1按钮可以获取更多关于相关标志的帮助信息。一个有用的标志L标志,代表函数的库函数。库函数是编译器产生而非恶意软件作者编写的函数;从代码分析的角度来看,恶意样本分析的重点应该在恶意代码上,而不是库函数本身。

2.2.3 输出窗口out window

输出窗口展示的是IDA以及IDA插件输出的相关信息。这些对于分析恶意样本以及样本对系统操作分析提供很多信息。可以通过查看输出在output窗口的内容可以获取IDA执行加载过程中的相关信息。

2.2.4 十六进制窗口Hex view window

通过点击HexView-1标签可以展示Hex窗口。Hex窗口可以展示一系列的十六进制转储内容以及ASCII字符。默认情况下,十六进制窗口(hex window)。默认情况下十六进制窗口同步反编译窗口(disassembly window)内容;也就是在反汇编窗口中选择了一部分字节的数据,相应的在十六进制窗口中同样的会进行标记高亮相关的内容,这对于标记内存地址很有帮助。

2.2.5 结构窗口structures window

点击structures windows标签,可以进入借口窗口。结构窗口展示程序使用的标准的数据结构,并且允许创建自建的数据结构。

2.2.6 引用窗口imports window

引用窗口是所有二进制程序引用的函数的列表。展示了引用的函数以及相关函数引用的库函数内容。

2.2.7 出口窗口exports window

出口窗口展示的是程序出口函数的列,出口函数通常在DLL动态链接库中,因此对于分析恶意样本DLL时有用。

2.2.8 字符窗口string window

IDA默认不展示字符窗口,你可以通过点击view/open subviews/strings(或者使用Shift+F12快捷方式打开)字符窗口。字符窗口展示的是从二进制和地址中能够发现字符列表。默认情况下,字符窗口仅展示长度不小于5的null-terminated ASCII字符串。有些恶意样本的二进制使用的是UNICODE字符。可以通过配置IDA显示不同的字符,右击Setup(或者Ctrl+U)检测Unicode C-style(16比特),点击ok即可。

2.2.9 段窗口segments window

段窗口可以通过view/open subviews/segments(或者使用shift+F7开启)。段窗口是展示(.text,.data等等)部分内容的列表。显示信息包括开始地址,以及结束地址,每个部分的内存权限。开始和结束的地址都有每个部分的虚拟地址的详细说明,可用于定位对应内存中的位置。

2.3 使用IDA提高反汇编

本部分将结合之前相关的知识内容进行反编译分析。考虑下面一个小程序从一个本地函数拷贝到另外一个变量中:

int main()
{
int x=1;
int y;
y=x;
return 0;
}

以上代码编译之后在IDA反汇编之后如下:

 .text:00401000 ; Attributes: bp-based frame ➊
.text:00401000
.text:00401000 ; ➋ int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000  ➐ _main proc near
.text:00401000
.text:00401000    var_8= dword ptr -8  ➌
.text:00401000    var_4= dword ptr -4  ➌
.text:00401000    argc= dword ptr 8   ➌
.text:00401000    argv= dword ptr 0Ch  ➌
.text:00401000    envp= dword ptr 10h  ➌
.text:00401000
.text:00401000    push ebp  ➏   
.text:00401001    mov ebp, esp  ➏
.text:00401003    sub esp, 8  ➏ .text:00401006    mov ➍ [ebp+var_4], 1
.text:0040100D    mov eax, [ebp+var_4] ➍
.text:00401010    mov ➎ [ebp+var_8], eax
.text:00401013    xor eax, eax 
.text:00401015    mov esp, ebp  ➏
.text:00401017    pop ebp  ➏
.text:00401018    retn

当加载可执行之后,IDA在每一个函数执行分析,反汇编确定栈框架。除此之外,使用大量的签名和运行特殊算法匹配提供IDA识别反汇编函数。注意到➊在执行过初始化分析之后,IDA添加了一个批注,用分号开头;这意味着ebp寄存器被局部变量和函数参数使用(前章节提到的函数在ebp堆栈寄存器基址中)。在➋中,IDA使用其规则可以确定main函数并添加在关于此函数的批注,这一特点可以用于确定函数需要接收多少个参数,以及参数的类型。

在➌中,IDA提供了一个总的栈的视角,IDA能够判断局部变量和函数参数。在主函数中IDA定义两个局部变量,并自动命名为var_4和var_8并分别赋值。-4和-8对应着与dbp(框架指针)的距离。➍和➎是IDA替换[ebp-4]与[ebp-8]的内容。

IDA会自动对变量或参数进行命名,并在代码中应用这些名称;IDA标记的var_xxx和arg_xxx可以节约人工标记并替换参数的工作,并便于识别变量名和参数。

function prologue, funcktion epilogue和在➏中用于分配的空间给局部变量的指令可以简易的忽略。这些函数仅用于设定函数的环境。梳理之后汇编代码简化为:

.text:00401006    mov [ebp+var_4], 1
.text:0040100D    mov eax, [ebp+var_4]
.text:00401010    mov [ebp+var_8], eax
.text:00401013    xor eax, eax
.text:00401018    retn
2.3.1 重命名地址

当分析恶意病毒的时候,可以将这些变量或函数改成更有意义的名字。有劲啊变量或者参数名,选择重命名(rename或者按快捷键“N”)。当重命名之后IDA将会同步新名字到与其相关的项目上。通过重命名可以给予变量或函数更加有意义的名字。

.text:00401006    mov [ebp+x], 1
.text:0040100D    mov eax, [ebp+x]
.text:00401010    mov [ebp+y], eax
.text:00401013    xor eax, eax
.text:00401018    retn
2.3.2 IDA标注功能

标注对于提示某一函数的作用很有帮助。为了添加一个合规的注释,首先将光标放在任何一个反编译列表里的一行中,然后使用快捷键(“:”),通过在新的对话框中填写相关信息并确定,完成相关备注。

.text:00401006    mov [ebp+x], 1
.text:0040100D    mov eax, [ebp+x]
.text:00401010    mov [ebp+y], eax
.text:00401013    xor eax, eax
.text:00401018    retn

常规的备注对于单行描述但行比较有用(多行也可以),但是如果可以把描述汇总到一起描述,类似主函数的描述就更好了。IDA提供了另一种备注,函数备注,允许组合备注,并且可以显示在函数反汇编列中。首先选择函数所在的虚拟地址,然后通过快捷键“:”添加备注即可,这里为sub_140001230,伪代码添加函数备注。可以看到这些备注与函数使用相同的虚拟地址。

当前相关修改参数变量名称、添加备注的名称都只保存在IDA的数据库中,并没有保存在二进制可执行文件中。

2.3.3 IDA 数据库

当可执行文件加载到IDA中,就会在工作目录中创建一个数据库该数据库一共包含5个文件(扩展名为:.id0,.id1,.nam,.id2以及.til)。每一个文件保存了大量的与可执行文件匹配的相关信息。这些文件被压缩和归档到以.idb(32进制)压缩文件中。当加载可执行程序后,从中读取创建信息保存在数据库中。大量的信息展都保存在数据库中以用于展示代码分析时有用的信息。任何的修改操作(如重命名,注释批注等等)都会显示在view中并且般存在数据库中,但是这些修改并不会修改原二进制文件。你可以通过关闭IDA保存数据库;当关闭IDA的时候将会提示是否保存数据库的提示框。默认情况下数据库包配置(默认配置)会将所有文件保存在IDB(.idb)或者i64(.i64)。当重新打开.idb或者.i64文件的时候,会看到重命名的变量和标注都在。

下面通过另一个简单的程序了解IDA的其他扩展特征。全局变量a、b,在主函数中赋值。参数x、y以及string为局部变量;a赋值给x,y和string都是保存的地址。

int a;
char b;
int main()
{
   a = 41;
   b = 'A';
   int x = a;
   int *y = &a;
   char *string = "test";
   return 0;
} 

程序转化为下面的反汇编列表。IDA也定义了全局变量和匹配名字例如dword_403374和byte_403370;记录如何补充内存地址并且在全局变量中被关联。当一个变量被定义之后在全局数据区域,对编译器来说变量的地址和变量的大小是明确的。全局的假的变量名被IDA详细知名变量的地址以及他们确切的数据类型。例如dword_403374则是说地址为0x403374可以接受dword(4bytes大小)的值。

IDA使用offset关键字表示变量地址被使用(而不是现实他们的值),当var_8、var_c被分配局部变量值时,可以认为他们被分配了值(指针变量值)。IDA使用aTest给地址确定字符(字符变量),这个名用于表示字符串,test用于添加批注,

.text:00401000    var_C= dword ptr -0Ch  ➊ 
.text:00401000    var_8= dword ptr -8  ➊ 
.text:00401000    var_4= dword ptr -4  ➊ 
.text:00401000    argc= dword ptr 8
.text:00401000    argv= dword ptr 0Ch
.text:00401000    envp= dword ptr 10h
.text:00401000
.text:00401000    push ebp
.text:00401001    mov ebp, esp
.text:00401003    sub esp, 0Ch
.text:00401006    mov ➋ dword_403374, 29h  
.text:00401010    mov ➌ byte_403370, 41h  
.text:00401017    mov eax, dword_403374  ➍ 
.text:0040101C    mov [ebp+var_4], eax
.text:0040101F    mov [ebp+var_8], offset dword_403374  ➎ 
.text:00401026    mov [ebp+var_C], offset aTest ; "test"  ➏
.text:0040102D    xor eax, eax
.text:0040102F    mov esp, ebp
.text:00401031    pop ebp
.text:00401032    retn
2.3.4 格式化转化操作数

在➋和➌中操作数(29h和41h)代表16进制格式数值,然而在源码中我们使用十进制的41和字符“A”。IDA可以将16进制值编码为十进制、八进制、二进制。ASCII也可以转为字符型。例如,如果要修改41h格式的值,右击在这个值上选择即可。

2.3.5 导航地址

IDA的另一个特征是可以在程序中导航任意地址更加方便。当程序被反编译,IDA就会标记每一个程序中的地址,双击字符则会在显示中跳转到对应字符所在的位置。如函数名或变量。
IDA保持跟踪导航历史;任何时候被重定向到另外一个地址,都可以使用返回按钮返回之前的地址。

跳转到指定地址可以点击jump/jump to Address(或者使用快捷键G)来跳转到地址。点击OK完成跳转。

2.3.6 交叉参考cross References

其他方式导航是通过交叉参考实现(也称为Xrefs)。交叉参考链接与地址链接关联。交叉参考可以不仅数据交叉,也可以代码交叉参考。

数据交叉参考描述了数据在二进制中如何交互。如➐、➑、➒。例如数据交叉,➑描述的是数据与命令相关联,从主函数开始偏移0x6长度。字符w表示一个交叉关联写;代表命令写入内存地址。字符r代表读相互关联,代表从内存中读取信息。省略号...代表更多相关联,但是他们由于显示限制不能显示。其他种类的关联数据是一个补充

.data:00403000    aTest db 'test',0  ➐; DATA XREF: _main+26o Similarly, double-clicking on the address dword_403374 relocates to the virtual address shown here: .data:00403374     dword_403374 dd ?    ➑; DATA XREF: _main+6w 
.data:00403374                       ➒; _main+17r ...