Angr-CTF学习笔记6-10

06_angr_symbolic_dynamic_memory

汇编代码:

.text:08048621                 push    9               ; size
.text:08048623                 call    _malloc
.text:08048628                 add     esp, 10h
.text:0804862B                 mov     ds:buffer0, eax
.text:08048630                 sub     esp, 0Ch
.text:08048633                 push    9               ; size
.text:08048635                 call    _malloc
.text:0804863A                 add     esp, 10h
.text:0804863D                 mov     ds:buffer1, eax
.text:08048642                 mov     eax, ds:buffer0
.text:08048647                 sub     esp, 4
.text:0804864A                 push    9               ; n
.text:0804864C                 push    0               ; c
.text:0804864E                 push    eax             ; s
.text:0804864F                 call    _memset
.text:08048654                 add     esp, 10h
.text:08048657                 mov     eax, ds:buffer1
.text:0804865C                 sub     esp, 4
.text:0804865F                 push    9               ; n
.text:08048661                 push    0               ; c
.text:08048663                 push    eax             ; s
.text:08048664                 call    _memset
.text:08048669                 add     esp, 10h
.text:0804866C                 sub     esp, 0Ch
.text:0804866F                 push    offset aEnterThePasswo ; "Enter the password: "
.text:08048674                 call    _printf
.text:08048679                 add     esp, 10h
.text:0804867C                 mov     edx, ds:buffer1
.text:08048682                 mov     eax, ds:buffer0
.text:08048687                 sub     esp, 4
.text:0804868A                 push    edx
.text:0804868B                 push    eax
.text:0804868C                 push    offset a8s8s    ; "%8s %8s"
.text:08048691                 call    ___isoc99_scanf

这次scanf() 有两个输入参数,数据保存的位置是通过全局变量的char* 指针来保存到buffer 中,大小为8 字节.

.text:08048699                 mov     [ebp+var_C], 0
.text:080486A0                 jmp     short loc_8048706
.text:080486A2 ; ---------------------------------------------------------------------------
.text:080486A2
.text:080486A2 loc_80486A2:                            ; CODE XREF: main+FE↓j
.text:080486A2                 mov     edx, ds:buffer0
.text:080486A8                 mov     eax, [ebp+var_C]
.text:080486AB                 lea     ebx, [edx+eax]
.text:080486AE                 mov     edx, ds:buffer0
.text:080486B4                 mov     eax, [ebp+var_C]
.text:080486B7                 add     eax, edx
.text:080486B9                 movzx   eax, byte ptr [eax]
.text:080486BC                 movsx   eax, al
.text:080486BF                 sub     esp, 8
.text:080486C2                 push    [ebp+var_C]
.text:080486C5                 push    eax
.text:080486C6                 call    complex_function
.text:080486CB                 add     esp, 10h
.text:080486CE                 mov     [ebx], al
.text:080486D0                 mov     edx, ds:buffer1
.text:080486D6                 mov     eax, [ebp+var_C]
.text:080486D9                 lea     ebx, [edx+eax]
.text:080486DC                 mov     eax, [ebp+var_C]
.text:080486DF                 lea     edx, [eax+20h]
.text:080486E2                 mov     ecx, ds:buffer1
.text:080486E8                 mov     eax, [ebp+var_C]
.text:080486EB                 add     eax, ecx
.text:080486ED                 movzx   eax, byte ptr [eax]
.text:080486F0                 movsx   eax, al
.text:080486F3                 sub     esp, 8
.text:080486F6                 push    edx
.text:080486F7                 push    eax
.text:080486F8                 call    complex_function
.text:080486FD                 add     esp, 10h
.text:08048700                 mov     [ebx], al
.text:08048702                 add     [ebp+var_C], 1
.text:08048706
.text:08048706 loc_8048706:                            ; CODE XREF: main+94↑j
.text:08048706                 cmp     [ebp+var_C], 7
.text:0804870A                 jle     short loc_80486A2

接下来就到了complex_function 运算的地方了,我们主要是对buffer0 和buffer1 指向的内存做求解.

.bss:09FD92AC buffer0         dd ?                    ; DATA XREF: main+1F↑w
.bss:09FD92AC                                         ; main+36↑r ...
.bss:09FD92B0                 public buffer3
.bss:09FD92B0 buffer3         db    ? ;
.bss:09FD92B1                 db    ? ;
.bss:09FD92B2                 db    ? ;
.bss:09FD92B3                 db    ? ;

所以,我们在利用 initial_state.memory.store() 构造内存时,还需要在里面填入指向保存数据的地址,Angr可以不用创建新内存(malloc),直接指向内存中一个任意位置即可,所以我们写0x4444440 和0x44444450 到buffer0 和buffer1 的内存中.

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

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

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

  fake_heap_address0 = 0x4444440
  pointer_to_malloc_memory_address0 = 0x9FD92AC
  initial_state.memory.store(pointer_to_malloc_memory_address0,fake_heap_address0, endness=project.arch.memory_endness)
  fake_heap_address1 = 0x4444450
  pointer_to_malloc_memory_address1 = 0x9FD92B0
  initial_state.memory.store(pointer_to_malloc_memory_address1,fake_heap_address1, endness=project.arch.memory_endness)

  initial_state.memory.store(fake_heap_address0, password0)
  initial_state.memory.store(fake_heap_address1, password1)

  simulation = project.factory.simgr(initial_state)

  simulation.explore(find=0x8048759, avoid=0x8048747)

  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 ]))

    print(solution)

