时时勤拂拭,勿使惹尘埃

TOC

Categories

Angr CTF(二)


04_angr_symbolic_stack——栈 

$ ./04_angr_symbolic_stack 
Enter the password: asdf
Try again.

这题跟上一题有点不一样,scanf获取数据后保存在栈上,不是寄存器

void handle_user(void)

{
  int local_14;
  int local_10 [3];

  __isoc99_scanf("%u %u",local_10,&local_14);
  local_10[0] = complex_function0(local_10[0]);
  local_14 = complex_function1(local_14);
  if ((local_10[0] == 0x5019fa83) && (local_14 == -0x13d421a4)) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }
  return;
}

查看汇编代码,两个参数分别保存在[ebp+var_10]和[ebp+var_C],scanf返回后的第一行代码“add esp, 10h”,是用于“ call ___isoc99_scanf”后平衡堆栈的,那么start_address选择后面第二行代码“.text:08048697 mov eax, [ebp+var_C]”

.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];start_address
.text:0804869A                 sub     esp, 0Ch
.text:0804869D                 push    eax
.text:0804869E                 call    complex_function0
.text:080486A3                 add     esp, 10h
.text:080486A6                 mov     [ebp+var_C], eax
.text:080486A9                 mov     eax, [ebp+var_10]
.text:080486AC                 sub     esp, 0Ch
.text:080486AF                 push    eax
.text:080486B0                 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, 773024D1h
.text:080486C3                 jnz     short loc_80486CF
.text:080486C5                 mov     eax, [ebp+var_10]
.text:080486C8                 cmp     eax, 0BC4311CFh
.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
.text:080486F3 ; } // starts at 8048679

修改angr脚本,需要注意实际栈空间

import angr
import claripy
import sys

def main(argv):
  path_to_binary = "./04_angr_symbolic_stack"
  project = angr.Project(path_to_binary)

  # start_address选择scanf返回后的位置
  start_address = 0x8048697
  initial_state = project.factory.blank_state(addr=start_address)

  # 将ESP指针恢复到和EBP指针一致,恢复栈帧初始状态,方便计算
  initial_state.regs.ebp = initial_state.regs.esp

  # 程序需要两个无符号的整数值(%u %u,即unsigned int),因此符号位向量的大小将为32位
  passwd_size_in_bits = 32
  passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
  passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)

  # 两个参数都是32bits,也就是4字节,所以总共需要8字节,也就是ESP - 0x8
  padding_length_in_bytes = 0x8  # :integer
  initial_state.regs.esp -= padding_length_in_bytes

  initial_state.stack_push(passwd0)  
  initial_state.stack_push(passwd1)

  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Good Job.\n' in stdout_output:
        return True
    else: 
        return False

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.\n' in  stdout_output:
        return True
    else: 
        return False

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

  if simulation.found:
    for i in simulation.found:
        solution_state = i
        solution0 = (solution_state.solver.eval(passwd0))
        solution1 = (solution_state.solver.eval(passwd1))
        print("[+] Success! Solution is: {0} {1}".format(solution0, solution1))

  else:
    raise Exception('Could not find the solution')

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

运行angr脚本,获取密码,验证通过

$ python3 scaffold04.py 
[+] Success! Solution is: 1704280884 2382341151
$ ./04_angr_symbolic_stack 
Enter the password: 1704280884
2382341151
Good Job.

05_angr_symbolic_memory——内存

$ ./05_angr_symbolic_memory 
Enter the password: asdfa
asdf
dfs
adg
Try again.

程序将四个8字节长的字符串作为输入,字符串分别位于以下地址[user_input=0xA1BA1C0, 0xA1BA1C8, 0xA1BA1D0, 0xA1BA1D8],输入的字符串循环输入complex_function()函数进行变换,循环变换后的字符串与 “NJPURZPCDYEAXCSJZJMPSOMBFDDLHBVN”比较前0x20个字符

undefined4 main(void)

{
  undefined uVar1;
  int iVar2;
  int local_14;

  memset(user_input,0,0x21);
  printf("Enter the password: ");
  __isoc99_scanf("%8s %8s %8s %8s",user_input,0xa623108,0xa623110,0xa623118);
  for (local_14 = 0; local_14 < 0x20; local_14 = local_14 + 1) {
    uVar1 = complex_function((int)(char)user_input[local_14],local_14);
    user_input[local_14] = uVar1;
  }
  iVar2 = strncmp(user_input,"GLCOREPCFIMXROLPXJNEXMLKSEIMFROR",0x20);
  if (iVar2 == 0) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }
  return 0;
}

