Angr-CTF学习笔记1-5

Angr-CTF

如何使用Angr-CTF

建议运行环境为Ubuntu 16.04 ,macOS 下安装Angr 存在一些Bug (比如说Angr 库的安装,Mach-O 文件格式的执行程序有Bug)

找到一个空白的目录,执行命令git clone https://github.com/jakespringer/angr_ctf.git 下载Angr-CTF 项目

如何编译程序

Angr-CTF 有很多题目,每一个目录是一个独立的题目,题目里面没有现成编译好的程序,需要我们手工来编译,我们以第一题为例子编译测试程序

root@sec:~/angr_ctf# cd 00_angr_find/
root@sec:~/angr_ctf/00_angr_find# python generate.py 1234 00_angr_find

generate.py 是程序生成脚本,它的原理是通过我们输入的一个随机数(这里是1234)来对.c.templite 文件进行混淆,然后编译输出到一个文件名(这里的文件名是00_angr_find ).

Angr-CTF 有一个解题的模版Python 文件(名字为scaffold00.py ),如果我们是在Python3 下安装的Angr 库,那么就需要使用Python3 来执行脚本,效果如下:

root@sec:~/angr_ctf/00_angr_find# python3 scaffold00.py 
WARNING | 2019-05-11 14:42:28,542 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2019-05-11 14:42:28,543 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2019-05-11 14:42:28,543 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state
WARNING | 2019-05-11 14:42:28,543 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2019-05-11 14:42:28,543 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages.
WARNING | 2019-05-11 14:42:28,543 | angr.state_plugins.symbolic_memory | Filling register edi with 4 unconstrained bytes referenced from 0x80486b1 (__libc_csu_init+0x1 in test_code (0x80486b1))
WARNING | 2019-05-11 14:42:28,547 | angr.state_plugins.symbolic_memory | Filling register ebx with 4 unconstrained bytes referenced from 0x80486b3 (__libc_csu_init+0x3 in test_code (0x80486b3))
WARNING | 2019-05-11 14:42:30,063 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fff0000 with 83 unconstrained bytes referenced from 0x9074ee0 (strcmp+0x0 in libc.so.6 (0x74ee0))
WARNING | 2019-05-11 14:42:30,064 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x9074ee0 (strcmp+0x0 in libc.so.6 (0x74ee0))
b'FMKGABFY'

话不多说,接下来开始体验Angr 符号执行库强大的地方吧~

00_angr_find

汇编代码:

.text:0804864E                 push    offset s2       ; "FPQPMQXT"
.text:08048653                 lea     eax, [ebp+s1]
.text:08048656                 push    eax             ; s1
.text:08048657                 call    _strcmp
.text:0804865C                 add     esp, 10h
.text:0804865F                 test    eax, eax
.text:08048661                 jz      short loc_8048675
.text:08048663                 sub     esp, 0Ch
.text:08048666                 push    offset s        ; "Try again."
.text:0804866B                 call    _puts
.text:08048670                 add     esp, 10h
.text:08048673                 jmp     short loc_8048685
.text:08048675 ; ---------------------------------------------------------------------------
.text:08048675
.text:08048675 loc_8048675:                            ; CODE XREF: main+9A↑j
.text:08048675                 sub     esp, 0Ch
.text:08048678                 push    offset aGoodJob ; "Good Job."
.text:0804867D                 call    _puts
.text:08048682                 add     esp, 10h
.text:08048685
.text:08048685 loc_8048685:

使用explore() 函数探索路径,主要目的是要找到'Good Job'这条路径,所以在expolore(find=???)这里填写的是0x8048678这个地址,然后让Angr自己去执行寻找路径

  path_to_binary = './test_code'  # :string
  project = angr.Project(path_to_binary)
  initial_state = project.factory.entry_state()
  simulation = project.factory.simgr(initial_state)
  print_good_address = 0x8048678  # :integer (probably in hexadecimal)
  simulation.explore(find=print_good_address)

Angr函数使用总结:

angr.Project(执行的二进制文件地址) => 打开二进制文件

project.factory.entry_state() => 创建空白的执行上下文环境

project.factory.simgr(上下文对象) => 创建模拟器

simulation.explore(find = 搜索程序执行路径的地址) => 执行路径探索

01_angr_avoid