Angr函数使用总结:

initial_state.memory.store(地址,数据,endness = 数据字节顺序) => 设置初始化内存数据

project.arch.memory_endness => 指的是内存字节顺序

07_angr_symbolic_file

汇编代码:

.text:0804888E                 push    40h             ; n
.text:08048890                 push    0               ; c
.text:08048892                 push    offset buffer   ; s
.text:08048897                 call    _memset
.text:0804889C                 add     esp, 10h
.text:0804889F                 sub     esp, 0Ch
.text:080488A2                 push    offset aEnterThePasswo ; "Enter the password: "
.text:080488A7                 call    _printf
.text:080488AC                 add     esp, 10h
.text:080488AF                 sub     esp, 8
.text:080488B2                 push    offset buffer
.text:080488B7                 push    offset a64s     ; "%64s"
.text:080488BC                 call    ___isoc99_scanf
.text:080488C1                 add     esp, 10h
.text:080488C4                 sub     esp, 8
.text:080488C7                 push    40h             ; n
.text:080488C9                 push    offset buffer   ; int
.text:080488CE                 call    ignore_me
.text:080488D3                 add     esp, 10h
.text:080488D6                 sub     esp, 4
.text:080488D9                 push    40h             ; n
.text:080488DB                 push    0               ; c
.text:080488DD                 push    offset buffer   ; s
.text:080488E2                 call    _memset
.text:080488E7                 add     esp, 10h
.text:080488EA                 sub     esp, 8
.text:080488ED                 push    offset aRb      ; "rb"
.text:080488F2                 push    offset name     ; "MRXJKZYR.txt"
.text:080488F7                 call    _fopen
.text:080488FC                 add     esp, 10h
.text:080488FF                 mov     ds:fp, eax
.text:08048904                 mov     eax, ds:fp
.text:08048909                 push    eax             ; stream
.text:0804890A                 push    40h             ; n
.text:0804890C                 push    1               ; size
.text:0804890E                 push    offset buffer   ; ptr
.text:08048913                 call    _fread
.text:08048918                 add     esp, 10h
.text:0804891B                 mov     eax, ds:fp
.text:08048920                 sub     esp, 0Ch
.text:08048923                 push    eax             ; stream
.text:08048924                 call    _fclose
.text:08048929                 add     esp, 10h
.text:0804892C                 sub     esp, 0Ch
.text:0804892F                 push    offset name     ; "MRXJKZYR.txt"
.text:08048934                 call    _unlink

程序逻辑是用户输入一串Key之后,经过计算保存到文件里(在ignore_me() 里面实现),然后通过fread() 读取文件获取数据.Angr库中有一个模拟的文件系统,我们可以通过这个文件系统来模拟fread() 出来的数据,继续往下看汇编

.text:0804893C                 mov     [ebp+var_C], 0
.text:08048943                 jmp     short loc_8048972
.text:08048945 ; ---------------------------------------------------------------------------
.text:08048945
.text:08048945 loc_8048945:                            ; CODE XREF: main+FC↓j
.text:08048945                 mov     eax, [ebp+var_C]
.text:08048948                 add     eax, 804A0A0h
.text:0804894D                 movzx   eax, byte ptr [eax]  ;  生成数据
.text:08048950                 movsx   eax, al
.text:08048953                 sub     esp, 8
.text:08048956                 push    [ebp+var_C]
.text:08048959                 push    eax
.text:0804895A                 call    complex_function
.text:0804895F                 add     esp, 10h
.text:08048962                 mov     edx, eax
.text:08048964                 mov     eax, [ebp+var_C]
.text:08048967                 add     eax, 804A0A0h
.text:0804896C                 mov     [eax], dl
.text:0804896E                 add     [ebp+var_C], 1
.text:08048972
.text:08048972 loc_8048972:                            ; CODE XREF: main+C9↑j
.text:08048972                 cmp     [ebp+var_C], 7
.text:08048976                 jle     short loc_8048945
.text:08048978                 sub     esp, 4
.text:0804897B                 push    9               ; n
.text:0804897D                 push    offset s2       ; "UKNRZDIR"
.text:08048982                 push    offset buffer   ; s1
.text:08048987                 call    _strncmp
.text:0804898C                 add     esp, 10h
.text:0804898F                 test    eax, eax
.text:08048991                 jz      short loc_80489AD  ;  校验用户输入和生成数据
.text:08048993                 sub     esp, 0Ch
.text:08048996                 push    offset s        ; "Try again."
.text:0804899B                 call    _puts
.text:080489A0                 add     esp, 10h
.text:080489A3                 sub     esp, 0Ch
.text:080489A6                 push    1               ; status
.text:080489A8                 call    _exit
.text:080489AD ; ---------------------------------------------------------------------------
.text:080489AD
.text:080489AD loc_80489AD:                            ; CODE XREF: main+117↑j
.text:080489AD                 sub     esp, 0Ch
.text:080489B0                 push    offset aGoodJob ; "Good Job."
.text:080489B5                 call    _puts
.text:080489BA                 add     esp, 10h
.text:080489BD                 sub     esp, 0Ch
.text:080489C0                 push    0               ; status
.text:080489C2                 call    _exit