start_address照样选择scanf返回后的地址:08048601

     080485e0 68 d8       PUSH     user_input[24]
             a1 1b 0a
     080485e5 68 d0       PUSH     user_input[16]
             a1 1b 0a
     080485ea 68 c8       PUSH     user_input[8]
             a1 1b 0a
     080485ef 68 c0       PUSH     user_input                            = ??
             a1 1b 0a
     080485f4 68 33       PUSH     s_%8s_%8s_%8s_%8s_08048733            = "%8s %8s %8s %8s"
             87 04 08
     080485f9 e8 02       CALL     <EXTERNAL>::__isoc99_scanf            undefined __isoc99_scanf()
             fe ff ff
     080485fe 83 c4 20    ADD      ESP,0x20
     08048601 c7 45       MOV      dword ptr [EBP + local_14],0x0
             f4 00 
             00 00 00
     08048608 eb 2d       JMP      LAB_08048637
                      LAB_0804860a                          XREF[1]:  0804863b(j)  
     0804860a 8b 45 f4    MOV      EAX,dword ptr [EBP + local_14]
     0804860d 05 c0       ADD      EAX,user_input                        = ??
             a1 1b 0a
     08048612 0f b6 00    MOVZX    EAX,byte ptr [EAX]=>user_input        = ??
     08048615 0f be c0    MOVSX    EAX,AL
     08048618 83 ec 08    SUB      ESP,0x8
     0804861b ff 75 f4    PUSH     dword ptr [EBP + local_14]
     0804861e 50          PUSH     EAX
     0804861f e8 25       CALL     complex_function                      undefined complex_functio
             ff ff ff

修改angr脚本,这里有四个内存值都需要计算

import angr
import claripy
import sys

def main(argv):
  path_to_binary = "./05_angr_symbolic_memory" 
  project = angr.Project(path_to_binary)

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

  # The binary is calling scanf("%8s %8s %8s %8s").
  # 每个值长度为8字节,即8*8=64bits
  passwd_size_in_bits = 64
  passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
  passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
  passwd2 = claripy.BVS('passwd2', passwd_size_in_bits)
  passwd3 = claripy.BVS('passwd3', passwd_size_in_bits)

  passwd0_address = 0xA1BA1C0
  #passwd1_address = 0xA1BA1C8
  #passwd2_address = 0xA1BA1D0
  #passwd3_address = 0xA1BA1D8
  initial_state.memory.store(passwd0_address, passwd0)
  initial_state.memory.store(passwd0_address + 0x8,  passwd1)
  initial_state.memory.store(passwd0_address + 0x10, passwd2)
  initial_state.memory.store(passwd0_address + 0x18, passwd3)
  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Good Job.\n' in stdout_output:
        return True
    else: 
        return False

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.\n' in  stdout_output:
        return True
    else: 
        return False

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

  if simulation.found:
    for i in simulation.found:
        solution_state = i
        solution0 = solution_state.solver.eval(passwd0,cast_to=bytes)
        solution1 = solution_state.solver.eval(passwd1,cast_to=bytes)
        solution2 = solution_state.solver.eval(passwd2,cast_to=bytes)
        solution3 = solution_state.solver.eval(passwd3,cast_to=bytes)
        solution = solution0 + b" " + solution1 + b" " + solution2 + b" " + solution3
        print("[+] Success! Solution is: {}".format(solution.decode("utf-8")))
  else:
    raise Exception('Could not find the solution')

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

运行angr脚本,获取密码,验证通过

$ python3 scaffold05.py 
WARNING  | 2023-12-25 21:39:32,058 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-25 21:39:32,058 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-25 21:39:32,058 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-25 21:39:32,059 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-25 21:39:32,059 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-25 21:39:32,059 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048601 (main+0x59 in 05_angr_symbolic_memory (0x8048601))
[+] Success! Solution is: NAXTHGNR JVSFTPWE LMGAUHWC XMDCPALU
$ ./05_angr_symbolic_memory 
Enter the password: NAXTHGNR
JVSFTPWE
LMGAUHWC
XMDCPALU
Good Job.

