• 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 有很多题目,每一个目录是一个独立的题目,题目里面没有现成编译好的程序,需要我们手工来编译,我们以第一题为例子编译测试程序

    1. root@sec:~/angr_ctf# cd 00_angr_find/
    2. 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 来执行脚本,效果如下:

    1. root@sec:~/angr_ctf/00_angr_find# python3 scaffold00.py
    2. 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.
    3. 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:
    4. WARNING | 2019-05-11 14:42:28,543 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state
    5. 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
    6. 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.
    7. 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))
    8. 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))
    9. 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))
    10. 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))
    11. b'FMKGABFY'

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

    00_angr_find

    汇编代码:

    1. .text:0804864E push offset s2 ; "FPQPMQXT"
    2. .text:08048653 lea eax, [ebp+s1]
    3. .text:08048656 push eax ; s1
    4. .text:08048657 call _strcmp
    5. .text:0804865C add esp, 10h
    6. .text:0804865F test eax, eax
    7. .text:08048661 jz short loc_8048675
    8. .text:08048663 sub esp, 0Ch
    9. .text:08048666 push offset s ; "Try again."
    10. .text:0804866B call _puts
    11. .text:08048670 add esp, 10h
    12. .text:08048673 jmp short loc_8048685
    13. .text:08048675 ; ---------------------------------------------------------------------------
    14. .text:08048675
    15. .text:08048675 loc_8048675: ; CODE XREF: main+9A↑j
    16. .text:08048675 sub esp, 0Ch
    17. .text:08048678 push offset aGoodJob ; "Good Job."
    18. .text:0804867D call _puts
    19. .text:08048682 add esp, 10h
    20. .text:08048685
    21. .text:08048685 loc_8048685:

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

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

    Angr函数使用总结:

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

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

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

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

    01_angr_avoid

    汇编代码:

    1. .text:0804890F jz short loc_804892E
    2. .text:08048911 call avoid_me
    3. .text:08048916 sub esp, 8
    4. .text:08048919 lea eax, [ebp+var_20]
    5. .text:0804891C push eax
    6. .text:0804891D lea eax, [ebp+var_34]
    7. .text:08048920 push eax
    8. .text:08048921 call maybe_good
    9. .text:08048926 add esp, 10h
    10. .text:08048929 jmp loc_80D456F
    11. .text:0804892E ; ---------------------------------------------------------------------------
    12. .text:0804892E
    13. .text:0804892E loc_804892E: ; CODE XREF: main+30D↑j
    14. .text:0804892E sub esp, 8
    15. .text:08048931 lea eax, [ebp+var_20]
    16. .text:08048934 push eax
    17. .text:08048935 lea eax, [ebp+var_34]
    18. .text:08048938 push eax
    19. .text:08048939 call maybe_good
    20. .text:0804893E add esp, 10h
    21. .text:08048941 jmp loc_80D456F
    22. .text:08048946 ; ---------------------------------------------------------------------------
    23. .text:08048946
    24. .text:08048946 loc_8048946: ; CODE XREF: main+2E5↑j
    25. .text:08048946 call avoid_me
    26. .....

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

    1. text:080485B5 public maybe_good
    2. .text:080485B5 maybe_good proc near ; CODE XREF: main+31F↓p
    3. .text:080485B5 ; main+337↓p ...
    4. .text:080485B5
    5. .text:080485B5 arg_0 = dword ptr 8
    6. .text:080485B5 arg_4 = dword ptr 0Ch
    7. .text:080485B5
    8. .text:080485B5 ; __unwind {
    9. .text:080485B5 push ebp
    10. .text:080485B6 mov ebp, esp
    11. .text:080485B8 sub esp, 8
    12. .text:080485BB movzx eax, should_succeed
    13. .text:080485C2 test al, al
    14. .text:080485C4 jz short loc_80485EF
    15. .text:080485C6 sub esp, 4
    16. .text:080485C9 push 8
    17. .text:080485CB push [ebp+arg_4]
    18. .text:080485CE push [ebp+arg_0]
    19. .text:080485D1 call _strncmp
    20. .text:080485D6 add esp, 10h
    21. .text:080485D9 test eax, eax
    22. .text:080485DB jnz short loc_80485EF
    23. .text:080485DD sub esp, 0Ch
    24. .text:080485E0 push offset aGoodJob ; "Good Job."
    25. .text:080485E5 call _puts
    26. .text:080485EA add esp, 10h
    27. .text:080485ED jmp short loc_80485FF
    28. .text:080485EF ; ---------------------------------------------------------------------------
    29. .text:080485EF
    30. .text:080485EF loc_80485EF: ; CODE XREF: maybe_good+F↑j
    31. .text:080485EF ; maybe_good+26↑j
    32. .text:080485EF sub esp, 0Ch
    33. .text:080485F2 push offset aTryAgain ; "Try again."
    34. .text:080485F7 call _puts
    35. .text:080485FC add esp, 10h
    36. .text:080485FF
    37. .text:080485FF loc_80485FF: ; CODE XREF: maybe_good+38↑j
    38. .text:080485FF nop
    39. .text:08048600 leave
    40. .text:08048601 retn
    41. .text:08048601 ; } // starts at 80485B5
    42. .text:08048601 maybe_good endp
    43. .text:08048601

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

    1. import angr
    2. import sys
    3. def main(argv):
    4. path_to_binary = './01_angr_avoid'
    5. project = angr.Project(path_to_binary)
    6. initial_state = project.factory.entry_state()
    7. simulation = project.factory.simgr(initial_state)
    8. print_good_address = 0x080485DD
    9. will_not_succeed_address = 0x80485EF
    10. simulation.explore(find=print_good_address, avoid=will_not_succeed_address)
    11. if simulation.found:
    12. solution_state = simulation.found[0]
    13. print(solution_state.posix.dumps(sys.stdin.fileno()))
    14. else:
    15. raise Exception('Could not find the solution')
    16. if __name__ == '__main__':
    17. main(sys.argv)

    Angr函数使用总结:

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

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

    solution_state.posix.dumps( => 获取Payload

    02_angr_find_condition

    汇编代码:

    1. .text:0804876B loc_804876B: ; CODE XREF: main+112↑j
    2. .text:0804876B cmp [ebp+var_38], 0DEADBEEFh
    3. .text:08048772 jz short loc_80487B5
    4. .text:08048774 sub esp, 8
    5. .text:08048777 lea eax, [ebp+s2]
    6. .text:0804877A push eax ; s2
    7. .text:0804877B lea eax, [ebp+s1]
    8. .text:0804877E push eax ; s1
    9. .text:0804877F call _strcmp
    10. .text:08048784 add esp, 10h
    11. .text:08048787 test eax, eax
    12. .text:08048789 jz short loc_80487A0
    13. .text:0804878B sub esp, 0Ch
    14. .text:0804878E push offset s ; "Try again."
    15. .text:08048793 call _puts
    16. .text:08048798 add esp, 10h
    17. .text:0804879B jmp loc_804D267
    18. .text:080487A0 ; ---------------------------------------------------------------------------
    19. .text:080487A0
    20. .text:080487A0 loc_80487A0: ; CODE XREF: main+1C1↑j
    21. .text:080487A0 sub esp, 0Ch
    22. .text:080487A3 push offset aGoodJob ; "Good Job."
    23. .text:080487A8 call _puts
    24. .text:080487AD add esp, 10h
    25. .text:080487B0 jmp loc_804D267
    26. .text:080487B5 ; ---------------------------------------------------------------------------
    27. .text:080487B5
    28. .text:080487B5 loc_80487B5: ; CODE XREF: main+1AA↑j
    29. .text:080487B5 sub esp, 8
    30. .text:080487B8 lea eax, [ebp+s2]
    31. .text:080487BB push eax ; s2
    32. .text:080487BC lea eax, [ebp+s1]
    33. .text:080487BF push eax ; s1
    34. .text:080487C0 call _strcmp
    35. .text:080487C5 add esp, 10h
    36. .text:080487C8 test eax, eax
    37. .text:080487CA jz short loc_80487E1
    38. .text:080487CC sub esp, 0Ch
    39. .text:080487CF push offset s ; "Try again."
    40. .text:080487D4 call _puts
    41. .text:080487D9 add esp, 10h
    42. .text:080487DC jmp loc_804D267
    43. .text:080487E1 ; ---------------------------------------------------------------------------
    44. .text:080487E1
    45. .text:080487E1 loc_80487E1: ; CODE XREF: main+202↑j
    46. .text:080487E1 sub esp, 0Ch
    47. .text:080487E4 push offset aGoodJob ; "Good Job."
    48. .text:080487E9 call _puts

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

    1. def main(argv):
    2. path_to_binary = argv[1]
    3. project = angr.Project(path_to_binary)
    4. initial_state = project.factory.entry_state()
    5. simulation = project.factory.simgr(initial_state)
    6. def is_successful(state):
    7. stdout_output = state.posix.dumps(sys.stdout.fileno())
    8. return 'Good Job' in str(stdout_output) # :boolean
    9. def should_abort(state):
    10. stdout_output = state.posix.dumps(sys.stdout.fileno())
    11. return 'Try again' in str(stdout_output) # :boolean
    12. 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

    汇编代码:

    1. .text:080488E8 ; int __cdecl main(int argc, const char **argv, const char **envp)
    2. .text:080488E8 public main
    3. .text:080488E8 main proc near ; DATA XREF: _start+17↑o
    4. .text:080488E8
    5. .text:080488E8 var_14 = dword ptr -14h
    6. .text:080488E8 var_10 = dword ptr -10h
    7. .text:080488E8 var_C = dword ptr -0Ch
    8. .text:080488E8 var_4 = dword ptr -4
    9. .text:080488E8 argc = dword ptr 8
    10. .text:080488E8 argv = dword ptr 0Ch
    11. .text:080488E8 envp = dword ptr 10h
    12. .text:080488E8
    13. .text:080488E8 ; __unwind {
    14. .text:080488E8 lea ecx, [esp+4]
    15. .text:080488EC and esp, 0FFFFFFF0h
    16. .text:080488EF push dword ptr [ecx-4]
    17. .text:080488F2 push ebp
    18. .text:080488F3 mov ebp, esp
    19. .text:080488F5 push ecx
    20. .text:080488F6 sub esp, 14h
    21. .text:080488F9 sub esp, 0Ch
    22. .text:080488FC push offset aEnterThePasswo ; "Enter the password: "
    23. .text:08048901 call _printf
    24. .text:08048906 add esp, 10h
    25. .text:08048909 call get_user_input
    26. .text:0804890E mov [ebp+var_14], eax
    27. .text:08048911 mov [ebp+var_10], ebx
    28. .text:08048914 mov [ebp+var_C], edx
    29. .text:08048917 sub esp, 0Ch
    30. .text:0804891A push [ebp+var_14]
    31. .text:0804891D call complex_function_1
    32. .text:08048922 add esp, 10h
    33. .text:08048925 mov ecx, eax
    34. .text:08048927 mov [ebp+var_14], ecx
    35. .text:0804892A sub esp, 0Ch
    36. .text:0804892D push [ebp+var_10]
    37. .text:08048930 call complex_function_2
    38. .text:08048935 add esp, 10h
    39. .text:08048938 mov ecx, eax
    40. .text:0804893A mov [ebp+var_10], ecx
    41. .text:0804893D sub esp, 0Ch
    42. .text:08048940 push [ebp+var_C]
    43. .text:08048943 call complex_function_3
    44. .text:08048948 add esp, 10h
    45. .text:0804894B mov ecx, eax
    46. .text:0804894D mov [ebp+var_C], ecx
    47. .text:08048950 cmp [ebp+var_14], 0
    48. .text:08048954 jnz short loc_8048962
    49. .text:08048956 cmp [ebp+var_10], 0
    50. .text:0804895A jnz short loc_8048962
    51. .text:0804895C cmp [ebp+var_C], 0
    52. .text:08048960 jz short loc_8048974
    53. .text:08048962
    54. .text:08048962 loc_8048962: ; CODE XREF: main+6C↑j
    55. .text:08048962 ; main+72↑j
    56. .text:08048962 sub esp, 0Ch
    57. .text:08048965 push offset s ; "Try again."
    58. .text:0804896A call _puts
    59. .text:0804896F add esp, 10h
    60. .text:08048972 jmp short loc_8048984
    61. .text:08048974 ; ---------------------------------------------------------------------------
    62. .text:08048974
    63. .text:08048974 loc_8048974: ; CODE XREF: main+78↑j
    64. .text:08048974 sub esp, 0Ch
    65. .text:08048977 push offset aGoodJob ; "Good Job."
    66. .text:0804897C call _puts
    67. .text:08048981 add esp, 10h
    68. .text:08048984
    69. .text:08048984 loc_8048984: ; CODE XREF: main+8A↑j
    70. .text:08048984 mov ecx, 0
    71. .text:08048989 mov eax, ecx
    72. .text:0804898B mov ecx, [ebp+var_4]
    73. .text:0804898E leave
    74. .text:0804898F lea esp, [ecx-4]
    75. .text:08048992 retn

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

    1. .text:0804890E mov [ebp+var_14], eax
    2. .text:08048911 mov [ebp+var_10], ebx
    3. .text:08048914 mov [ebp+var_C], edx
    4. .text:08048917 sub esp, 0Ch
    5. .text:0804891A push [ebp+var_14]
    6. .text:0804891D call complex_function_1
    7. .text:08048922 add esp, 10h
    8. .text:08048925 mov ecx, eax
    9. .text:08048927 mov [ebp+var_14], ecx
    10. .text:0804892A sub esp, 0Ch
    11. .text:0804892D push [ebp+var_10]
    12. .text:08048930 call complex_function_2
    13. .text:08048935 add esp, 10h
    14. .text:08048938 mov ecx, eax
    15. .text:0804893A mov [ebp+var_10], ecx
    16. .text:0804893D sub esp, 0Ch
    17. .text:08048940 push [ebp+var_C]
    18. .text:08048943 call complex_function_3

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

    1. def main(argv):
    2. path_to_binary = argv[1]
    3. project = angr.Project(path_to_binary)
    4. start_address = 0x804890E # :integer (probably hexadecimal)
    5. initial_state = project.factory.blank_state(addr=start_address)
    6. password0_size_in_bits = 4 * 8 # 因为complex_function 输出一个int 类型的数据,那就是32bits
    7. password0 = claripy.BVS('password0', password0_size_in_bits)
    8. password1 = claripy.BVS('password1', password0_size_in_bits)
    9. password2 = claripy.BVS('password2', password0_size_in_bits)
    10. initial_state.regs.eax = password0 # 告诉符号执行引擎这三个寄存器分别是complex_function 的参数
    11. initial_state.regs.ebx = password1
    12. initial_state.regs.edx = password2
    13. simulation = project.factory.simgr(initial_state)
    14. def is_successful(state):
    15. stdout_output = state.posix.dumps(sys.stdout.fileno())
    16. return 'Good Job' in str(stdout_output) # 根据输出来判断执行路径
    17. def should_abort(state):
    18. stdout_output = state.posix.dumps(sys.stdout.fileno())
    19. return 'Try again.' in str(stdout_output)
    20. simulation.explore(find=is_successful, avoid=should_abort)
    21. if simulation.found:
    22. solution_state = simulation.found[0]
    23. solution0 = solution_state.se.eval(password0)
    24. solution1 = solution_state.se.eval(password1)
    25. solution2 = solution_state.se.eval(password2)
    26. solution = ' '.join(map('{:x}'.format, [ solution0, solution1, solution2 ])) # :string
    27. print(solution)
    28. else:
    29. 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

    汇编代码:

    1. .text:080486F4 ; int __cdecl main(int argc, const char **argv, const char **envp)
    2. .text:080486F4 public main
    3. .text:080486F4 main proc near ; DATA XREF: _start+17↑o
    4. .text:080486F4
    5. .text:080486F4 var_4 = dword ptr -4
    6. .text:080486F4 argc = dword ptr 8
    7. .text:080486F4 argv = dword ptr 0Ch
    8. .text:080486F4 envp = dword ptr 10h
    9. .text:080486F4
    10. .text:080486F4 ; __unwind {
    11. .text:080486F4 lea ecx, [esp+4]
    12. .text:080486F8 and esp, 0FFFFFFF0h
    13. .text:080486FB push dword ptr [ecx-4]
    14. .text:080486FE push ebp
    15. .text:080486FF mov ebp, esp
    16. .text:08048701 push ecx
    17. .text:08048702 sub esp, 4
    18. .text:08048705 sub esp, 0Ch
    19. .text:08048708 push offset aEnterThePasswo ; "Enter the password: "
    20. .text:0804870D call _printf
    21. .text:08048712 add esp, 10h
    22. .text:08048715 call handle_user
    23. .text:0804871A mov eax, 0
    24. .text:0804871F mov ecx, [ebp+var_4]
    25. .text:08048722 leave
    26. .text:08048723 lea esp, [ecx-4]
    27. .text:08048726 retn

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

    1. .text:08048679 handle_user proc near ; CODE XREF: main+21↓p
    2. .text:08048679
    3. .text:08048679 var_10 = dword ptr -10h
    4. .text:08048679 var_C = dword ptr -0Ch
    5. .text:08048679
    6. .text:08048679 ; __unwind {
    7. .text:08048679 push ebp
    8. .text:0804867A mov ebp, esp
    9. .text:0804867C sub esp, 18h
    10. .text:0804867F sub esp, 4
    11. .text:08048682 lea eax, [ebp+var_10]
    12. .text:08048685 push eax
    13. .text:08048686 lea eax, [ebp+var_C]
    14. .text:08048689 push eax
    15. .text:0804868A push offset aUU ; "%u %u"
    16. .text:0804868F call ___isoc99_scanf
    17. .text:08048694 add esp, 10h
    18. .text:08048697 mov eax, [ebp+var_C] ; Argument complex_function0
    19. .text:0804869A sub esp, 0Ch
    20. .text:0804869D push eax
    21. .text:0804869E call complex_function0 ; Call complex_function0
    22. .text:080486A3 add esp, 10h
    23. .text:080486A6 mov [ebp+var_C], eax
    24. .text:080486A9 mov eax, [ebp+var_10] ; Argument complex_function1
    25. .text:080486AC sub esp, 0Ch
    26. .text:080486AF push eax
    27. .text:080486B0 call complex_function1 ; Call complex_function1
    28. .text:080486B5 add esp, 10h
    29. .text:080486B8 mov [ebp+var_10], eax
    30. .text:080486BB mov eax, [ebp+var_C]
    31. .text:080486BE cmp eax, 0D3062A4Ch
    32. .text:080486C3 jnz short loc_80486CF ; Check Value with input
    33. .text:080486C5 mov eax, [ebp+var_10]
    34. .text:080486C8 cmp eax, 694E5BA0h
    35. .text:080486CD jz short loc_80486E1
    36. .text:080486CF
    37. .text:080486CF loc_80486CF: ; CODE XREF: handle_user+4A↑j
    38. .text:080486CF sub esp, 0Ch
    39. .text:080486D2 push offset s ; "Try again."
    40. .text:080486D7 call _puts
    41. .text:080486DC add esp, 10h
    42. .text:080486DF jmp short loc_80486F1
    43. .text:080486E1 ; ---------------------------------------------------------------------------
    44. .text:080486E1
    45. .text:080486E1 loc_80486E1: ; CODE XREF: handle_user+54↑j
    46. .text:080486E1 sub esp, 0Ch
    47. .text:080486E4 push offset aGoodJob ; "Good Job."
    48. .text:080486E9 call _puts
    49. .text:080486EE add esp, 10h
    50. .text:080486F1
    51. .text:080486F1 loc_80486F1: ; CODE XREF: handle_user+66↑j
    52. .text:080486F1 nop
    53. .text:080486F2 leave
    54. .text:080486F3 retn

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

    1. .text:080484A9 complex_function0 proc near ; CODE XREF: handle_user+25↓p
    2. .text:080484A9
    3. .text:080484A9 arg_0 = dword ptr 8
    4. .text:080484A9
    5. .text:080484A9 ; __unwind {
    6. .text:080484A9 push ebp
    7. .text:080484AA mov ebp, esp
    8. .text:080484AC xor [ebp+arg_0], 0D53642BEh
    9. .text:080484B3 xor [ebp+arg_0], 58FC2926h
    10. .text:080484BA xor [ebp+arg_0], 25596A36h
    11. .text:080484C1 xor [ebp+arg_0], 0A7AFAA43h
    12. .text:080484C8 xor [ebp+arg_0], 1559CAFEh
    13. .text:080484CF xor [ebp+arg_0], 0D8D89C66h
    14. .text:080484D6 xor [ebp+arg_0], 6B8B30B6h
    15. .text:080484DD xor [ebp+arg_0], 0B5E7C180h
    16. .text:080484E4 xor [ebp+arg_0], 1FA429F6h
    17. .text:080484EB xor [ebp+arg_0], 21C70AF4h
    18. .text:080484F2 xor [ebp+arg_0], 0B7261E1Dh
    19. .text:080484F9 xor [ebp+arg_0], 0ADD88AD8h
    20. .text:08048500 xor [ebp+arg_0], 3E16A0F2h
    21. .text:08048507 xor [ebp+arg_0], 0DF2308FBh
    22. .text:0804850E xor [ebp+arg_0], 2273AAFh
    23. .text:08048515 xor [ebp+arg_0], 8E69AC70h
    24. .text:0804851C xor [ebp+arg_0], 0AC8924h
    25. .text:08048523 xor [ebp+arg_0], 561B782h
    26. .text:0804852A xor [ebp+arg_0], 5A64A924h
    27. .text:08048531 xor [ebp+arg_0], 0B118005Bh
    28. .text:08048538 xor [ebp+arg_0], 61461EA2h
    29. .text:0804853F xor [ebp+arg_0], 0E0E04E79h
    30. .text:08048546 xor [ebp+arg_0], 0A8DDACAAh
    31. .text:0804854D xor [ebp+arg_0], 82AF667Dh
    32. .text:08048554 xor [ebp+arg_0], 0B3CB4464h
    33. .text:0804855B xor [ebp+arg_0], 43B7BB1Ah
    34. .text:08048562 xor [ebp+arg_0], 0DF30F25Bh
    35. .text:08048569 xor [ebp+arg_0], 4C0F3376h
    36. .text:08048570 xor [ebp+arg_0], 0B2E462E5h
    37. .text:08048577 xor [ebp+arg_0], 7BF4CFC3h
    38. .text:0804857E xor [ebp+arg_0], 0C2960388h
    39. .text:08048585 xor [ebp+arg_0], 27071524h
    40. .text:0804858C mov eax, [ebp+arg_0]
    41. .text:0804858F pop ebp
    42. .text:08048590 retn
    43. .text:08048590 ; } // starts at 80484A9

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

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

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

    1. def main(argv):
    2. path_to_binary = argv[1]
    3. project = angr.Project(path_to_binary)
    4. start_address = 0x8048697
    5. initial_state = project.factory.blank_state(addr=start_address)
    6. initial_state.regs.ebp = initial_state.regs.esp
    7. password0 = claripy.BVS('password0', 4 * 8) # int
    8. password1 = claripy.BVS('password1', 4 * 8)
    9. padding_length_in_bytes = 8 # integer * 2
    10. initial_state.regs.esp -= padding_length_in_bytes
    11. initial_state.stack_push(password0) # :bitvector (claripy.BVS, claripy.BVV, claripy.BV)
    12. initial_state.stack_push(password1)
    13. simulation = project.factory.simgr(initial_state)
    14. simulation.explore(find=0x80486E4,avoid=0x80486D2)
    15. if simulation.found:
    16. solution_state = simulation.found[0]
    17. solution0 = solution_state.se.eval(password0)
    18. solution1 = solution_state.se.eval(password1)
    19. solution = ' '.join(map('{:x}'.format, [ solution0, solution1 ])) # :string
    20. print(solution)

    05_angr_symbolic_memory

    汇编代码:

    1. .text:080485E0 push offset unk_9FD92B8
    2. .text:080485E5 push offset unk_9FD92B0
    3. .text:080485EA push offset unk_9FD92A8
    4. .text:080485EF push offset user_input
    5. .text:080485F4 push offset a8s8s8s8s ; "%8s %8s %8s %8s"
    6. .text:080485F9 call ___isoc99_scanf ; 用户输入
    7. .text:080485FE add esp, 20h
    8. .text:08048601 mov [ebp+var_C], 0
    9. .text:08048608 jmp short loc_8048637 ; 注意这里有一个循环
    10. .text:0804860A ; ---------------------------------------------------------------------------
    11. .text:0804860A
    12. .text:0804860A loc_804860A: ; CODE XREF: main+93↓j
    13. .text:0804860A mov eax, [ebp+var_C]
    14. .text:0804860D add eax, 9FD92A0h
    15. .text:08048612 movzx eax, byte ptr [eax] ; Argument complex_function
    16. .text:08048615 movsx eax, al
    17. .text:08048618 sub esp, 8
    18. .text:0804861B push [ebp+var_C]
    19. .text:0804861E push eax
    20. .text:0804861F call complex_function ; 计算函数
    21. .text:08048624 add esp, 10h
    22. .text:08048627 mov edx, eax
    23. .text:08048629 mov eax, [ebp+var_C]
    24. .text:0804862C add eax, 9FD92A0h
    25. .text:08048631 mov [eax], dl
    26. .text:08048633 add [ebp+var_C], 1
    27. .text:08048637
    28. .text:08048637 loc_8048637: ; CODE XREF: main+60↑j
    29. .text:08048637 cmp [ebp+var_C], 1Fh
    30. .text:0804863B jle short loc_804860A
    31. .text:0804863D sub esp, 4
    32. .text:08048640 push 20h ; n
    33. .text:08048642 push offset s2 ; "THNJXTHBJUCDIMEEMLZNGMHISXAIXDQG"
    34. .text:08048647 push offset user_input ; s1
    35. .text:0804864C call _strncmp
    36. .text:08048651 add esp, 10h
    37. .text:08048654 test eax, eax
    38. .text:08048656 jz short loc_804866A ; 判断输入和complex_function是否相等
    39. .text:08048658 sub esp, 0Ch
    40. .text:0804865B push offset s ; "Try again."
    41. .text:08048660 call _puts
    42. .text:08048665 add esp, 10h
    43. .text:08048668 jmp short loc_804867A
    44. .text:0804866A ; ---------------------------------------------------------------------------
    45. .text:0804866A
    46. .text:0804866A loc_804866A: ; CODE XREF: main+AE↑j
    47. .text:0804866A sub esp, 0Ch
    48. .text:0804866D push offset aGoodJob ; "Good Job."
    49. .text:08048672 call _puts
    50. .text:08048677 add esp, 10h
    51. .text:0804867A
    52. .text:0804867A loc_804867A: ; CODE XREF: main+C0↑j
    53. .text:0804867A mov eax, 0
    54. .text:0804867F mov ecx, [ebp+var_4]
    55. .text:08048682 leave
    56. .text:08048683 lea esp, [ecx-4]
    57. .text:08048686 retn

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

    1. .text:080485E0 push offset unk_9FD92B8
    2. .text:080485E5 push offset unk_9FD92B0
    3. .text:080485EA push offset unk_9FD92A8
    4. .text:080485EF push offset user_input

    每一块内存的大小是8Byte

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

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

    1. def main(argv):
    2. path_to_binary = argv[1]
    3. project = angr.Project(path_to_binary)
    4. start_address = 0x8048601
    5. initial_state = project.factory.blank_state(addr=start_address)
    6. password0 = claripy.BVS('password0', 8 * 8)
    7. password1 = claripy.BVS('password1', 8 * 8)
    8. password2 = claripy.BVS('password2', 8 * 8)
    9. password3 = claripy.BVS('password3', 8 * 8)
    10. password0_address = 0x9FD92A0
    11. initial_state.memory.store(password0_address, password0)
    12. password1_address = 0x9FD92A8
    13. initial_state.memory.store(password1_address, password1)
    14. password2_address = 0x9FD92B0
    15. initial_state.memory.store(password2_address, password2)
    16. password3_address = 0x9FD92B8
    17. initial_state.memory.store(password3_address, password3)
    18. simulation = project.factory.simgr(initial_state)
    19. def is_successful(state):
    20. stdout_output = state.posix.dumps(sys.stdout.fileno())
    21. return 'Good Job' in str(stdout_output)
    22. def should_abort(state):
    23. stdout_output = state.posix.dumps(sys.stdout.fileno())
    24. return 'Try again' in str(stdout_output)
    25. simulation.explore(find=is_successful, avoid=should_abort)
    26. if simulation.found:
    27. solution_state = simulation.found[0]
    28. solution0 = solution_state.se.eval(password0)
    29. solution1 = solution_state.se.eval(password1)
    30. solution2 = solution_state.se.eval(password2)
    31. solution3 = solution_state.se.eval(password3)
    32. solution = ' '.join(map('{:x}'.format, [ solution0, solution1,solution2,solution3 ]))
    33. print(solution)

    Angr函数使用总结:

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

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

    github haidragon

  • 相关阅读:
    muduo源码剖析之AsyncLogging异步日志类
    Python 常用模块(一):时间模块
    QGIS如何将路网中的多条路段合并成一条完整的路
    Spring Security认证源码解析(示意图)
    CentOS 7基础操作06_Linux创建目录和文件
    静态通讯录
    在 python 中使用 Haar-Cascade 进行人脸检测
    机器学习1
    【jetpack】Navigation
    LeetCode每日一题:754. 到达终点数字
  • 原文地址:https://blog.csdn.net/sinat_35360663/article/details/127588259