这段代码并没有什么特别之处,只是简单地对进行数据生成和校验然后输出判断结果.那么现在我们使用Angr的文件系统来进行求解

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

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

  filename = 'MRXJKZYR.txt'  # :string
  symbolic_file_size_bytes = 0x40

  password = claripy.BVS('password', symbolic_file_size_bytes * 8)

  password_file = angr.storage.SimFile(filename, password, size=symbolic_file_size_bytes) # 模拟读文件,默认的文件内容是password,文件大小是symbolic_file_size_bytes

  symbolic_filesystem = {
    filename : password_file
  }
  initial_state.posix.fs = symbolic_filesystem # 构建状态上下文里的文件系统数据

  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]

    solution = solution_state.se.eval(password)#,cast_to=str)

    print(solution)

我们也可以继续用旧的内存跟踪的方法来做,示例代码如下

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

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

  password = claripy.BVS('password', 0x40 * 8)

  initial_state.memory.store(0x804A0A0, password)

  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]

    solution = solution_state.se.eval(password)

    print(solution)

注意事项:

在Angr-CTF 的解题Python 文件中,我们直接执行程序可能会遇到下面这个结果

Traceback (most recent call last):
File “solve07.py”, line 130, in
main(sys.argv)
File “solve07.py”, line 91, in main
password_file = angr.storage.SimFile(filename, file_options, content=symbolic_file_backing_memory, size=symbolic_file_size_bytes)
TypeError: init() got multiple values for argument ‘content’

这是因为Angr-CTF 是使用旧版的Angr 库来实现的,现在SimFile() 函数的声明已经被修改如下

SimFile(name, content=None**,** size=None**,** has_end=None**,** seekable=True**,** writable=True**,** ident=None**,** concrete=None**,** kwargs)

也就是说,SimFile 的第二个参数不再是file_options 了,所以我们可以看到SimFile() 提示content 参数被传递了两个参数;而且新版的SimFile() 函数的content 参数只接受字符串或者BitVector ,所以用solution 中的解题脚本会出问题是因为它传递的是内存对象而不是BitVector ,于是代码修改如下

password_file = angr.storage.SimFile(filename, password, size=symbolic_file_size_bytes)

Angr函数使用总结:

angr.storage.SimFile(文件名,文件内容, size = 文件大小) => 创建一个模拟文件,当有被执行的程序fopen 打开文件时,我们可以控制其里面的内容

initial_state.posix.fs => 状态上下文的文件系统对象

08_angr_constraints

汇编代码:

.text:080485C4                 mov     ds:password, 4A58524Dh  ;  这个是全局变量:0x804A040
.text:080485CE                 mov     ds:dword_804A044, 52595A4Bh
.text:080485D8                 mov     ds:dword_804A048, 454B4D4Bh
.text:080485E2                 mov     ds:dword_804A04C, 425A464Eh
.text:080485EC                 sub     esp, 4
.text:080485EF                 push    11h             ; n
.text:080485F1                 push    0               ; c
.text:080485F3                 push    offset buffer   ; s
.text:080485F8                 call    _memset
.text:080485FD                 add     esp, 10h
.text:08048600                 sub     esp, 0Ch
.text:08048603                 push    offset aEnterThePasswo ; "Enter the password: "
.text:08048608                 call    _printf
.text:0804860D                 add     esp, 10h
.text:08048610                 sub     esp, 8
.text:08048613                 push    offset buffer
.text:08048618                 push    offset a16s     ; "%16s"
.text:0804861D                 call    ___isoc99_scanf  ;  用户输入,这个也是全局变量:0x804A050
.text:08048622                 add     esp, 10h
.text:08048625                 mov     [ebp+var_C], 0
.text:0804862C                 jmp     short loc_8048663
.text:0804862E ; ---------------------------------------------------------------------------
.text:0804862E
.text:0804862E loc_804862E:                            ; CODE XREF: main+B4↓j
.text:0804862E                 mov     eax, 0Fh
.text:08048633                 sub     eax, [ebp+var_C]
.text:08048636                 mov     edx, eax
.text:08048638                 mov     eax, [ebp+var_C]
.text:0804863B                 add     eax, 804A050h
.text:08048640                 movzx   eax, byte ptr [eax]
.text:08048643                 movsx   eax, al
.text:08048646                 sub     esp, 8
.text:08048649                 push    edx
.text:0804864A                 push    eax
.text:0804864B                 call    complex_function  ;  对用户输入进行计算
.text:08048650                 add     esp, 10h
.text:08048653                 mov     edx, eax
.text:08048655                 mov     eax, [ebp+var_C]
.text:08048658                 add     eax, 804A050h
.text:0804865D                 mov     [eax], dl
.text:0804865F                 add     [ebp+var_C], 1
.text:08048663
.text:08048663 loc_8048663:                            ; CODE XREF: main+79↑j
.text:08048663                 cmp     [ebp+var_C], 0Fh
.text:08048667                 jle     short loc_804862E
.text:08048669                 sub     esp, 8
.text:0804866C                 push    10h
.text:0804866E                 push    offset buffer
.text:08048673                 call    check_equals_MRXJKZYRKMKENFZB  ;  check_equals() 函数是把buffer 和password 来对比
.text:08048678                 add     esp, 10h
.text:0804867B                 test    eax, eax
.text:0804867D                 jnz     short loc_8048691  ;  对比校验结果
.text:0804867F                 sub     esp, 0Ch
.text:08048682                 push    offset s        ; "Try again."
.text:08048687                 call    _puts
.text:0804868C                 add     esp, 10h
.text:0804868F                 jmp     short loc_80486A1
.text:08048691 ; ---------------------------------------------------------------------------
.text:08048691
.text:08048691 loc_8048691:                            ; CODE XREF: main+CA↑j
.text:08048691                 sub     esp, 0Ch
.text:08048694                 push    offset aGoodJob ; "Good Job."
.text:08048699                 call    _puts
.text:0804869E                 add     esp, 10h
.text:080486A1