06_angr_symbolic_dynamic_memory——动态内存

$ ./06_angr_symbolic_dynamic_memory 
Enter the password: ASDF
AG
Try again.

跟上一题基本一致,不过字符串的内存是通过堆malloc(),而不是栈分配

undefined4 main(void)

{
  char *pcVar1;
  char cVar2;
  int iVar3;
  int local_14;

  buffer0 = (char *)malloc(9);
  buffer1 = (char *)malloc(9);
  memset(buffer0,0,9);
  memset(buffer1,0,9);
  printf("Enter the password: ");
  __isoc99_scanf("%8s %8s",buffer0,buffer1);
  for (local_14 = 0; local_14 < 8; local_14 = local_14 + 1) {
    pcVar1 = buffer0 + local_14;
    cVar2 = complex_function((int)buffer0[local_14],local_14);
    *pcVar1 = cVar2;
    pcVar1 = buffer1 + local_14;
    cVar2 = complex_function((int)buffer1[local_14],local_14 + 0x20);
    *pcVar1 = cVar2;
  }
  iVar3 = strncmp(buffer0,"UODXLZBI",8);
  if (iVar3 == 0) {
    iVar3 = strncmp(buffer1,"UAORRAYF",8);
    if (iVar3 == 0) {
      puts("Good Job.");
      goto LAB_08048766;
    }
  }
  puts("Try again.");
LAB_08048766:
  free(buffer0);
  free(buffer1);
  return 0;
}

[buffer1]=0abcc8ac; [buffer0]=0abcc8a4; start_address=08048699;

     0804867c 8b 15       MOV      EDX,dword ptr [buffer1]               = ??
             ac c8 
             bc 0a
     08048682 a1 a4       MOV      EAX,[buffer0]                         = ??
             c8 bc 0a
     08048687 83 ec 04    SUB      ESP,0x4
     0804868a 52          PUSH     EDX
     0804868b 50          PUSH     EAX
     0804868c 68 43       PUSH     s_%8s_%8s_08048843                    = "%8s %8s"
             88 04 08
     08048691 e8 ca       CALL     <EXTERNAL>::__isoc99_scanf            undefined __isoc99_scanf()
             fd ff ff
     08048696 83 c4 10    ADD      ESP,0x10
     08048699 c7 45       MOV      dword ptr [EBP + local_14],0x0
             f4 00 
             00 00 00
     080486a0 eb 64       JMP      LAB_08048706

根据上述信息修改配置脚本

import angr
import claripy
import sys

def main(argv):
  path_to_binary = "./06_angr_symbolic_dynamic_memory" 
  project = angr.Project(path_to_binary)

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

  passwd_size_in_bits = 64
  passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
  passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)

  # fake_heap_address可以任意设置
  fake_heap_address0 = 0xffffc8a4
  pointer_to_malloc_memory_address0 = 0xabcc8a4
  fake_heap_address1 = 0xffffc8ac
  pointer_to_malloc_memory_address1 = 0xabcc8ac

  initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness)
  initial_state.memory.store(pointer_to_malloc_memory_address1, fake_heap_address1, endness=project.arch.memory_endness)

  initial_state.memory.store(fake_heap_address0, passwd0)  
  initial_state.memory.store(fake_heap_address1, passwd1)

  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Good Job.\n' in stdout_output:
        return True
    else: 
        return False

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.\n' in  stdout_output:
        return True
    else: 
        return False

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

  if simulation.found:
    for i in simulation.found:
        solution_state = i
        solution0 = solution_state.solver.eval(passwd0, cast_to=bytes)
        solution1 = solution_state.solver.eval(passwd1, cast_to=bytes)
        print("[+] Success! Solution is: {0} {1}".format(solution0.decode('utf-8'), solution1.decode('utf-8')))
  else:
    raise Exception('Could not find the solution')

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

运行angr脚本,获取密码,验证通过