汇编代码:


.text:0804890F                 jz      short loc_804892E
.text:08048911                 call    avoid_me
.text:08048916                 sub     esp, 8
.text:08048919                 lea     eax, [ebp+var_20]
.text:0804891C                 push    eax
.text:0804891D                 lea     eax, [ebp+var_34]
.text:08048920                 push    eax
.text:08048921                 call    maybe_good
.text:08048926                 add     esp, 10h
.text:08048929                 jmp     loc_80D456F
.text:0804892E ; ---------------------------------------------------------------------------
.text:0804892E
.text:0804892E loc_804892E:                            ; CODE XREF: main+30D↑j
.text:0804892E                 sub     esp, 8
.text:08048931                 lea     eax, [ebp+var_20]
.text:08048934                 push    eax
.text:08048935                 lea     eax, [ebp+var_34]
.text:08048938                 push    eax
.text:08048939                 call    maybe_good
.text:0804893E                 add     esp, 10h
.text:08048941                 jmp     loc_80D456F
.text:08048946 ; ---------------------------------------------------------------------------
.text:08048946
.text:08048946 loc_8048946:                            ; CODE XREF: main+2E5↑j
.text:08048946                 call    avoid_me

.....

01_angr_avoid 有很多垃圾代码插入在main() 函数这里,我们没有办法直接在main() 函数的这些分支语句中定位准确的路径,所以我们需要换一个方式,来看一下maybe_good() 函数的代码

text:080485B5                 public maybe_good
.text:080485B5 maybe_good      proc near               ; CODE XREF: main+31F↓p
.text:080485B5                                         ; main+337↓p ...
.text:080485B5
.text:080485B5 arg_0           = dword ptr  8
.text:080485B5 arg_4           = dword ptr  0Ch
.text:080485B5
.text:080485B5 ; __unwind {
.text:080485B5                 push    ebp
.text:080485B6                 mov     ebp, esp
.text:080485B8                 sub     esp, 8
.text:080485BB                 movzx   eax, should_succeed
.text:080485C2                 test    al, al
.text:080485C4                 jz      short loc_80485EF
.text:080485C6                 sub     esp, 4
.text:080485C9                 push    8
.text:080485CB                 push    [ebp+arg_4]
.text:080485CE                 push    [ebp+arg_0]
.text:080485D1                 call    _strncmp
.text:080485D6                 add     esp, 10h
.text:080485D9                 test    eax, eax
.text:080485DB                 jnz     short loc_80485EF
.text:080485DD                 sub     esp, 0Ch
.text:080485E0                 push    offset aGoodJob ; "Good Job."
.text:080485E5                 call    _puts
.text:080485EA                 add     esp, 10h
.text:080485ED                 jmp     short loc_80485FF
.text:080485EF ; ---------------------------------------------------------------------------
.text:080485EF
.text:080485EF loc_80485EF:                            ; CODE XREF: maybe_good+F↑j
.text:080485EF                                         ; maybe_good+26↑j
.text:080485EF                 sub     esp, 0Ch
.text:080485F2                 push    offset aTryAgain ; "Try again."
.text:080485F7                 call    _puts
.text:080485FC                 add     esp, 10h
.text:080485FF
.text:080485FF loc_80485FF:                            ; CODE XREF: maybe_good+38↑j
.text:080485FF                 nop
.text:08048600                 leave
.text:08048601                 retn
.text:08048601 ; } // starts at 80485B5
.text:08048601 maybe_good      endp
.text:08048601

在maybe_good() 函数的实现里,发现和00_angr_find 一样的逻辑 — 一个分支和两个输出,那么我们就应该知道:"Good Job" 是我们要搜索的目标路径,"Try Again" 是我们要排除的路径,那么用explore() 函数来筛选,方式是用explore(find=0x80485E0,avoid=0x80485F2)来筛选,解答代码如下:

import angr
import sys

def main(argv):
  path_to_binary = './01_angr_avoid'
  project = angr.Project(path_to_binary)
  initial_state = project.factory.entry_state()
  simulation = project.factory.simgr(initial_state)

  print_good_address = 0x080485DD
  will_not_succeed_address = 0x80485EF
  simulation.explore(find=print_good_address, avoid=will_not_succeed_address)

  if simulation.found:
    solution_state = simulation.found[0]
    print(solution_state.posix.dumps(sys.stdin.fileno()))
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

