CTF-All-In-One/doc/5.3_angr.md
2017-12-04 16:13:39 +08:00

12 KiB
Raw Blame History

5.3 angr 二进制自动化分析

angr 是一个多架构的二进制分析平台,具备对二进制文件的动态符号执行能力和多种静态分析能力。在近几年的 CTF 中也大有用途。

安装

在 Ubuntu 上,首先我们应该安装所有的编译所需要的依赖环境:

$ sudo apt install python-dev libffi-dev build-essential virtualenvwrapper

强烈建议在虚拟环境中安装 angr因为有几个 angr 的依赖比如z3是从他们的原始库中 fork 而来,如果你已经安装了 z3,那么你肯定不希望 angr 的依赖覆盖掉官方的共享库。

对于大多数 *nix系统只需要 mkvirtualenv angr && pip install angr 安装就好了。

如果这样安装失败的话,那么你可以按照这样的顺序:

1. claripy
2. archinfo
3. pyvex
4. cle
5. angr

从angr的官方仓库安装。

附安装方法:

$ git clone https://github.com/angr/claripy
$ cd claripy
$ sudo pip install -r requirements.txt
$ sudo python setup.py build
$ sudo python setup.py install

其他几个 angr 官方库的安装也是如此。

一些 import angr 可能出现的问题

如果你在安装angr之后进入python环境在import之后有这样的报错

Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "angr/__init__.py", line 25, in <module>
    from .project import *
  File "angr/project.py", line 592, in <module>
    from .analyses.analysis import Analyses
  File "angr/analyses/__init__.py", line 22, in <module>
    from .reassembler import Reassembler
  File "angr/analyses/reassembler.py", line 9, in <module>
    import capstone
  File "/usr/local/lib/python2.7/dist-packages/capstone/__init__.py", line 6, in <module>
    from . import arm, arm64, mips, ppc, sparc, systemz, x86, xcore
ImportError: cannot import name arm
>>>

可以看到,是 capstone 出现了问题,解决方法是重新安装 angr

$ sudo pip install -I --no-use-wheel capstone

若是问题依然存在,那么请先卸载所有的 capstone

$ sudo pip3 uninstall capstone
$ sudo pip uninstall capstone

然后再从 pypi 源中获取最新版本安装:

$ wget https://pypi.python.org/packages/fd/33/d1fc2d01b85572b88c9b4c359f36f88f8c32f2f0b9ffb2d21cd41bad2257/capstone-3.0.5rc2-py2-none-manylinux1_x86_64.whl#md5=ecd7e1e39ea6dacf027c0cfe7eb1bf94
$ sudo pip2 install capstone-3.0.5rc2-py2-none-manylinux1_x86_64.whl

其他问题可以到官方文档中查看。

使用 angr

angr 在 CTF 中的运用

re DefcampCTF2015 entry_language

这是一题标准的密码验证题,输入一个字符串,程序验证对误。

$ file entry_language
defcamp_r100: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=0f464824cc8ee321ef9a80a799c70b1b6aec8168, stripped
$ ./entry_language
Enter the password: ABCD
Incorrect password!

为了与 angr 的自动化做对比,我们先使用传统的方法,逆向算法求解,main 函数和验证函数 fcn.004006fd 如下:

[0x00400610]> pdf @ main
/ (fcn) main 153
|   main ();
|           ; var int local_110h @ rbp-0x110
|           ; var int local_8h @ rbp-0x8
|              ; DATA XREF from 0x0040062d (entry0)
|           0x004007e8      55             push rbp
|           0x004007e9      4889e5         mov rbp, rsp
|           0x004007ec      4881ec100100.  sub rsp, 0x110
|           0x004007f3      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=-1 ; '(' ; 40
|           0x004007fc      488945f8       mov qword [local_8h], rax
|           0x00400800      31c0           xor eax, eax
|           0x00400802      bf37094000     mov edi, str.Enter_the_password: ; 0x400937 ; "Enter the password: "
|           0x00400807      b800000000     mov eax, 0
|           0x0040080c      e8affdffff     call sym.imp.printf         ; int printf(const char *format)
|           0x00400811      488b15500820.  mov rdx, qword [obj.stdin]  ; [0x601068:8]=0
|           0x00400818      488d85f0feff.  lea rax, [local_110h]
|           0x0040081f      beff000000     mov esi, 0xff               ; 255
|           0x00400824      4889c7         mov rdi, rax
|           0x00400827      e8b4fdffff     call sym.imp.fgets          ; char *fgets(char *s, int size, FILE *stream)
|           0x0040082c      4885c0         test rax, rax
|       ,=< 0x0040082f      7435           je 0x400866
|       |   0x00400831      488d85f0feff.  lea rax, [local_110h]
|       |   0x00400838      4889c7         mov rdi, rax
|       |   0x0040083b      e8bdfeffff     call fcn.004006fd            ; 调用验证函数
|       |   0x00400840      85c0           test eax, eax
|      ,==< 0x00400842      7511           jne 0x400855
|      ||   0x00400844      bf4c094000     mov edi, str.Nice_          ; 0x40094c ; "Nice!"
|      ||   0x00400849      e852fdffff     call sym.imp.puts           ; int puts(const char *s)
|      ||   0x0040084e      b800000000     mov eax, 0
|     ,===< 0x00400853      eb16           jmp 0x40086b
|     |||      ; JMP XREF from 0x00400842 (main)
|     |`--> 0x00400855      bf52094000     mov edi, str.Incorrect_password_ ; 0x400952 ; "Incorrect password!"
|     | |   0x0040085a      e841fdffff     call sym.imp.puts           ; int puts(const char *s)
|     | |   0x0040085f      b801000000     mov eax, 1
|     |,==< 0x00400864      eb05           jmp 0x40086b
|     |||      ; JMP XREF from 0x0040082f (main)
|     ||`-> 0x00400866      b800000000     mov eax, 0
|     ||       ; JMP XREF from 0x00400864 (main)
|     ||       ; JMP XREF from 0x00400853 (main)
|     ``--> 0x0040086b      488b4df8       mov rcx, qword [local_8h]
|           0x0040086f      6448330c2528.  xor rcx, qword fs:[0x28]
|       ,=< 0x00400878      7405           je 0x40087f
|       |   0x0040087a      e831fdffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       |      ; JMP XREF from 0x00400878 (main)
|       `-> 0x0040087f      c9             leave
\           0x00400880      c3             ret
[0x00400610]> pdf @ fcn.004006fd
/ (fcn) fcn.004006fd 171
|   fcn.004006fd (int arg_bh);
|           ; var int local_38h @ rbp-0x38
|           ; var int local_24h @ rbp-0x24
|           ; var int local_20h @ rbp-0x20
|           ; var int local_18h @ rbp-0x18
|           ; var int local_10h @ rbp-0x10
|           ; arg int arg_bh @ rbp+0xb
|              ; CALL XREF from 0x0040083b (main)
|           0x004006fd      55             push rbp
|           0x004006fe      4889e5         mov rbp, rsp
|           0x00400701      48897dc8       mov qword [local_38h], rdi
|           0x00400705      c745dc000000.  mov dword [local_24h], 0
|           0x0040070c      48c745e01409.  mov qword [local_20h], str.Dufhbmf ; 0x400914 ; "Dufhbmf"
|           0x00400714      48c745e81c09.  mov qword [local_18h], str.pG_imos ; 0x40091c ; "pG`imos"
|           0x0040071c      48c745f02409.  mov qword [local_10h], str.ewUglpt ; 0x400924 ; "ewUglpt"
|           0x00400724      c745dc000000.  mov dword [local_24h], 0
|       ,=< 0x0040072b      eb6e           jmp 0x40079b
|       |      ; JMP XREF from 0x0040079f (fcn.004006fd)
|      .--> 0x0040072d      8b4ddc         mov ecx, dword [local_24h]
|      :|   0x00400730      ba56555555     mov edx, 0x55555556
|      :|   0x00400735      89c8           mov eax, ecx
|      :|   0x00400737      f7ea           imul edx
|      :|   0x00400739      89c8           mov eax, ecx
|      :|   0x0040073b      c1f81f         sar eax, 0x1f
|      :|   0x0040073e      29c2           sub edx, eax
|      :|   0x00400740      89d0           mov eax, edx
|      :|   0x00400742      01c0           add eax, eax
|      :|   0x00400744      01d0           add eax, edx
|      :|   0x00400746      29c1           sub ecx, eax
|      :|   0x00400748      89ca           mov edx, ecx
|      :|   0x0040074a      4863c2         movsxd rax, edx
|      :|   0x0040074d      488b74c5e0     mov rsi, qword [rbp + rax*8 - 0x20]
|      :|   0x00400752      8b4ddc         mov ecx, dword [local_24h]
|      :|   0x00400755      ba56555555     mov edx, 0x55555556
|      :|   0x0040075a      89c8           mov eax, ecx
|      :|   0x0040075c      f7ea           imul edx
|      :|   0x0040075e      89c8           mov eax, ecx
|      :|   0x00400760      c1f81f         sar eax, 0x1f
|      :|   0x00400763      29c2           sub edx, eax
|      :|   0x00400765      89d0           mov eax, edx
|      :|   0x00400767      01c0           add eax, eax
|      :|   0x00400769      4898           cdqe
|      :|   0x0040076b      4801f0         add rax, rsi                ; '+'
|      :|   0x0040076e      0fb600         movzx eax, byte [rax]
|      :|   0x00400771      0fbed0         movsx edx, al
|      :|   0x00400774      8b45dc         mov eax, dword [local_24h]
|      :|   0x00400777      4863c8         movsxd rcx, eax
|      :|   0x0040077a      488b45c8       mov rax, qword [local_38h]
|      :|   0x0040077e      4801c8         add rax, rcx                ; '&'
|      :|   0x00400781      0fb600         movzx eax, byte [rax]
|      :|   0x00400784      0fbec0         movsx eax, al
|      :|   0x00400787      29c2           sub edx, eax
|      :|   0x00400789      89d0           mov eax, edx
|      :|   0x0040078b      83f801         cmp eax, 1                  ; 1
|     ,===< 0x0040078e      7407           je 0x400797                  ; = 1 时跳转,验证成功
|     |:|   0x00400790      b801000000     mov eax, 1                   ; 返回 1验证失败
|    ,====< 0x00400795      eb0f           jmp 0x4007a6
|    ||:|      ; JMP XREF from 0x0040078e (fcn.004006fd)
|    |`---> 0x00400797      8345dc01       add dword [local_24h], 1     ; i = i + 1
|    | :|      ; JMP XREF from 0x0040072b (fcn.004006fd)
|    | :`-> 0x0040079b      837ddc0b       cmp dword [local_24h], 0xb  ; [0xb:4]=-1 ; 11
|    | `==< 0x0040079f      7e8c           jle 0x40072d                 ; i <= 11 时跳转
|    |      0x004007a1      b800000000     mov eax, 0                   ; 返回 0
|    |         ; JMP XREF from 0x00400795 (fcn.004006fd)
|    `----> 0x004007a6      5d             pop rbp
\           0x004007a7      c3             ret