$ python3 scaffold06.py 
WARNING  | 2023-12-25 22:25:35,135 | angr.storage.memory_mixins.bvv_conversion_mixin | Unknown size for memory data 0xffffc93c. Default to arch.bits.
WARNING  | 2023-12-25 22:25:35,136 | angr.storage.memory_mixins.bvv_conversion_mixin | Unknown size for memory data 0xffffc94c. Default to arch.bits.
WARNING  | 2023-12-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-25 22:25:35,142 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048699 (main+0x8d in 06_angr_symbolic_dynamic_memory (0x8048699))
WARNING  | 2023-12-25 22:25:36,807 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc944 with 8 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING  | 2023-12-25 22:25:36,807 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc954 with 105 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING  | 2023-12-25 22:25:38,488 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc9bd with 16 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
[+] Success! Solution is: UBDKLMBV UNOERNYS
$ ./06_angr_symbolic_dynamic_memory 
Enter the password: UBDKLMBV
UNOERNYS
Good Job.

Angr CTF(一)


0x0 前言

最近打了一个ctf,几个Re题尝试用angr来做,但写的脚本跑了一天都没跑出结果,太菜了,还是不太会用angr。

遂找了一套angr ctf题来练练:https://github.com/jakespringer/angr_ctf

参考:https://github.com/ZERO-A-ONE/AngrCTF_FITM

00_angr_find——基础用法

这是一道最基本的入门题,运行会提示要求输入密码:

$ ./00_angr_find 
Enter the password: asdf;kl
Try again.

反编译伪代码如下,密码变换处理后最后与”JACEJGCS”比较,正确输出”Good Job.”,错误输出”Try again.”:

  local_c = (undefined *)&param_1;
  local_14 = *(int *)(in_GS_OFFSET + 0x14);
  printf("Enter the password: ");
  __isoc99_scanf(&DAT_08048753,local_1d);
  for (local_24 = 0; local_24 < 8; local_24 = local_24 + 1) {
    cVar1 = complex_function((int)local_1d[local_24],local_24);
    local_1d[local_24] = cVar1;
  }
  iVar2 = strcmp(local_1d,"JACEJGCS");
  if (iVar2 == 0) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }

使用angr求解,主要需要找到输出”Good Job.”的代码位置:08048678

                      LAB_08048675                          XREF[1]:  08048661(j)  
     08048675 83 ec 0c    SUB      ESP,0xc
     08048678 68 60       PUSH     s_Good_Job._08048760                  = "Good Job."
             87 04 08
     0804867d e8 7e       CALL     <EXTERNAL>::puts                      int puts(char * __s)
             fd ff ff
     08048682 83 c4 10    ADD      ESP,0x10
                      LAB_08048685                          XREF[1]:  08048673(j)  
     08048685 b8 00       MOV      EAX,0x0
             00 00 00
     0804868a 8b 4d f4    MOV      ECX,dword ptr [EBP + local_14]
     0804868d 65 33       XOR      ECX,dword ptr GS:[0x14]
             0d 14 
             00 00 00
     08048694 74 05       JZ       LAB_0804869b
     08048696 e8 55       CALL     <EXTERNAL>::__stack_chk_fail          undefined
__stack_chk_fai
             fd ff ff
                      -- Flow Override: CALL_RETURN (CALL_TERMINATOR)

修改angr脚本,angr ctf给每个题都写好了模板,只需要修改几个关键信息即可:

# 加载angr模块
import angr
import sys

def main(argv):
  # 加载目标文件,创建project
  path_to_binary = "./00_angr_find"  # :string
  project = angr.Project(path_to_binary)

  # 设置state
  # state代表程序的一个实例镜像,模拟执行某个时刻的状态,就类似于快照。保存运行状态的上下文信息,如内存/寄存器等,这里使用project.factory.entry_state()告诉符号执行引擎从程序的入口点开始符号执行
  initial_state = project.factory.entry_state()

  # 设置 Simulation Managers
  # SimState 对象包含程序运行时信息,如内存/寄存器/文件系统数据等。SM(Simulation Managers)是angr中最重要的控制接口,它使你能够同时控制一组状态(state)的符号执行,应用搜索策略来探索程序的状态空间。
  simulation = project.factory.simgr(initial_state)

  # 运行,探索满足路径需要的值
  # 符号执行最普遍的操作是找到能够到达某个地址的状态,同时丢弃其他不能到达这个地址的状态。SM为使用这种执行模式提供了.explore()方法
  # 当使用find参数启动.explore()方法时,程序将会一直执行,直到发现了一个和find参数指定的条件相匹配的状态。find参数的内容可以是想要执行到的某个地址、或者想要执行到的地址列表、或者一个获取state作为参数并判断这个state是否满足某些条件的函数。当activestash中的任意状态和find中的条件匹配的时候,它们就会被放到found stash中,执行随即停止。之后你可以探索找到的状态,或者决定丢弃它,转而探索其它状态。
  print_good_address = 0x08048678  # :integer (probably in hexadecimal)
  simulation.explore(find=print_good_address)

  # 获取执行结果
  if simulation.found:
    # 此时相关的状态已经保存在了simgr当中,可以通过simgr.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脚本,获取密码,验证通过