Angr函数使用总结:

simulation.explore(find = 要搜索的路径地址, avoid = 要排除执行路径地址) => 路径探索

simulation.found => 搜索结果集合,这是一个python list 对象

solution_state.posix.dumps( => 获取Payload

02_angr_find_condition

汇编代码:

.text:0804876B loc_804876B:                            ; CODE XREF: main+112↑j
.text:0804876B                 cmp     [ebp+var_38], 0DEADBEEFh
.text:08048772                 jz      short loc_80487B5
.text:08048774                 sub     esp, 8
.text:08048777                 lea     eax, [ebp+s2]
.text:0804877A                 push    eax             ; s2
.text:0804877B                 lea     eax, [ebp+s1]
.text:0804877E                 push    eax             ; s1
.text:0804877F                 call    _strcmp
.text:08048784                 add     esp, 10h
.text:08048787                 test    eax, eax
.text:08048789                 jz      short loc_80487A0
.text:0804878B                 sub     esp, 0Ch
.text:0804878E                 push    offset s        ; "Try again."
.text:08048793                 call    _puts
.text:08048798                 add     esp, 10h
.text:0804879B                 jmp     loc_804D267
.text:080487A0 ; ---------------------------------------------------------------------------
.text:080487A0
.text:080487A0 loc_80487A0:                            ; CODE XREF: main+1C1↑j
.text:080487A0                 sub     esp, 0Ch
.text:080487A3                 push    offset aGoodJob ; "Good Job."
.text:080487A8                 call    _puts
.text:080487AD                 add     esp, 10h
.text:080487B0                 jmp     loc_804D267
.text:080487B5 ; ---------------------------------------------------------------------------
.text:080487B5
.text:080487B5 loc_80487B5:                            ; CODE XREF: main+1AA↑j
.text:080487B5                 sub     esp, 8
.text:080487B8                 lea     eax, [ebp+s2]
.text:080487BB                 push    eax             ; s2
.text:080487BC                 lea     eax, [ebp+s1]
.text:080487BF                 push    eax             ; s1
.text:080487C0                 call    _strcmp
.text:080487C5                 add     esp, 10h
.text:080487C8                 test    eax, eax
.text:080487CA                 jz      short loc_80487E1
.text:080487CC                 sub     esp, 0Ch
.text:080487CF                 push    offset s        ; "Try again."
.text:080487D4                 call    _puts
.text:080487D9                 add     esp, 10h
.text:080487DC                 jmp     loc_804D267
.text:080487E1 ; ---------------------------------------------------------------------------
.text:080487E1
.text:080487E1 loc_80487E1:                            ; CODE XREF: main+202↑j
.text:080487E1                 sub     esp, 0Ch
.text:080487E4                 push    offset aGoodJob ; "Good Job."
.text:080487E9                 call    _puts

02_angr_find_condition 主要是把逻辑判断通过混淆打乱在各个分支上,导致无法使用find 和avoid 直接对单个地址进行定位.explore() 的find 和avoid 可以通过传递回调函数来实现目的地址检验和排除判断.对于这样的混淆思路,解决方法是通过判断控制台输出数据是不是"Good Job" 和"Try again" 来确认执行到了成功还是失败分支.

def main(argv):
  path_to_binary = argv[1]
  project = angr.Project(path_to_binary)
  initial_state = project.factory.entry_state()
  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())

    return 'Good Job' in str(stdout_output) # :boolean

  def should_abort(state): 
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    
    return 'Try again' in str(stdout_output)  # :boolean

  simulation.explore(find=is_successful, avoid=should_abort) 
    

Angr函数使用总结:

simulation.explore(find = 回调函数, avoid = 回调函数) => 路径探索

explore() 函数的回调函数格式为:

def recall_explore(state) :

​ ...

​ return True / False # True 意思是发现了该路径,False 则是忽略

state.posix.dumps(sys.stdout.fileno()) => 获取模拟执行的控制台输出

03_angr_symbolic_registers

汇编代码:

.text:080488E8 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:080488E8                 public main
.text:080488E8 main            proc near               ; DATA XREF: _start+17↑o
.text:080488E8
.text:080488E8 var_14          = dword ptr -14h
.text:080488E8 var_10          = dword ptr -10h
.text:080488E8 var_C           = dword ptr -0Ch
.text:080488E8 var_4           = dword ptr -4
.text:080488E8 argc            = dword ptr  8
.text:080488E8 argv            = dword ptr  0Ch
.text:080488E8 envp            = dword ptr  10h
.text:080488E8
.text:080488E8 ; __unwind {
.text:080488E8                 lea     ecx, [esp+4]
.text:080488EC                 and     esp, 0FFFFFFF0h
.text:080488EF                 push    dword ptr [ecx-4]
.text:080488F2                 push    ebp
.text:080488F3                 mov     ebp, esp
.text:080488F5                 push    ecx
.text:080488F6                 sub     esp, 14h
.text:080488F9                 sub     esp, 0Ch
.text:080488FC                 push    offset aEnterThePasswo ; "Enter the password: "
.text:08048901                 call    _printf
.text:08048906                 add     esp, 10h
.text:08048909                 call    get_user_input
.text:0804890E                 mov     [ebp+var_14], eax
.text:08048911                 mov     [ebp+var_10], ebx
.text:08048914                 mov     [ebp+var_C], edx
.text:08048917                 sub     esp, 0Ch
.text:0804891A                 push    [ebp+var_14]
.text:0804891D                 call    complex_function_1
.text:08048922                 add     esp, 10h
.text:08048925                 mov     ecx, eax
.text:08048927                 mov     [ebp+var_14], ecx
.text:0804892A                 sub     esp, 0Ch
.text:0804892D                 push    [ebp+var_10]
.text:08048930                 call    complex_function_2
.text:08048935                 add     esp, 10h
.text:08048938                 mov     ecx, eax
.text:0804893A                 mov     [ebp+var_10], ecx
.text:0804893D                 sub     esp, 0Ch
.text:08048940                 push    [ebp+var_C]
.text:08048943                 call    complex_function_3
.text:08048948                 add     esp, 10h
.text:0804894B                 mov     ecx, eax
.text:0804894D                 mov     [ebp+var_C], ecx
.text:08048950                 cmp     [ebp+var_14], 0
.text:08048954                 jnz     short loc_8048962
.text:08048956                 cmp     [ebp+var_10], 0
.text:0804895A                 jnz     short loc_8048962
.text:0804895C                 cmp     [ebp+var_C], 0
.text:08048960                 jz      short loc_8048974
.text:08048962
.text:08048962 loc_8048962:                            ; CODE XREF: main+6C↑j
.text:08048962                                         ; main+72↑j
.text:08048962                 sub     esp, 0Ch
.text:08048965                 push    offset s        ; "Try again."
.text:0804896A                 call    _puts
.text:0804896F                 add     esp, 10h
.text:08048972                 jmp     short loc_8048984
.text:08048974 ; ---------------------------------------------------------------------------
.text:08048974
.text:08048974 loc_8048974:                            ; CODE XREF: main+78↑j
.text:08048974                 sub     esp, 0Ch
.text:08048977                 push    offset aGoodJob ; "Good Job."
.text:0804897C                 call    _puts
.text:08048981                 add     esp, 10h
.text:08048984
.text:08048984 loc_8048984:                            ; CODE XREF: main+8A↑j
.text:08048984                 mov     ecx, 0
.text:08048989                 mov     eax, ecx
.text:0804898B                 mov     ecx, [ebp+var_4]
.text:0804898E                 leave
.text:0804898F                 lea     esp, [ecx-4]
.text:08048992                 retn

03_angr_symbolic_registers 主要是多个complex_function 生成数据然后和用户输入进行判断,然后把输入校验的结果在(0x8048950 - 0x8048960)这几个cmp + jz/jnz 判断中进行校验,因为这个时候有三个输入,所以需要分开来求解,我们先来看complex_function() 函数的调用部分

.text:0804890E                 mov     [ebp+var_14], eax
.text:08048911                 mov     [ebp+var_10], ebx
.text:08048914                 mov     [ebp+var_C], edx
.text:08048917                 sub     esp, 0Ch
.text:0804891A                 push    [ebp+var_14]
.text:0804891D                 call    complex_function_1
.text:08048922                 add     esp, 10h
.text:08048925                 mov     ecx, eax
.text:08048927                 mov     [ebp+var_14], ecx
.text:0804892A                 sub     esp, 0Ch
.text:0804892D                 push    [ebp+var_10]
.text:08048930                 call    complex_function_2
.text:08048935                 add     esp, 10h
.text:08048938                 mov     ecx, eax
.text:0804893A                 mov     [ebp+var_10], ecx
.text:0804893D                 sub     esp, 0Ch
.text:08048940                 push    [ebp+var_C]
.text:08048943                 call    complex_function_3

可以看到,EAX EBX EDX 分别是complex_function1-3 的输入参数,那么我们就需要求解EAX EBX EDX 的值.那么我们就需要从0x804890E 处开始执行代码,并在符合条件的路径("Good Job")处求解EAX EBX EDX 的值.

def main(argv):
  path_to_binary = argv[1]
  project = angr.Project(path_to_binary)
  start_address = 0x804890E  # :integer (probably hexadecimal)
  initial_state = project.factory.blank_state(addr=start_address)

  password0_size_in_bits = 4 * 8  #  因为complex_function 输出一个int 类型的数据,那就是32bits
  password0 = claripy.BVS('password0', password0_size_in_bits)
  password1 = claripy.BVS('password1', password0_size_in_bits)
  password2 = claripy.BVS('password2', password0_size_in_bits)

  initial_state.regs.eax = password0  #  告诉符号执行引擎这三个寄存器分别是complex_function 的参数
  initial_state.regs.ebx = password1
  initial_state.regs.edx = password2

  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return 'Good Job' in str(stdout_output)  #  根据输出来判断执行路径

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return 'Try again.' in str(stdout_output)

  simulation.explore(find=is_successful, avoid=should_abort)

  if simulation.found:
    solution_state = simulation.found[0]

    solution0 = solution_state.se.eval(password0)
    solution1 = solution_state.se.eval(password1)
    solution2 = solution_state.se.eval(password2)

    solution = ' '.join(map('{:x}'.format, [ solution0, solution1, solution2 ]))  # :string
    print(solution)
  else:
    raise Exception('Could not find the solution')

Angr函数使用总结:

project.factory.blank_state(addr=start_address) => 创建自定义入口的状态上下文

initial_state.regs => 操作状态上下文的寄存器

claripy.BVS('变量名', 变量大小) => 创建求解变量

solution_state.se.eval(变量) => 求解符号变量

04_angr_symbolic_registers

汇编代码:

.text:080486F4 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:080486F4                 public main
.text:080486F4 main            proc near               ; DATA XREF: _start+17↑o
.text:080486F4
.text:080486F4 var_4           = dword ptr -4
.text:080486F4 argc            = dword ptr  8
.text:080486F4 argv            = dword ptr  0Ch
.text:080486F4 envp            = dword ptr  10h
.text:080486F4
.text:080486F4 ; __unwind {
.text:080486F4                 lea     ecx, [esp+4]
.text:080486F8                 and     esp, 0FFFFFFF0h
.text:080486FB                 push    dword ptr [ecx-4]
.text:080486FE                 push    ebp
.text:080486FF                 mov     ebp, esp
.text:08048701                 push    ecx
.text:08048702                 sub     esp, 4
.text:08048705                 sub     esp, 0Ch
.text:08048708                 push    offset aEnterThePasswo ; "Enter the password: "
.text:0804870D                 call    _printf
.text:08048712                 add     esp, 10h
.text:08048715                 call    handle_user
.text:0804871A                 mov     eax, 0
.text:0804871F                 mov     ecx, [ebp+var_4]
.text:08048722                 leave
.text:08048723                 lea     esp, [ecx-4]
.text:08048726                 retn

主要的代码逻辑在handle_user() 函数里面,再来看看代码

.text:08048679 handle_user     proc near               ; CODE XREF: main+21↓p
.text:08048679
.text:08048679 var_10          = dword ptr -10h
.text:08048679 var_C           = dword ptr -0Ch
.text:08048679
.text:08048679 ; __unwind {
.text:08048679                 push    ebp
.text:0804867A                 mov     ebp, esp
.text:0804867C                 sub     esp, 18h
.text:0804867F                 sub     esp, 4
.text:08048682                 lea     eax, [ebp+var_10]
.text:08048685                 push    eax
.text:08048686                 lea     eax, [ebp+var_C]
.text:08048689                 push    eax
.text:0804868A                 push    offset aUU      ; "%u %u"
.text:0804868F                 call    ___isoc99_scanf
.text:08048694                 add     esp, 10h
.text:08048697                 mov     eax, [ebp+var_C]   ; Argument complex_function0
.text:0804869A                 sub     esp, 0Ch
.text:0804869D                 push    eax
.text:0804869E                 call    complex_function0  ; Call complex_function0
.text:080486A3                 add     esp, 10h
.text:080486A6                 mov     [ebp+var_C], eax
.text:080486A9                 mov     eax, [ebp+var_10]  ; Argument complex_function1
.text:080486AC                 sub     esp, 0Ch
.text:080486AF                 push    eax
.text:080486B0                 call    complex_function1  ; Call complex_function1
.text:080486B5                 add     esp, 10h
.text:080486B8                 mov     [ebp+var_10], eax
.text:080486BB                 mov     eax, [ebp+var_C]
.text:080486BE                 cmp     eax, 0D3062A4Ch
.text:080486C3                 jnz     short loc_80486CF  ; Check Value with input
.text:080486C5                 mov     eax, [ebp+var_10]
.text:080486C8                 cmp     eax, 694E5BA0h
.text:080486CD                 jz      short loc_80486E1
.text:080486CF
.text:080486CF loc_80486CF:                            ; CODE XREF: handle_user+4A↑j
.text:080486CF                 sub     esp, 0Ch
.text:080486D2                 push    offset s        ; "Try again."
.text:080486D7                 call    _puts
.text:080486DC                 add     esp, 10h
.text:080486DF                 jmp     short loc_80486F1
.text:080486E1 ; ---------------------------------------------------------------------------
.text:080486E1
.text:080486E1 loc_80486E1:                            ; CODE XREF: handle_user+54↑j
.text:080486E1                 sub     esp, 0Ch
.text:080486E4                 push    offset aGoodJob ; "Good Job."
.text:080486E9                 call    _puts
.text:080486EE                 add     esp, 10h
.text:080486F1
.text:080486F1 loc_80486F1:                            ; CODE XREF: handle_user+66↑j
.text:080486F1                 nop
.text:080486F2                 leave
.text:080486F3                 retn

可以看到,现在complex_function 的参数是通过栈来传输的,complex_function0 主要的代码是运算一些数据保存到arg_0 中,所以我们才需要跟踪执行这个栈上的参数

.text:080484A9 complex_function0 proc near             ; CODE XREF: handle_user+25↓p
.text:080484A9
.text:080484A9 arg_0           = dword ptr  8
.text:080484A9
.text:080484A9 ; __unwind {
.text:080484A9                 push    ebp
.text:080484AA                 mov     ebp, esp
.text:080484AC                 xor     [ebp+arg_0], 0D53642BEh
.text:080484B3                 xor     [ebp+arg_0], 58FC2926h
.text:080484BA                 xor     [ebp+arg_0], 25596A36h
.text:080484C1                 xor     [ebp+arg_0], 0A7AFAA43h
.text:080484C8                 xor     [ebp+arg_0], 1559CAFEh
.text:080484CF                 xor     [ebp+arg_0], 0D8D89C66h
.text:080484D6                 xor     [ebp+arg_0], 6B8B30B6h
.text:080484DD                 xor     [ebp+arg_0], 0B5E7C180h
.text:080484E4                 xor     [ebp+arg_0], 1FA429F6h
.text:080484EB                 xor     [ebp+arg_0], 21C70AF4h
.text:080484F2                 xor     [ebp+arg_0], 0B7261E1Dh
.text:080484F9                 xor     [ebp+arg_0], 0ADD88AD8h
.text:08048500                 xor     [ebp+arg_0], 3E16A0F2h
.text:08048507                 xor     [ebp+arg_0], 0DF2308FBh
.text:0804850E                 xor     [ebp+arg_0], 2273AAFh
.text:08048515                 xor     [ebp+arg_0], 8E69AC70h
.text:0804851C                 xor     [ebp+arg_0], 0AC8924h
.text:08048523                 xor     [ebp+arg_0], 561B782h
.text:0804852A                 xor     [ebp+arg_0], 5A64A924h
.text:08048531                 xor     [ebp+arg_0], 0B118005Bh
.text:08048538                 xor     [ebp+arg_0], 61461EA2h
.text:0804853F                 xor     [ebp+arg_0], 0E0E04E79h
.text:08048546                 xor     [ebp+arg_0], 0A8DDACAAh
.text:0804854D                 xor     [ebp+arg_0], 82AF667Dh
.text:08048554                 xor     [ebp+arg_0], 0B3CB4464h
.text:0804855B                 xor     [ebp+arg_0], 43B7BB1Ah
.text:08048562                 xor     [ebp+arg_0], 0DF30F25Bh
.text:08048569                 xor     [ebp+arg_0], 4C0F3376h
.text:08048570                 xor     [ebp+arg_0], 0B2E462E5h
.text:08048577                 xor     [ebp+arg_0], 7BF4CFC3h
.text:0804857E                 xor     [ebp+arg_0], 0C2960388h
.text:08048585                 xor     [ebp+arg_0], 27071524h
.text:0804858C                 mov     eax, [ebp+arg_0]
.text:0804858F                 pop     ebp
.text:08048590                 retn
.text:08048590 ; } // starts at 80484A9

再回来看两个函数的调用的栈情况:

.text:08048697           >     mov     eax, [ebp+var_C]   ; Argument complex_function0
.text:0804869A                 sub     esp, 0Ch
.text:0804869D                 push    eax
.text:0804869E                 call    complex_function0  ; Call complex_function0
.text:080486A3                 add     esp, 10h
.text:080486A6                 mov     [ebp+var_C], eax
.text:080486A9           >     mov     eax, [ebp+var_10]  ; Argument complex_function1
.text:080486AC                 sub     esp, 0Ch
.text:080486AF                 push    eax
.text:080486B0                 call    complex_function1  ; Call complex_function1

此时我们可以知道,var_C 和var_10 都是在栈上是连续的,那么我们就需要构造两个连续的push data ,把password 保存到栈上,并调节esp - 8 .

def main(argv):
  path_to_binary = argv[1]
  project = angr.Project(path_to_binary)
  start_address = 0x8048697
  initial_state = project.factory.blank_state(addr=start_address)
  
  initial_state.regs.ebp = initial_state.regs.esp

  password0 = claripy.BVS('password0', 4 * 8)  #  int 
  password1 = claripy.BVS('password1', 4 * 8)
  
  padding_length_in_bytes = 8  # integer * 2
  initial_state.regs.esp -= padding_length_in_bytes

  initial_state.stack_push(password0)  # :bitvector (claripy.BVS, claripy.BVV, claripy.BV)
  initial_state.stack_push(password1)

  simulation = project.factory.simgr(initial_state)

  simulation.explore(find=0x80486E4,avoid=0x80486D2)

  if simulation.found:
    solution_state = simulation.found[0]

    solution0 = solution_state.se.eval(password0)
    solution1 = solution_state.se.eval(password1)
    solution = ' '.join(map('{:x}'.format, [ solution0, solution1 ]))  # :string
    print(solution)
  

05_angr_symbolic_memory

汇编代码:

.text:080485E0                 push    offset unk_9FD92B8
.text:080485E5                 push    offset unk_9FD92B0
.text:080485EA                 push    offset unk_9FD92A8
.text:080485EF                 push    offset user_input
.text:080485F4                 push    offset a8s8s8s8s ; "%8s %8s %8s %8s"
.text:080485F9                 call    ___isoc99_scanf  ;  用户输入
.text:080485FE                 add     esp, 20h
.text:08048601                 mov     [ebp+var_C], 0
.text:08048608                 jmp     short loc_8048637  ;  注意这里有一个循环
.text:0804860A ; ---------------------------------------------------------------------------
.text:0804860A
.text:0804860A loc_804860A:                            ; CODE XREF: main+93↓j
.text:0804860A                 mov     eax, [ebp+var_C]
.text:0804860D                 add     eax, 9FD92A0h
.text:08048612                 movzx   eax, byte ptr [eax]  ;  Argument complex_function
.text:08048615                 movsx   eax, al
.text:08048618                 sub     esp, 8
.text:0804861B                 push    [ebp+var_C]
.text:0804861E                 push    eax
.text:0804861F                 call    complex_function  ;  计算函数
.text:08048624                 add     esp, 10h
.text:08048627                 mov     edx, eax
.text:08048629                 mov     eax, [ebp+var_C]
.text:0804862C                 add     eax, 9FD92A0h
.text:08048631                 mov     [eax], dl
.text:08048633                 add     [ebp+var_C], 1
.text:08048637
.text:08048637 loc_8048637:                            ; CODE XREF: main+60↑j
.text:08048637                 cmp     [ebp+var_C], 1Fh
.text:0804863B                 jle     short loc_804860A
.text:0804863D                 sub     esp, 4
.text:08048640                 push    20h             ; n
.text:08048642                 push    offset s2       ; "THNJXTHBJUCDIMEEMLZNGMHISXAIXDQG"
.text:08048647                 push    offset user_input ; s1
.text:0804864C                 call    _strncmp
.text:08048651                 add     esp, 10h
.text:08048654                 test    eax, eax
.text:08048656                 jz      short loc_804866A  ; 判断输入和complex_function是否相等
.text:08048658                 sub     esp, 0Ch
.text:0804865B                 push    offset s        ; "Try again."
.text:08048660                 call    _puts
.text:08048665                 add     esp, 10h
.text:08048668                 jmp     short loc_804867A
.text:0804866A ; ---------------------------------------------------------------------------
.text:0804866A
.text:0804866A loc_804866A:                            ; CODE XREF: main+AE↑j
.text:0804866A                 sub     esp, 0Ch
.text:0804866D                 push    offset aGoodJob ; "Good Job."
.text:08048672                 call    _puts
.text:08048677                 add     esp, 10h
.text:0804867A
.text:0804867A loc_804867A:                            ; CODE XREF: main+C0↑j
.text:0804867A                 mov     eax, 0
.text:0804867F                 mov     ecx, [ebp+var_4]
.text:08048682                 leave
.text:08048683                 lea     esp, [ecx-4]
.text:08048686                 retn

那么现在我们的目标就是要关注下面这四块内存

.text:080485E0                 push    offset unk_9FD92B8
.text:080485E5                 push    offset unk_9FD92B0
.text:080485EA                 push    offset unk_9FD92A8
.text:080485EF                 push    offset user_input

每一块内存的大小是8Byte

.text:080485F4                 push    offset a8s8s8s8s ; "%8s %8s %8s %8s"
.text:080485F9                 call    ___isoc99_scanf  ;  用户输入

程序执行地址为0x8048601 ,在scanf 调整栈内存之后(.text:080485FE add esp, 20h)开始执行.

def main(argv):
  path_to_binary = argv[1]
  project = angr.Project(path_to_binary)

  start_address = 0x8048601
  initial_state = project.factory.blank_state(addr=start_address)

  password0 = claripy.BVS('password0', 8 * 8)
  password1 = claripy.BVS('password1', 8 * 8)
  password2 = claripy.BVS('password2', 8 * 8)
  password3 = claripy.BVS('password3', 8 * 8)

  password0_address = 0x9FD92A0
  initial_state.memory.store(password0_address, password0)
  password1_address = 0x9FD92A8
  initial_state.memory.store(password1_address, password1)
  password2_address = 0x9FD92B0
  initial_state.memory.store(password2_address, password2)
  password3_address = 0x9FD92B8
  initial_state.memory.store(password3_address, password3)

  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return 'Good Job' in str(stdout_output)

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return 'Try again' in str(stdout_output)

  simulation.explore(find=is_successful, avoid=should_abort)

  if simulation.found:
    solution_state = simulation.found[0]
    solution0 = solution_state.se.eval(password0)
    solution1 = solution_state.se.eval(password1)
    solution2 = solution_state.se.eval(password2)
    solution3 = solution_state.se.eval(password3)
    solution = ' '.join(map('{:x}'.format, [ solution0, solution1,solution2,solution3 ]))

    print(solution)

Angr函数使用总结:

initial_state.memory.store(地址,数据) => 初始化内存地址中的数据

点击关注,共同学习!
安全狗的自我修养

github haidragon

posted @ 2022-10-29 15:20  syscallwww  阅读(194)  评论(0编辑  收藏  举报