代码分析

代码分析是在提取汇编码内信息的一种普遍技术,radare2核心实现多种代码分析技术,可以通过不同的命令调用。

由于r2的全部功能都可以通过API或命令来实现,因此你可以使用任何编程语言来实现自己的代码分析方法, 甚至可以用r2 oneliners/shellscripts/原生插件等进行实现。

r2的分析将显示内部数据结构,以识别基本块和函数树,并提取操作码级别的信息。

radare2最常用的分析命令就是aa,代表“analyze all”,其中的“all”代指所有的symbols和EP(入口点)。如果是一个stripped的二进制文件,那可能还需要使用其他命令,比如aaaaabaaraac等。

花些时间了解各个命令都完成了哪些工作,以及他们会返回哪些信息,再从中选择最适合你的。

[0x08048440]> aa
[0x08048440]> pdf @ main
           ; DATA XREF from 0x08048457 (entry0)
/ (fcn) fcn.08048648 141
|     ;-- main:
|     0x08048648    8d4c2404     lea ecx, [esp+0x4]
|     0x0804864c    83e4f0       and esp, 0xfffffff0
|     0x0804864f    ff71fc       push dword [ecx-0x4]
|     0x08048652    55           push ebp
|     ; CODE (CALL) XREF from 0x08048734 (fcn.080486e5)
|     0x08048653    89e5         mov ebp, esp
|     0x08048655    83ec28       sub esp, 0x28
|     0x08048658    894df4       mov [ebp-0xc], ecx
|     0x0804865b    895df8       mov [ebp-0x8], ebx
|     0x0804865e    8975fc       mov [ebp-0x4], esi
|     0x08048661    8b19         mov ebx, [ecx]
|     0x08048663    8b7104       mov esi, [ecx+0x4]
|     0x08048666    c744240c000. mov dword [esp+0xc], 0x0
|     0x0804866e    c7442408010. mov dword [esp+0x8], 0x1 ;  0x00000001
|     0x08048676    c7442404000. mov dword [esp+0x4], 0x0
|     0x0804867e    c7042400000. mov dword [esp], 0x0
|     0x08048685    e852fdffff   call sym..imp.ptrace
|        sym..imp.ptrace(unk, unk)
|     0x0804868a    85c0         test eax, eax
| ,=< 0x0804868c    7911         jns 0x804869f
| |   0x0804868e    c70424cf870. mov dword [esp], str.Don_tuseadebuguer_ ;  0x080487cf
| |   0x08048695    e882fdffff   call sym..imp.puts
| |      sym..imp.puts()
| |   0x0804869a    e80dfdffff   call sym..imp.abort
| |      sym..imp.abort()
| `-> 0x0804869f    83fb02       cmp ebx, 0x2
|,==< 0x080486a2    7411         je 0x80486b5
||    0x080486a4    c704240c880. mov dword [esp], str.Youmustgiveapasswordforusethisprogram_ ;  0x0804880c
||    0x080486ab    e86cfdffff   call sym..imp.puts
||       sym..imp.puts()
||    0x080486b0    e8f7fcffff   call sym..imp.abort
||       sym..imp.abort()
|`--> 0x080486b5    8b4604       mov eax, [esi+0x4]
|     0x080486b8    890424       mov [esp], eax
|     0x080486bb    e8e5feffff   call fcn.080485a5
|        fcn.080485a5() ; fcn.080484c6+223
|     0x080486c0    b800000000   mov eax, 0x0
|     0x080486c5    8b4df4       mov ecx, [ebp-0xc]
|     0x080486c8    8b5df8       mov ebx, [ebp-0x8]
|     0x080486cb    8b75fc       mov esi, [ebp-0x4]
|     0x080486ce    89ec         mov esp, ebp
|     0x080486d0    5d           pop ebp
|     0x080486d1    8d61fc       lea esp, [ecx-0x4]
\     0x080486d4    c3           ret

在这个例子中,我们分析了整个文件(aa),然后打印了main()函数的反汇编(pdf)。aa命令属于自动分析命令族,仅执行最基本的命令自动分析步骤。在radare2中,有许多不同类型的自动分析命令,其中包含不同程度的分析,包括进行部分仿真:aaaaaaabaaaa,...这些命令也映射到r2的命令行选项:r2 -Ar2 -AA等。

我们都知道,完全自动化的分析产生的结果不一定正确,因此Radar2为分析的特定阶段提供了单独的命令,可在细粒度上控制分析过程。此外,radare2还提供了一些配置变量来控制分析结果。可以在配置变量中的anal.*emu.*命名空间中找到它们。

分析函数