$ python scaffold00.py 
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-24 15:41:36,065 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80486b1 (__libc_csu_init+0x1 in 00_angr_find (0x80486b1))
WARNING  | 2023-12-24 15:41:36,066 | angr.storage.memory_mixi ns.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x80486b3 (__libc_csu_init+0x3 in 00_angr_find (0x80486b3)) 
WARNING  | 2023-12-24 15:41:37,355 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
b'JXWVXRKX'
$ ./00_angr_find 
Enter the password: JXWVXRKX
Good Job.

01_angr_avoid——条件收敛

这题跟上面一样

$ ./01_angr_avoid 
Enter the password: asdflk
Try again.

找到正确输出位置,定位到”Good Job.”

void maybe_good(char *param_1,char *param_2)
{
  int iVar1;
  if ((should_succeed != '\0') && (iVar1 = strncmp(param_1,param_2,8), iVar1 == 0)) {
    puts("Good Job.");
    return;
  }
  puts("Try again.");
  return;
}

确定”Good Job.”代码位置:080485e0

     080485d6 83 c4 10    ADD      ESP,0x10
     080485d9 85 c0       TEST     EAX,EAX
     080485db 75 12       JNZ      LAB_080485ef
     080485dd 83 ec 0c    SUB      ESP,0xc
     080485e0 68 1e       PUSH     s_Good_Job._080d461e                  = "Good Job."
             46 0d 08
     080485e5 e8 e6       CALL     <EXTERNAL>::puts                      int puts(char * __s)
             fd ff ff
     080485ea 83 c4 10    ADD      ESP,0x10
     080485ed eb 10       JMP      LAB_080485ff
                      LAB_080485ef                          XREF[2]:  080485c4(j), 080485db(j)  
     080485ef 83 ec 0c    SUB      ESP,0xc
     080485f2 68 13       PUSH     s_Try_again._080d4613                 = "Try again."
             46 0d 08
     080485f7 e8 d4       CALL     <EXTERNAL>::puts                      int puts(char * __s)
             fd ff ff
     080485fc 83 c4 10    ADD      ESP,0x10

完善angr脚本,不过这次不只查找达到print_good_address的状态,还要查找没有达到will_not_succeed_address的状态。
通常二进制文件都非常庞大,为了节省时间,添加一些收敛条件可以减少计算量。

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 = 0x080485e0
  will_not_succeed_address = 0x080485a8
  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)

本次will_not_succeed_address选择多处调用的avoid_me

void avoid_me(void)
{
  should_succeed = 0;
  return;
}

运行angr脚本,获取密码,验证通过

$ python scaffold01.py 
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-24 16:09:32,083 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80d4591 (__libc_csu_init+0x1 in 01_angr_avoid (0x80d4591))
WARNING  | 2023-12-24 16:09:32,084 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x80d4593 (__libc_csu_init+0x3 in 01_angr_avoid (0x80d4593))
WARNING  | 2023-12-24 16:09:35,100 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff3d with 11 unconstrained bytes referenced from 0x819a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING  | 2023-12-24 16:09:35,100 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x819a410 (strncmp+0x0 in libc.so.6 (0x9a410))
b'HUJOZMYS'
$ ./01_angr_avoid 
Enter the password: HUJOZMYS
Good Job.

02_angr_find_condition——动态获取state

这题主要想教会如何动态获取state,即不用每次都分析具体的代码位置,而是通过检索字符串自动获取

$ ./02_angr_find_condition 
Enter the password: df;l
Try again.