主要的思路是把complex_function() 计算的结果和字符串MRXJKZYRKMKENFZB 来做对比,我们假定complex_function() 的输入是未知的,check_equals() 函数中对比的内容是已知的,那么我们的关注点就在于对输入进行求解.

首先第一步,我们需要在complex_function() 循环计算之后(地址0x804866C)就可以得到buffer 的符号执行内容,接下来我们需要根据buffer 的内容和对比的字符串MRXJKZYRKMKENFZB来计算是否有满足的解.

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

  start_address = 0x8048625  #  scanf() 之后的位置
  initial_state = project.factory.blank_state(addr=start_address)

  buffer = claripy.BVS('buffer', 0x10* 8)  #  buffer的大小是0x10 字节,也就是0x10 * 8 比特
  buffer_address = 0x804A050
  initial_state.memory.store(buffer_address, buffer)  #  buffer 是全局变量,地址是0x804A050

  simulation = project.factory.simgr(initial_state)
  address_to_check_constraint = 0x804866C
  simulation.explore(find=address_to_check_constraint)  #  compilex_function() 计算结束位置

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

    constrained_parameter_address = 0x804A050  #  计算约束的内存位置
    constrained_parameter_size_bytes = 0x10    #  计算约束的内存大小
    constrained_parameter_bitvector = solution_state.memory.load(  #  加载内存
      constrained_parameter_address,
      constrained_parameter_size_bytes
    )

    constrained_parameter_desired_value = 'MRXJKZYRKMKENFZB' #  Key-String

    solution_state.add_constraints(constrained_parameter_bitvector == constrained_parameter_desired_value)  #  添加约束条件使用求解器求解内存中的bitvector 和Key-String 相等时是否有满足的解

    solution = solution_state.se.eval(buffer)

    print(solution)

Angr函数使用总结:

solution_state.memory.load(内存地址,内存大小) => 加载内存

solution_state.add_constraints(约束条件) => 添加约束条件

09_angr_hooks

汇编代码:

.text:08048604                 mov     ds:password, 4A58524Dh
.text:0804860E                 mov     ds:dword_804A048, 52595A4Bh
.text:08048618                 mov     ds:dword_804A04C, 454B4D4Bh
.text:08048622                 mov     ds:dword_804A050, 425A464Eh  ;  Key-String
.text:0804862C                 sub     esp, 4
.text:0804862F                 push    11h             ; n
.text:08048631                 push    0               ; c
.text:08048633                 push    offset buffer   ; s
.text:08048638                 call    _memset         
.text:0804863D                 add     esp, 10h
.text:08048640                 sub     esp, 0Ch
.text:08048643                 push    offset aEnterThePasswo ; "Enter the password: "
.text:08048648                 call    _printf
.text:0804864D                 add     esp, 10h
.text:08048650                 sub     esp, 8
.text:08048653                 push    offset buffer
.text:08048658                 push    offset a16s     ; "%16s"
.text:0804865D                 call    ___isoc99_scanf ; 用户输入Key
.text:08048662                 add     esp, 10h
.text:08048665                 mov     [ebp+var_10], 0
.text:0804866C                 jmp     short loc_80486A3
.text:0804866E ; ---------------------------------------------------------------------------
.text:0804866E
.text:0804866E loc_804866E:                            ; CODE XREF: main+B4↓j
.text:0804866E                 mov     eax, 12h
.text:08048673                 sub     eax, [ebp+var_10]
.text:08048676                 mov     edx, eax
.text:08048678                 mov     eax, [ebp+var_10]
.text:0804867B                 add     eax, 804A054h   ;  这个地址是用户输入buffer
.text:08048680                 movzx   eax, byte ptr [eax]
.text:08048683                 movsx   eax, al
.text:08048686                 sub     esp, 8
.text:08048689                 push    edx
.text:0804868A                 push    eax
.text:0804868B                 call    complex_function  ;  对用户输入进行计算
.text:08048690                 add     esp, 10h
.text:08048693                 mov     edx, eax
.text:08048695                 mov     eax, [ebp+var_10]
.text:08048698                 add     eax, 804A054h
.text:0804869D                 mov     [eax], dl
.text:0804869F                 add     [ebp+var_10], 1
.text:080486A3
.text:080486A3 loc_80486A3:                            ; CODE XREF: main+79↑j
.text:080486A3                 cmp     [ebp+var_10], 0Fh
.text:080486A7                 jle     short loc_804866E  ;  这是一个循环complex_function() 计算
.text:080486A9                 sub     esp, 8
.text:080486AC                 push    10h
.text:080486AE                 push    offset buffer
.text:080486B3                 call    check_equals_MRXJKZYRKMKENFZB  ;  check_equals() 对比结果
.text:080486B8                 add     esp, 10h
.text:080486BB                 mov     ds:equals, eax  ;  check_equals 的结果保存在全局变量equals里
.text:080486C0                 mov     [ebp+var_C], 0
.text:080486C7                 jmp     short loc_80486FA
.text:080486C9 ; ---------------------------------------------------------------------------
.text:080486C9
.text:080486C9 loc_80486C9:                            ; CODE XREF: main+10B↓j
.text:080486C9                 mov     eax, [ebp+var_C]
.text:080486CC                 lea     edx, [eax+9]
.text:080486CF                 mov     eax, [ebp+var_C]
.text:080486D2                 add     eax, 804A044h  ;  这个是Key-String
.text:080486D7                 movzx   eax, byte ptr [eax]
.text:080486DA                 movsx   eax, al
.text:080486DD                 sub     esp, 8
.text:080486E0                 push    edx
.text:080486E1                 push    eax
.text:080486E2                 call    complex_function  ;  这次是对Key-String 进行计算了
.text:080486E7                 add     esp, 10h
.text:080486EA                 mov     edx, eax
.text:080486EC                 mov     eax, [ebp+var_C]
.text:080486EF                 add     eax, 804A044h
.text:080486F4                 mov     [eax], dl
.text:080486F6                 add     [ebp+var_C], 1
.text:080486FA
.text:080486FA loc_80486FA:                            ; CODE XREF: main+D4↑j
.text:080486FA                 cmp     [ebp+var_C], 0Fh
.text:080486FE                 jle     short loc_80486C9  ;  comp
.text:08048700                 sub     esp, 8
.text:08048703                 push    offset buffer
.text:08048708                 push    offset a16s     ; "%16s"
.text:0804870D                 call    ___isoc99_scanf
.text:08048712                 add     esp, 10h
.text:08048715                 mov     eax, ds:equals
.text:0804871A                 test    eax, eax
.text:0804871C                 jz      short loc_8048740  ;  对比第二次用户输入
.text:0804871E                 sub     esp, 4
.text:08048721                 push    10h             ; n
.text:08048723                 push    offset password ; s2
.text:08048728                 push    offset buffer   ; s1
.text:0804872D                 call    _strncmp
.text:08048732                 add     esp, 10h
.text:08048735                 test    eax, eax
.text:08048737                 jnz     short loc_8048740
.text:08048739                 mov     eax, 1
.text:0804873E                 jmp     short loc_8048745
.text:08048740 ; ---------------------------------------------------------------------------
.text:08048740
.text:08048740 loc_8048740:                            ; CODE XREF: main+129↑j
.text:08048740                                         ; main+144↑j
.text:08048740                 mov     eax, 0
.text:08048745
.text:08048745 loc_8048745:                            ; CODE XREF: main+14B↑j
.text:08048745                 mov     ds:equals, eax
.text:0804874A                 mov     eax, ds:equals
.text:0804874F                 test    eax, eax
.text:08048751                 jnz     short loc_8048765
.text:08048753                 sub     esp, 0Ch
.text:08048756                 push    offset s        ; "Try again."
.text:0804875B                 call    _puts
.text:08048760                 add     esp, 10h
.text:08048763                 jmp     short loc_8048775
.text:08048765 ; ---------------------------------------------------------------------------
.text:08048765
.text:08048765 loc_8048765:                            ; CODE XREF: main+15E↑j
.text:08048765                 sub     esp, 0Ch
.text:08048768                 push    offset aGoodJob ; "Good Job."
.text:0804876D                 call    _puts
.text:08048772                 add     esp, 10h  