整理后可以得到下面的伪代码:

int fcn_004006fd(int *passwd) {
    char *str_1 = "Dufhbmf";
    char *str_2 = "pG`imos";
    char *str_3 = "ewUglpt";
    for (int i = 0; i <= 11; i++) {
        if((&str_3)[i % 3][2 * (1 / 3)] - *(i + passwd) != 1) {
            return 1;
        }
    }
    return 0;
}

然后写出逆向脚本:

str_list = ["Dufhbmf", "pG`imos", "ewUglpt"]
passwd = []
for i in range(12):
    passwd.append(chr(ord(str_list[i % 3][2 * (i / 3)]) - 1))
print ''.join(passwd)

逆向算法似乎也很简单,但如果连算法都不用逆的话,下面就是见证 angr 魔力的时刻,我们只需要指定让程序运行到 0x400844,即验证通过时的位置,而不用管验证的逻辑是怎么样的。完整的 exp 如下,其他文件在 github 相应文件夹中。

import angr

project = angr.Project("entry_language", auto_load_libs=False)

@project.hook(0x400844)
def print_flag(state):
    print "FLAG SHOULD BE:", state.posix.dump_fd(0)
    project.terminate_execution()

project.execute()

Bingo!!!

$ python2 exp_angr.py 
FLAG SHOULD BE: Code_Talkers
$ ./entry_language 
Enter the password: Code_Talkers
Nice!

参考资料