代码逻辑依然是正确输出”Good Job.”,错误输出”Try again.”

  local_c = (undefined *)&param_1;
  local_14 = *(int *)(in_GS_OFFSET + 0x14);
  for (local_48 = 0; local_48 < 0x14; local_48 = local_48 + 1) {
    *(undefined *)((int)&local_28 + local_48) = 0;
  }
  local_28 = 0x52525856;
  local_24 = 0x5255454a;
  printf("Enter the password: ");
  __isoc99_scanf(&DAT_0804d333,local_3c);
  for (local_44 = 0; local_44 < 8; local_44 = local_44 + 1) {
    cVar1 = complex_function((int)local_3c[local_44],local_44 + 8);
    local_3c[local_44] = cVar1;
  }
  iVar2 = strcmp(local_3c,(char *)&local_28);
  if (iVar2 == 0) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }
  if (local_14 == *(int *)(in_GS_OFFSET + 0x14)) {
    return 0;
  }

由于使用动态获取state,直接修改脚本,检索字符串,但这里实际是字节对象,所以必须使用b’Good Job.’而不是仅”Good Job.”

import angr
import sys
def main(argv):
  path_to_binary = "./02_angr_find_condition" 
  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())
    if b'Good Job.' in stdout_output:
        return True
    else: 
        return False

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.' in  stdout_output:
        return True
    else: 
        return False
  simulation.explore(find=is_successful, avoid=should_abort)
  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脚本,获取密码,验证通过

$ python scaffold02.py 
WARNING  | 2023-12-24 16:40:09,183 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x804d291 (__libc_csu_init+0x1 in 02_angr_find_condition (0x804d291))
WARNING  | 2023-12-24 16:40:09,185 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x804d293 (__libc_csu_init+0x3 in 02_angr_find_condition (0x804d293))
WARNING  | 2023-12-24 16:40:10,374 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff2d with 11 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
WARNING  | 2023-12-24 16:40:10,374 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff50 with 4 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
b'HETOBRCU'
$ ./02_angr_find_condition 
Enter the password: HETOBRCU
Good Job.

03_angr_symbolic_registers——寄存器

$ ./03_angr_symbolic_registers 
Enter the password: asdfa
Try again.

这次的输入是在get_user_input函数里,调用的scanf获取格式化字符串,”%x %x %x”说明使用三个十六进制值作为输入

undefined4 main(void)
{
  int iVar1;
  int iVar2;
  int iVar3;
  undefined4 unaff_EBX;
  undefined8 uVar4;
  printf("Enter the password: ");
  uVar4 = get_user_input();
  iVar1 = complex_function_1((int)uVar4);
  iVar2 = complex_function_2(unaff_EBX);
  iVar3 = complex_function_3((int)((ulonglong)uVar4 >> 0x20));
  if (((iVar1 == 0) && (iVar2 == 0)) && (iVar3 == 0)) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }
  return 0;
}
undefined8 get_user_input(void)
{
  int in_GS_OFFSET;
  undefined4 local_1c;
  undefined local_18 [4];
  undefined4 local_14;
  int local_10;
  local_10 = *(int *)(in_GS_OFFSET + 0x14);
  __isoc99_scanf("%x %x %x",&local_1c,local_18,&local_14);
  if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return CONCAT44(local_14,local_1c);
}

查看汇编代码,这里使用EAX、EBX、EDX三个寄存器来存储

     08048919 89 4d f4    MOV      dword ptr [EBP + local_10],ECX
     0804891c 31 c9       XOR      ECX,ECX
     0804891e 8d 4d f0    LEA      ECX=>local_14,[EBP + -0x10]
     08048921 51          PUSH     ECX
     08048922 8d 4d ec    LEA      ECX=>local_18,[EBP + -0x14]
     08048925 51          PUSH     ECX
     08048926 8d 4d e8    LEA      ECX=>local_1c,[EBP + -0x18]       08048929 51          PUSH     ECX
     0804892a 68 93       PUSH     s_%x_%x_%x_08048a93                   = "%x %x %x"
             8a 04 0
     0804892f e8 9c       CALL     <EXTERNAL>::__isoc99_scanf            undefined __isoc99_scanf()
             fa ff ff
     08048934 83 c4 10    ADD      ESP,0x10
     08048937 8b 4d e8    MOV      ECX,dword ptr [EBP + local_1c]
     0804893a 89 c8       MOV      EAX,ECX
     0804893c 8b 4d ec    MOV      ECX,dword ptr [EBP + local_18]
     0804893f 89 cb       MOV      EBX,ECX
     08048941 8b 4d f0    MOV      ECX,dword ptr [EBP + local_14]
     08048944 89 ca       MOV      EDX,ECX     08048946 90          NOP
     08048947 8b 4d f4    MOV      ECX,dword ptr [EBP + local_10]
     0804894a 65 33       XOR      ECX,dword ptr GS:[0x14]
             0d 14 
             00 00 00
     08048951 74 05       JZ       LAB_08048958
     08048953 e8 48       CALL     <EXTERNAL>::__stack_chk_fail          undefined __stack_chk_fai
             fa ff ff
                      -- Flow Override: CALL_RETURN (CALL_TERMINATOR)