由上面的代码我们可以知道基本的逻辑,第一部分是对用户输入进行complex_function() 计算,然后把计算结果传给check_equals() 检查对比;第二部分是把Key-String 传递给complex_function() 计算,再通过第二次用户输入来进行结果对比.

我们来看一下check_equals() 函数的代码,check_equals() 函数主要功能是对比complex_function() 函数计算结果和Key-String 进行对比,相等则返回1 ,不相等返回0 .

.text:080485A5 check_equals_MRXJKZYRKMKENFZB proc near ; CODE XREF: main+C0↓p
.text:080485A5
.text:080485A5 var_8           = dword ptr -8
.text:080485A5 var_4           = dword ptr -4
.text:080485A5 arg_0           = dword ptr  8
.text:080485A5 arg_4           = dword ptr  0Ch
.text:080485A5
.text:080485A5 ; __unwind {
.text:080485A5                 push    ebp
.text:080485A6                 mov     ebp, esp
.text:080485A8                 sub     esp, 10h
.text:080485AB                 mov     [ebp+var_8], 0
.text:080485B2                 mov     [ebp+var_4], 0
.text:080485B9                 jmp     short loc_80485DD
.text:080485BB ; ---------------------------------------------------------------------------
.text:080485BB
.text:080485BB loc_80485BB:                            ; CODE XREF: check_equals_MRXJKZYRKMKENFZB+3E↓j
.text:080485BB                 mov     edx, [ebp+var_4]
.text:080485BE                 mov     eax, [ebp+arg_0]
.text:080485C1                 add     eax, edx
.text:080485C3                 movzx   edx, byte ptr [eax]
.text:080485C6                 mov     eax, [ebp+var_4]
.text:080485C9                 add     eax, 804A044h  ;  Key-String ..
.text:080485CE                 movzx   eax, byte ptr [eax]
.text:080485D1                 cmp     dl, al
.text:080485D3                 jnz     short loc_80485D9
.text:080485D5                 add     [ebp+var_8], 1
.text:080485D9
.text:080485D9 loc_80485D9:                            ; CODE XREF: check_equals_MRXJKZYRKMKENFZB+2E↑j
.text:080485D9                 add     [ebp+var_4], 1
.text:080485DD
.text:080485DD loc_80485DD:                            ; CODE XREF: check_equals_MRXJKZYRKMKENFZB+14↑j
.text:080485DD                 mov     eax, [ebp+var_4]
.text:080485E0                 cmp     eax, [ebp+arg_4]
.text:080485E3                 jb      short loc_80485BB
.text:080485E5                 mov     eax, [ebp+var_8]
.text:080485E8                 cmp     eax, [ebp+arg_4]
.text:080485EB                 setz    al
.text:080485EE                 movzx   eax, al
.text:080485F1                 leave
.text:080485F2                 retn
.text:080485F2 ; } // starts at 80485A5

在这一题里,使用的方法是Angr Hook .那么我们需要设计一个Hook check_equals() 函数来模拟它的功能.Hook 的插入位置在.text:080486B3 call check_equals_MRXJKZYRKMKENFZB .

根据这些信息,构造的solver.py 代码如下:


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

  initial_state = project.factory.entry_state()

  check_equals_called_address = 0x80486B3  #  Hook Call Address
  instruction_to_skip_length = 0x5         #  Call instruction length

  @project.hook(check_equals_called_address, length=instruction_to_skip_length)
  def skip_check_equals_(state):
    user_input_buffer_address = 0x804A054  #  The input buffer address
    user_input_buffer_length = 0x10        #  input buffer length

    user_input_string = state.memory.load( #  load this buffer to check 
      user_input_buffer_address,
      user_input_buffer_length
    )

    check_against_string = 'MRXJKZYRKMKENFZB' # :string

    state.regs.eax = claripy.If(           #  Add a constraint .
      user_input_string == check_against_string,  #  Check condition
      claripy.BVV(1, 32),                  #  The condition is True than return a int value 1
      claripy.BVV(0, 32)                   #  The condition is False than return a int value 0
    )

  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)  #  Try Explore Execute Path

  if simulation.found:
    solution_state = simulation.found[0]
    solution = solution_state.posix.dumps(sys.stdin.fileno())  #  Get data ..

    print(solution)

Angr函数使用总结:

Hook回调函数格式:

@project.hook(Hook地址,执行完Hook函数后指令往后跳转n字节)
def skip_check_equals_(state):

pass

claripy.If(条件,条件为True时的返回值,条件为False时的返回值) => 创建条件判断

claripy.BVV(值,值大小) => 创建一个数值

10_angr_simprocedures

汇编代码:

.text:08048678                 push    10h             ; n
.text:0804867A                 push    offset aMrxjkzyrkmkenf ; "MRXJKZYRKMKENFZB"
.text:0804867F                 push    offset password ; dest
.text:08048684                 call    _memcpy         ;  Key-String = MRXJKZYRKMKENFZB
.text:08048689                 add     esp, 10h
.text:0804868C                 sub     esp, 4
.text:0804868F                 push    11h             ; n
.text:08048691                 push    0               ; c
.text:08048693                 lea     eax, [ebp+s]
.text:08048696                 push    eax             ; s
.text:08048697                 call    _memset         ;  注意这次buffer 是在栈上的
.text:0804869C                 add     esp, 10h
.text:0804869F                 sub     esp, 0Ch
.text:080486A2                 push    offset aEnterThePasswo ; "Enter the password: "
.text:080486A7                 call    _printf
.text:080486AC                 add     esp, 10h
.text:080486AF                 sub     esp, 8
.text:080486B2                 lea     eax, [ebp+s]
.text:080486B5                 push    eax
.text:080486B6                 push    offset a16s     ; "%16s"
.text:080486BB                 call    ___isoc99_scanf ;  用户输入16 字节大小的内容
.text:080486C0                 add     esp, 10h
.text:080486C3                 mov     [ebp+var_28], 0
.text:080486CA                 jmp     short loc_8048701
.text:080486CC ; ---------------------------------------------------------------------------
.text:080486CC
.text:080486CC loc_80486CC:                            ; CODE XREF: main+C2↓j
.text:080486CC                 mov     eax, 12h
.text:080486D1                 sub     eax, [ebp+var_28]
.text:080486D4                 mov     edx, eax
.text:080486D6                 lea     ecx, [ebp+s]
.text:080486D9                 mov     eax, [ebp+var_28]
.text:080486DC                 add     eax, ecx
.text:080486DE                 movzx   eax, byte ptr [eax]
.text:080486E1                 movsx   eax, al
.text:080486E4                 sub     esp, 8
.text:080486E7                 push    edx
.text:080486E8                 push    eax
.text:080486E9                 call    complex_function  ;  这是复杂的complex_function() 计算操作
.text:080486EE                 add     esp, 10h
.text:080486F1                 mov     ecx, eax
.text:080486F3                 lea     edx, [ebp+s]
.text:080486F6                 mov     eax, [ebp+var_28]
.text:080486F9                 add     eax, edx
.text:080486FB                 mov     [eax], cl
.text:080486FD                 add     [ebp+var_28], 1
.text:08048701
.text:08048701 loc_8048701:                            ; CODE XREF: main+87↑j
.text:08048701                 cmp     [ebp+var_28], 0Fh
.text:08048705                 jle     short loc_80486CC
.text:08048707                 cmp     [ebp+var_24], 0DEADBEEFh
.text:0804870E                 jz      loc_8049847
.text:08048714                 cmp     [ebp+var_24], 0DEADBEEFh
.text:0804871B                 jnz     loc_8048FB4
.text:08048721                 cmp     [ebp+var_24], 0DEADBEEFh
.text:08048728                 jnz     loc_8048B71
.text:0804872E                 cmp     [ebp+var_24], 0DEADBEEFh
.text:08048735                 jnz     loc_8048956
.text:0804873B                 cmp     [ebp+var_24], 0DEADBEEFh
.text:08048742                 jz      loc_804884F
.text:08048748                 cmp     [ebp+var_24], 0DEADBEEFh
.text:0804874F                 jz      short loc_80487D0
.text:08048751                 cmp     [ebp+var_24], 0DEADBEEFh
.text:08048758                 jnz     short loc_8048795
.text:0804875A                 cmp     [ebp+var_24], 0DEADBEEFh
.text:08048761                 jz      short loc_804877C

代码的第一部分对用户输入做了一系列复杂的计算操作之后,然后就分别跳转到不同的位置,最后执行check_equals() 函数进行数据校验.

.text:08048758                 jnz     short loc_8048795
.text:0804875A                 cmp     [ebp+var_24], 0DEADBEEFh
.text:08048761                 jz      short loc_804877C
.text:08048763                 sub     esp, 8
.text:08048766                 push    10h
.text:08048768                 lea     eax, [ebp+s]
.text:0804876B                 push    eax
.text:0804876C                 call    check_equals_MRXJKZYRKMKENFZB
.text:08048771                 add     esp, 10h
.text:08048774                 mov     [ebp+var_2C], eax
.text:08048777                 jmp     loc_804A969
.text:0804877C ; ---------------------------------------------------------------------------
.text:0804877C
.text:0804877C loc_804877C:                            ; CODE XREF: main+11E↑j
.text:0804877C                 sub     esp, 8
.text:0804877F                 push    10h
.text:08048781                 lea     eax, [ebp+s]
.text:08048784                 push    eax
.text:08048785                 call    check_equals_MRXJKZYRKMKENFZB
.text:0804878A                 add     esp, 10h
.text:0804878D                 mov     [ebp+var_2C], eax
.text:08048790                 jmp     loc_804A969
.text:08048795 ; ---------------------------------------------------------------------------
.text:08048795
.text:08048795 loc_8048795:                            ; CODE XREF: main+115↑j
.text:08048795                 cmp     [ebp+var_24], 0DEADBEEFh
.text:0804879C                 jz      short loc_80487B7
.text:0804879E                 sub     esp, 8
.text:080487A1                 push    10h
.text:080487A3                 lea     eax, [ebp+s]
.text:080487A6                 push    eax
.text:080487A7                 call    check_equals_MRXJKZYRKMKENFZB
.text:080487AC                 add     esp, 10h
.text:080487AF                 mov     [ebp+var_2C], eax
.text:080487B2                 jmp     loc_804A969
.text:080487B7 ; ---------------------------------------------------------------------------
.text:080487B7
.text:080487B7 loc_80487B7:                            ; CODE XREF: main+159↑j
.text:080487B7                 sub     esp, 8
.text:080487BA                 push    10h
.text:080487BC                 lea     eax, [ebp+s]
.text:080487BF                 push    eax
.text:080487C0                 call    check_equals_MRXJKZYRKMKENFZB
.text:080487C5                 add     esp, 10h
.text:080487C8                 mov     [ebp+var_2C], eax
.text:080487CB                 jmp     loc_804A969