最终要的分析命令之一就是af命令族,af的意思是”analyze function“。使用该命令可以进行全自动分析,或手动对其分析。

使用afl可以列出分析中发现的函数。

afl有许多有用的下属子命令,比如aflj可以以JSON格式将发现的函数列出来,aflm以makefile的格式列出这些函数。

这里还有一个afl=命令,可以以ASCII字符画的风格用条柱显示函数的范围。

其余的命令可以通过afl?查看。

执行函数分析时,最难的一些任务是函数名绑定,微调和调整大小。与其他分析命令一样,有两种模式可以选择:半自动和手动。 半自动模式下,可以使用afm <function name>将当前的函数和参数中给出的名字进行绑定。aff用于在函数修改后对函数进行调整,afu <address>用于将当前函数的范围调整到指定地址,并重新分析。 与半自动模式不同,可以通过af+进入手动模式,然后通过afb命令对基础块进行编辑。 在修改函数的基本块之前,建议先看看现有的基本块:

手动创建函数

before start, let's prepare a binary file first, for example:

then compile it with gcc -c example.c -m32 -O0 -fno-pie, we will get the object file example.o. open it with radare2.

since we haven't analyzed it yet, the pdf command will not print out the disassembly here:

our goal is to hand craft a function with the following structure

analyze_one

In most cases, we use jump or call instructions as code block boundaries. so the range of first block is from 0x08000034 push ebp to 0x08000048 jmp 0x8000052. use afb+ command to add it.

note that the basic syntax of afb+ is afb+ function_address block_address block_size [jump] [fail]. the final instruction of this block points to a new address(jmp 0x8000052), thus we add the address of jump target (0x8000052) to reflect the jump info.

the next block (0x08000052 ~ 0x08000056) is more likeyly an if conditional statement which has two branches. It will jump to 0x800004a if jle-less or equal, otherwise (the fail condition) jump to next instruction -- 0x08000058.:

follow the control flow and create the remaining two blocks (two branches) :

check our work:

handcraft_one

有两个非常重要的命令:“ afc”和“ afB”,对于某些平台(例如ARM),后者是必不可少的命令。因为它提供了一种修改特定函数的“bitness”的方法,主要来说就是其允许选择在ARM和Thumb模式之间进行。afc 则是允许手动指定调用约定,可以在calling_conventions这一节了解更多信息。

递归分析

有5个重要的半自动分析命令用于程序内分析:

  • aab - perform basic-block analysis ("Nucleus" algorithm)

  • aac - analyze function calls from one (selected or current function)

  • aaf - analyze all function calls

  • aar - analyze data references

  • aad - analyze pointers to pointers references

这些只是通用的半自动化引用搜索方法,Radare2提供了各式各样的引用创建方法,如要进行细粒度的控制,可以使用ax命令。

最常用的ax族命令是axtaxf,尤其是作为r2pipe脚本的一部分时。假设在数据中发现了字符串,我们想找到所有引用它的位置,需要使用axt

axt下还有些其他有用的命令,axtg可以输出radare2的命令,这些命令可以可以帮助你根据XREFs生成交叉引用图。

使用axt*可以将这些命令分割开,然后在特定的XREFs位置上设置标记。

axg也在ax命令下,可以以XREFs图的形式显示该地址到当前函数/位置之间的路径,比如:

使用axg*生成的radare2命令可以帮助你根据XREFs,借助agnage命令绘制XREF图。

除了用预定义算法识别函数之外,还可以指定一个带有配置选项“anal.prelude”的函数前导码。例如,像 e anal.prelude = 0x554889e5,其在x86_64平台代表如下含义:

在x86_64平台上,任何分析命令 之前 都应有该前导码存在。

配置

Radare2几乎允许对任何分析阶段或命令的行为进行修改。 底下是不同种类的配置选项:

  • Flow control

  • Basic blocks control

  • References control

  • IO/Ranges

  • Jump tables analysis control

  • Platform/target specific options

控制流配置

改变radare2中控制流分析行为的两个最常用的选项是anal.hasnextanal.afterjump。第一个允许radare2在函数结束后强制进行分析,即使接下来的代码块不曾被调用,因此能分析所有潜在的函数。后一个选项允许radare2强制对无条件跳转之后的代码部分继续分析。

除了这些,我们还可以设置anal.ijmp来跟随间接跳转,继续进行分析;设置anal.pushretpush ...;ret;序列也作为跳转进行分析;设置anal.nopskip跳过函数开头的NOP序列。

现在,radare2还允许您使用anal.bb.maxsize选项更改最大基本块大小。默认块值在大多数情况下都适用,但是在分析混淆代码时增加该值会比较有用。要注意的是,块控制相关的选项在未来可能会取消,我们 将来更倾向于采用自动化的方式对该值进行设置。