那么 start_address选择get_user_input返回后的地址:08048980

     0804896e 68 9c       PUSH     s_Enter_the_password:_08048a9c        = "Enter the password: "
             8a 04 08
     08048973 e8 18       CALL     <EXTERNAL>::printf                    int printf(char * __forma
             fa ff ff
     08048978 83 c4 10    ADD      ESP,0x10
     0804897b e8 8c       CALL     get_user_input                        undefined get_user_input()
             ff ff ff
     08048980 89 45 ec    MOV      dword ptr [EBP + local_1c],EAX
     08048983 89 5d f0    MOV      dword ptr [EBP + local_18],EBX
     08048986 89 55 f4    MOV      dword ptr [EBP + local_14],EDX
     08048989 83 ec 0c    SUB      ESP,0xc
     0804898c ff 75 ec    PUSH     dword ptr [EBP + local_1c]
     0804898f e8 75       CALL     complex_function_1                    undefined complex_functio
             fb ff ff
     08048994 83 c4 10    ADD      ESP,0x10
     08048997 89 c1       MOV      ECX,EAX
     08048999 89 4d ec    MOV      dword ptr [EBP + local_1c],ECX
     0804899c 83 ec 0c    SUB      ESP,0xc
     0804899f ff 75 f0    PUSH     dword ptr [EBP + local_18]
     080489a2 e8 b3       CALL     complex_function_2                    undefined complex_functio
             fc ff ff
     080489a7 83 c4 10    ADD      ESP,0x10
     080489aa 89 c1       MOV      ECX,EAX
     080489ac 89 4d f0    MOV      dword ptr [EBP + local_18],ECX
     080489af 83 ec 0c    SUB      ESP,0xc
     080489b2 ff 75 f4    PUSH     dword ptr [EBP + local_14]
     080489b5 e8 23       CALL     complex_function_3                    undefined complex_functio
             fe ff ff

修改angr脚本,这里需要计算3个寄存器的值

import angr
import claripy
import sys
def main(argv):
  path_to_binary = "./03_angr_symbolic_registers"
  project = angr.Project(path_to_binary)
  start_address = 0x08048980  # :intege (probably hexadecimal)  
  initial_state = project.factory.blank_state(addr=start_address)
  passwd_size_in_bits = 32
  passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
  passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
  passwd2 = claripy.BVS('passwd2', passwd_size_in_bits)
  initial_state.regs.eax = passwd0
  initial_state.regs.ebx = passwd1
  initial_state.regs.edx = passwd2
  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Good Job.' in stdout_output:
        return True
    else: 
        return False

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.' in  stdout_output:
        return True
    else: 
        return False

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

  if simulation.found:
    for i in simulation.found:
      solution_state = i
      solution0 = format(solution_state.solver.eval(passwd0), 'x')
      solution1 = format(solution_state.solver.eval(passwd1), 'x')
      solution2 = format(solution_state.solver.eval(passwd2), 'x')
      solution = solution0 + " " + solution1 + " " + solution2
      print(solution)
  else:
    raise Exception('Could not find the solution')

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

运行angr脚本,获取密码,验证通过

$ python scaffold03.py 
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048980 (main+0x26 in 03_angr_symbolic_registers (0x8048980))
b9ffd04e ccf63fe8 8fd4d959
$ ./03_angr_symbolic_registers 
Enter the password: b9ffd04e
ccf63fe8
8fd4d959
Good Job.