那么我们来看看check_equals() 的代码:

.text:080485F5 check_equals_MRXJKZYRKMKENFZB proc near ; CODE XREF: main+129↓p
.text:080485F5                                         ; main+142↓p ...
.text:080485F5
.text:080485F5 var_8           = dword ptr -8
.text:080485F5 var_4           = dword ptr -4
.text:080485F5 arg_0           = dword ptr  8
.text:080485F5 arg_4           = dword ptr  0Ch
.text:080485F5
.text:080485F5 ; __unwind {
.text:080485F5                 push    ebp
.text:080485F6                 mov     ebp, esp
.text:080485F8                 sub     esp, 10h
.text:080485FB                 mov     [ebp+var_8], 0
.text:08048602                 mov     [ebp+var_4], 0
.text:08048609                 jmp     short loc_804862D
.text:0804860B ; ---------------------------------------------------------------------------
.text:0804860B
.text:0804860B loc_804860B:                            ; CODE XREF: check_equals_MRXJKZYRKMKENFZB+3E↓j
.text:0804860B                 mov     edx, [ebp+var_4]
.text:0804860E                 mov     eax, [ebp+arg_0]
.text:08048611                 add     eax, edx
.text:08048613                 movzx   edx, byte ptr [eax]
.text:08048616                 mov     eax, [ebp+var_4]
.text:08048619                 add     eax, 804C048h   ;  Key-String 的地址
.text:0804861E                 movzx   eax, byte ptr [eax]
.text:08048621                 cmp     dl, al
.text:08048623                 jnz     short loc_8048629 
.text:08048625                 add     [ebp+var_8], 1
.text:08048629
.text:08048629 loc_8048629:                            ; CODE XREF: check_equals_MRXJKZYRKMKENFZB+2E↑j
.text:08048629                 add     [ebp+var_4], 1
.text:0804862D
.text:0804862D loc_804862D:                            ; CODE XREF: check_equals_MRXJKZYRKMKENFZB+14↑j
.text:0804862D                 mov     eax, [ebp+var_4]
.text:08048630                 cmp     eax, [ebp+arg_4]
.text:08048633                 jb      short loc_804860B  ;  这是一个for 循环的Buffer 内容校验逻辑
.text:08048635                 mov     eax, [ebp+var_8]
.text:08048638                 cmp     eax, [ebp+arg_4]
.text:0804863B                 setz    al
.text:0804863E                 movzx   eax, al
.text:08048641                 leave
.text:08048642                 retn

所以,我们需要Hook check_equals() 并模拟它的执行,这个题目和09 题不同之处在于,09 题我们可以通过一处指令Hook 来实现,但是10 题我们就不能这么做了,是因为Call check_equals() 的地址太多,用09 题的方式不方便,所以我们可以用Angr 的Hook Symbol 来实现对check_equals() 函数的Hook ,而不是想09 题那样只对指令进行Hook .代码如下:

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

  initial_state = project.factory.entry_state()

  class ReplacementCheckEquals(angr.SimProcedure):

    def run(self, check_data_address, check_data_length):  #  基本和09 题的逻辑一样,唯独不同的是我们可以获取check_equals() 的参数
      check_input_string = self.state.memory.load(
        check_data_address,
        check_data_length
      )

      check_against_string = 'MRXJKZYRKMKENFZB'

      return claripy.If(check_input_string == check_against_string, claripy.BVV(1, 32), claripy.BVV(0, 32))

  check_equals_symbol = 'check_equals_MRXJKZYRKMKENFZB' # :string
  project.hook_symbol(check_equals_symbol, ReplacementCheckEquals())  #  Hook Symbol

  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]

    solution = solution_state.posix.dumps(sys.stdin.fileno())
    print(solution)

Angr函数使用总结:

Hook 回调函数格式:

class ReplacementCheckEquals(angr.SimProcedure):

def run(self, Hook的函数参数列表):

​ ….

​ return 函数返回值 # 如果是void函数可以省略

project.hook_symbol(要Hook的函数名,SimProcedure类实例)

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

github haidragon

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值