对于一些不寻常的二进制文件或目标,有一个选项anal.noncode可用。Radare2默认情况下不会将数据段作为代码进行分析,但是在某些情况下比如恶意软件,加壳的二进制文件,或者是嵌入式系统上的文件通常需要这么分析,因此我们便提供了这个选项。

引用控制

这个关键选项可以彻底改变分析结果。有时候可以禁用此选项以节省分析大文件的时间,以及节省内存。

  • anal.jmpref - to allow references creation for unconditional jumps

  • anal.cjmpref - same, but for conditional jumps

  • anal.datarefs - to follow the data references in code

  • anal.refstr - search for strings in data references

  • anal.strings - search for strings and creating references

注意默认情况下string reference是禁用的,因为它会增加分析消耗的时间。

分析范围

这里是一些用于控制分析范围的选项:

  • anal.limits - enables the range limits for analysis operations

  • anal.from - starting address of the limit range

  • anal.to - the corresponding end of the limit range

  • anal.in - specify search boundaries for analysis. You can set it to io.maps, io.sections.exec, dbg.maps and many more. For example:

    • To analyze a specific memory map with anal.from and anal.to, set anal.in = dbg.maps.

    • To analyze in the boundaries set by anal.from and anal.to, set anal.in=range.

    • To analyze in the current mapped segment or section, you can put anal.in=bin.segment or anal.in=bin.section, respectively.

    • To analyze in the current memory map, specify anal.in=dbg.map.

    • To analyze in the stack or heap, you can set anal.in=dbg.stack or anal.in=dbg.heap.

    • To analyze in the current function or basic block, you can specify anal.in=anal.fcn or anal.in=anal.bb.

请参阅 e anal.in=?? 获取完整的选项列表。

跳转表

跳转表是二进制逆向工程中最棘手的目标之一,有几百个不同的类型,其最终的结果取决于编译器/链接器和LTO优化阶段。因此,radare2允许使用anal.jmptbl选项启用一些实验性质的跳转表检测算法。最终,算法一旦开始在每个受支持的平台/目标/测试用例上工作,便进入默认的分析循环。 下面的两个选项也能影响跳转表分析的工作:

  • anal.ijmp - follow the indirect jumps, some jump tables rely on them

  • anal.datarefs - follow the data references, some jump tables use those

特定于平台的一些控制

分析嵌入式目标时,有两个常见问题:ARM/Thumb检测和MIPS GP值。如果是ARM二进制文件,radare2支持对ARM/Thumb模式切换的某些自动检测,但是要注意它使用部分ESIL仿真技术,从而减慢了分析过程。如果你不愿意接受,可以使用afB命令覆盖特定函数的ARM/Thumb模式。

MIPS GP问题更加棘手。基本上来说,GP值不仅在整个程序可以是不同的,而且在函数内也可以不同。anal.gpanal.gp2可以部分解决这些问题。第一个选项设置整个程序或特定函数的GP值,后一个选项则是在某些函数想要修改GP值时,去”固定“GP值,即在GP值被修改之后恢复到原来状态。这些都是实验性质的选项,在未来倾向于以更自动化的方式代替。

可视化

查看分析命令与变量更改的最简单方法之一是在“ Vv”视觉模式下滚动屏幕,并且可以预览函数:

vv

当我们在面对大函数时, 若想看看分析对结果有何影响,可以使用minimap,以便在相同的屏幕尺寸下查看更大的流程图。通过VV然后按下2次p可以进入minimap模式。

vv2

这种模式可以分别查看每个节点的反汇编,只需使用“ Tab”键在它们之间切换即可。

分析推断(analysis hints)

即使您尝试了每一个配置选项,分析结果也仍不完美的情况并不少见。这时候Radare2的“analysis hints”技术就该登场啦。它允许重写一些基本的操作码或元信息属性,甚至重写整个操作码串。这些命令位于“ah”命名空间下:

最常见的情况之一是为立即数设置特定的数字基数:

值得注意的是,某些分析阶段中或某些命令会添加radare2内部的分析推断,可以用ah命令检查:

有时我们需要覆盖跳转或调用地址,例如,在棘手的情况下进行重定位,这对于radare2是未知的,因此我们得手动更改该值。 可以使用ao命令检查有关特定操作码的当前分析信息,我们可以使用ahc命令执行这样的更改:

正如你所见到的那样,尽管反汇编视图不变,但操作码中的跳转地址已更改(jump选项)。

如果先前描述的任何方法都不起效,则可以用任何你喜欢的东西简单地覆盖显示的反汇编:

最后更新于

这有帮助吗?