├── 00_angr_find ├── 00_angr_find └── myexp.py ├── 01_angr_avoid ├── 01_angr_avoid ├── 01_angr_avoid.bc ├── 01_angr_avoid.c ├── 01_angr_avoid.config.json ├── 01_angr_avoid.dsm ├── 01_angr_avoid.ll └── myexp.py ├── 02_angr_find_condition ├── 02_angr_find_condition └── myexp.py ├── 03_angr_symbolic_registers ├── 03_angr_symbolic_registers └── myexp.py ├── 04_angr_symbolic_stack ├── 04_angr_symbolic_stack └── myexp.py ├── 05_angr_symbolic_memory ├── 05_angr_symbolic_memory └── myexp.py ├── 06_angr_symbolic_dynamic_memory ├── 06_angr_symbolic_dynamic_memory └── myexp.py ├── 07_angr_symbolic_file ├── 07_angr_symbolic_file ├── 07_angr_symbolic_file.idb └── myexp.py ├── 08_angr_constraints ├── 08_angr_constraints ├── 08_angr_constraints.idb └── myexp.py ├── 09_angr_hooks ├── 09_angr_hooks ├── 09_angr_hooks.idb ├── import angr.py └── myexp.py ├── 10_angr_simprocedures ├── 10_angr_simprocedures ├── 10_angr_simprocedures.idb └── myexp.py ├── 11_angr_sim_scanf ├── 11_angr_sim_scanf ├── 11_angr_sim_scanf.idb └── myexp.py ├── 12_angr_veritesting ├── 12_angr_veritesting ├── 12_angr_veritesting.idb └── myexp.py ├── 13_angr_static_binary ├── 13_angr_static_binary ├── 13_angr_static_binary.idb └── myexp.py ├── 14_angr_shared_library ├── 14_angr_shared_library ├── 14_angr_shared_library.idb ├── lib14_angr_shared_library.idb └── lib14_angr_shared_library.so ├── 15_angr_arbitrary_read ├── 15_angr_arbitrary_read ├── 15_angr_arbitrary_read.idb └── myexp.py ├── 16_angr_arbitrary_write ├── 16_angr_arbitrary_write └── myexp.py ├── 17_angr_arbitrary_jump ├── 17_angr_arbitrary_jump └── myexp.py ├── LICENSE ├── README.md └── 笔记 ├── 01 ├── Angr_CTF从入门到精通(一).md └── 图片 │ ├── 1596338176(1).jpg │ ├── 微信图片_20200802165123.png │ ├── 微信图片_20200802212543.png │ ├── 微信图片_20200802212828.png │ ├── 微信图片_20200802213325.png │ ├── 微信图片_20200802213419.png │ ├── 微信图片_20200802214124.png │ ├── 微信图片_20200804170205.png │ ├── 微信图片_20200804171032.png │ ├── 微信图片_20200804171308.png │ ├── 微信图片_20200804171438.png │ ├── 微信图片_20200804174207.png │ ├── 微信图片_20200804220541.png │ ├── 微信图片_20200804221823.png │ └── 微信截图_20200802164840.png ├── 02 ├── Angr_CTF从入门到精通(二).md └── 图片 │ ├── 微信图片_20200805100657.png │ ├── 微信图片_20200805161930.png │ ├── 微信图片_20200806210927.png │ ├── 微信图片_20200806212602.png │ ├── 微信图片_20200807213352.png │ ├── 微信图片_20200807213941.png │ ├── 微信图片_20200807215247.png │ ├── 微信图片_20200807222523.png │ └── 微信图片_20200808171501.png ├── 03 ├── Angr_CTF从入门到精通(三).md └── 图片 │ ├── 微信图片_20200810161941.png │ ├── 微信图片_20200816163605.png │ ├── 微信图片_20200816220158.png │ ├── 微信截图_20200811210031.png │ ├── 微信截图_20200811210154.png │ ├── 微信截图_20200811220414.png │ ├── 微信截图_20200811221326.png │ └── 微信截图_20200812220216.png └── 04 ├── Angr_CTF从入门到精通(四).md └── 图片 ├── 261737366704808.jpg ├── QQ图片20200901210631.png ├── QQ图片20200903172213.png ├── 微信图片_20200818201017.png ├── 微信图片_20200818214519.png ├── 微信图片_20200819172721.png ├── 微信图片_20200819173153.png ├── 微信图片_20200819173251.png ├── 微信图片_20200819173332.png ├── 微信图片_20200819174508.png ├── 微信图片_20200819202222.png ├── 微信图片_20200819220653.png ├── 微信截图_20200901091836.png ├── 微信截图_20200901092304.png ├── 微信截图_20200901210802.png ├── 微信截图_20200901223730.png └── 微信截图_20200903220756.png /00_angr_find/00_angr_find: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/00_angr_find/00_angr_find -------------------------------------------------------------------------------- /00_angr_find/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | def Go(): 4 | path_to_binary = "./00_angr_find" 5 | project = angr.Project(path_to_binary, auto_load_libs=False) 6 | initial_state = project.factory.entry_state() 7 | simulation = project.factory.simgr(initial_state) 8 | 9 | print_good_address = 0x8048678 10 | simulation.explore(find=print_good_address) 11 | 12 | if simulation.found: 13 | solution_state = simulation.found[0] 14 | solution = solution_state.posix.dumps(sys.stdin.fileno()) 15 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 16 | else: 17 | raise Exception('Could not find the solution') 18 | if __name__ == "__main__": 19 | Go() -------------------------------------------------------------------------------- /01_angr_avoid/01_angr_avoid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/01_angr_avoid/01_angr_avoid -------------------------------------------------------------------------------- /01_angr_avoid/01_angr_avoid.bc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/01_angr_avoid/01_angr_avoid.bc -------------------------------------------------------------------------------- /01_angr_avoid/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | def Go(): 4 | path_to_binary = "./01_angr_avoid" 5 | project = angr.Project(path_to_binary, auto_load_libs=False) 6 | initial_state = project.factory.entry_state() 7 | simulation = project.factory.simgr(initial_state) 8 | 9 | avoid_me_address = 0x080485A8 10 | maybe_good_address = 0x080485E0 11 | 12 | simulation.explore(find=maybe_good_address, avoid=avoid_me_address) 13 | 14 | if simulation.found: 15 | solution_state = simulation.found[0] 16 | solution = solution_state.posix.dumps(sys.stdin.fileno()) 17 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 18 | else: 19 | raise Exception('Could not find the solution') 20 | if __name__ == "__main__": 21 | Go() -------------------------------------------------------------------------------- /02_angr_find_condition/02_angr_find_condition: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/02_angr_find_condition/02_angr_find_condition -------------------------------------------------------------------------------- /02_angr_find_condition/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | def Go(): 4 | path_to_binary = "./02_angr_find_condition" 5 | project = angr.Project(path_to_binary, auto_load_libs=False) 6 | initial_state = project.factory.entry_state() 7 | simulation = project.factory.simgr(initial_state) 8 | 9 | def is_successful(state): 10 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 11 | if b'Good Job.' in stdout_output: 12 | return True 13 | else: 14 | return False 15 | 16 | def should_abort(state): 17 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 18 | if b'Try again.' in stdout_output: 19 | return True 20 | else: 21 | return False 22 | 23 | simulation.explore(find=is_successful, avoid=should_abort) 24 | 25 | if simulation.found: 26 | solution_state = simulation.found[0] 27 | solution = solution_state.posix.dumps(sys.stdin.fileno()) 28 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 29 | else: 30 | raise Exception('Could not find the solution') 31 | 32 | if __name__ == "__main__": 33 | Go() -------------------------------------------------------------------------------- /03_angr_symbolic_registers/03_angr_symbolic_registers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/03_angr_symbolic_registers/03_angr_symbolic_registers -------------------------------------------------------------------------------- /03_angr_symbolic_registers/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | def Go(): 5 | path_to_binary = "./03_angr_symbolic_registers" 6 | project = angr.Project(path_to_binary, auto_load_libs=False) 7 | start_address = 0x08048980 8 | initial_state = project.factory.blank_state(addr=start_address) 9 | 10 | passwd_size_in_bits = 32 11 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 12 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 13 | passwd2 = claripy.BVS('passwd2', passwd_size_in_bits) 14 | 15 | initial_state.regs.eax = passwd0 16 | initial_state.regs.ebx = passwd1 17 | initial_state.regs.edx = passwd2 18 | 19 | simulation = project.factory.simgr(initial_state) 20 | 21 | def is_successful(state): 22 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 23 | if b'Good Job.\n' in stdout_output: 24 | return True 25 | else: 26 | return False 27 | 28 | def should_abort(state): 29 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 30 | if b'Try again.\n' in stdout_output: 31 | return True 32 | else: 33 | return False 34 | 35 | simulation.explore(find=is_successful, avoid=should_abort) 36 | 37 | if simulation.found: 38 | for i in simulation.found: 39 | solution_state = i 40 | solution0 = format(solution_state.solver.eval(passwd0), 'x') 41 | solution1 = format(solution_state.solver.eval(passwd1), 'x') 42 | solution2 = format(solution_state.solver.eval(passwd2), 'x') 43 | solution = solution0 + " " + solution1 + " " + solution2 44 | print("[+] Success! Solution is: {}".format(solution)) 45 | else: 46 | raise Exception('Could not find the solution') 47 | 48 | if __name__ == "__main__": 49 | Go() -------------------------------------------------------------------------------- /04_angr_symbolic_stack/04_angr_symbolic_stack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/04_angr_symbolic_stack/04_angr_symbolic_stack -------------------------------------------------------------------------------- /04_angr_symbolic_stack/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | def Go(): 5 | path_to_binary = "./04_angr_symbolic_stack" 6 | project = angr.Project(path_to_binary, auto_load_libs=False) 7 | start_address = 0x8048697 8 | initial_state = project.factory.blank_state(addr=start_address) 9 | 10 | initial_state.regs.ebp = initial_state.regs.esp 11 | 12 | passwd_size_in_bits = 32 13 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 14 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 15 | 16 | padding_length_in_bytes = 0x8 17 | initial_state.regs.esp -= padding_length_in_bytes 18 | 19 | initial_state.stack_push(passwd0) 20 | initial_state.stack_push(passwd1) 21 | 22 | simulation = project.factory.simgr(initial_state) 23 | 24 | def is_successful(state): 25 | stdout_output = state.posix.dumps(1) 26 | if b'Good Job.\n' in stdout_output: 27 | return True 28 | else: 29 | return False 30 | 31 | def should_abort(state): 32 | stdout_output = state.posix.dumps(1) 33 | if b'Try again.\n' in stdout_output: 34 | return True 35 | else: 36 | return False 37 | 38 | simulation.explore(find=is_successful, avoid=should_abort) 39 | 40 | if simulation.found: 41 | for i in simulation.found: 42 | solution_state = i 43 | solution0 = (solution_state.solver.eval(passwd0)) 44 | solution1 = (solution_state.solver.eval(passwd1)) 45 | print("[+] Success! Solution is: {0} {1}".format(solution0, solution1)) 46 | #print(solution0, solution1) 47 | else: 48 | raise Exception('Could not find the solution') 49 | 50 | if __name__ == "__main__": 51 | Go() -------------------------------------------------------------------------------- /05_angr_symbolic_memory/05_angr_symbolic_memory: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/05_angr_symbolic_memory/05_angr_symbolic_memory -------------------------------------------------------------------------------- /05_angr_symbolic_memory/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | def Go(): 5 | path_to_binary = "./05_angr_symbolic_memory" 6 | project = angr.Project(path_to_binary, auto_load_libs=False) 7 | start_address = 0x8048601 8 | initial_state = project.factory.blank_state(addr=start_address) 9 | 10 | passwd_size_in_bits = 64 11 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 12 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 13 | passwd2 = claripy.BVS('passwd2', passwd_size_in_bits) 14 | passwd3 = claripy.BVS('passwd3', passwd_size_in_bits) 15 | 16 | passwd0_address = 0xA1BA1C0 17 | #passwd1_address = 0xA1BA1C8 18 | #passwd2_address = 0xA1BA1D0 19 | #passwd3_address = 0xA1BA1D8 20 | initial_state.memory.store(passwd0_address, passwd0) 21 | initial_state.memory.store(passwd0_address + 0x8, passwd1) 22 | initial_state.memory.store(passwd0_address + 0x10, passwd2) 23 | initial_state.memory.store(passwd0_address + 0x18, passwd3) 24 | 25 | simulation = project.factory.simgr(initial_state) 26 | 27 | def is_successful(state): 28 | stdout_output = state.posix.dumps(1) 29 | if b'Good Job.\n' in stdout_output: 30 | return True 31 | else: 32 | return False 33 | 34 | def should_abort(state): 35 | stdout_output = state.posix.dumps(1) 36 | if b'Try again.\n' in stdout_output: 37 | return True 38 | else: 39 | return False 40 | 41 | simulation.explore(find=is_successful, avoid=should_abort) 42 | 43 | if simulation.found: 44 | for i in simulation.found: 45 | solution_state = i 46 | solution0 = solution_state.solver.eval(passwd0,cast_to=bytes) 47 | solution1 = solution_state.solver.eval(passwd1,cast_to=bytes) 48 | solution2 = solution_state.solver.eval(passwd2,cast_to=bytes) 49 | solution3 = solution_state.solver.eval(passwd3,cast_to=bytes) 50 | solution = solution0 + b" " + solution1 + b" " + solution2 + b" " + solution3 51 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 52 | #print(solution0, solution1, solution2, solution3) 53 | else: 54 | raise Exception('Could not find the solution') 55 | 56 | if __name__ == "__main__": 57 | Go() -------------------------------------------------------------------------------- /06_angr_symbolic_dynamic_memory/06_angr_symbolic_dynamic_memory: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/06_angr_symbolic_dynamic_memory/06_angr_symbolic_dynamic_memory -------------------------------------------------------------------------------- /06_angr_symbolic_dynamic_memory/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | def Go(): 5 | path_to_binary = "./06_angr_symbolic_dynamic_memory" 6 | project = angr.Project(path_to_binary, auto_load_libs=False) 7 | start_address = 0x8048699 8 | initial_state = project.factory.blank_state(addr=start_address) 9 | 10 | passwd_size_in_bits = 64 11 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 12 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 13 | 14 | fake_heap_address0 = 0xffffc93c 15 | pointer_to_malloc_memory_address0 = 0xabcc8a4 16 | fake_heap_address1 = 0xffffc94c 17 | pointer_to_malloc_memory_address1 = 0xabcc8ac 18 | initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness) 19 | initial_state.memory.store(pointer_to_malloc_memory_address1, fake_heap_address1, endness=project.arch.memory_endness) 20 | 21 | initial_state.memory.store(fake_heap_address0, password0) 22 | initial_state.memory.store(fake_heap_address1, password1) 23 | 24 | simulation = project.factory.simgr(initial_state) 25 | 26 | def is_successful(state): 27 | stdout_output = state.posix.dumps(1) 28 | if b'Good Job.\n' in stdout_output: 29 | return True 30 | else: 31 | return False 32 | 33 | def should_abort(state): 34 | stdout_output = state.posix.dumps(1) 35 | if b'Try again.\n' in stdout_output: 36 | return True 37 | else: 38 | return False 39 | 40 | simulation.explore(find=is_successful, avoid=should_abort) 41 | 42 | if simulation.found: 43 | for i in simulation.found: 44 | solution_state = i 45 | solution0 = solution_state.solver.eval(passwd0, cast_to=bytes) 46 | solution1 = solution_state.solver.eval(passwd1, cast_to=bytes) 47 | print("[+] Success! Solution is: {0} {1}".format(solution0.decode('utf-8'), solution1.decode('utf-8'))) 48 | #print(solution0, solution1) 49 | else: 50 | raise Exception('Could not find the solution') 51 | 52 | if __name__ == "__main__": 53 | Go() -------------------------------------------------------------------------------- /07_angr_symbolic_file/07_angr_symbolic_file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/07_angr_symbolic_file/07_angr_symbolic_file -------------------------------------------------------------------------------- /07_angr_symbolic_file/07_angr_symbolic_file.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/07_angr_symbolic_file/07_angr_symbolic_file.idb -------------------------------------------------------------------------------- /07_angr_symbolic_file/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | def Go(): 5 | path_to_binary = "./07_angr_symbolic_file" 6 | project = angr.Project(path_to_binary, auto_load_libs=False) 7 | start_address = 0x80488EA 8 | initial_state = project.factory.blank_state(addr=start_address) 9 | 10 | filename = 'OJKSQYDP.txt' 11 | symbolic_file_size_bytes = 64 12 | passwd0 = claripy.BVS('password', symbolic_file_size_bytes * 8) 13 | passwd_file = angr.storage.SimFile(filename, content=passwd0, size=symbolic_file_size_bytes) 14 | 15 | initial_state.fs.insert(filename, passwd_file) 16 | 17 | simulation = project.factory.simgr(initial_state) 18 | 19 | def is_successful(state): 20 | stdout_output = state.posix.dumps(1) 21 | if b'Good Job.\n' in stdout_output: 22 | return True 23 | else: 24 | return False 25 | 26 | def should_abort(state): 27 | stdout_output = state.posix.dumps(1) 28 | if b'Try again.\n' in stdout_output: 29 | return True 30 | else: 31 | return False 32 | 33 | simulation.explore(find=is_successful, avoid=should_abort) 34 | 35 | if simulation.found: 36 | for i in simulation.found: 37 | solution_state = i 38 | solution0 = solution_state.solver.eval(passwd0, cast_to=bytes) 39 | print("[+] Success! Solution is: {0}".format(solution0.decode('utf-8'))) 40 | #print(solution0) 41 | else: 42 | raise Exception('Could not find the solution') 43 | 44 | if __name__ == "__main__": 45 | Go() -------------------------------------------------------------------------------- /08_angr_constraints/08_angr_constraints: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/08_angr_constraints/08_angr_constraints -------------------------------------------------------------------------------- /08_angr_constraints/08_angr_constraints.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/08_angr_constraints/08_angr_constraints.idb -------------------------------------------------------------------------------- /08_angr_constraints/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | def Go(): 5 | path_to_binary = "./08_angr_constraints" 6 | project = angr.Project(path_to_binary, auto_load_libs=False) 7 | 8 | start_address = 0x8048625 9 | buff_addr = 0x0804A050 10 | address_to_check_constraint = 0x08048565 11 | 12 | initial_state = project.factory.blank_state(addr=start_address) 13 | 14 | char_size_in_bits = 8 15 | passwd_len = 16 16 | passwd0 = claripy.BVS('passwd0', char_size_in_bits*passwd_len) 17 | initial_state.memory.store(buff_addr, passwd0) 18 | 19 | simulation = project.factory.simgr(initial_state) 20 | simulation.explore(find=address_to_check_constraint) 21 | 22 | if simulation.found: 23 | solution_state = simulation.found[0] 24 | constrained_parameter_address = buff_addr 25 | constrained_parameter_size_bytes = 16 26 | constrained_parameter_bitvector = solution_state.memory.load( 27 | constrained_parameter_address, 28 | constrained_parameter_size_bytes 29 | ) 30 | constrained_parameter_desired_value = 'AUPDNNPROEZRJWKB' 31 | solution_state.solver.add(constrained_parameter_bitvector == constrained_parameter_desired_value) 32 | solution0 = solution_state.solver.eval(passwd0,cast_to=bytes) 33 | print("[+] Success! Solution is: {0}".format(solution0)) 34 | else: 35 | raise Exception('Could not find the solution') 36 | 37 | if __name__ == "__main__": 38 | Go() -------------------------------------------------------------------------------- /09_angr_hooks/09_angr_hooks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/09_angr_hooks/09_angr_hooks -------------------------------------------------------------------------------- /09_angr_hooks/09_angr_hooks.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/09_angr_hooks/09_angr_hooks.idb -------------------------------------------------------------------------------- /09_angr_hooks/import angr.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import claripy 3 | import sys 4 | 5 | def main(argv): 6 | bin_path = argv[1] 7 | project = angr.Project(bin_path) 8 | 9 | initial_state = project.factory.entry_state() 10 | 11 | check_equals_called_address = 0x80486B3 12 | 13 | instruction_to_skip_length = 5 14 | 15 | @project.hook(check_equals_called_address, length=instruction_to_skip_length) 16 | def skip_check_equals_(state): 17 | user_input_buff_address = 0x804a054 18 | user_input_buff_length = 16 19 | user_input_string = state.memory.load( 20 | user_input_buff_address, 21 | user_input_buff_length 22 | ) 23 | 24 | check_against_string = "XKSPZSJKJYQCQXZV" 25 | 26 | state.regs.eax = claripy.If ( 27 | user_input_string == check_against_string, 28 | claripy.BVV(1, 32), 29 | claripy.BVV(0, 32) 30 | ) 31 | 32 | simulation = project.factory.simgr(initial_state) 33 | 34 | def is_successful(state): 35 | stdout_output = state.posix.dumps(1) 36 | return b"Good Job." in stdout_output 37 | 38 | def should_abort(state): 39 | stdout_output = state.posix.dumps(1) 40 | return b"Try again." in stdout_output 41 | 42 | simulation.explore(find = is_successful, avoid = should_abort) 43 | 44 | if simulation.found: 45 | print(simulation.found[0].posix.dumps(0)) 46 | else: 47 | raise(Exception("Could not find the solution")) 48 | 49 | if __name__ == "__main__": 50 | main(sys.argv) -------------------------------------------------------------------------------- /09_angr_hooks/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | def Go(): 5 | path_to_binary = "./09_angr_hooks" 6 | project = angr.Project(path_to_binary, auto_load_libs=False) 7 | initial_state = project.factory.entry_state() 8 | 9 | check_equals_called_address = 0x80486B3 10 | instruction_to_skip_length = 5 11 | 12 | @project.hook(check_equals_called_address, length=instruction_to_skip_length) 13 | def skip_check_equals_(state): 14 | user_input_buffer_address = 0x804A054 15 | user_input_buffer_length = 16 16 | 17 | user_input_string = state.memory.load( 18 | user_input_buffer_address, 19 | user_input_buffer_length 20 | ) 21 | 22 | check_against_string = 'XKSPZSJKJYQCQXZV' 23 | 24 | register_size_bit = 32 25 | state.regs.eax = claripy.If( 26 | user_input_string == check_against_string, 27 | claripy.BVV(1, register_size_bit), 28 | claripy.BVV(0, register_size_bit) 29 | ) 30 | 31 | simulation = project.factory.simgr(initial_state) 32 | 33 | def is_successful(state): 34 | stdout_output = state.posix.dumps(1) 35 | if b'Good Job.\n' in stdout_output: 36 | return True 37 | else: 38 | return False 39 | 40 | def should_abort(state): 41 | stdout_output = state.posix.dumps(1) 42 | if b'Try again.\n' in stdout_output: 43 | return True 44 | else: 45 | return False 46 | 47 | simulation.explore(find=is_successful, avoid=should_abort) 48 | 49 | if simulation.found: 50 | for i in simulation.found: 51 | solution_state = i 52 | solution = solution_state.posix.dumps(0) 53 | print("[+] Success! Solution is: {0}".format(solution.decode('utf-8'))) 54 | #print(solution0) 55 | else: 56 | raise Exception('Could not find the solution') 57 | 58 | if __name__ == "__main__": 59 | Go() -------------------------------------------------------------------------------- /10_angr_simprocedures/10_angr_simprocedures: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/10_angr_simprocedures/10_angr_simprocedures -------------------------------------------------------------------------------- /10_angr_simprocedures/10_angr_simprocedures.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/10_angr_simprocedures/10_angr_simprocedures.idb -------------------------------------------------------------------------------- /10_angr_simprocedures/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import claripy 3 | import sys 4 | 5 | def Go(): 6 | path_to_binary = "./10_angr_simprocedures" 7 | project = angr.Project(path_to_binary, auto_load_libs=False) 8 | initial_state = project.factory.entry_state() 9 | 10 | class ReplacementCheckEquals(angr.SimProcedure): 11 | def run(self, to_check, length): 12 | user_input_buffer_address = to_check 13 | user_input_buffer_length = length 14 | user_input_string = self.state.memory.load( 15 | user_input_buffer_address, 16 | user_input_buffer_length 17 | ) 18 | check_against_string = 'ORSDDWXHZURJRBDH' 19 | return claripy.If( 20 | user_input_string == check_against_string, 21 | claripy.BVV(1, 32), 22 | claripy.BVV(0, 32) 23 | ) 24 | 25 | check_equals_symbol = 'check_equals_ORSDDWXHZURJRBDH' 26 | project.hook_symbol(check_equals_symbol, ReplacementCheckEquals()) 27 | 28 | simulation = project.factory.simgr(initial_state) 29 | 30 | def is_successful(state): 31 | stdout_output = state.posix.dumps(1) 32 | if b'Good Job.\n' in stdout_output: 33 | return True 34 | else: 35 | return False 36 | 37 | def should_abort(state): 38 | stdout_output = state.posix.dumps(1) 39 | if b'Try again.\n' in stdout_output: 40 | return True 41 | else: 42 | return False 43 | 44 | simulation.explore(find=is_successful, avoid=should_abort) 45 | 46 | if simulation.found: 47 | for i in simulation.found: 48 | solution_state = i 49 | solution = solution_state.posix.dumps(0) 50 | print("[+] Success! Solution is: {0}".format(solution.decode('utf-8'))) 51 | #print(solution0) 52 | else: 53 | raise Exception('Could not find the solution') 54 | 55 | if __name__ == "__main__": 56 | Go() -------------------------------------------------------------------------------- /11_angr_sim_scanf/11_angr_sim_scanf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/11_angr_sim_scanf/11_angr_sim_scanf -------------------------------------------------------------------------------- /11_angr_sim_scanf/11_angr_sim_scanf.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/11_angr_sim_scanf/11_angr_sim_scanf.idb -------------------------------------------------------------------------------- /11_angr_sim_scanf/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import claripy 3 | import sys 4 | 5 | def Go(): 6 | path_to_binary = "./11_angr_sim_scanf" 7 | project = angr.Project(path_to_binary, auto_load_libs=False) 8 | initial_state = project.factory.entry_state() 9 | 10 | class ReplacementScanf(angr.SimProcedure): 11 | def run(self, format_string, param0, param1): 12 | scanf0 = claripy.BVS('scanf0', 32) 13 | scanf1 = claripy.BVS('scanf1', 32) 14 | 15 | scanf0_address = param0 16 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 17 | scanf1_address = param1 18 | self.state.memory.store(scanf1_address, scanf1, endness=project.arch.memory_endness) 19 | 20 | self.state.globals['solutions'] = (scanf0, scanf1) 21 | 22 | scanf_symbol = '__isoc99_scanf' 23 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 24 | 25 | simulation = project.factory.simgr(initial_state) 26 | 27 | def is_successful(state): 28 | stdout_output = state.posix.dumps(1) 29 | if b'Good Job.\n' in stdout_output: 30 | return True 31 | else: 32 | return False 33 | 34 | def should_abort(state): 35 | stdout_output = state.posix.dumps(1) 36 | if b'Try again.\n' in stdout_output: 37 | return True 38 | else: 39 | return False 40 | 41 | simulation.explore(find=is_successful, avoid=should_abort) 42 | 43 | if simulation.found: 44 | for i in simulation.found: 45 | solution_state = i 46 | stored_solutions = solution_state.globals['solutions'] 47 | scanf0_solution = solution_state.solver.eval(stored_solutions[0]) 48 | scanf1_solution = solution_state.solver.eval(stored_solutions[1]) 49 | print("[+] Success! Solution is: {0} {1}".format(scanf0_solution,scanf1_solution)) 50 | #print(scanf0_solution, scanf1_solution) 51 | else: 52 | raise Exception('Could not find the solution') 53 | 54 | if __name__ == "__main__": 55 | Go() -------------------------------------------------------------------------------- /12_angr_veritesting/12_angr_veritesting: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/12_angr_veritesting/12_angr_veritesting -------------------------------------------------------------------------------- /12_angr_veritesting/12_angr_veritesting.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/12_angr_veritesting/12_angr_veritesting.idb -------------------------------------------------------------------------------- /12_angr_veritesting/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import claripy 3 | import sys 4 | 5 | def Go(): 6 | path_to_binary = "./12_angr_veritesting" 7 | project = angr.Project(path_to_binary, auto_load_libs=False) 8 | initial_state = project.factory.entry_state() 9 | simulation = project.factory.simgr(initial_state, veritesting=True) 10 | 11 | def is_successful(state): 12 | stdout_output = state.posix.dumps(1) 13 | if b'Good Job.\n' in stdout_output: 14 | return True 15 | else: 16 | return False 17 | 18 | def should_abort(state): 19 | stdout_output = state.posix.dumps(1) 20 | if b'Try again.\n' in stdout_output: 21 | return True 22 | else: 23 | return False 24 | 25 | simulation.explore(find=is_successful, avoid=should_abort) 26 | 27 | if simulation.found: 28 | for i in simulation.found: 29 | solution_state = i 30 | solution = solution_state.posix.dumps(0) 31 | print("[+] Success! Solution is: {0}".format(solution)) 32 | #print(scanf0_solution, scanf1_solution) 33 | else: 34 | raise Exception('Could not find the solution') 35 | 36 | if __name__ == "__main__": 37 | Go() -------------------------------------------------------------------------------- /13_angr_static_binary/13_angr_static_binary: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/13_angr_static_binary/13_angr_static_binary -------------------------------------------------------------------------------- /13_angr_static_binary/13_angr_static_binary.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/13_angr_static_binary/13_angr_static_binary.idb -------------------------------------------------------------------------------- /13_angr_static_binary/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import claripy 3 | import sys 4 | 5 | def Go(): 6 | path_to_binary = "./13_angr_static_binary" 7 | project = angr.Project(path_to_binary, auto_load_libs=False) 8 | initial_state = project.factory.entry_state() 9 | 10 | project.hook(0x804ed40, angr.SIM_PROCEDURES['libc']['printf']()) 11 | project.hook(0x804ed80, angr.SIM_PROCEDURES['libc']['scanf']()) 12 | project.hook(0x804f350, angr.SIM_PROCEDURES['libc']['puts']()) 13 | project.hook(0x8048d10, angr.SIM_PROCEDURES['glibc']['__libc_start_main']()) 14 | 15 | simulation = project.factory.simgr(initial_state, veritesting=True) 16 | 17 | def is_successful(state): 18 | stdout_output = state.posix.dumps(1) 19 | if b'Good Job.\n' in stdout_output: 20 | return True 21 | else: 22 | return False 23 | 24 | def should_abort(state): 25 | stdout_output = state.posix.dumps(1) 26 | if b'Try again.\n' in stdout_output: 27 | return True 28 | else: 29 | return False 30 | 31 | simulation.explore(find=is_successful, avoid=should_abort) 32 | 33 | if simulation.found: 34 | for i in simulation.found: 35 | solution_state = i 36 | solution = solution_state.posix.dumps(0) 37 | print("[+] Success! Solution is: {0}".format(solution)) 38 | #print(scanf0_solution, scanf1_solution) 39 | else: 40 | raise Exception('Could not find the solution') 41 | 42 | if __name__ == "__main__": 43 | Go() -------------------------------------------------------------------------------- /14_angr_shared_library/14_angr_shared_library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/14_angr_shared_library/14_angr_shared_library -------------------------------------------------------------------------------- /14_angr_shared_library/14_angr_shared_library.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/14_angr_shared_library/14_angr_shared_library.idb -------------------------------------------------------------------------------- /14_angr_shared_library/lib14_angr_shared_library.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/14_angr_shared_library/lib14_angr_shared_library.idb -------------------------------------------------------------------------------- /14_angr_shared_library/lib14_angr_shared_library.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/14_angr_shared_library/lib14_angr_shared_library.so -------------------------------------------------------------------------------- /15_angr_arbitrary_read/15_angr_arbitrary_read: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/15_angr_arbitrary_read/15_angr_arbitrary_read -------------------------------------------------------------------------------- /15_angr_arbitrary_read/15_angr_arbitrary_read.idb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/15_angr_arbitrary_read/15_angr_arbitrary_read.idb -------------------------------------------------------------------------------- /15_angr_arbitrary_read/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | def Go(): 5 | path_to_binary = "./15_angr_arbitrary_read" 6 | project = angr.Project(path_to_binary, auto_load_libs=False) 7 | initial_state = project.factory.entry_state() 8 | 9 | class ReplacementScanf(angr.SimProcedure): 10 | def run(self, format_string, param0, param1): 11 | scanf0 = claripy.BVS('scanf0', 32) 12 | scanf1 = claripy.BVS('scanf1', 20*8) 13 | for char in scanf1.chop(bits=8): 14 | self.state.add_constraints(char >= 'A', char <= 'Z') 15 | scanf0_address = param0 16 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 17 | scanf1_address = param1 18 | self.state.memory.store(scanf1_address, scanf1) 19 | self.state.globals['solutions'] = (scanf0, scanf1) 20 | 21 | scanf_symbol = '__isoc99_scanf' 22 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 23 | 24 | def check_puts(state): 25 | puts_parameter = state.memory.load(state.regs.esp + 4, 4, endness=project.arch.memory_endness) 26 | if state.se.symbolic(puts_parameter): 27 | good_job_string_address = 0x594e4257 28 | is_vulnerable_expression = puts_parameter == good_job_string_address 29 | 30 | copied_state = state.copy() 31 | copied_state.add_constraints(is_vulnerable_expression) 32 | 33 | if copied_state.satisfiable(): 34 | state.add_constraints(is_vulnerable_expression) 35 | return True 36 | else: 37 | return False 38 | else: 39 | return False 40 | 41 | simulation = project.factory.simgr(initial_state) 42 | 43 | def is_successful(state): 44 | puts_address = 0x8048370 45 | if state.addr == puts_address: 46 | return check_puts(state) 47 | else: 48 | return False 49 | 50 | simulation.explore(find=is_successful) 51 | 52 | if simulation.found: 53 | solution_state = simulation.found[0] 54 | (scanf0, scanf1) = solution_state.globals['solutions'] 55 | solution0 = (solution_state.solver.eval(scanf0)) 56 | solution1 = (solution_state.solver.eval(scanf1,cast_to=bytes)) 57 | print("[+] Success! Solution is: {0} {1}".format(solution0, solution1)) 58 | else: 59 | raise Exception('Could not find the solution') 60 | 61 | if __name__ == "__main__": 62 | Go() -------------------------------------------------------------------------------- /16_angr_arbitrary_write/16_angr_arbitrary_write: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/16_angr_arbitrary_write/16_angr_arbitrary_write -------------------------------------------------------------------------------- /16_angr_arbitrary_write/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import claripy 3 | import sys 4 | 5 | def Go(): 6 | path_to_binary = "./16_angr_arbitrary_write" 7 | project = angr.Project(path_to_binary) 8 | 9 | initial_state = project.factory.entry_state() 10 | 11 | class ReplacementScanf(angr.SimProcedure): 12 | def run(self, format_string, param0, param1): 13 | scanf0 = claripy.BVS('scanf0', 32) 14 | scanf1 = claripy.BVS('scanf1', 20*8) 15 | 16 | for char in scanf1.chop(bits=8): 17 | self.state.add_constraints(char >= 48, char <= 96) 18 | 19 | scanf0_address = param0 20 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 21 | scanf1_address = param1 22 | self.state.memory.store(scanf1_address, scanf1) 23 | 24 | self.state.globals['solutions'] = (scanf0, scanf1) 25 | 26 | scanf_symbol = '__isoc99_scanf' 27 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 28 | 29 | 30 | def check_strncpy(state): 31 | strncpy_src = state.memory.load(state.regs.esp + 8, 4, endness=project.arch.memory_endness) 32 | strncpy_dest = state.memory.load(state.regs.esp + 4, 4, endness=project.arch.memory_endness) 33 | strncpy_len = state.memory.load(state.regs.esp + 12, 4, endness=project.arch.memory_endness) 34 | 35 | src_contents = state.memory.load(strncpy_src, strncpy_len) 36 | 37 | if state.solver.symbolic(src_contents) and state.solver.symbolic(strncpy_dest): 38 | password_string = 'DVTBOGZL' 39 | buffer_address = 0x4655544c 40 | 41 | does_src_hold_password = src_contents[-1:-64] == password_string 42 | does_dest_equal_buffer_address = strncpy_dest == buffer_address 43 | 44 | if state.satisfiable(extra_constraints=(does_src_hold_password, does_dest_equal_buffer_address)): 45 | state.add_constraints(does_src_hold_password, does_dest_equal_buffer_address) 46 | return True 47 | else: 48 | return False 49 | else: 50 | return False 51 | 52 | simulation = project.factory.simgr(initial_state) 53 | 54 | def is_successful(state): 55 | strncpy_address = 0x8048410 56 | if state.addr == strncpy_address: 57 | return check_strncpy(state) 58 | else: 59 | return False 60 | 61 | simulation.explore(find=is_successful) 62 | 63 | if simulation.found: 64 | solution_state = simulation.found[0] 65 | 66 | scanf0, scanf1 = solution_state.globals['solutions'] 67 | solution0 = (solution_state.solver.eval(scanf0)) 68 | solution1 = (solution_state.solver.eval(scanf1,cast_to=bytes)) 69 | print("[+] Success! Solution is: {0} {1}".format(solution0, solution1)) 70 | else: 71 | raise Exception('Could not find the solution') 72 | 73 | if __name__ == '__main__': 74 | Go() 75 | -------------------------------------------------------------------------------- /17_angr_arbitrary_jump/17_angr_arbitrary_jump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/17_angr_arbitrary_jump/17_angr_arbitrary_jump -------------------------------------------------------------------------------- /17_angr_arbitrary_jump/myexp.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import claripy 3 | import sys 4 | 5 | def Go(): 6 | path_to_binary = "./17_angr_arbitrary_jump" 7 | project = angr.Project(path_to_binary) 8 | initial_state = project.factory.entry_state() 9 | 10 | class ReplacementScanf(angr.SimProcedure): 11 | def run(self, format_string, input_buffer_address): 12 | input_buffer = claripy.BVS( 13 | 'input_buffer', 64 * 8) 14 | for char in input_buffer.chop(bits=8): 15 | self.state.add_constraints(char >= '0', char <= 'z') 16 | 17 | self.state.memory.store( 18 | input_buffer_address, input_buffer, endness=project.arch.memory_endness) 19 | self.state.globals['solution'] = input_buffer 20 | 21 | scanf_symbol = '__isoc99_scanf' 22 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 23 | 24 | simulation = project.factory.simgr( 25 | initial_state, 26 | save_unconstrained=True, 27 | stashes={ 28 | 'active' : [initial_state], 29 | 'unconstrained' : [], 30 | 'found' : [], 31 | 'not_needed' : [] 32 | } 33 | ) 34 | 35 | def check_vulnerable(state): 36 | return state.solver.symbolic(state.regs.eip) 37 | 38 | def has_found_solution(): 39 | return simulation.found 40 | 41 | def has_unconstrained_to_check(): 42 | return simulation.unconstrained 43 | 44 | def has_active(): 45 | return simulation.active 46 | 47 | while (has_active() or has_unconstrained_to_check()) and (not has_found_solution()): 48 | for unconstrained_state in simulation.unconstrained: 49 | def should_move(s): 50 | return s is unconstrained_state 51 | simulation.move('unconstrained', 'found', filter_func=should_move) 52 | simulation.step() 53 | 54 | if simulation.found: 55 | solution_state = simulation.found[0] 56 | solution_state.add_constraints(solution_state.regs.eip == 0x4d4c4749) 57 | solution = solution_state.solver.eval( 58 | solution_state.globals['solution'], cast_to=bytes) 59 | print(solution[::-1]) 60 | else: 61 | raise Exception('Could not find the solution') 62 | 63 | if __name__ == '__main__': 64 | Go() 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AngrCTF_FITM 2 | Angr CTF From introduction to mastery 3 | 4 | 本文基于一个GitHub上关于Angr在CTF上应用的题库,具体的地址为: 5 | 6 | ``` 7 | https://github.com/jakespringer/angr_ctf 8 | ``` 9 | 10 | 本文所使用Ubuntu环境皆为20.04 LTS 版本 11 | 12 | -------------------------------------------------------------------------------- /笔记/01/Angr_CTF从入门到精通(一).md: -------------------------------------------------------------------------------- 1 | # Angr_CTF从入门到精通(一) 2 | 3 | 本文基于一个GitHub上关于Angr在CTF上应用的题库,具体的地址为: 4 | 5 | ``` 6 | https://github.com/jakespringer/angr_ctf 7 | ``` 8 | 9 | 本文所使用Ubuntu环境皆为20.04 LTS 版本 10 | 11 | 我自己的笔记和题目二进制文件,以及注释脚本EXP我另外开了一个仓库: 12 | 13 | ``` 14 | https://github.com/ZERO-A-ONE/AngrCTF_FITM 15 | ``` 16 | 17 | ## 安装篇 18 | 19 | 建议首先修改Linux和Python镜像至国内镜像加速体验 20 | 21 | ### Linux/Ubuntu国内镜像 22 | 23 | - 首先打开Software&Updates 24 | - 选择Download from 25 | - 选择Other... 26 | - 在国家列表中找到China 27 | - 推荐选择的镜像源为: 28 | - 清华大学镜像站:`mirrors.tuna.tsinghua.edu.cn` 29 | - 阿里云镜像站:`mirrors.aliyun.com` 30 | - Choose Server 31 | - 选择Close后等待刷新镜像仓库 32 | - 在shell中执行 33 | - `sudo apt update` 34 | - `sudo apt upgrade` 35 | 36 | ### Python国内镜像加速 37 | 38 | 首先切回用户主目录 39 | 40 | ```shell 41 | cd ~ 42 | ``` 43 | 44 | 然后创建`.pip`目录 45 | 46 | ```shell 47 | mkdir ~/.pip 48 | cd ~/.pip 49 | ``` 50 | 51 | 这里推荐编辑器使用传说中的神器`vim` 52 | 53 | ```shell 54 | sudo apt install vim 55 | ``` 56 | 57 | 在.pip目录下创建一个`pip.conf`文件 58 | 59 | ```shell 60 | vim pip.conf 61 | ``` 62 | 63 | 填入以下内容保存即可 64 | 65 | ```shell 66 | [global] 67 | index-url = https://pypi.tuna.tsinghua.edu.cn/simple 68 | [install] 69 | trusted-host=mirrors.aliyun.com 70 | ``` 71 | 72 | ### 安装Angr 73 | 74 | 这里主要参照官方手册文档的教学 75 | 76 | 首先是安装必要的软件环境 77 | 78 | ```shell 79 | sudo apt-get install python3-dev libffi-dev build-essential virtualenvwrapper 80 | ``` 81 | 82 | 开始正式安装angr 83 | 84 | ```shell 85 | mkvirtualenv --python=$(which python3) angr && pip install angr 86 | ``` 87 | 88 | angr安装完毕 89 | 90 | angr官方推荐使用虚拟环境运行,每次需要调用具有angr环境时,只需要执行 91 | 92 | ```shell 93 | mkvirtualenv --python=$(which python3) angr 94 | ``` 95 | 96 | ### Tips: 97 | 98 | #### 1)如果遇上 mkvirtualenv: command not found 问题 99 | 100 | - 在终端命令行输入以下命令: 101 | 102 | - `sudo pip install virtualenv` 103 | - `sudo pip install virtualenvwrapper` 104 | 105 | - 没问题下一步 106 | 107 | - `cd ~/` 108 | 109 | - 找到virtualenvwrapper.sh所在的位置 110 | 111 | - ```bash 112 | syc@ubuntu:~/Desktop$ find / -name 'virtualenvwrapper.sh' 113 | /usr/share/virtualenvwrapper/virtualenvwrapper.sh 114 | ``` 115 | 116 | - 修改bashrc文件 117 | 118 | - `vim .bashrc` 119 | - 在文件末尾添加两行代码 120 | - `export WORKON_HOME=~/.environments` 121 | - `source /usr/share/virtualenvwrapper/virtualenvwrapper.sh` 122 | 123 | - 保存退出即可 124 | 125 | - 重新加载.bashrc文件 126 | 127 | - `source ~/.bashrc` 128 | 129 | ## 简介 130 | 131 | ### 符号执行 132 | 133 | ​ 符号执行就是在运行程序时,用符号来替代真实值。符号执行相较于真实值执行的优点在于,当使用真实值执行程序时,我们能够遍历的程序路径只有一条, 而使用符号进行执行时,由于符号是可变的,我们就可以利用这一特性,尽可能的将程序的每一条路径遍历,这样的话,必定存在至少一条能够输出正确结果的分支, 每一条分支的结果都可以表示为一个离散关系式,使用约束求解引擎即可分析出正确结果。 134 | 135 | ### Angr 136 | 137 | ​ Angr是加州大学圣芭芭拉分校基于Python设计的工具,它结合了静态分析技术与动态分析技术是当前符号化执行领域较为先进的工具,其挖掘漏洞效果好,在许多竞赛中表现卓越。Angr总的来说是一个多架构的二进制分析平台,具备对二进制文件的动态符号执行能力和多种静态分析能力。在逆向中,一般使用的其动态符号执行解出Flag,但其实Angr还在诸多领域存在应用,比如对程序脆弱性的分析中。 138 | 139 | ## 00_angr_find 140 | 141 | 首先检查一下文件: 142 | 143 | ```bash 144 | syc@ubuntu:~/Desktop/TEMP$ checksec '/home/syc/Desktop/TEMP/00_angr_find' 145 | [*] '/home/syc/Desktop/TEMP/00_angr_find' 146 | Arch: i386-32-little 147 | RELRO: Partial RELRO 148 | Stack: Canary found 149 | NX: NX enabled 150 | PIE: No PIE (0x8048000) 151 | ``` 152 | 153 | 用IDA打开查看一下伪C代码: 154 | 155 | ```c 156 | int __cdecl main(int argc, const char **argv, const char **envp) 157 | { 158 | signed int i; // [esp+1Ch] [ebp-1Ch] 159 | char s1[9]; // [esp+23h] [ebp-15h] 160 | unsigned int v6; // [esp+2Ch] [ebp-Ch] 161 | 162 | v6 = __readgsdword(0x14u); 163 | printf("Enter the password: "); 164 | __isoc99_scanf("%8s", s1); 165 | for ( i = 0; i <= 7; ++i ) 166 | s1[i] = complex_function(s1[i], i); 167 | if ( !strcmp(s1, "JACEJGCS") ) 168 | puts("Good Job."); 169 | else 170 | puts("Try again."); 171 | return 0; 172 | } 173 | ``` 174 | 175 | 这里可以发现关键的函数complex_function对我们输入的字符串处理后,与字符串"JACEJGCS"进行了比较,我们可以进入查看该函数: 176 | 177 | ```c 178 | int __cdecl complex_function(signed int a1, int a2) 179 | { 180 | if ( a1 <= 64 || a1 > 90 ) 181 | { 182 | puts("Try again."); 183 | exit(1); 184 | } 185 | return (3 * a2 + a1 - 65) % 26 + 65; 186 | } 187 | ``` 188 | 189 | 很标准的可以使用Angr进行解题的题型,也可以使用很常规的爆破手段去解题: 190 | 191 | ```python 192 | str1 = "JACEJGCS" 193 | flag = "" 194 | def complex_function(a1,a2): 195 | return (3 * a2 + a1 - 65) % 26 + 65 196 | if __name__ == "__main__": 197 | for i in range(len(str1)): 198 | for j in range(64,90): 199 | if ord(str1[i]) == complex_function(j,i): 200 | flag += chr(j) 201 | break 202 | print(flag) 203 | ``` 204 | 205 | 这题基础题主要是熟悉一下Angr的基本使用步骤,一般来说使用Angr的步骤可以分为: 206 | 207 | - 创建 project 208 | - 设置 state 209 | - 新建符号量 : BVS (bitvector symbolic ) 或 BVV (bitvector value) 210 | - 把符号量设置到内存或者其他地方 211 | - 设置 Simulation Managers , 进行路径探索的对象 212 | - 运行,探索满足路径需要的值 213 | - 约束求解,获取执行结果 214 | 215 | 先放一下解这题的脚本,然后逐以解释: 216 | 217 | ```python 218 | import angr 219 | import sys 220 | def Go(): 221 | path_to_binary = "./00_angr_find" 222 | project = angr.Project(path_to_binary, auto_load_libs=False) 223 | initial_state = project.factory.entry_state() 224 | simulation = project.factory.simgr(initial_state) 225 | 226 | print_good_address = 0x8048678 227 | simulation.explore(find=print_good_address) 228 | 229 | if simulation.found: 230 | solution_state = simulation.found[0] 231 | solution = solution_state.posix.dumps(sys.stdin.fileno()) 232 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 233 | else: 234 | raise Exception('Could not find the solution') 235 | if __name__ == "__main__": 236 | Go() 237 | ``` 238 | 239 | 查看运行一下看看: 240 | 241 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%80/1596338176%281%29.jpg) 242 | 243 | 得到flag:JXWVXRKX,和我们之前用传统方法得到答案一致,接下来开始分析一个简单的angr脚本构成 244 | 245 | ### 创建Project 246 | 247 | ```python 248 | path_to_binary = "./00_angr_find" 249 | project = angr.Project(path_to_binary, auto_load_libs=False) 250 | ``` 251 | 252 | 使用 angr的首要步骤就是创建Project加载二进制文件。angr的二进制装载组件是CLE,它负责装载二进制对象(以及它依赖的任何库)和把这个对象以易于操作的方式交给angr的其他组件。angr将这些包含在Project类中。一个Project类是代表了你的二进制文件的实体。你与angr的大部分操作都会经过它 253 | 254 | auto_load_libs 设置是否自动载入依赖的库,在基础题目中我们一般不需要分析引入的库文件,这里设置为否 255 | 256 | > - 如果`auto_load_libs`是`True`(默认值),真正的库函数会被执行。这可能正是也可能不是你想要的,取决于具体的函数。比如说一些libc的函数分析起来过于复杂并且很有可能引起path对其的尝试执行过程中的state数量的爆炸增长 257 | > - 如果`auto_load_libs`是`False`,且外部函数是无法找到的,并且Project会将它们引用到一个通用的叫做`ReturnUnconstrained`的`SimProcedure`上去,它就像它的名字所说的那样:它返回一个不受约束的值 258 | 259 | ### 设置 state 260 | 261 | ```python 262 | initial_state = project.factory.entry_state() 263 | ``` 264 | 265 | state代表程序的一个实例镜像,模拟执行某个时刻的状态,就类似于**快照**。保存运行状态的上下文信息,如内存/寄存器等,我们这里使用`project.factory.entry_state()`告诉符号执行引擎从程序的入口点开始符号执行,除了使用`.entry_state()` 创建 state 对象, 我们还可以根据需要使用其他构造函数创建 state 266 | 267 | ### 设置 Simulation Managers 268 | 269 | ```python 270 | simulation = project.factory.simgr(initial_state) 271 | ``` 272 | 273 | Project 对象仅表示程序一开始的样子,而在执行时,我们实际上是对SimState对象进行操作,它代表程序的一个实例镜像,模拟执行某个时刻的状态 274 | 275 | `SimState` 对象包含程序运行时信息,如内存/寄存器/文件系统数据等。SM(Simulation Managers)是angr中最重要的控制接口,它使你能够同时控制一组状态(state)的符号执行,应用搜索策略来探索程序的状态空间。 276 | 277 | ### 运行,探索满足路径需要的值 278 | 279 | ```python 280 | print_good_address = 0x8048678 281 | simulation.explore(find=print_good_address) 282 | ``` 283 | 284 | 符号执行最普遍的操作是找到能够到达某个地址的状态,同时丢弃其他不能到达这个地址的状态。SM为使用这种执行模式提供了`.explore()`方法 285 | 286 | 当使用`find`参数启动`.explore()`方法时,程序将会一直执行,直到发现了一个和`find`参数指定的条件相匹配的状态。`find`参数的内容可以是想要执行到的某个地址、或者想要执行到的地址列表、或者一个获取state作为参数并判断这个state是否满足某些条件的函数。当`active`stash中的任意状态和`find`中的条件匹配的时候,它们就会被放到`found stash`中,执行随即停止。之后你可以探索找到的状态,或者决定丢弃它,转而探索其它状态。 287 | 288 | 这里0x8048678的地址值是根据IDA打开后我们可以发现是保存导致打印“ Good Job”的块地址的变量 289 | 290 | ![](https://note-book.obs.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200802164840.png) 291 | 292 | ![](https://note-book.obs.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200802165123.png) 293 | 294 | 若能输出正确的字符串"Good Job"即代表我们的执行路径是正确的 295 | 296 | ### 获取执行结果 297 | 298 | ```python 299 | if simulation.found: 300 | solution_state = simulation.found[0] # 获取通过 explore 找到符合条件的状态 301 | solution = solution_state.posix.dumps(sys.stdin.fileno()) 302 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 303 | ``` 304 | 305 | 此时相关的状态已经保存在了`simgr`当中,我们可以通过`simgr.found`来访问所有符合条件的分支,这里我们为了解题,就选择第一个符合条件的分支即可 306 | 307 | 这里解释一下`sys.stdin.fileno()`,在UNIX中,按照惯例,三个文件描述符分别表示标准输入、标准输出和标准错误 308 | 309 | ```python 310 | >>> import sys 311 | >>> sys.stdin.fileno() 312 | 0 313 | >>> sys.stdout.fileno() 314 | 1 315 | >>> sys.stderr.fileno() 316 | 2 317 | ``` 318 | 319 | 所以一般也可以写成: 320 | 321 | ```python 322 | solution = solution_state.posix.dumps(0) 323 | ``` 324 | 325 | ## 01_angr_avoid 326 | 327 | 这题主要是引入了`.explore()`方法的另一个参数void,我们可以看看这个方法的原型 328 | 329 | ```python 330 | def explore(self, stash='active', n=None, find=None, avoid=None, find_stash='found', avoid_stash='avoid', cfg=None,um_find=1, **kwargs): 331 | ``` 332 | 333 | 和之前提到过的find参数类是,你还可以按照和`find`相同的格式设置另一个参数——`avoid`。当一个状态和`avoid`中的条件匹配时,它就会被放进`avoided stash`中,之后继续执行。 334 | 335 | 首先检查一下文件: 336 | 337 | ```bash 338 | syc@ubuntu:~/Desktop/TEMP$ checksec '/home/syc/Desktop/TEMP/01_angr_avoid' 339 | [*] '/home/syc/Desktop/TEMP/01_angr_avoid' 340 | Arch: i386-32-little 341 | RELRO: Partial RELRO 342 | Stack: Canary found 343 | NX: NX enabled 344 | PIE: No PIE (0x8048000) 345 | ``` 346 | 347 | 这题用IDA打开想F5的话,会提示main函数过大,虽然其实也不用F5,但是我还是想看看反汇编出来的main函数,这里我使用的是retdec(https://github.com/avast/retdec) 348 | 349 | 下载下来后,运行这段命令执行反汇编操作: 350 | 351 | ```powershell 352 | PS C:\Users\syc> python C:\Users\syc\Downloads\retdec-v4.0-windows-64b\retdec\bin\retdec-decompiler.py D:\build\AngrCTF_FITM\01_angr_avoid\01_angr_avoid 353 | ``` 354 | 355 | 运行需要一点时间,稍等一会儿就可以在根目录获得C语言的源代码 356 | 357 | ![](https://note-book.obs.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200802212543.png) 358 | 359 | 查看一下获得的源代码的main函数 360 | 361 | ![](https://note-book.obs.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200802212828.png) 362 | 363 | 这题就是为了angr而生的题目,我们只需要让执行流只进入maybe_good函数,而避免进入avoid_me函数即可,现在需要拿到这两个函数的地址 364 | 365 | ![](https://note-book.obs.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200802213325.png) 366 | 367 | ![](https://note-book.obs.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200802213419.png) 368 | 369 | 最后编写的EXP: 370 | 371 | ```python 372 | import angr 373 | import sys 374 | def Go(): 375 | path_to_binary = "./01_angr_avoid" 376 | project = angr.Project(path_to_binary, auto_load_libs=False) 377 | initial_state = project.factory.entry_state() 378 | simulation = project.factory.simgr(initial_state) 379 | 380 | avoid_me_address = 0x080485A8 381 | maybe_good_address = 0x080485E0 382 | 383 | simulation.explore(find=maybe_good_address, avoid=avoid_me_address) 384 | 385 | if simulation.found: 386 | solution_state = simulation.found[0] 387 | solution = solution_state.posix.dumps(sys.stdin.fileno()) 388 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 389 | else: 390 | raise Exception('Could not find the solution') 391 | if __name__ == "__main__": 392 | Go() 393 | ``` 394 | 395 | 运行获得flag: 396 | 397 | ![](https://note-book.obs.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200802214124.png) 398 | 399 | ## 02_angr_find_condition 400 | 401 | 这一题和之前的题目其实一样的,只不过题目本意是教会我们如何根据程序本身的输出来告诉angr应避免或保留的内容。因为有时候打开二进制文件将看到有很多打印“ Good Job”的块,或“Try Again”的块。每次都记录下这些块的所有起始地址是一个麻烦的的问题,这时候我们可以直接根据打印到stdout的内容告诉angr保留或丢弃状态 402 | 403 | 先检查一下文件: 404 | 405 | ```bash 406 | syc@ubuntu:~/Desktop/TEMP$ checksec '/home/syc/Desktop/TEMP/02_angr_find_condition' 407 | [*] '/home/syc/Desktop/TEMP/02_angr_find_condition' 408 | Arch: i386-32-little 409 | RELRO: Partial RELRO 410 | Stack: Canary found 411 | NX: NX enabled 412 | PIE: No PIE (0x8048000) 413 | ``` 414 | 415 | 用ida打开一下查看一下main函数 416 | 417 | ```c 418 | int __cdecl main(int argc, const char **argv, const char **envp) 419 | { 420 | signed int i; // [esp+18h] [ebp-40h] 421 | signed int j; // [esp+1Ch] [ebp-3Ch] 422 | char s1[20]; // [esp+24h] [ebp-34h] 423 | char s2[4]; // [esp+38h] [ebp-20h] 424 | int v8; // [esp+3Ch] [ebp-1Ch] 425 | unsigned int v9; // [esp+4Ch] [ebp-Ch] 426 | 427 | v9 = __readgsdword(0x14u); 428 | for ( i = 0; i <= 19; ++i ) 429 | s2[i] = 0; 430 | *(_DWORD *)s2 = 1381128278; 431 | v8 = 1381320010; 432 | printf("Enter the password: "); 433 | __isoc99_scanf("%8s", s1); 434 | for ( j = 0; j <= 7; ++j ) 435 | s1[j] = complex_function(s1[j], j + 8); 436 | if ( !strcmp(s1, s2) ) 437 | puts("Good Job."); 438 | else 439 | puts("Try again."); 440 | return 0; 441 | } 442 | ``` 443 | 444 | ```c 445 | int __cdecl complex_function(signed int a1, int a2) 446 | { 447 | if ( a1 <= 64 || a1 > 90 ) 448 | { 449 | puts("Try again."); 450 | exit(1); 451 | } 452 | return (31 * a2 + a1 - 65) % 26 + 65; 453 | } 454 | ``` 455 | 456 | 几乎没变,用之前的脚本改一改也能跑出flag: 457 | 458 | ```python 459 | str1 = "VXRRJEUR" 460 | flag = "" 461 | def complex_function(a1,a2): 462 | return (31 * a2 + a1 - 65) % 26 + 65 463 | if __name__ == "__main__": 464 | for i in range(len(str1)): 465 | for j in range(64,90): 466 | if ord(str1[i]) == complex_function(j,i+8): 467 | print(i+8) 468 | flag += chr(j) 469 | break 470 | print(flag) 471 | ``` 472 | 473 | angr的exp: 474 | 475 | ```python 476 | import angr 477 | import sys 478 | def Go(): 479 | path_to_binary = "./02_angr_find_condition" 480 | project = angr.Project(path_to_binary, auto_load_libs=False) 481 | initial_state = project.factory.entry_state() 482 | simulation = project.factory.simgr(initial_state) 483 | 484 | def is_successful(state): 485 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 486 | if b'Good Job.' in stdout_output: 487 | return True 488 | else: 489 | return False 490 | 491 | def should_abort(state): 492 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 493 | if b'Try again.' in stdout_output: 494 | return True 495 | else: 496 | return False 497 | 498 | simulation.explore(find=is_successful, avoid=should_abort) 499 | 500 | if simulation.found: 501 | solution_state = simulation.found[0] 502 | solution = solution_state.posix.dumps(sys.stdin.fileno()) 503 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 504 | else: 505 | raise Exception('Could not find the solution') 506 | 507 | if __name__ == "__main__": 508 | Go() 509 | ``` 510 | 511 | 重点是分析一下引入的两个新函数,选择其中一个来说一说: 512 | 513 | ```python 514 | def is_successful(state): 515 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 516 | if b'Good Job.' in stdout_output: 517 | return True 518 | else: 519 | return False 520 | ``` 521 | 522 | 我们将打印到标准输出的内容放入`stdout_output`变量中。请注意,这不是字符串,而是字节对象,这意味着我们必须使用`b'Good Job.'`而不是仅`"Good Job."`来检查我们是否正确输出了“ Good Job” 523 | 524 | 引入一个函数来对状态进行检测是为了实现动态的选择想获取的state。回想一下之前我们的`simulation.explore`都是固定写死的具体地址,但我们引入一个函数就可以动态的进行分析获取state 525 | 526 | 运行一下获得答案: 527 | 528 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200804174207.png) 529 | 530 | ## 03_angr_simbolic_registers 531 | 532 | 这题主要是因为angr在处理复杂格式的字符串scanf()输入的时候不是很好,我们可以直接将符号之注入寄存器,也就是主要学会符号化寄存器 533 | 534 | 首先检查一下文件: 535 | 536 | ```bash 537 | syc@ubuntu:~/Desktop/TEMP$ checksec 03_angr_symbolic_registers 538 | [*] '/home/syc/Desktop/TEMP/03_angr_symbolic_registers' 539 | Arch: i386-32-little 540 | RELRO: Partial RELRO 541 | Stack: Canary found 542 | NX: NX enabled 543 | PIE: No PIE (0x8048000) 544 | ``` 545 | 546 | 拖入IDA查看一下程序逻辑: 547 | 548 | ```c 549 | int __cdecl main(int argc, const char **argv, const char **envp) 550 | { 551 | int v3; // ebx 552 | int v4; // eax 553 | int v5; // edx 554 | int v6; // ST1C_4 555 | unsigned int v7; // ST14_4 556 | unsigned int v9; // [esp+8h] [ebp-10h] 557 | unsigned int v10; // [esp+Ch] [ebp-Ch] 558 | 559 | printf("Enter the password: "); 560 | v4 = get_user_input(); 561 | v6 = v5; 562 | v7 = complex_function_1(v4); 563 | v9 = complex_function_2(v3); 564 | v10 = complex_function_3(v6); 565 | if ( v7 || v9 || v10 ) 566 | puts("Try again."); 567 | else 568 | puts("Good Job."); 569 | return 0; 570 | } 571 | ``` 572 | 573 | 关键的函数就是需要分析`get_user_input()`和`complex_function()` 574 | 575 | ```c 576 | int get_user_input() 577 | { 578 | int v1; // [esp+0h] [ebp-18h] 579 | int v2; // [esp+4h] [ebp-14h] 580 | int v3; // [esp+8h] [ebp-10h] 581 | unsigned int v4; // [esp+Ch] [ebp-Ch] 582 | 583 | v4 = __readgsdword(0x14u); 584 | __isoc99_scanf("%x %x %x", &v1, &v2, &v3); 585 | return v1; 586 | } 587 | ``` 588 | 589 | ```c 590 | unsigned int __cdecl complex_function_1(int a1) 591 | { 592 | return (((((((((((((((((((((a1 + 17062705) ^ 0xB168C552) + 647103529) ^ 0x9F14CFD7) - 548738866) ^ 0xF78063EF) 593 | - 1352480098) ^ 0x5D1F4C6) 594 | - 57802472) ^ 0xB6F70BF8) 595 | - 1347645151 596 | + 648671421) ^ 0x3D5082FE) 597 | - 9365053) ^ 0xD0150EAD) 598 | + 1067946459) ^ 0xE6E03877) 599 | - 359192087 600 | + 961945065) ^ 0xE1EECD69) 601 | - 1817072919) ^ 0x6B86ECF5) 602 | - 449212884) ^ 0x2012CCDB; 603 | } 604 | ``` 605 | 606 | 可以发现这次的输入是一个复杂的格式化字符串,`"%x %x %x"`意味着使用三个十六进制值作为输入,我们看一下汇编代码 607 | 608 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200804220541.png) 609 | 610 | 可以得知我们输入的三个值最后是分别赋值给了EAX,EBX,EDX寄存器,所以我们要控制输入只需要控制这三个寄存器的值就行 611 | 612 | 看一下最后的EXP,然后再逐步分析: 613 | 614 | ```python 615 | import angr 616 | import sys 617 | import claripy 618 | def Go(): 619 | path_to_binary = "./03_angr_symbolic_registers" 620 | project = angr.Project(path_to_binary, auto_load_libs=False) 621 | start_address = 0x08048980 622 | initial_state = project.factory.blank_state(addr=start_address) 623 | 624 | passwd_size_in_bits = 32 625 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 626 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 627 | passwd2 = claripy.BVS('passwd2', passwd_size_in_bits) 628 | 629 | initial_state.regs.eax = passwd0 630 | initial_state.regs.ebx = passwd1 631 | initial_state.regs.edx = passwd2 632 | 633 | simulation = project.factory.simgr(initial_state) 634 | 635 | def is_successful(state): 636 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 637 | if b'Good Job.\n' in stdout_output: 638 | return True 639 | else: 640 | return False 641 | 642 | def should_abort(state): 643 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 644 | if b'Try again.\n' in stdout_output: 645 | return True 646 | else: 647 | return False 648 | 649 | simulation.explore(find=is_successful, avoid=should_abort) 650 | 651 | if simulation.found: 652 | for i in simulation.found: 653 | solution_state = i 654 | solution0 = format(solution_state.solver.eval(passwd0), 'x') 655 | solution1 = format(solution_state.solver.eval(passwd1), 'x') 656 | solution2 = format(solution_state.solver.eval(passwd2), 'x') 657 | solution = solution0 + " " + solution1 + " " + solution2 658 | print("[+] Success! Solution is: {}".format(solution)) 659 | # print(simgr.found[0].posix.dumps(0)) 660 | else: 661 | raise Exception('Could not find the solution') 662 | 663 | if __name__ == "__main__": 664 | Go() 665 | ``` 666 | 667 | 运行一下获得结果: 668 | 669 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%80/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200804221823.png) 670 | 671 | 这次我们可以不用从main函数的开头开始,这里我们直接跳过`get_user_input()`函数,直接设置寄存器`eax, ebx, edx` 672 | 673 | ### states 674 | 675 | 从这题开始,我们可以更多的窥见states的功能,states这只是factory提供的多个构造函数中的一个,即 `AngrObjectFactory`,提供重要分析对象的接口 676 | 677 | #### 状态预设 678 | 679 | 除了使用`.entry_state()` 创建 state 对象, 我们还可以根据需要使用其他构造函数创建 state: 680 | 681 | | 名称 | 描述 | 682 | | :----------------: | :----------------------------------------------------------: | 683 | | `.entry_state()` | 构造一个已经准备好从函数入口点执行的状态 | 684 | | `.blank_state` | 构造一个“空状态”,它的大多数数据都是未初始化的。当使用未初始化的的数据时,一个不受约束的符号值将会被返回 | 685 | | `.call_state` | 构造一个已经准备好执行某个函数的状态 | 686 | | `.full_init_state` | 构造一个已经执行过所有与需要执行的初始化函数,并准备从函数入口点执行的状态。比如,共享库构造函数(constructor)或预初始化器。当这些执行完之后,程序将会跳到入口点 | 687 | 688 | 请注意,这次我们使用的是`blank_state()`方法,而不是`entry_state()`。通过传递`addr=start_address`,我们有效地告诉`blank_state()`在该特定地址创建一个新状态 689 | 690 | ```python 691 | start_address = 0x08048980 692 | initial_state = project.factory.blank_state(addr=start_address) 693 | ``` 694 | 695 | #### 位向量(bitvector) 696 | 697 | 更应该准确的说是符号位向量,符号位向量是angr用于将符号值注入程序的数据类型。这些将是angr将解决的方程式的“ x”,也就是约束求解时的自变量。可以通过 `BVV(value,size)` 和 `BVS( name, size)` 接口创建位向量,也可以用 FPV 和 FPS 来创建浮点值和符号 698 | 699 | 在这里我们使用claripy通过`BVS()`方法生成三个位向量。此方法有两个参数:第一个是angr用来引用位向量的名称,第二个是位向量本身的大小(以位为单位)。由于符号值存储在寄存器中,并且寄存器的长度为32位,因此位向量的大小将为32位 700 | 701 | ```python 702 | passwd_size_in_bits = 32 703 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 704 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 705 | passwd2 = claripy.BVS('passwd2', passwd_size_in_bits) 706 | ``` 707 | 708 | #### 访问寄存器 709 | 710 | `get_user_input()`对输入进行了解析并将其放入三个寄存器中,我们可以通过 `state.regs` 对象的属性访问以及修改寄存器的数据 711 | 712 | 是时候把我们之前创建的符号位向量(bitvectors)放入属于他们的地方:寄存器`EAX`,`EBX`和`EDX`。我们将修改`initial_state`之前创建的内容并更新寄存器的内容 713 | 714 | ```python 715 | initial_state.regs.eax = passwd0 716 | initial_state.regs.ebx = passwd1 717 | initial_state.regs.edx = passwd2 718 | ``` 719 | 720 | 现在我们必须定义`find`and `avoid`状态,我们将像以前一样进行操作: 721 | 722 | ```python 723 | def is_successful(state): 724 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 725 | if b'Good Job.\n' in stdout_output: 726 | return True 727 | else: 728 | return False 729 | 730 | def should_abort(state): 731 | stdout_output = state.posix.dumps(sys.stdout.fileno()) 732 | if b'Try again.\n' in stdout_output: 733 | return True 734 | else: 735 | return False 736 | 737 | simulation.explore(find=is_successful, avoid=should_abort) 738 | ``` 739 | 740 | #### 约束求解 741 | 742 | 可以通过使用`state.solver.eval(symbol)`对各个断言进行评测来求出一个合法的符号值(若有多个合法值,返回其中的一个),我们根据`eval()`之前注入的三个符号值调用求解器引擎的方法 743 | 744 | ```python 745 | solution0 = format(solution_state.solver.eval(passwd0), 'x') 746 | solution1 = format(solution_state.solver.eval(passwd1), 'x') 747 | solution2 = format(solution_state.solver.eval(passwd2), 'x') 748 | solution = solution0 + " " + solution1 + " " + solution2 749 | print("[+] Success! Solution is: {}".format(solution)) 750 | ``` 751 | 752 | 最后运行脚本即可获得答案 753 | 754 | ## 参考文献 755 | 756 | 【1】angr官方文档—— https://docs.angr.io/core-concepts 757 | 758 | 【2】angr 系列教程(一)核心概念及模块解读—— https://xz.aliyun.com/t/7117#toc-14 759 | 760 | 【3】Introduction to angr Part 0 —— https://blog.notso.pro/2019-03-20-angr-introduction-part0/ 761 | 762 | 【4】angr文档翻译 —— https://www.jianshu.com/p/3ecafe70157 -------------------------------------------------------------------------------- /笔记/01/图片/1596338176(1).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/1596338176(1).jpg -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200802165123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200802165123.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200802212543.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200802212543.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200802212828.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200802212828.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200802213325.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200802213325.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200802213419.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200802213419.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200802214124.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200802214124.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200804170205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200804170205.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200804171032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200804171032.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200804171308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200804171308.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200804171438.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200804171438.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200804174207.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200804174207.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200804220541.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200804220541.png -------------------------------------------------------------------------------- /笔记/01/图片/微信图片_20200804221823.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信图片_20200804221823.png -------------------------------------------------------------------------------- /笔记/01/图片/微信截图_20200802164840.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/01/图片/微信截图_20200802164840.png -------------------------------------------------------------------------------- /笔记/02/Angr_CTF从入门到精通(二).md: -------------------------------------------------------------------------------- 1 | # Angr_CTF从入门到精通(二) 2 | 3 | 承接上一篇的文章,我们学习了angr应用的大概基础框架和符号化寄存器,现在我们来继续学习angr的更多神奇用法 4 | 5 | ## 04_angr_symbolic_stack 6 | 7 | 上一题我们学习了符号化寄存器,这题主要是学习如何符号化栈上的值 8 | 9 | 首先检查一下文件: 10 | 11 | ```bash 12 | syc@ubuntu:~/Desktop/TEMP$ checksec '/home/syc/Desktop/TEMP/04_angr_symbolic_stack' 13 | [*] '/home/syc/Desktop/TEMP/04_angr_symbolic_stack' 14 | Arch: i386-32-little 15 | RELRO: Partial RELRO 16 | Stack: No canary found 17 | NX: NX enabled 18 | PIE: No PIE (0x8048000) 19 | ``` 20 | 21 | 然后拖进IDA查看一下程序 22 | 23 | ```c 24 | int __cdecl main(int argc, const char **argv, const char **envp) 25 | { 26 | printf("Enter the password: "); 27 | handle_user(); 28 | return 0; 29 | } 30 | ``` 31 | 32 | ```c 33 | int handle_user() 34 | { 35 | int result; // eax 36 | int v1; // [esp+8h] [ebp-10h] 37 | int v2; // [esp+Ch] [ebp-Ch] 38 | 39 | __isoc99_scanf("%u %u", &v2, &v1); 40 | v2 = complex_function0(v2); 41 | v1 = complex_function1(v1); 42 | if ( v2 == 1999643857 && v1 == -1136455217 ) 43 | result = puts("Good Job."); 44 | else 45 | result = puts("Try again."); 46 | return result; 47 | } 48 | ``` 49 | 50 | ```c 51 | int __cdecl complex_function0(int a1) 52 | { 53 | return a1 ^ 0x12A567E5; 54 | } 55 | ``` 56 | 57 | ```c 58 | int __cdecl complex_function1(int a1) 59 | { 60 | return a1 ^ 0x31BCB5D0; 61 | } 62 | ``` 63 | 64 | 这里我们具体看一下是如何传送参数的 65 | 66 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200805100657.png) 67 | 68 | 不难发现从之前的寄存器传参变成了利用栈空间传参,这时我们就需要学会对栈上的值进行符号化处理 69 | 70 | 先提供一下angr的EXP 71 | 72 | ```python 73 | import angr 74 | import sys 75 | import claripy 76 | def Go(): 77 | path_to_binary = "./04_angr_symbolic_stack" 78 | project = angr.Project(path_to_binary, auto_load_libs=False) 79 | start_address = 0x8048697 80 | initial_state = project.factory.blank_state(addr=start_address) 81 | 82 | initial_state.regs.ebp = initial_state.regs.esp 83 | 84 | passwd_size_in_bits = 32 85 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 86 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 87 | 88 | padding_length_in_bytes = 0x8 89 | initial_state.regs.esp -= padding_length_in_bytes 90 | 91 | initial_state.stack_push(passwd0) 92 | initial_state.stack_push(passwd1) 93 | 94 | simulation = project.factory.simgr(initial_state) 95 | 96 | def is_successful(state): 97 | stdout_output = state.posix.dumps(1) 98 | if b'Good Job.\n' in stdout_output: 99 | return True 100 | else: 101 | return False 102 | 103 | def should_abort(state): 104 | stdout_output = state.posix.dumps(1) 105 | if b'Try again.\n' in stdout_output: 106 | return True 107 | else: 108 | return False 109 | 110 | simulation.explore(find=is_successful, avoid=should_abort) 111 | 112 | if simulation.found: 113 | for i in simulation.found: 114 | solution_state = i 115 | solution0 = (solution_state.solver.eval(passwd0)) 116 | solution1 = (solution_state.solver.eval(passwd1)) 117 | print("[+] Success! Solution is: {0} {1}".format(solution0, solution1)) 118 | #print(solution0, solution1) 119 | else: 120 | raise Exception('Could not find the solution') 121 | 122 | if __name__ == "__main__": 123 | Go() 124 | ``` 125 | 126 | 运行一下查看结果: 127 | 128 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200805161930.png) 129 | 130 | 现在我们来逐步解析,我们需要确定angr应该从哪里开始,我们跳过`scanf()`了将从`0x8048697`处的指令开始 131 | 132 | ```python 133 | start_address = 0x8048697 134 | initial_state = project.factory.blank_state(addr=start_address) 135 | ``` 136 | 137 | 我们从之前知道要注入的两个值位于@ `[EBP - 0x10]`,`[EBP - 0xC]`因此我们需要在压入堆栈之前布置好堆栈指针,这里就需要学习一下Linux的栈,我们先将ESP指针恢复到和EBP指针一致,恢复栈帧初始状态,方便我们计算: 138 | 139 | ```python 140 | initial_state.regs.esp = initial_state.regs.ebp #ESP=EBP 141 | ``` 142 | 143 | 这里我们先整理一下我们所需要的参数在栈上的分布情况 144 | 145 | | Low Address | | 146 | | :--------------: | :--: | 147 | | [ EBP - 0x10H ] | S1 | 148 | | [ EBP - 0x0FH ] | S1 | 149 | | [ EBP - 0x0EH ] | S1 | 150 | | [ EBP - 0x0DH ] | S1 | 151 | | [ EBP - 0x0CH ] | S2 | 152 | | [ EBP - 0x0BH ] | S2 | 153 | | [ EBP - 0x0AH ] | S2 | 154 | | [ EBP - 0x09H ] | S2 | 155 | | **High Address** | | 156 | 157 | 因为是32位下的程序,s1和s2都是32bit大小,也就是4字节,故: 158 | 159 | - S1需要占用以下地址:`| 0x10 | 0x0F | 0x0E | 0x0D |` 160 | 161 | - S2需要占用以下地址:`| 0x0C | 0x0B | 0x0A | 0x09 |` 162 | 163 | 故我们需要先抬高栈,以便在将符号值压入堆栈之前提供填充,但是栈是从高地址到低地址增长的,所以我们真正需要的是ESP - 0x8 164 | 165 | ```python 166 | padding_length_in_bytes = 0x08 167 | initial_state.regs.esp -= padding_length_in_bytes 168 | ``` 169 | 170 | 现在是时候创建我们的符号位向量并将其压入堆栈了。请记住,程序需要两个无符号的整数值(`%u %u`格式字符串可以理解),因此符号位向量的大小将为32位,因为这是x86架构上无符号整数 171 | 172 | ```c 173 | passwd0 = claripy.BVS('passwd0', 32) #s1 174 | passwd1 = claripy.BVS('passwd1', 32) #s2 175 | 176 | initial_state.stack_push(passwd0) 177 | initial_state.stack_push(passwd1) 178 | ``` 179 | 180 | 之后,其余部分基本上与以前的脚本相同,我们只需要求解符号位向量并打印即可: 181 | 182 | ```python 183 | if simulation.found: 184 | for i in simulation.found: 185 | solution_state = i 186 | solution0 = (solution_state.solver.eval(passwd0)) 187 | solution1 = (solution_state.solver.eval(passwd1)) 188 | print("[+] Success! Solution is: {0} {1}".format(solution0, solution1)) 189 | #print(solution0, solution1) 190 | else: 191 | raise Exception('Could not find the solution') 192 | ``` 193 | 194 | ### eval 195 | 196 | - `solver.eval(expression)` 将会解出一个可行解 197 | - `solver.eval_one(expression)`将会给出一个表达式的可行解,若有多个可行解,则抛出异常。 198 | - `solver.eval_upto(expression, n)`将会给出最多n个可行解,如果不足n个就给出所有的可行解。 199 | - `solver.eval_exact(expression, n)`将会给出n个可行解,如果解的个数不等于n个,将会抛出异常。 200 | - `solver.min(expression)`将会给出最小可行解 201 | - `solver.max(expression)`将会给出最大可行解 202 | 203 | 另外还有还有`cast_to`可以接收一个参数来指定把结果映射到哪种数据类型。目前这个参数只能是`str`,它将会以字符串形式展示返回的结果 204 | 205 | ## 05_angr_symbolic_memory 206 | 207 | 如题目所言这题主要学会符号化内存 208 | 209 | 我们先检查一下文件: 210 | 211 | ```bash 212 | syc@ubuntu:~/Desktop/TEMP$ checksec 05_angr_symbolic_memory 213 | [*] '/home/syc/Desktop/TEMP/05_angr_symbolic_memory' 214 | Arch: i386-32-little 215 | RELRO: Partial RELRO 216 | Stack: No canary found 217 | NX: NX enabled 218 | PIE: No PIE (0x8048000) 219 | ``` 220 | 221 | 然后拖进IDA查看一下程序 222 | 223 | ```c 224 | int __cdecl main(int argc, const char **argv, const char **envp) 225 | { 226 | int i; // [esp+Ch] [ebp-Ch] 227 | 228 | memset(user_input, 0, 0x21u); 229 | printf("Enter the password: "); 230 | __isoc99_scanf("%8s %8s %8s %8s", user_input, &unk_A1BA1C8, &unk_A1BA1D0, &unk_A1BA1D8); 231 | for ( i = 0; i <= 31; ++i ) 232 | *(_BYTE *)(i + 0xA1BA1C0) = complex_function(*(char *)(i + 0xA1BA1C0), i);// user_input = 0xA1BA1C0 233 | if ( !strncmp(user_input, "NJPURZPCDYEAXCSJZJMPSOMBFDDLHBVN", 0x20u) )// 用来比较s1和s2字符串的前n个字符 234 | puts("Good Job."); 235 | else 236 | puts("Try again."); 237 | return 0; 238 | } 239 | ``` 240 | 241 | ```c 242 | int __cdecl complex_function(signed int a1, int a2) 243 | { 244 | if ( a1 <= 64 || a1 > 90 ) 245 | { 246 | puts("Try again."); 247 | exit(1); 248 | } 249 | return (9 * a2 + a1 - 65) % 26 + 65; 250 | } 251 | ``` 252 | 253 | 我们查看一下user_input的所处于的地址 254 | 255 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200806210927.png) 256 | 257 | 不难发现程序的逻辑结构就是: 258 | 259 | - 程序将四个8字节长的字符串作为输入 260 | - 字符串分别位于以下地址[0xA1BA1C0, 0xA1BA1C8, 0xA1BA1D0, 0xA1BA1D8] 261 | - 输入的字符串循环输入`complex_function()`函数进行变换 262 | - 循环变换后的字符串与 `"NJPURZPCDYEAXCSJZJMPSOMBFDDLHBVN"`比较前0x20个字符 263 | 264 | 好了,我们现在有足够的信息来开始编写EXP的工作,先放一下EXP: 265 | 266 | ```python 267 | import angr 268 | import sys 269 | import claripy 270 | def Go(): 271 | path_to_binary = "./05_angr_symbolic_memory" 272 | project = angr.Project(path_to_binary, auto_load_libs=False) 273 | start_address = 0x8048601 274 | initial_state = project.factory.blank_state(addr=start_address) 275 | 276 | passwd_size_in_bits = 64 277 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 278 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 279 | passwd2 = claripy.BVS('passwd2', passwd_size_in_bits) 280 | passwd3 = claripy.BVS('passwd3', passwd_size_in_bits) 281 | 282 | passwd0_address = 0xA1BA1C0 283 | #passwd1_address = 0xA1BA1C8 284 | #passwd2_address = 0xA1BA1D0 285 | #passwd3_address = 0xA1BA1D8 286 | initial_state.memory.store(passwd0_address, passwd0) 287 | initial_state.memory.store(passwd0_address + 0x8, passwd1) 288 | initial_state.memory.store(passwd0_address + 0x10, passwd2) 289 | initial_state.memory.store(passwd0_address + 0x18, passwd3) 290 | 291 | simulation = project.factory.simgr(initial_state) 292 | 293 | def is_successful(state): 294 | stdout_output = state.posix.dumps(1) 295 | if b'Good Job.\n' in stdout_output: 296 | return True 297 | else: 298 | return False 299 | 300 | def should_abort(state): 301 | stdout_output = state.posix.dumps(1) 302 | if b'Try again.\n' in stdout_output: 303 | return True 304 | else: 305 | return False 306 | 307 | simulation.explore(find=is_successful, avoid=should_abort) 308 | 309 | if simulation.found: 310 | for i in simulation.found: 311 | solution_state = i 312 | solution0 = solution_state.solver.eval(passwd0,cast_to=bytes) 313 | solution1 = solution_state.solver.eval(passwd1,cast_to=bytes) 314 | solution2 = solution_state.solver.eval(passwd2,cast_to=bytes) 315 | solution3 = solution_state.solver.eval(passwd3,cast_to=bytes) 316 | solution = solution0 + b" " + solution1 + b" " + solution2 + b" " + solution3 317 | print("[+] Success! Solution is: {}".format(solution.decode("utf-8"))) 318 | #print(solution0, solution1, solution2, solution3) 319 | else: 320 | raise Exception('Could not find the solution') 321 | 322 | if __name__ == "__main__": 323 | Go() 324 | ``` 325 | 326 | 运行一下查看一下结果: 327 | 328 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200806212602.png) 329 | 330 | 这次`start_addr`从`0x08048601`也就是跳过`scanf` 331 | 332 | ```python 333 | path_to_binary = "./05_angr_symbolic_memory" 334 | project = angr.Project(path_to_binary, auto_load_libs=False) 335 | start_address = 0x8048601 336 | initial_state = project.factory.blank_state(addr=start_address) 337 | ``` 338 | 339 | 因为这次输入的是四个8字节64比特大小的字符串,所以我们需要创建四个相同大小的符号位向量 340 | 341 | ```python 342 | passwd_size_in_bits = 64 343 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 344 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 345 | passwd2 = claripy.BVS('passwd2', passwd_size_in_bits) 346 | passwd3 = claripy.BVS('passwd3', passwd_size_in_bits) 347 | ``` 348 | 349 | ### state.memory 350 | 351 | 前面提到可以通过 `state.mem[index]` 访问内存,但对于一段连续内存的操作十分不方便。因此我们也可以使用 `state.memory` 的 `.load(addr, size) / .store(addr, val)` 接口读写内存, size 以 bytes 为单位 352 | 353 | 这些函数的原型: 354 | 355 | ```python 356 | def load(self, addr, size=None, condition=None, fallback=None, add_constraints=None, action=None, endness=None, 357 | inspect=True, disable_actions=False, ret_on_segv=False): 358 | """ 359 | Loads size bytes from dst. 360 | :param addr: The address to load from. #读取的地址 361 | :param size: The size (in bytes) of the load. #大小 362 | :param condition: A claripy expression representing a condition for a conditional load. 363 | :param fallback: A fallback value if the condition ends up being False. 364 | :param add_constraints: Add constraints resulting from the merge (default: True). 365 | :param action: A SimActionData to fill out with the constraints. 366 | :param endness: The endness to load with. #端序 367 | ``` 368 | 369 | ```python 370 | def store(self, addr, data, size=None, condition=None, add_constraints=None, endness=None, action=None, 371 | inspect=True, priv=None, disable_actions=False): 372 | """ 373 | Stores content into memory. 374 | :param addr: A claripy expression representing the address to store at. #内存地址 375 | :param data: The data to store (claripy expression or something convertable to a claripy expression).#写入的数据 376 | :param size: A claripy expression representing the size of the data to store. #大小 377 | ... 378 | ``` 379 | 380 | 然后就可以开始符号化内存,因为这四个地址均是连续的地址,我们只需要连续加上0x8即可 381 | 382 | ```python 383 | passwd0_address = 0xA1BA1C0 384 | #passwd1_address = 0xA1BA1C8 385 | #passwd2_address = 0xA1BA1D0 386 | #passwd3_address = 0xA1BA1D8 387 | initial_state.memory.store(passwd0_address, passwd0) 388 | initial_state.memory.store(passwd0_address + 0x8, passwd1) 389 | initial_state.memory.store(passwd0_address + 0x10, passwd2) 390 | initial_state.memory.store(passwd0_address + 0x18, passwd3) 391 | ``` 392 | 393 | 然后我们将模拟管理器重置为我们之前设置好的状态 394 | 395 | ```python 396 | simulation = project.factory.simgr(initial_state) 397 | ``` 398 | 399 | 接下来就和之前一直做的一样,设定好模拟器的路径选择,执行并提取查看结果即可 400 | 401 | ## 06_angr_symbolic_dynamic_memory 402 | 403 | 这题主要是学会符号化动态内存,这个题与上题没有太大区别,除了字符串的内存是通过堆`malloc()`而不是堆栈分配的 404 | 405 | 我们先检查一下文件: 406 | 407 | ```bash 408 | Arch: i386-32-little 409 | RELRO: Partial RELRO 410 | Stack: No canary found 411 | NX: NX enabled 412 | PIE: No PIE (0x8048000) 413 | ``` 414 | 415 | 然后拖进IDA查看一下程序 416 | 417 | ```C 418 | int __cdecl main(int argc, const char **argv, const char **envp) 419 | { 420 | char *v3; // ebx 421 | char *v4; // ebx 422 | int v6; // [esp-10h] [ebp-1Ch] 423 | signed int i; // [esp+0h] [ebp-Ch] 424 | 425 | buffer0 = (char *)malloc(9u); 426 | buffer1 = (char *)malloc(9u); 427 | memset(buffer0, 0, 9u); 428 | memset(buffer1, 0, 9u); 429 | printf("Enter the password: "); 430 | __isoc99_scanf("%8s %8s", buffer0, buffer1, v6); 431 | for ( i = 0; i <= 7; ++i ) 432 | { 433 | v3 = &buffer0[i]; 434 | *v3 = complex_function(buffer0[i], i); 435 | v4 = &buffer1[i]; 436 | *v4 = complex_function(buffer1[i], i + 32); 437 | } 438 | if ( !strncmp(buffer0, "UODXLZBI", 8u) && !strncmp(buffer1, "UAORRAYF", 8u) ) 439 | puts("Good Job."); 440 | else 441 | puts("Try again."); 442 | free(buffer0); 443 | free(buffer1); 444 | return 0; 445 | } 446 | ``` 447 | 448 | ```C 449 | int __cdecl complex_function(signed int a1, int a2) 450 | { 451 | if ( a1 <= 64 || a1 > 90 ) 452 | { 453 | puts("Try again."); 454 | exit(1); 455 | } 456 | return (13 * a2 + a1 - 65) % 26 + 65; 457 | } 458 | ``` 459 | 460 | 我们可以总结出这个程序的逻辑: 461 | 462 | - 程序使用`malloc()`函数分配出了两个大小为9字节的缓冲区,并将其初始化为0 463 | - 然后将两个字符串以`scanf("%8s %8s")`作为格式化字符串分别输入进缓冲区内 464 | - 然后利用`complex_function()`函数分别对两个字符串进行变换 465 | - 然后将变换后的字符串分别与**"UODXLZBI"**和**"UAORRAYF"**进行比较 466 | 467 | 先放一下EXP: 468 | 469 | ```python 470 | import angr 471 | import sys 472 | import claripy 473 | def Go(): 474 | path_to_binary = "./06_angr_symbolic_dynamic_memory" 475 | project = angr.Project(path_to_binary, auto_load_libs=False) 476 | start_address = 0x8048699 477 | initial_state = project.factory.blank_state(addr=start_address) 478 | 479 | passwd_size_in_bits = 64 480 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 481 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 482 | 483 | fake_heap_address0 = 0xffffc93c 484 | pointer_to_malloc_memory_address0 = 0xabcc8a4 485 | fake_heap_address1 = 0xffffc94c 486 | pointer_to_malloc_memory_address1 = 0xabcc8ac 487 | initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness) 488 | initial_state.memory.store(pointer_to_malloc_memory_address1, fake_heap_address1, endness=project.arch.memory_endness) 489 | 490 | initial_state.memory.store(fake_heap_address0, passwd0) 491 | initial_state.memory.store(fake_heap_address1, passwd1) 492 | 493 | simulation = project.factory.simgr(initial_state) 494 | 495 | def is_successful(state): 496 | stdout_output = state.posix.dumps(1) 497 | if b'Good Job.\n' in stdout_output: 498 | return True 499 | else: 500 | return False 501 | 502 | def should_abort(state): 503 | stdout_output = state.posix.dumps(1) 504 | if b'Try again.\n' in stdout_output: 505 | return True 506 | else: 507 | return False 508 | 509 | simulation.explore(find=is_successful, avoid=should_abort) 510 | 511 | if simulation.found: 512 | for i in simulation.found: 513 | solution_state = i 514 | solution0 = solution_state.solver.eval(passwd0, cast_to=bytes) 515 | solution1 = solution_state.solver.eval(passwd1, cast_to=bytes) 516 | print("[+] Success! Solution is: {0} {1}".format(solution0.decode('utf-8'), solution1.decode('utf-8'))) 517 | #print(solution0, solution1) 518 | else: 519 | raise Exception('Could not find the solution') 520 | 521 | if __name__ == "__main__": 522 | Go() 523 | ``` 524 | 525 | 运行一下: 526 | 527 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200807215247.png) 528 | 529 | 我们从`0x8048699`指向的地址开始,该地址指向`MOV DWORD [EBP - 0xC], 0x0`调用之后的指令`scanf()`。我们基本上会跳过所有`malloc()`,因为我们稍后将在脚本中处理它们 530 | 531 | ```python 532 | path_to_binary = "./06_angr_symbolic_dynamic_memory" 533 | project = angr.Project(path_to_binary, auto_load_libs=False) 534 | start_address = 0x8048699 535 | initial_state = project.factory.blank_state(addr=start_address) 536 | ``` 537 | 538 | 然后因为缓冲区的大小是8字节,故换算成比特即为64比特的大小,最后我们初始化两个大小为64位的符号位向量 539 | 540 | ```python 541 | passwd_size_in_bits = 64 542 | passwd0 = claripy.BVS('passwd0', passwd_size_in_bits) 543 | passwd1 = claripy.BVS('passwd1', passwd_size_in_bits) 544 | ``` 545 | 546 | 我们注意到这次scanf函数的输入使用的是两个存储区 547 | 548 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200807213352.png) 549 | 550 | 我们可以看到`malloc()`分配了两个缓冲区,因为`maclloc()`函数只有一个参数,通过`push 9`不难推测出两个缓冲区大小为9字节(IDA也能直接看到的说),并返回缓冲区地址到EAX寄存器中 551 | 552 | 实际上,可以看到在两次调用之后,根据`mov ds:buffer0, eax`和`mov ds:buffer1, eax`得知开辟后的缓冲区被复制到标识为`buffer0`和`buffer1`的两个存储区中 553 | 554 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200807213941.png) 555 | 556 | 且根据IDA可以得知`buffer0`的地址为**0xABCC8A4**,`buffer1`的地址为**0xABCC8AC** 557 | 558 | 回到我们最开始认识angr的时候,我们知道angr并没有真正“运行”二进制文件(至少到目前为止),它只是在模拟运行状态,因此它实际上不需要将内存分配到堆中,实际上可以伪造任何地址。我们所做的是我们在堆栈选择两个地址存放我们的缓冲区地址。之后我们告诉angr,将两个fake address分别保存到 `buffer0`,`buffer1` ,因为程序实际执行的时候就会把 **malloc**返回的地址保存到这里。最后我们把符号位向量保存到 伪造的地址里。 559 | 560 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200807222523.png) 561 | 562 | 这里我们选择**0xffffc93c**和**0xffffc94c**即可,然后将我们分别的缓冲区地址放入这两个地址中,参数 `endness` 用于设置端序,angr默认为大端序,总共可选的值如下: 563 | 564 | ``` 565 | LE – 小端序(little endian, least significant byte is stored at lowest address) 566 | BE – 大端序(big endian, most significant byte is stored at lowest address) 567 | ME – 中间序(Middle-endian. Yep.) 568 | ``` 569 | 570 | 这里我们直接设置为与项目的程序相同即可 571 | 572 | ```python 573 | fake_heap_address0 = 0xffffc93c 574 | pointer_to_malloc_memory_address0 = 0xabcc8a4 575 | fake_heap_address1 = 0xffffc94c 576 | pointer_to_malloc_memory_address1 = 0xabcc8ac 577 | initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness) 578 | initial_state.memory.store(pointer_to_malloc_memory_address1, fake_heap_address1, endness=project.arch.memory_endness) 579 | ``` 580 | 581 | 这里总的逻辑是这样的,之前是buffer指向的是malloc分配好的内存地址,string存在这里。现在是buffer指向的是我们伪造的地址,符号位向量存在这里 582 | 583 | ``` 584 | BEFORE: 585 | buffer0 -> malloc()ed address 0 -> string 0 586 | buffer1 -> malloc()ed address 1 -> string 1 587 | 588 | AFTER: 589 | buffer0 -> fake address 0 -> symbolic bitvector 0 590 | buffer1 -> fake address 1 -> symbolic bitvector 1 591 | ``` 592 | 593 | 其余的部分和之前的情况差不多,不再赘述 594 | 595 | ## 07_angr_symbolic_file 596 | 597 | 这题主要学习如何符号化一个文件里面的内容 598 | 599 | 先检查一下文件: 600 | 601 | ```bash 602 | syc@ubuntu:~/Desktop/TEMP$ checksec 07_angr_symbolic_file 603 | [*] '/home/syc/Desktop/TEMP/07_angr_symbolic_file' 604 | Arch: i386-32-little 605 | RELRO: Partial RELRO 606 | Stack: Canary found 607 | NX: NX enabled 608 | PIE: No PIE (0x8048000) 609 | ``` 610 | 611 | 然后拖进IDA查看一下程序 612 | 613 | ```c 614 | int __cdecl __noreturn main(int argc, const char **argv, const char **envp) 615 | { 616 | int i; // [esp+Ch] [ebp-Ch] 617 | 618 | memset(buffer, 0, 0x40u); 619 | printf("Enter the password: "); 620 | __isoc99_scanf("%64s", buffer); 621 | ignore_me((int)buffer, 0x40u); 622 | memset(buffer, 0, 0x40u); 623 | fp = fopen("OJKSQYDP.txt", "rb"); 624 | fread(buffer, 1u, 0x40u, fp); 625 | fclose(fp); 626 | unlink("OJKSQYDP.txt"); 627 | for ( i = 0; i <= 7; ++i ) 628 | *(_BYTE *)(i + 0x804A0A0) = complex_function(*(char *)(i + 0x804A0A0), i); 629 | if ( strncmp(buffer, "AQWLCTXB", 9u) ) 630 | { 631 | puts("Try again."); 632 | exit(1); 633 | } 634 | puts("Good Job."); 635 | exit(0); 636 | } 637 | ``` 638 | 639 | ```c 640 | unsigned int __cdecl ignore_me(int a1, size_t n) 641 | { 642 | void *v2; // esp 643 | int v4; // [esp+0h] [ebp-28h] 644 | void *ptr; // [esp+Ch] [ebp-1Ch] 645 | size_t v6; // [esp+10h] [ebp-18h] 646 | void *s; // [esp+14h] [ebp-14h] 647 | FILE *stream; // [esp+18h] [ebp-10h] 648 | unsigned int v9; // [esp+1Ch] [ebp-Ch] 649 | 650 | ptr = (void *)a1; 651 | v9 = __readgsdword(0x14u); 652 | v6 = n - 1; 653 | v2 = alloca(16 * ((n + 15) / 0x10)); 654 | s = &v4; 655 | memset(&v4, 0, n); 656 | unlink("OJKSQYDP.txt"); 657 | stream = fopen("OJKSQYDP.txt", "a+b"); 658 | fwrite(ptr, 1u, n, stream); 659 | fseek(stream, 0, 0); 660 | __isoc99_fscanf(stream, "%64s", s); 661 | fseek(stream, 0, 0); 662 | fwrite(s, 1u, n, stream); 663 | fclose(stream); 664 | return __readgsdword(0x14u) ^ v9; 665 | } 666 | ``` 667 | 668 | ```c 669 | int __cdecl complex_function(signed int a1, int a2) 670 | { 671 | if ( a1 <= 64 || a1 > 90 ) 672 | { 673 | puts("Try again."); 674 | exit(1); 675 | } 676 | return (17 * a2 + a1 - 65) % 26 + 65; 677 | } 678 | ``` 679 | 680 | 我们可以得知程序使用fread函数从文件中加载密码,如果密码正确,则会打印“ Good Job”。`ignore_me `主要是把第一个读取的内容存入`OJKSQYDP.txt`, 不用我们自己创建文件 ,然后从文件`OJKSQYDP.txt`读取数据存入buff 681 | 682 | 老样子先上EXP在逐一分析: 683 | 684 | ```python 685 | import angr 686 | import sys 687 | import claripy 688 | def Go(): 689 | path_to_binary = "./07_angr_symbolic_file" 690 | project = angr.Project(path_to_binary, auto_load_libs=False) 691 | start_address = 0x80488EA 692 | initial_state = project.factory.blank_state(addr=start_address) 693 | 694 | filename = 'OJKSQYDP.txt' 695 | symbolic_file_size_bytes = 64 696 | passwd0 = claripy.BVS('password', symbolic_file_size_bytes * 8) 697 | passwd_file = angr.storage.SimFile(filename, content=passwd0, size=symbolic_file_size_bytes) 698 | 699 | initial_state.fs.insert(filename, passwd_file) 700 | 701 | simulation = project.factory.simgr(initial_state) 702 | 703 | def is_successful(state): 704 | stdout_output = state.posix.dumps(1) 705 | if b'Good Job.\n' in stdout_output: 706 | return True 707 | else: 708 | return False 709 | 710 | def should_abort(state): 711 | stdout_output = state.posix.dumps(1) 712 | if b'Try again.\n' in stdout_output: 713 | return True 714 | else: 715 | return False 716 | 717 | simulation.explore(find=is_successful, avoid=should_abort) 718 | 719 | if simulation.found: 720 | for i in simulation.found: 721 | solution_state = i 722 | solution0 = solution_state.solver.eval(passwd0, cast_to=bytes) 723 | print("[+] Success! Solution is: {0}".format(solution0.decode('utf-8'))) 724 | #print(solution0) 725 | else: 726 | raise Exception('Could not find the solution') 727 | 728 | if __name__ == "__main__": 729 | Go() 730 | ``` 731 | 732 | 运行一下查看结果: 733 | 734 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%BA%8C/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200808171501.png) 735 | 736 | 我们可以知道这个程序: 737 | 738 | - 读取一个名叫'OJKSQYDP.txt'的文件作为密码 739 | - 我们需要使用Angr模拟一个文件系统,其中该文件被我们自己的模拟文件所替代 740 | - 然后将该文件进行符号化处理 741 | 742 | #### 状态插件(state plugin) 743 | 744 | 除了刚刚讨论过的选项集,所有存储在SimState中的东西实际上都存储在附加在state上的“插件”中。到目前为止我们讨论的几乎所有state的属性都是一个插件——`memory`、`registers`、`mem`、`regs`、`solver`等等。这种设计带来了代码的模块化和能够便捷地为模拟状态的其他方面实现新的数据存储,或者提供插件的替代实现能力。 745 | 746 | 比如说,通常`memory`插件模拟一个平坦地址空间,但是在分析中可以选择开启“抽象内存”插件来支持`state.memory`,“抽象内存”使用新的数据类型表示地址,以模拟浮动的独立内存空间映射。反过来,插件可以减少代码的复杂性:`state.memory`和`state.registers`实际上是同一个插件的不同实例,因为寄存器也是用一块地址空间模拟的。 747 | 748 | 能够控制仿真程序所看到的环境,包括如何从环境中引入符号数据,这一点非常重要!angr具有一系列可靠的抽象概念,可帮助您设置所需的环境。 749 | 750 | ### 仿真文件系统-The Emulated Filesystem 751 | 752 | 这题的关键是利用了angr强大的仿真文件系统。在angr中与文件系统,套接字,管道或终端的任何交互的根源都是SimFile对象。SimFile是一种存储抽象,它定义符号或其他形式的字节序列。您可以从某个位置读取文件,可以在某个位置写入文件,可以询问文件中当前存储了多少字节,还可以具体化文件,并为其生成测试用例。 753 | 754 | 简单来说利用`SimFile`形成符号化的文件的格式: 755 | 756 | ```python 757 | simgr_file = angr.storage.SimFile(filename, content=xxxxxx, size=file_size) 758 | ``` 759 | 760 | 然后需要传给state的初始化过程来影响对文件系统的使用。我们可以利用`fs`选项以文件名的字典来预配置SimFile对象,也可以`fs.insert`是将文件插入到文件系统中,需要文件名与符号化的文件 761 | 762 | ``` 763 | initial_state.fs.insert(filename, simgr_file) 764 | ``` 765 | 766 | 我们从IDA可以知道输入的是格式化字符串`%64s`也就是64个字符,一个字符是8比特,故最后是512比特 767 | 768 | 最后的得到这一部分代码: 769 | 770 | ```python 771 | filename = 'OJKSQYDP.txt' 772 | symbolic_file_size_bytes = 64 773 | passwd0 = claripy.BVS('password', symbolic_file_size_bytes * 8) 774 | passwd_file = angr.storage.SimFile(filename, content=passwd0, size=symbolic_file_size_bytes) 775 | 776 | initial_state.fs.insert(filename, passwd_file) 777 | 778 | simulation = project.factory.simgr(initial_state) 779 | ``` 780 | 781 | 其它的与之前的题目一致,不再赘述 782 | 783 | ## 参考文献 784 | 785 | 【1】angr官方文档—— https://docs.angr.io/core-concepts 786 | 787 | 【2】angr 系列教程(一)核心概念及模块解读—— https://xz.aliyun.com/t/7117#toc-14 788 | 789 | 【3】Introduction to angr Part 2 —— https://blog.notso.pro/2019-03-26-angr-introduction-part2/ 790 | 791 | 【4】Introduction to angr Part 3 —— https://blog.notso.pro/2019-04-10-angr-introduction-part3/ 792 | 793 | 【4】angr文档翻译 —— https://www.jianshu.com/p/3ecafe701578 794 | -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200805100657.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200805100657.png -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200805161930.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200805161930.png -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200806210927.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200806210927.png -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200806212602.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200806212602.png -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200807213352.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200807213352.png -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200807213941.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200807213941.png -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200807215247.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200807215247.png -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200807222523.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200807222523.png -------------------------------------------------------------------------------- /笔记/02/图片/微信图片_20200808171501.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/02/图片/微信图片_20200808171501.png -------------------------------------------------------------------------------- /笔记/03/Angr_CTF从入门到精通(三).md: -------------------------------------------------------------------------------- 1 | # Angr_CTF从入门到精通(三) 2 | 3 | PS:因为最近开始考试,耽误了很多时间,重新开始恢复 4 | 5 | 在之前的学习中我们学会了利用angr符号化寄存器、栈上的值、内存、malloc开辟的动态内存和文件系统,感受到了angr强大的仿真系统,在CTF中题目的简单利用,接下来我们要学习angr的更多的高级用法 6 | 7 | > 由于angr的api一直有一些变化,网上的很多脚本需要修改才能运行 8 | 9 | ## 08_angr_constraints 10 | 11 | 该题主要学习通过添加约束条件来解决路径爆炸问题 12 | 13 | 首先检查一下该程序: 14 | 15 | ```bash 16 | syc@ubuntu:~/Desktop/TEMP$ checksec 08_angr_constraints 17 | [*] '/home/syc/Desktop/TEMP/08_angr_constraints' 18 | Arch: i386-32-little 19 | RELRO: Partial RELRO 20 | Stack: No canary found 21 | NX: NX enabled 22 | PIE: No PIE (0x8048000) 23 | ``` 24 | 25 | 然后进入IDA查看该: 26 | 27 | ```c 28 | int __cdecl main(int argc, const char **argv, const char **envp) 29 | { 30 | signed int i; // [esp+Ch] [ebp-Ch] 31 | 32 | password = 1146115393; 33 | dword_804A044 = 1380994638; 34 | dword_804A048 = 1381647695; 35 | dword_804A04C = 1112233802; 36 | memset(&buffer, 0, 0x11u); 37 | printf("Enter the password: "); 38 | __isoc99_scanf("%16s", &buffer); 39 | for ( i = 0; i <= 15; ++i ) 40 | *(_BYTE *)(i + 0x804A050) = complex_function(*(char *)(i + 0x804A050), 15 - i); 41 | if ( check_equals_AUPDNNPROEZRJWKB(&buffer, 16) ) 42 | puts("Good Job."); 43 | else 44 | puts("Try again."); 45 | return 0; 46 | } 47 | ``` 48 | 49 | ```c 50 | int __cdecl complex_function(signed int a1, int a2) 51 | { 52 | if ( a1 <= 64 || a1 > 90 ) 53 | { 54 | puts("Try again."); 55 | exit(1); 56 | } 57 | return (a1 - 65 + 53 * a2) % 26 + 65; 58 | } 59 | ``` 60 | 61 | ```c 62 | _BOOL4 __cdecl check_equals_AUPDNNPROEZRJWKB(int a1, unsigned int a2) 63 | { 64 | int v3; // [esp+8h] [ebp-8h] 65 | unsigned int i; // [esp+Ch] [ebp-4h] 66 | 67 | v3 = 0; 68 | for ( i = 0; i < a2; ++i ) 69 | { 70 | if ( *(_BYTE *)(i + a1) == *(_BYTE *)(i + 0x804A040) ) 71 | ++v3; 72 | } 73 | return v3 == a2; 74 | } 75 | ``` 76 | 77 | ### 路径爆炸 78 | 79 | 通过我们之前的学习体验感觉到angr这么强大的应用怎么没有在实际的测试生产中大规模应用,这是因为给符号执行技术在复杂程序的测试案例生成的应用中造成阻碍的两个大问题:一个是约束求解问题,另一个就是路径爆炸问题 80 | 81 | 所谓符号执行就是把程序中的变量符号化去模拟程序运行,搜集路径约束条件并使用约束求解器对其进行求解后得到结果。当一个程序存在循环结构时,即使逻辑十分简单也可能会产生规模十分巨大的执行路径。在符号执行的过程中,每个分支点都会产生两个实例,当程序中存在循环结构展开时,可能会导致程序分支路径数呈指数级增长,即路径爆炸问题。故我们需要提供更多的约束条件控制路径爆照问题 82 | 83 | 回到这个题目本身 84 | 85 | ```c 86 | _BOOL4 __cdecl check_equals_AUPDNNPROEZRJWKB(int a1, unsigned int a2) 87 | { 88 | int v3; // [esp+8h] [ebp-8h] 89 | unsigned int i; // [esp+Ch] [ebp-4h] 90 | 91 | v3 = 0; 92 | for ( i = 0; i < a2; ++i ) 93 | { 94 | if ( *(_BYTE *)(i + a1) == *(_BYTE *)(i + 0x804A040) ) 95 | ++v3; 96 | } 97 | return v3 == a2; 98 | } 99 | ``` 100 | 101 | `check_equals_AUPDNNPROEZRJWKB()`函数就是一个字符一个字符的比较,就会产生路径爆炸问题,原始也是每次调用循环中的if语句(16次)时,计算机都需要产生判断分支,从而导致2 ^ 16 = 65,536分支,这将花费很长时间来测试并获得我们的答案。我们解决这个问题的答案,直接用约束条件取代这个判断函数,用字符串直接比较约束,从而避免因为循环和判断语句逐一字符比较而产生分支引起路径爆炸问题 102 | 103 | ### 约束求解 104 | 105 | 在angr中提供了可以用加入一个约束条件到一个state中的方法(`state.solver.add`),将每一个符号化的布尔值作为一个关于符号变量合法性的断言。之后可以通过使用`state.solver.eval(symbol)`对各个断言进行评测来求出一个合法的符号值(若有多个合法值,返回其中的一个)。简单来说就是通过 `.add` 对 state 对象添加约束,并使用 `.eval` 接口求解,得到符号变量的可行解 106 | 107 | 例如: 108 | 109 | ```python 110 | # fresh state 111 | >>> state = proj.factory.entry_state() 112 | >>> state.solver.add(x - y >= 4) 113 | >>> state.solver.add(y > 0) 114 | >>> state.solver.eval(x) 115 | 5 116 | >>> state.solver.eval(y) 117 | 1 118 | >>> state.solver.eval(x + y) 119 | 6 120 | ``` 121 | 122 | 总而言之先放EXP,再逐步分析: 123 | 124 | ```python 125 | import angr 126 | import sys 127 | import claripy 128 | def Go(): 129 | path_to_binary = "./08_angr_constraints" 130 | project = angr.Project(path_to_binary, auto_load_libs=False) 131 | 132 | start_address = 0x8048625 133 | buff_addr = 0x0804A050 134 | address_to_check_constraint = 0x08048565 135 | 136 | initial_state = project.factory.blank_state(addr=start_address) 137 | 138 | char_size_in_bits = 8 139 | passwd_len = 16 140 | passwd0 = claripy.BVS('passwd0', char_size_in_bits*passwd_len) 141 | initial_state.memory.store(buff_addr, passwd0) 142 | 143 | simulation = project.factory.simgr(initial_state) 144 | simulation.explore(find=address_to_check_constraint) 145 | 146 | if simulation.found: 147 | solution_state = simulation.found[0] 148 | constrained_parameter_address = buff_addr 149 | constrained_parameter_size_bytes = 16 150 | constrained_parameter_bitvector = solution_state.memory.load( 151 | constrained_parameter_address, 152 | constrained_parameter_size_bytes 153 | ) 154 | constrained_parameter_desired_value = 'AUPDNNPROEZRJWKB' 155 | solution_state.solver.add(constrained_parameter_bitvector == constrained_parameter_desired_value) 156 | solution0 = solution_state.solver.eval(passwd0,cast_to=bytes) 157 | print("[+] Success! Solution is: {0}".format(solution0)) 158 | else: 159 | raise Exception('Could not find the solution') 160 | 161 | if __name__ == "__main__": 162 | Go() 163 | ``` 164 | 165 | 运行一下测试: 166 | 167 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%89/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200810161941.png) 168 | 169 | ```assembly 170 | .text:080485C4 mov ds:password, 'DPUA' 171 | .text:080485CE mov ds:dword_804A044, 'RPNN' 172 | .text:080485D8 mov ds:dword_804A048, 'RZEO' 173 | .text:080485E2 mov ds:dword_804A04C, 'BKWJ' 174 | ``` 175 | 176 | 通过这里不难的得出需要比较的字符串是**AUPDNNPROEZRJWKB**(虽然从函数名也能看出来,但是还是从汇编解释一下为好) 177 | 178 | 首先总结一下我们的思路: 179 | 180 | - 用户输入的字符串存储在buffer,buffer的地址为:0x804A050 181 | - 比较函数`check_equals_AUPDNNPROEZRJWKB`的地址为:0x08048565 182 | - 其实只要当程序运行到地址0x08048565时,处于buffer地址内的字符串等于AUPDNNPROEZRJWKB即可 183 | - 添加上述约束条件即可一步得出结果,而不用进入比较函数逐一字符比较而产生路径爆炸问题 184 | 185 | 故一开始先填入需要利用到的地址: 186 | 187 | ```python 188 | path_to_binary = "./08_angr_constraints" 189 | project = angr.Project(path_to_binary, auto_load_libs=False) 190 | start_address = 0x8048625 191 | buff_addr = 0x0804A050 192 | address_to_check_constraint = 0x08048565 193 | initial_state = project.factory.blank_state(addr=start_address) 194 | ``` 195 | 196 | 因为输入是`scanf("%16s", &buffer);`,如之前一样,不难得出我们需要构建的符号位向量的参数 197 | 198 | ```python 199 | char_size_in_bits = 8 200 | passwd_len = 16 201 | passwd0 = claripy.BVS('passwd0', char_size_in_bits*passwd_len) 202 | initial_state.memory.store(buff_addr, passwd0) 203 | ``` 204 | 205 | 然后初始化并执行模拟管理器,运行到调用check函数的状态 206 | 207 | ```python 208 | simulation = project.factory.simgr(initial_state) 209 | simulation.explore(find=address_to_check_constraint) 210 | ``` 211 | 212 | 然后利用使用 `state.memory` 的 `.load(addr, size)`接口读出`buffer`处的内存数据 213 | 214 | ```python 215 | if simulation.found: 216 | solution_state = simulation.found[0] 217 | constrained_parameter_address = buff_addr 218 | constrained_parameter_size_bytes = 16 219 | constrained_parameter_bitvector = solution_state.memory.load( 220 | constrained_parameter_address, 221 | constrained_parameter_size_bytes 222 | ) 223 | ``` 224 | 225 | 利用slover求解引擎提供的add方法加入约束条件 226 | 227 | ```python 228 | constrained_parameter_desired_value = 'AUPDNNPROEZRJWKB' 229 | solution_state.solver.add(constrained_parameter_bitvector == constrained_parameter_desired_value) 230 | ``` 231 | 232 | 接下来和之前的题目类似,不再赘述 233 | 234 | ## 09_angr_hooks 235 | 236 | 这题如题目所言,主要就是学习使用angr的hook技术解决路径爆炸问题,与我们之前利用的约束条件不同,hook技术则更为强大 237 | 238 | > 以下内容来自维基百科: 239 | > 240 | > **钩子编程**(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码,被称为**钩子**(hook)。 241 | > 242 | > 简单来说就是用我们自己设计的函数去取代被hook的函数 243 | 244 | 首先检查一下该程序: 245 | 246 | ```bash 247 | syc@ubuntu:~/Desktop/TEMP$ checksec 09_angr_hooks 248 | [*] '/home/syc/Desktop/TEMP/09_angr_hooks' 249 | Arch: i386-32-little 250 | RELRO: Partial RELRO 251 | Stack: No canary found 252 | NX: NX enabled 253 | PIE: No PIE (0x8048000) 254 | ``` 255 | 256 | 用IDA查看一下: 257 | 258 | ```c 259 | int __cdecl main(int argc, const char **argv, const char **envp) 260 | { 261 | _BOOL4 v3; // eax 262 | signed int i; // [esp+8h] [ebp-10h] 263 | signed int j; // [esp+Ch] [ebp-Ch] 264 | 265 | qmemcpy(password, "XYMKBKUHNIQYNQXE", 16); 266 | memset(buffer, 0, 0x11u); 267 | printf("Enter the password: "); 268 | __isoc99_scanf("%16s", buffer); 269 | for ( i = 0; i <= 15; ++i ) 270 | *(_BYTE *)(i + 0x804A054) = complex_function(*(char *)(i + 0x804A054), 18 - i); 271 | equals = check_equals_XYMKBKUHNIQYNQXE(buffer, 16); 272 | for ( j = 0; j <= 15; ++j ) 273 | *(_BYTE *)(j + 0x804A044) = complex_function(*(char *)(j + 0x804A044), j + 9); 274 | __isoc99_scanf("%16s", buffer); 275 | v3 = equals && !strncmp(buffer, password, 0x10u); 276 | equals = v3; 277 | if ( v3 ) 278 | puts("Good Job."); 279 | else 280 | puts("Try again."); 281 | return 0; 282 | } 283 | ``` 284 | 285 | ```c 286 | _BOOL4 __cdecl check_equals_XYMKBKUHNIQYNQXE(int a1, unsigned int a2) 287 | { 288 | int v3; // [esp+8h] [ebp-8h] 289 | unsigned int i; // [esp+Ch] [ebp-4h] 290 | 291 | v3 = 0; 292 | for ( i = 0; i < a2; ++i ) 293 | { 294 | if ( *(_BYTE *)(i + a1) == *(_BYTE *)(i + 0x804A044) ) 295 | ++v3; 296 | } 297 | return v3 == a2; 298 | } 299 | ``` 300 | 301 | ```c 302 | int __cdecl complex_function(signed int a1, int a2) 303 | { 304 | if ( a1 <= 64 || a1 > 90 ) 305 | { 306 | puts("Try again."); 307 | exit(1); 308 | } 309 | return (a1 - 65 + 23 * a2) % 26 + 65; 310 | } 311 | ``` 312 | 313 | 其实和上一题并没有什么太大的变化,主要是我们上一题是使用增加条件约束的方法减少路径分支,而这一题我们直接利用hook改写`complex_function`函数为我们自己的函数 314 | 315 | ### Hook 316 | 317 | angr使用一系列引擎(SimEngine的子类)来模拟被执行代码对输入状态产生的影响。其中就有`hook engine`来处理hook的情况。默认情况下,angr 会使用 `SimProcedures` 中的符号摘要替换库函数,即设置 Hooking,这些 python 函数摘要高效地模拟库函数对状态的影响。可以通过 `angr.procedures`或 `angr.SimProcedures` 查看列表 318 | 319 | `SimProcedure` 其实就是 Hook 机制,可以通过 `proj.hook(addr,hook)` 设置,其中 hook 是一个 `SimProcedure` 实例。 通过 `.is_hooked / .unhook / .hook_by` 进行管理。将 `proj.hook(addr)` 作为函数装饰器,可以编写自己的 hook 函数。还可以通过 `proj.hook_symbol(name,hook)` hook 函数 320 | 321 | 一个简单的例子: 322 | 323 | ```python 324 | >>> @project.hook(0x1234, length=5) 325 | ... def set_rax(state): 326 | ... state.regs.rax = 1 327 | ``` 328 | 329 | 其中第一个参数即需要Hook的调用函数的地址,第二个参数`length`即指定执行引擎在完成挂钩后应跳过多少字节。具体多少字节由Hook处地址的指令长度确定,例如本题: 330 | 331 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%89/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200811210031.png) 332 | 333 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%89/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200811210154.png) 334 | 335 | 我们需要Hook地址的机器指令长度为5个字节,故最后的hook函数: 336 | 337 | ```python 338 | @project.hook(0x80486B3, length=5) 339 | ``` 340 | 341 | 老样子先放最后EXP,再逐一分析: 342 | 343 | ```python 344 | import angr 345 | import sys 346 | import claripy 347 | def Go(): 348 | path_to_binary = "./09_angr_hooks" 349 | project = angr.Project(path_to_binary, auto_load_libs=False) 350 | initial_state = project.factory.entry_state() 351 | 352 | check_equals_called_address = 0x80486B3 353 | instruction_to_skip_length = 5 354 | 355 | @project.hook(check_equals_called_address, length=instruction_to_skip_length) 356 | def skip_check_equals_(state): 357 | user_input_buffer_address = 0x804A054 358 | user_input_buffer_length = 16 359 | 360 | user_input_string = state.memory.load( 361 | user_input_buffer_address, 362 | user_input_buffer_length 363 | ) 364 | 365 | check_against_string = 'XKSPZSJKJYQCQXZV' 366 | 367 | register_size_bit = 32 368 | state.regs.eax = claripy.If( 369 | user_input_string == check_against_string, 370 | claripy.BVV(1, register_size_bit), 371 | claripy.BVV(0, register_size_bit) 372 | ) 373 | 374 | simulation = project.factory.simgr(initial_state) 375 | 376 | def is_successful(state): 377 | stdout_output = state.posix.dumps(1) 378 | if b'Good Job.\n' in stdout_output: 379 | return True 380 | else: 381 | return False 382 | 383 | def should_abort(state): 384 | stdout_output = state.posix.dumps(1) 385 | if b'Try again.\n' in stdout_output: 386 | return True 387 | else: 388 | return False 389 | 390 | simulation.explore(find=is_successful, avoid=should_abort) 391 | 392 | if simulation.found: 393 | for i in simulation.found: 394 | solution_state = i 395 | solution = solution_state.posix.dumps(0) 396 | print("[+] Success! Solution is: {0}".format(solution.decode('utf-8'))) 397 | #print(solution0) 398 | else: 399 | raise Exception('Could not find the solution') 400 | 401 | if __name__ == "__main__": 402 | Go() 403 | ``` 404 | 405 | 运行一下查看结果: 406 | 407 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%89/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200811220414.png) 408 | 409 | 下面来逐步分析: 410 | 411 | 由于Angr可以处理对scanf的初始调用,因此我们可以从头开始 412 | 413 | ```python 414 | path_to_binary = "./09_angr_hooks" 415 | project = angr.Project(path_to_binary, auto_load_libs=False) 416 | initial_state = project.factory.entry_state() 417 | ``` 418 | 419 | 如之前分析的而言,首先找到需要Hook的函数地址`0x080486B3`,然后设定指令长度 420 | 421 | ```python 422 | check_equals_called_address = 0x80486B3 423 | instruction_to_skip_length = 5 424 | ``` 425 | 426 | 然后我们需要在在`@project.hook`语句之后书写我们的模拟函数。然后如上题一致,我们利用使用 `state.memory` 的 `.load(addr, size)`接口读出`buffer`处的内存数据,与答案进行比较 427 | 428 | ```python 429 | @project.hook(check_equals_called_address, length=instruction_to_skip_length) 430 | def skip_check_equals_(state): 431 | user_input_buffer_address = 0x804A054 432 | user_input_buffer_length = 16 433 | 434 | user_input_string = state.memory.load( 435 | user_input_buffer_address, 436 | user_input_buffer_length 437 | ) 438 | 439 | check_against_string = 'XKSPZSJKJYQCQXZV' 440 | ``` 441 | 442 | 然后这里的关键是,我们模拟一个函数就是把它视作一个黑盒,能成功模拟输入相对应的输出即可,所以我们需要处理check函数的返回值 443 | 444 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%89/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200811221326.png) 445 | 446 | 不难发现这个函数是利用EAX寄存器作为返回值,然后成功则返回1,不成功则返回0,还需要注意在构建符号位向量的时候EAX寄存器是32位寄存器 447 | 448 | ```python 449 | register_size_bit = 32 450 | state.regs.eax = claripy.If( 451 | user_input_string == check_against_string, 452 | claripy.BVV(1, register_size_bit), 453 | claripy.BVV(0, register_size_bit) 454 | ) 455 | ``` 456 | 457 | 接下来同之前差不多,不再赘述 458 | 459 | ## 10_angr_simprocedures 460 | 461 | 这题主要学习如何利用函数名进行hook,而不是复杂的利用函数的调用地址 462 | 463 | 首先检查一下程序: 464 | 465 | ```bash 466 | syc@ubuntu:~/Desktop/TEMP$ checksec 10_angr_simprocedures 467 | [*] '/home/syc/Desktop/TEMP/10_angr_simprocedures' 468 | Arch: i386-32-little 469 | RELRO: Partial RELRO 470 | Stack: Canary found 471 | NX: NX enabled 472 | PIE: No PIE (0x8048000) 473 | ``` 474 | 475 | 用IDA打开看一下: 476 | 477 | ```c 478 | int __cdecl main(int argc, const char **argv, const char **envp) 479 | { 480 | signed int i; // [esp+20h] [ebp-28h] 481 | char s[17]; // [esp+2Bh] [ebp-1Dh] 482 | unsigned int v6; // [esp+3Ch] [ebp-Ch] 483 | 484 | v6 = __readgsdword(0x14u); 485 | memcpy(&password, "ORSDDWXHZURJRBDH", 0x10u); 486 | memset(s, 0, 0x11u); 487 | printf("Enter the password: "); 488 | __isoc99_scanf("%16s", s); 489 | for ( i = 0; i <= 15; ++i ) 490 | s[i] = complex_function(s[i], 18 - i); 491 | if ( check_equals_ORSDDWXHZURJRBDH(s, 16) ) 492 | puts("Good Job."); 493 | else 494 | puts("Try again."); 495 | return 0; 496 | } 497 | ``` 498 | 499 | ```c 500 | int __cdecl complex_function(signed int a1, int a2) 501 | { 502 | if ( a1 <= 64 || a1 > 90 ) 503 | { 504 | puts("Try again."); 505 | exit(1); 506 | } 507 | return (a1 - 65 + 29 * a2) % 26 + 65; 508 | } 509 | ``` 510 | 511 | ```c 512 | _BOOL4 __cdecl check_equals_ORSDDWXHZURJRBDH(int a1, unsigned int a2) 513 | { 514 | int v3; // [esp+8h] [ebp-8h] 515 | unsigned int i; // [esp+Ch] [ebp-4h] 516 | 517 | v3 = 0; 518 | for ( i = 0; i < a2; ++i ) 519 | { 520 | if ( *(_BYTE *)(i + a1) == *(_BYTE *)(i + 0x804C048) ) 521 | ++v3; 522 | } 523 | return v3 == a2; 524 | } 525 | ``` 526 | 527 | 这一题与上一题相似, 我们必须替换check_equals函数 。但是,我们可以发现check_equals被调用了很多次,以致于无法通过地址Hook每个调用位置。 这时我们必须使用SimProcedure编写我们自己的check_equals实现,然后通过函数名Hook所有对`check_equals`的调用 528 | 529 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%89/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200812220216.png) 530 | 531 | ### Hooking Symbols 532 | 533 | 每一个程序都有一个符号表,angr可以确保从每个导入符号都可以解析出地址,可以使用angr提供的`Project.hook_symbol`API来通过符号名来Hook函数所有的调用地址,这意味着可以用自己的代码替换函数,一个简单的例子: 534 | 535 | ```python 536 | >>> class NotVeryRand(SimProcedure): 537 | ... def run(self, return_values=None): 538 | ... rand_idx = self.state.globals.get('rand_idx', 0) % len(return_values) 539 | ... out = return_values[rand_idx] 540 | ... self.state.globals['rand_idx'] = rand_idx + 1 541 | ... return out 542 | 543 | >>> project.hook_symbol('rand', NotVeryRand(return_values=[413, 612, 1025, 1111])) 544 | ``` 545 | 546 | 老样子别说话,上EXP: 547 | 548 | ```python 549 | import angr 550 | import claripy 551 | import sys 552 | 553 | def Go(): 554 | path_to_binary = "./10_angr_simprocedures" 555 | project = angr.Project(path_to_binary, auto_load_libs=False) 556 | initial_state = project.factory.entry_state() 557 | 558 | class ReplacementCheckEquals(angr.SimProcedure): 559 | def run(self, to_check, length): 560 | user_input_buffer_address = to_check 561 | user_input_buffer_length = length 562 | user_input_string = self.state.memory.load( 563 | user_input_buffer_address, 564 | user_input_buffer_length 565 | ) 566 | check_against_string = 'ORSDDWXHZURJRBDH' 567 | return claripy.If( 568 | user_input_string == check_against_string, 569 | claripy.BVV(1, 32), 570 | claripy.BVV(0, 32) 571 | ) 572 | 573 | check_equals_symbol = 'check_equals_ORSDDWXHZURJRBDH' 574 | project.hook_symbol(check_equals_symbol, ReplacementCheckEquals()) 575 | 576 | simulation = project.factory.simgr(initial_state) 577 | 578 | def is_successful(state): 579 | stdout_output = state.posix.dumps(1) 580 | if b'Good Job.\n' in stdout_output: 581 | return True 582 | else: 583 | return False 584 | 585 | def should_abort(state): 586 | stdout_output = state.posix.dumps(1) 587 | if b'Try again.\n' in stdout_output: 588 | return True 589 | else: 590 | return False 591 | 592 | simulation.explore(find=is_successful, avoid=should_abort) 593 | 594 | if simulation.found: 595 | for i in simulation.found: 596 | solution_state = i 597 | solution = solution_state.posix.dumps(0) 598 | print("[+] Success! Solution is: {0}".format(solution.decode('utf-8'))) 599 | #print(solution0) 600 | else: 601 | raise Exception('Could not find the solution') 602 | 603 | if __name__ == "__main__": 604 | Go() 605 | ``` 606 | 607 | 运行一下查看结果 608 | 609 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%89/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200816163605.png) 610 | 611 | 这里前面的部分都可以直接照抄上面一题的代码,关键是定义一个继承angr.SimProcedure的类,以利用Angr的SimProcedures。 612 | 613 | ```python 614 | class ReplacementCheckEquals(angr.SimProcedure): 615 | ``` 616 | 617 | SimProcedure用Python编写的我们自己的函数代替了原来函数。 除了用Python编写之外,该函数的行为与用C编写的任何函数基本相同。`self`之后的任何参数都将被视为要替换的函数的参数, 参数将是符号位向量。 另外,Python可以以常用的Python方式返回,Angr将以与原来函数相同的方式对待它 618 | 619 | 我们先来看一下函数原型: 620 | 621 | ```c 622 | _BOOL4 __cdecl check_equals_ORSDDWXHZURJRBDH(char *to_check, unsigned int length) 623 | { 624 | int v3; // [esp+8h] [ebp-8h] 625 | unsigned int i; // [esp+Ch] [ebp-4h] 626 | 627 | v3 = 0; 628 | for ( i = 0; i < length; ++i ) 629 | { 630 | if ( to_check[i] == *(_BYTE *)(i + 0x804C048) ) 631 | ++v3; 632 | } 633 | return v3 == length; 634 | } 635 | ``` 636 | 637 | 不难发现函数的第一个参数是待检测字符串首地址指针,然后就是字符串的长度,接下来我们就可以开始书写我们的模拟函数 638 | 639 | ```python 640 | def run(self, to_check, length): 641 | #即第一个参数 642 | user_input_buffer_address = to_check 643 | #即第二个参数 644 | user_input_buffer_length = length 645 | #使用self.state在SimProcedure中查找系统状态,从该状态的内存中提取出数据 646 | user_input_string = self.state.memory.load( 647 | user_input_buffer_address, 648 | user_input_buffer_length 649 | ) 650 | check_against_string = 'ORSDDWXHZURJRBDH' 651 | #如果符合条件则返回输入的符号位向量 652 | return claripy.If( 653 | user_input_string == check_against_string, 654 | claripy.BVV(1, 32), 655 | claripy.BVV(0, 32) 656 | ) 657 | ``` 658 | 659 | Hook上check_equals函数, angr会自动查找与该函数符号关联的地址 660 | 661 | ```python 662 | check_equals_symbol = 'check_equals_WQNDNKKWAWOLXBAC' 663 | project.hook_symbol(check_equals_symbol, ReplacementCheckEquals()) 664 | ``` 665 | 666 | 之后的操作与其他题目类似,不再赘述 667 | 668 | ## 11_angr_sim_scanf 669 | 670 | 如题,这题主要是学习如何hook`scanf`函数,步骤其实与上一题是几乎一致的,得先找到需要hook的函数符号,然后编写一个继承angr.SimProcedure的类,然后利用`hook_symbol`对函数进行hook 671 | 672 | 首先检测一下程序: 673 | 674 | ```bash 675 | syc@ubuntu:~/Desktop/TEMP$ checksec 11_angr_sim_scanf 676 | [*] '/home/syc/Desktop/TEMP/11_angr_sim_scanf' 677 | Arch: i386-32-little 678 | RELRO: Partial RELRO 679 | Stack: Canary found 680 | NX: NX enabled 681 | PIE: No PIE (0x8048000) 682 | ``` 683 | 684 | 打开IDA查看一下程序: 685 | 686 | ```c 687 | int __cdecl main(int argc, const char **argv, const char **envp) 688 | { 689 | _BOOL4 v3; // eax 690 | int i; // [esp+20h] [ebp-28h] 691 | char s[4]; // [esp+28h] [ebp-20h] 692 | int v7; // [esp+2Ch] [ebp-1Ch] 693 | unsigned int v8; // [esp+3Ch] [ebp-Ch] 694 | 695 | v8 = __readgsdword(0x14u); 696 | print_msg(); 697 | memset(s, 0, 0x14u); 698 | qmemcpy(s, "DCLUESMR", 8); 699 | for ( i = 0; i <= 7; ++i ) 700 | s[i] = complex_function(s[i], i); 701 | printf("Enter the password: "); 702 | __isoc99_scanf("%u %u", buffer0, buffer1); 703 | v3 = !strncmp(buffer0, s, 4u) && !strncmp(buffer1, (const char *)&v7, 4u); 704 | if ( v3 ) 705 | puts("Good Job."); 706 | else 707 | puts("Try again."); 708 | return 0; 709 | } 710 | ``` 711 | 712 | ```c 713 | int __cdecl complex_function(signed int a1, int a2) 714 | { 715 | if ( a1 <= 64 || a1 > 90 ) 716 | { 717 | puts("Try again."); 718 | exit(1); 719 | } 720 | return (a1 - 65 + 29 * a2) % 26 + 65; 721 | } 722 | ``` 723 | 724 | 还记得之前我们有一题也是scanf函数的复杂格式化字符串处理吗?没错,就是`03_angr_simbolic_registers`,那一题我们是利用符号化寄存器实现了scanf函数的多参数处理。而在这一题中,我们采用的是Hook重写库函数`scnaf`实现复杂格式化字符串的支持 725 | 726 | 客官新鲜的二两EXP这就奉上 727 | 728 | ```python 729 | import angr 730 | import claripy 731 | import sys 732 | 733 | def Go(): 734 | path_to_binary = "./11_angr_sim_scanf" 735 | project = angr.Project(path_to_binary, auto_load_libs=False) 736 | initial_state = project.factory.entry_state() 737 | 738 | class ReplacementScanf(angr.SimProcedure): 739 | def run(self, format_string, param0, param1): 740 | scanf0 = claripy.BVS('scanf0', 32) 741 | scanf1 = claripy.BVS('scanf1', 32) 742 | 743 | scanf0_address = param0 744 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 745 | scanf1_address = param1 746 | self.state.memory.store(scanf1_address, scanf1, endness=project.arch.memory_endness) 747 | 748 | self.state.globals['solutions'] = (scanf0, scanf1) 749 | 750 | scanf_symbol = '__isoc99_scanf' 751 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 752 | 753 | simulation = project.factory.simgr(initial_state) 754 | 755 | def is_successful(state): 756 | stdout_output = state.posix.dumps(1) 757 | if b'Good Job.\n' in stdout_output: 758 | return True 759 | else: 760 | return False 761 | 762 | def should_abort(state): 763 | stdout_output = state.posix.dumps(1) 764 | if b'Try again.\n' in stdout_output: 765 | return True 766 | else: 767 | return False 768 | 769 | simulation.explore(find=is_successful, avoid=should_abort) 770 | 771 | if simulation.found: 772 | for i in simulation.found: 773 | solution_state = i 774 | stored_solutions = solution_state.globals['solutions'] 775 | scanf0_solution = solution_state.solver.eval(stored_solutions[0]) 776 | scanf1_solution = solution_state.solver.eval(stored_solutions[1]) 777 | print("[+] Success! Solution is: {0} {1}".format(scanf0_solution,scanf1_solution)) 778 | #print(scanf0_solution, scanf1_solution) 779 | else: 780 | raise Exception('Could not find the solution') 781 | 782 | if __name__ == "__main__": 783 | Go() 784 | ``` 785 | 786 | 运行一下查看结果 787 | 788 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E4%B8%89/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200816220158.png) 789 | 790 | 之前的步骤很多都和上一题一样,只不过在编写模拟的scanf函数的时候有一些不太一样 791 | 792 | ```python 793 | class ReplacementScanf(angr.SimProcedure): 794 | def run(self, format_string, param0, param1): 795 | scanf0 = claripy.BVS('scanf0', 32) 796 | scanf1 = claripy.BVS('scanf1', 32) 797 | 798 | scanf0_address = param0 799 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 800 | scanf1_address = param1 801 | self.state.memory.store(scanf1_address, scanf1, endness=project.arch.memory_endness) 802 | ``` 803 | 804 | 还记得之前在`05_angr_symbolic_memory`我们学会的如何符号化内存吗?因为我们这里Scanf是要向内存写入数据的,于是我们利用使用 `state.memory` 的 `.store(addr, val)` 接口将符号位向量写入两个字符串的内存区域 805 | 806 | ### globals 807 | 808 | 这里的关键我们都知道Python的变量生存周期,在这里`scanf0`和`scanf1`是函数`ReplacementScanf`的局部变量,为了让函数外部也能获得我们输入的符号位向量,从而调用求解器获得答案,需要将这两个符号位向量变为全局变量,这里我们需要调用带有全局状态的globals插件中“保存”对我们的符号值的引用。globals插件允许使用列表,元组或多个键的字典来存储多个位向量 809 | 810 | ```python 811 | self.state.globals['solutions'] = (scanf0, scanf1) 812 | ``` 813 | 814 | 之后的操作与其他题目类似,不再赘述 815 | 816 | ## 参考文献 817 | 818 | 【1】angr官方文档—— https://docs.angr.io/core-concepts 819 | 820 | 【2】angr 系列教程(一)核心概念及模块解读—— https://xz.aliyun.com/t/7117#toc-14 821 | 822 | 【3】王田园. 符号执行的路径爆炸及约束求解问题研究[D].大连海事大学,2019. 823 | 824 | 【4】曹琰. 面向软件脆弱性分析的并行符号执行技术研究[D].解放军信息工程大学,2013. -------------------------------------------------------------------------------- /笔记/03/图片/微信图片_20200810161941.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/03/图片/微信图片_20200810161941.png -------------------------------------------------------------------------------- /笔记/03/图片/微信图片_20200816163605.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/03/图片/微信图片_20200816163605.png -------------------------------------------------------------------------------- /笔记/03/图片/微信图片_20200816220158.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/03/图片/微信图片_20200816220158.png -------------------------------------------------------------------------------- /笔记/03/图片/微信截图_20200811210031.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/03/图片/微信截图_20200811210031.png -------------------------------------------------------------------------------- /笔记/03/图片/微信截图_20200811210154.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/03/图片/微信截图_20200811210154.png -------------------------------------------------------------------------------- /笔记/03/图片/微信截图_20200811220414.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/03/图片/微信截图_20200811220414.png -------------------------------------------------------------------------------- /笔记/03/图片/微信截图_20200811221326.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/03/图片/微信截图_20200811221326.png -------------------------------------------------------------------------------- /笔记/03/图片/微信截图_20200812220216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/03/图片/微信截图_20200812220216.png -------------------------------------------------------------------------------- /笔记/04/Angr_CTF从入门到精通(四).md: -------------------------------------------------------------------------------- 1 | # Angr_CTF从入门到精通完结篇(四) 2 | 3 | > PS:打国赛、强网杯开学七七八八的杂事拖延了一点时间,拖更一下,上次提到的API变化可以查看一下附录 4 | 5 | 承接上一篇文章,上一章教程我们主要学习了angr的Hook接口的利用,这次我们把剩下的题目一网打尽 6 | 7 | ## 12_angr_veritesting 8 | 9 | 如题主要学习使用`Veritesting`的技术解决路径爆炸问题 10 | 11 | ### Veritesting 12 | 13 | 动态符号执行(DSE)和静态符号执行(SSE)一个为路径生成公式,一个为语句生成公式。前者生成公式时会产生很高的负载,但生成的公式很容易解;后者生成公式很容易,公式也能覆盖更多的路径,但是公式更长更难解。方法上的区别在于DSE会摘要路径汇合点上两条分支的情况,而SSE为两条分支fork两条独立的执行路径 14 | 15 | SSE目前还不能对大规模的程序分析(如Cloud9+state merging),问题主要在于循环的表示、方程复杂度、缺少具体状态、和对syscall等的模拟。Veritesting可以在SSE和DSE之间切换,减少负载和公式求解难度,并解决静态方法需要摘要或其他方法才能处理的系统调用和间接跳转 16 | 17 | 简单来说就是Veritesting结合了静态符合执行与动态符号执行,减少了路径爆炸的影响,在angr里我们只要在构造模拟管理器时,启用Veritesting了就行 18 | 19 | ```python 20 | project.factory.simgr(initial_state, veritesting=True) 21 | ``` 22 | 23 | 首先检测一下文件: 24 | 25 | ```bash 26 | zxy@ubuntu:~/Desktop/TEMP$ checksec 12_angr_veritesting 27 | [*] '/home/syc/Desktop/TEMP/12_angr_veritesting' 28 | Arch: i386-32-little 29 | RELRO: Partial RELRO 30 | Stack: Canary found 31 | NX: NX enabled 32 | PIE: No PIE (0x8048000) 33 | ``` 34 | 35 | 用IDA打开查看一下函数: 36 | 37 | ```c 38 | int __cdecl main(int argc, const char **argv, const char **envp) 39 | { 40 | const char **v3; // ST1C_4 41 | int v4; // ST08_4 42 | int v5; // ebx 43 | int v7; // [esp-10h] [ebp-5Ch] 44 | int v8; // [esp-Ch] [ebp-58h] 45 | int v9; // [esp-8h] [ebp-54h] 46 | int v10; // [esp-4h] [ebp-50h] 47 | int v11; // [esp+4h] [ebp-48h] 48 | int v12; // [esp+8h] [ebp-44h] 49 | int v13; // [esp+Ch] [ebp-40h] 50 | int v14; // [esp+10h] [ebp-3Ch] 51 | int v15; // [esp+10h] [ebp-3Ch] 52 | int v16; // [esp+14h] [ebp-38h] 53 | signed int i; // [esp+14h] [ebp-38h] 54 | int v18; // [esp+18h] [ebp-34h] 55 | int string; // [esp+1Ch] [ebp-30h] 56 | int v20; // [esp+20h] [ebp-2Ch] 57 | int v21; // [esp+24h] [ebp-28h] 58 | int v22; // [esp+28h] [ebp-24h] 59 | int v23; // [esp+2Ch] [ebp-20h] 60 | int v24; // [esp+30h] [ebp-1Ch] 61 | unsigned int v25; // [esp+40h] [ebp-Ch] 62 | int *v26; // [esp+44h] [ebp-8h] 63 | 64 | v26 = &argc; 65 | v3 = argv; 66 | v25 = __readgsdword(0x14u); 67 | print_msg(); 68 | memset((char *)&string + 3, 0, 0x21u); 69 | printf("Enter the password: "); 70 | __isoc99_scanf( 71 | "%32s", 72 | (char *)&string + 3, 73 | v4, 74 | v7, 75 | v8, 76 | v9, 77 | v10, 78 | v3, 79 | v11, 80 | v12, 81 | v13, 82 | v14, 83 | v16, 84 | v18, 85 | string, 86 | v20, 87 | v21, 88 | v22, 89 | v23, 90 | v24); 91 | v15 = 0; 92 | for ( i = 0; i <= 31; ++i ) 93 | { 94 | v5 = *((char *)&string + i + 3); 95 | if ( v5 == complex_function(87, i + 186) ) 96 | ++v15; 97 | } 98 | if ( v15 != 32 || (_BYTE)v25 ) 99 | puts("Try again."); 100 | else 101 | puts("Good Job."); 102 | return 0; 103 | } 104 | ``` 105 | 106 | ```c 107 | int __cdecl complex_function(signed int a1, int a2) 108 | { 109 | if ( a1 <= 64 || a1 > 90 ) 110 | { 111 | puts("Try again."); 112 | exit(1); 113 | } 114 | return (a1 - 65 + 47 * a2) % 26 + 65; 115 | } 116 | ``` 117 | 118 | 回忆一下`08_angr_constraints`我们很快就能发现容易产生路径爆炸的地方 119 | 120 | ```c 121 | for ( i = 0; i <= 31; ++i ) 122 | { 123 | v5 = *((char *)&string + i + 3); 124 | if ( v5 == complex_function(87, i + 186) ) 125 | ++v15; 126 | } 127 | ``` 128 | 129 | 在之前我们是通过增加条件约束和Hook函数避免路径爆炸,我们也可以尝试一下使用之前的方法,但是这题我们启用了Veritesting就变得简单了很多,不用过多的手动设定太多参数 130 | 131 | 话不多说,先直接上EXP: 132 | 133 | ```python 134 | import angr 135 | import claripy 136 | import sys 137 | 138 | def Go(): 139 | path_to_binary = "./12_angr_veritesting" 140 | project = angr.Project(path_to_binary, auto_load_libs=False) 141 | initial_state = project.factory.entry_state() 142 | simulation = project.factory.simgr(initial_state, veritesting=True) 143 | 144 | def is_successful(state): 145 | stdout_output = state.posix.dumps(1) 146 | if b'Good Job.\n' in stdout_output: 147 | return True 148 | else: 149 | return False 150 | 151 | def should_abort(state): 152 | stdout_output = state.posix.dumps(1) 153 | if b'Try again.\n' in stdout_output: 154 | return True 155 | else: 156 | return False 157 | 158 | simulation.explore(find=is_successful, avoid=should_abort) 159 | 160 | if simulation.found: 161 | for i in simulation.found: 162 | solution_state = i 163 | solution = solution_state.posix.dumps(0) 164 | print("[+] Success! Solution is: {0}".format(solution)) 165 | #print(scanf0_solution, scanf1_solution) 166 | else: 167 | raise Exception('Could not find the solution') 168 | 169 | if __name__ == "__main__": 170 | Go() 171 | ``` 172 | 173 | 查看一下运行结果: 174 | 175 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200818201017.png) 176 | 177 | 其实这题就是体验一下Veritesting的强大功能 178 | 179 | ## 13_angr_static_binary 180 | 181 | 这题如题就是主要学习如何使用angr解出静态编译的题目,学习如何Hook静态库函数 182 | 183 | ### 静态编译 184 | 185 | 不同于动态编译是将应用程序需要的模块都编译成动态链接库,启动程序(初始化)时,这些模块不会被加载,运行时用到哪个模块就调用哪个。静态编译就是在编译时,把所有模块都编译进可执行文件里,当启动这个可执行文件时,所有模块都被加载进来,反映在现实中就是程序体积会相对大一些,在IDA中会发现所有用到函数都是静态编译好的 186 | 187 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200818214519.png) 188 | 189 | 我们先检查一下文件: 190 | 191 | ```bash 192 | syc@ubuntu:~/Desktop/TEMP$ checksec 13_angr_static_binary 193 | [*] '/home/syc/Desktop/TEMP/13_angr_static_binary' 194 | Arch: i386-32-little 195 | RELRO: Partial RELRO 196 | Stack: Canary found 197 | NX: NX enabled 198 | PIE: No PIE (0x8048000) 199 | ``` 200 | 201 | 拖进IDA查看一下函数: 202 | 203 | ```c 204 | int __cdecl main(int argc, const char **argv, const char **envp) 205 | { 206 | signed int i; // [esp+1Ch] [ebp-3Ch] 207 | signed int j; // [esp+20h] [ebp-38h] 208 | char s1[20]; // [esp+24h] [ebp-34h] 209 | char s2[4]; // [esp+38h] [ebp-20h] 210 | int v8; // [esp+3Ch] [ebp-1Ch] 211 | unsigned int v9; // [esp+4Ch] [ebp-Ch] 212 | 213 | v9 = __readgsdword(0x14u); 214 | print_msg(); 215 | for ( i = 0; i <= 19; ++i ) 216 | s2[i] = 0; 217 | *(_DWORD *)s2 = 'NVJL'; 218 | v8 = 'UAPE'; 219 | printf("Enter the password: "); 220 | _isoc99_scanf("%8s", s1); 221 | for ( j = 0; j <= 7; ++j ) 222 | s1[j] = complex_function(s1[j], j); 223 | if ( !strcmp(s1, s2) ) 224 | puts("Good Job."); 225 | else 226 | puts("Try again."); 227 | return 0; 228 | } 229 | ``` 230 | 231 | ```c# 232 | int __cdecl complex_function(signed int a1, int a2) 233 | { 234 | if ( a1 <= 64 || a1 > 90 ) 235 | { 236 | puts("Try again."); 237 | exit(1); 238 | } 239 | return (37 * a2 + a1 - 65) % 26 + 65; 240 | } 241 | ``` 242 | 243 | 通常,Angr会自动地用工作速度快得多的simprocedure代替标准库函数,但是这题中库函数都已经因为静态编译成了静态函数了,angr没法自动替换。要解决这题,需要手动Hook所有使用标准库的C函数,angr已经在simprocedure中为我们提供了这些静态函数, 这里列举一些常用的函数 244 | 245 | ```python 246 | angr.SIM_PROCEDURES['libc']['malloc'] 247 | angr.SIM_PROCEDURES['libc']['fopen'] 248 | angr.SIM_PROCEDURES['libc']['fclose'] 249 | angr.SIM_PROCEDURES['libc']['fwrite'] 250 | angr.SIM_PROCEDURES['libc']['getchar'] 251 | angr.SIM_PROCEDURES['libc']['strncmp'] 252 | angr.SIM_PROCEDURES['libc']['strcmp'] 253 | angr.SIM_PROCEDURES['libc']['scanf'] 254 | angr.SIM_PROCEDURES['libc']['printf'] 255 | angr.SIM_PROCEDURES['libc']['puts'] 256 | angr.SIM_PROCEDURES['libc']['exit'] 257 | ``` 258 | 259 | 我们只需要手动找到程序中用到静态函数的地址,将其利用simprocedure提供的函数Hook掉即可 260 | 261 | 话不多说上EXP: 262 | 263 | ```python 264 | import angr 265 | import claripy 266 | import sys 267 | 268 | def Go(): 269 | path_to_binary = "./13_angr_static_binary" 270 | project = angr.Project(path_to_binary, auto_load_libs=False) 271 | initial_state = project.factory.entry_state() 272 | 273 | project.hook(0x804ed40, angr.SIM_PROCEDURES['libc']['printf']()) 274 | project.hook(0x804ed80, angr.SIM_PROCEDURES['libc']['scanf']()) 275 | project.hook(0x804f350, angr.SIM_PROCEDURES['libc']['puts']()) 276 | project.hook(0x8048d10, angr.SIM_PROCEDURES['glibc']['__libc_start_main']()) 277 | 278 | simulation = project.factory.simgr(initial_state, veritesting=True) 279 | 280 | def is_successful(state): 281 | stdout_output = state.posix.dumps(1) 282 | if b'Good Job.\n' in stdout_output: 283 | return True 284 | else: 285 | return False 286 | 287 | def should_abort(state): 288 | stdout_output = state.posix.dumps(1) 289 | if b'Try again.\n' in stdout_output: 290 | return True 291 | else: 292 | return False 293 | 294 | simulation.explore(find=is_successful, avoid=should_abort) 295 | 296 | if simulation.found: 297 | for i in simulation.found: 298 | solution_state = i 299 | solution = solution_state.posix.dumps(0) 300 | print("[+] Success! Solution is: {0}".format(solution)) 301 | #print(scanf0_solution, scanf1_solution) 302 | else: 303 | raise Exception('Could not find the solution') 304 | 305 | if __name__ == "__main__": 306 | Go() 307 | ``` 308 | 309 | 运行一下查看结果: 310 | 311 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200819172721.png) 312 | 313 | 这题解题真正需要用的函数也就`printf`,`scnaf`,`puts`,即完成了angr需要的输出、输入、路径选择的功能,我们手动找到这几个函数的地址 314 | 315 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200819173153.png) 316 | 317 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200819173251.png) 318 | 319 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200819173332.png) 320 | 321 | 这里比较容易忽略的一个函数就是`__libc_start_main` 322 | 323 | 让我们回忆一下在linux下一个c程序是如何启动的: 324 | 325 | 1. execve 开始执行 326 | 2. execve 内部会把bin程序加载后,就把.interp指定的 动态加载器加载 327 | 3. 动态加载器把需要加载的so都加载起来,特别的把 libc.so.6 加载 328 | 4. 调用到libc.so.6里的__libc_start_main函数,真正开始执行程序 329 | 5. libc_start_main做了一些事后,调用到main()函数 330 | 331 | 所以程序是一定需要用到`__libc_start_main`,分析后得到地址:0x8048D10,于是得到代码: 332 | 333 | ```python 334 | project.hook(0x804ed40, angr.SIM_PROCEDURES['libc']['printf']()) 335 | project.hook(0x804ed80, angr.SIM_PROCEDURES['libc']['scanf']()) 336 | project.hook(0x804f350, angr.SIM_PROCEDURES['libc']['puts']()) 337 | project.hook(0x8048d10, angr.SIM_PROCEDURES['glibc']['__libc_start_main']()) 338 | ``` 339 | 340 | 其它的部分和之前做过的`02_angr_find_condition`一致,不再赘述 341 | 342 | ## 14_angr_shared_library 343 | 344 | 这题如题主要是学习如何使用angr求解函数是外部导入在动态库(.so)里的题目,这题我们有了两个文件,一个是主程序`14_angr_shared_library`,另一个就是库文件`lib14_angr_shared_library.so` 345 | 346 | 我们先来检查一下这两个文件: 347 | 348 | ```bash 349 | syc@ubuntu:~/Desktop/TEMP$ checksec 14_angr_shared_library 350 | [*] '/home/syc/Desktop/TEMP/14_angr_shared_library' 351 | Arch: i386-32-little 352 | RELRO: Partial RELRO 353 | Stack: Canary found 354 | NX: NX enabled 355 | PIE: No PIE (0x8048000) 356 | ``` 357 | 358 | ```bash 359 | syc@ubuntu:~/Desktop/TEMP$ checksec lib14_angr_shared_library.so 360 | [*] '/home/syc/Desktop/TEMP/lib14_angr_shared_library.so' 361 | Arch: i386-32-little 362 | RELRO: Partial RELRO 363 | Stack: No canary found 364 | NX: NX enabled 365 | PIE: PIE enabled 366 | ``` 367 | 368 | 我们用IDA打开这个文件,看一看函数: 369 | 370 | ```c 371 | int __cdecl main(int argc, const char **argv, const char **envp) 372 | { 373 | char s; // [esp+1Ch] [ebp-1Ch] 374 | unsigned int v5; // [esp+2Ch] [ebp-Ch] 375 | 376 | v5 = __readgsdword(0x14u); 377 | memset(&s, 0, 0x10u); 378 | print_msg(); 379 | printf("Enter the password: "); 380 | __isoc99_scanf("%8s", &s); 381 | if ( validate(&s, 8) ) 382 | puts("Good Job."); 383 | else 384 | puts("Try again."); 385 | return 0; 386 | } 387 | ``` 388 | 389 | 这题特殊就特殊在这个关键的`validate`函数,我们在IDA分析`14_angr_shared_library`时点击进去看发现无法查看源代码: 390 | 391 | ```c 392 | int __cdecl validate(int a1, int a2) 393 | { 394 | return validate(a1, a2); 395 | } 396 | ``` 397 | 398 | 原因很简单,`validate`是一个外部导入函数,其真正的二进制代码不在源程序里,在它所处的库文件`lib14_angr_shared_library.so`里面 399 | 400 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200819202222.png) 401 | 402 | 我们用IDA打开并分析库文件`lib14_angr_shared_library.so`,找到了`validate`函数的具体实现 403 | 404 | ```c 405 | _BOOL4 __cdecl validate(char *s1, int a2) 406 | { 407 | char *v3; // esi 408 | char s2[4]; // [esp+4h] [ebp-24h] 409 | int v5; // [esp+8h] [ebp-20h] 410 | int j; // [esp+18h] [ebp-10h] 411 | int i; // [esp+1Ch] [ebp-Ch] 412 | 413 | if ( a2 <= 7 ) 414 | return 0; 415 | for ( i = 0; i <= 19; ++i ) 416 | s2[i] = 0; 417 | *(_DWORD *)s2 = 'GKLW'; 418 | v5 = 'HWJL'; 419 | for ( j = 0; j <= 7; ++j ) 420 | { 421 | v3 = &s1[j]; 422 | *v3 = complex_function(s1[j], j); 423 | } 424 | return strcmp(s1, s2) == 0; 425 | } 426 | ``` 427 | 428 | ```c 429 | int __cdecl complex_function(signed int a1, int a2) 430 | { 431 | if ( a1 <= 64 || a1 > 90 ) 432 | { 433 | puts("Try again."); 434 | exit(1); 435 | } 436 | return (41 * a2 + a1 - 65) % 26 + 65; 437 | } 438 | ``` 439 | 440 | 其实和之前的题目并没有什么太大的不同,关键在于如果让angr处理这个外部导入函数 441 | 442 | ### 动态链接 443 | 444 | 要详细了解,这里推荐阅读《程序员的自我修养——链接、装载与库》 445 | 446 | 在Linux下使用GCC将源码编译成可执行文件的过程可以分解为4个步骤,分别是预处理(Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。一个简单的hello word程序编译过程如下: 447 | 448 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/261737366704808.jpg) 449 | 450 | 动态链接的基本思想是把程序按照模块拆分成相对独立的部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有的程序模块都连接成一个单独的可执行文件。ELF动态链接文件被称为动态共享对象(DSO,Dynamic Shared Object),简称共享对象,它们一般都是.so为扩展名的文件。相比静态链接,动态链接有两个优势,一是共享对象在磁盘和内存只有一份,节省了空间;二是升级某个共享模块时,只需要将目标文件替换,而无须将所有的程序重新链接 451 | 452 | 共享对象的最终装载地址在编译时是不确定的,而是在装载时,装载器根据当前地址空间的空闲情况,动态分配一块足够大小的虚拟地址空间给相应的共享对象。为了能够使共享对象在任意地址装载,在连接时对所有绝对地址的引用不作重定位,而把这一步推迟到装载时再完成,即装载时重定位 453 | 454 | 这题我们简单理解共享库都是是用位置无关的代码编译的,我们需要指定基址。共享库中的所有地址都是base + offset,其中offset是它们在文件中的偏移地址 455 | 456 | 我们现在先上EXP,然后再逐步分析: 457 | 458 | ```python 459 | import angr 460 | import claripy 461 | import sys 462 | 463 | def Go(): 464 | path_to_binary = "./lib14_angr_shared_library.so" 465 | 466 | base = 0x4000000 467 | project = angr.Project(path_to_binary, load_options={ 468 | 'main_opts' : { 469 | 'custom_base_addr' : base 470 | } 471 | }) 472 | 473 | buffer_pointer = claripy.BVV(0x3000000, 32) 474 | 475 | validate_function_address = base + 0x6d7 476 | initial_state = project.factory.call_state(validate_function_address, buffer_pointer, claripy.BVV(8, 32)) 477 | 478 | password = claripy.BVS('password', 8*8) 479 | initial_state.memory.store(buffer_pointer, password) 480 | 481 | simulation = project.factory.simgr(initial_state) 482 | 483 | success_address = base + 0x783 484 | simulation.explore(find=success_address) 485 | 486 | if simulation.found: 487 | for i in simulation.found: 488 | solution_state = i 489 | solution_state.add_constraints(solution_state.regs.eax != 0) 490 | solution = solution_state.solver.eval(password,cast_to=bytes) 491 | print("[+] Success! Solution is: {0}".format(solution)) 492 | #print(scanf0_solution, scanf1_solution) 493 | else: 494 | raise Exception('Could not find the solution') 495 | 496 | if __name__ == "__main__": 497 | Go() 498 | ``` 499 | 500 | 运行一下查看: 501 | 502 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200819220653.png) 503 | 504 | 这题直接对库文件`lib14_angr_shared_library.so`进行符号执行求解,但问题在于库文件是需要装载才能运行的,无法单独运行,于是我们需要指定基地址 505 | 506 | 还记得我们查看的程序信息嘛 507 | 508 | ```bash 509 | syc@ubuntu:~/Desktop/TEMP$ checksec 14_angr_shared_library 510 | [*] '/home/syc/Desktop/TEMP/14_angr_shared_library' 511 | Arch: i386-32-little 512 | RELRO: Partial RELRO 513 | Stack: Canary found 514 | NX: NX enabled 515 | PIE: No PIE (0x8048000) 516 | ``` 517 | 518 | 这题是没有开启PIE,所以加载基地址是不会变化的,我们可以直接设定0x8048000 519 | 520 | ### pre-binary 选项 521 | 522 | 如果你想要对一个特定的二进制对象设置一些选项,CLE也能满足你的需求在加载二进制文件时可以设置特定的参数,使用 `main_opts` 和 `lib_opts` 参数进行设置。 523 | 524 | - `backend` - 指定 backend 525 | - `base_addr` - 指定基址 526 | - `entry_point` - 指定入口点 527 | - `arch` - 指定架构 528 | 529 | 示例如下: 530 | 531 | ```python 532 | >>> angr.Project('examples/fauxware/fauxware', main_opts={'backend': 'blob', 'arch': 'i386'}, lib_opts={'libc.so.6': {'backend': 'elf'}}) 533 | 534 | ``` 535 | 536 | 参数`main_opts`和`lib_opts`接收一个以python字典形式存储的选项组。`main_opts`接收一个形如{选项名1:选项值1,选项名2:选项值2……}的字典,而`lib_opts`接收一个库名到形如{选项名1:选项值1,选项名2:选项值2……}的字典的映射。 537 | 538 | > lib_opts是二级字典,原因是一个二进制文件可能加载多个库,而main_opts指定的是主程序加载参数,而主程序一般只有一个,因此是一级字典。 539 | 540 | 这些选项的内容因不同的后台而异,下面是一些通用的选项: 541 | 542 | - backend —— 使用哪个后台,可以是一个对象,也可以是一个名字(字符串) 543 | - custom_base_addr —— 使用的基地址 544 | - custom_entry_point —— 使用的入口点 545 | - custom_arch —— 使用的处理器体系结构的名字 546 | 547 | 所以我们可以得到脚本的第一部分 548 | 549 | ```python 550 | path_to_binary = "./lib14_angr_shared_library.so" 551 | base = 0x8048000 552 | project = angr.Project(path_to_binary, load_options={ 553 | 'main_opts' : { 554 | 'custom_base_addr' : base 555 | } 556 | }) 557 | ``` 558 | 559 | 我们这里调用的是使用`.call_state`创建 state 对象,构造一个已经准备好执行`validate`函数的状态,所以我们需要设定好需要传入的参数。先回顾一下`validate`函数的原型 560 | 561 | ```c 562 | validate(char *s1, int a2) 563 | ``` 564 | 565 | 我们可以通过 `BVV(value,size)` 和 `BVS( name, size)` 接口创建位向量,先创建一个缓冲区buffer作为参数`char *s1`,因为设定的缓冲区地址在0x3000000,又因为32位程序里int类型为4字节,即32比特,故得 566 | 567 | ```python 568 | buffer_pointer = claripy.BVV(0x3000000, 32) 569 | ``` 570 | 571 | 然后从IDA中不难得出`validate`的偏移量为0x6D7,然后因为需要比较的字符串长度为8,故利用BVV传入参数`int a2`,最后得到 572 | 573 | ```python 574 | buffer_pointer = claripy.BVV(0x3000000, 32) 575 | validate_function_address = base + 0x6d7 576 | initial_state = project.factory.call_state(validate_function_address, buffer_pointer, claripy.BVV(8, 32)) 577 | ``` 578 | 579 | 然后利用BVS创建一个符号位向量,作为符号化的传入字符串传入我们之前设定好的缓冲区地址中,这里继续利用`memory.store`接口 580 | 581 | ```python 582 | password = claripy.BVS('password', 8*8) 583 | initial_state.memory.store(buffer_pointer, password) 584 | ``` 585 | 586 | 这里判断我们路径正确的方法有两种 587 | 588 | - 同我们之前Hook部分一样,Hook判断部分 589 | - 搜索函数执行完的返回地址,然后根据诺正确则EAX的值不为0,添加约束条件求解 590 | 591 | 这里我们选用了第二种方式 592 | 593 | ```python 594 | success_address = base + 0x783 595 | simulation.explore(find=success_address) 596 | ``` 597 | 598 | 之后的部分同之前的题目类似,不再赘述 599 | 600 | ## 15_angr_arbitrary_read 601 | 602 | 这题如题主要是学习如何利用Angr实现内存地址的任意读,和CTF中的PWN题很像,这里的例子也都是很简单的漏洞利用 603 | 604 | 首先检测一下文件: 605 | 606 | ```bash 607 | syc@ubuntu:~/Desktop/TEMP$ checksec 15_angr_arbitrary_read 608 | [*] '/home/syc/Desktop/TEMP/15_angr_arbitrary_read' 609 | Arch: i386-32-little 610 | RELRO: Partial RELRO 611 | Stack: No canary found 612 | NX: NX enabled 613 | PIE: No PIE (0x8048000) 614 | ``` 615 | 616 | 我们用IDA打开这个文件,看一看函数: 617 | 618 | ```c 619 | int __cdecl main(int argc, const char **argv, const char **envp) 620 | { 621 | char v4; // [esp+Ch] [ebp-1Ch] 622 | char *s; // [esp+1Ch] [ebp-Ch] 623 | 624 | s = try_again; 625 | print_msg(); 626 | printf("Enter the password: "); 627 | __isoc99_scanf("%u %20s", &key, &v4); 628 | if ( key == 19511649 ) 629 | puts(s); 630 | else 631 | puts(try_again); 632 | return 0; 633 | } 634 | ``` 635 | 636 | 刚开始拿到这题我们是有点懵逼的,因为太简单了,不懂利用点在哪里,其实我们只要铭记所有题目的核心关键是输出“Godd Job” 637 | 638 | 我们看一下puts函数的用法 639 | 640 | ```c 641 | int puts(const char *string); 642 | ``` 643 | 644 | 传入的是一个字符串指针,我们所有题目的目标都是最后获得输出Good Job,这题单单看反汇编代码无法发现如何获得正确输出,回想一下标题任意读,我们可以发现这题的关键是修改s处内存的指针地址,然后搜索一下程序的字符串表 645 | 646 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200901091836.png) 647 | 648 | 发现程序中存在"Good Job."字符串验证了我们之前的想法,我们目前需要做的事情就是把s存储的地址修改为Good Job所在的地址即**0x594e4257** 649 | 650 | 那我们如何修改呢,视线回到充满着漏洞和内存泄漏的scanf函数,观察一下v4的栈结构 651 | 652 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200901092304.png) 653 | 654 | 可以发现v4和s在内存上是相邻的,且只相差20地址,回到scanf函数 655 | 656 | ```c 657 | __isoc99_scanf("%u %20s", &key, &v4); 658 | ``` 659 | 660 | 允许我们输入20个字符,存在越界写的问题,可输入的字符串刚刚好可以让我们覆盖到 `s`,这就给了我们可以修改s字符的机会 661 | 662 | 先上EXP,再逐步分析: 663 | 664 | ```python 665 | import angr 666 | import sys 667 | import claripy 668 | def Go(): 669 | path_to_binary = "./15_angr_arbitrary_read" 670 | project = angr.Project(path_to_binary, auto_load_libs=False) 671 | initial_state = project.factory.entry_state() 672 | 673 | class ReplacementScanf(angr.SimProcedure): 674 | def run(self, format_string, param0, param1): 675 | scanf0 = claripy.BVS('scanf0', 32) 676 | scanf1 = claripy.BVS('scanf1', 20*8) 677 | for char in scanf1.chop(bits=8): 678 | self.state.add_constraints(char >= 'A', char <= 'Z') 679 | scanf0_address = param0 680 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 681 | scanf1_address = param1 682 | self.state.memory.store(scanf1_address, scanf1) 683 | self.state.globals['solutions'] = (scanf0, scanf1) 684 | 685 | scanf_symbol = '__isoc99_scanf' 686 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 687 | 688 | def check_puts(state): 689 | puts_parameter = state.memory.load(state.regs.esp + 4, 4, endness=project.arch.memory_endness) 690 | if state.se.symbolic(puts_parameter): 691 | good_job_string_address = 0x594e4257 692 | is_vulnerable_expression = puts_parameter == good_job_string_address 693 | 694 | copied_state = state.copy() 695 | copied_state.add_constraints(is_vulnerable_expression) 696 | 697 | if copied_state.satisfiable(): 698 | state.add_constraints(is_vulnerable_expression) 699 | return True 700 | else: 701 | return False 702 | else: 703 | return False 704 | 705 | simulation = project.factory.simgr(initial_state) 706 | 707 | def is_successful(state): 708 | puts_address = 0x8048370 709 | if state.addr == puts_address: 710 | return check_puts(state) 711 | else: 712 | return False 713 | 714 | simulation.explore(find=is_successful) 715 | 716 | if simulation.found: 717 | solution_state = simulation.found[0] 718 | (scanf0, scanf1) = solution_state.globals['solutions'] 719 | solution0 = (solution_state.solver.eval(scanf0)) 720 | solution1 = (solution_state.solver.eval(scanf1,cast_to=bytes)) 721 | print("[+] Success! Solution is: {0} {1}".format(solution0, solution1)) 722 | else: 723 | raise Exception('Could not find the solution') 724 | 725 | if __name__ == "__main__": 726 | Go() 727 | ``` 728 | 729 | 运行一下验证结果: 730 | 731 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/QQ%E5%9B%BE%E7%89%8720200901210631.png) 732 | 733 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200901210802.png) 734 | 735 | 现在开始解析脚本,一开始的时候同往常一样,angr可以自动处理 736 | 737 | ```python 738 | path_to_binary = "./15_angr_arbitrary_read" 739 | project = angr.Project(path_to_binary, auto_load_libs=False) 740 | initial_state = project.factory.entry_state() 741 | ``` 742 | 743 | 接下来我们需要同之前几题一样Hook Scanf函数,为此我们需要自己编写一个替换函数,注意同之前的知识相结合 744 | 745 | ```python 746 | class ReplacementScanf(angr.SimProcedure): 747 | def run(self, format_string, param0, param1): 748 | scanf0 = claripy.BVS('scanf0', 32) 749 | scanf1 = claripy.BVS('scanf1', 20*8) 750 | for char in scanf1.chop(bits=8): 751 | self.state.add_constraints(char >= 'A', char <= 'Z') 752 | scanf0_address = param0 753 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 754 | scanf1_address = param1 755 | self.state.memory.store(scanf1_address, scanf1) 756 | self.state.globals['solutions'] = (scanf0, scanf1) 757 | ``` 758 | 759 | 因为第一个参数key是无符号整数,即占用8*4=32比特,第二个参数v4我们需要输入20个字符才能覆盖到s的地址,故总共需要20\*8个比特,我们这就完成了两个符号位向量的构建 760 | 761 | ```python 762 | scanf0 = claripy.BVS('scanf0', 32) 763 | scanf1 = claripy.BVS('scanf1', 20*8) 764 | ``` 765 | 766 | 这题我们需要确保字符串中的每个字符都是可打印的,这就需要我们添加新的条件约束,即约束每个字节的范围在ASCII码中,同时因为一个字符是8比特,故我们需要将scanf1这个符号位向量按8比特一组切分为一个字节一个字节 767 | 768 | ```python 769 | for char in scanf1.chop(bits=8): 770 | self.state.add_constraints(char >= 'A', char <= 'Z') 771 | ``` 772 | 773 | 这里我们引入`project.arch.memory_endness`将符号位向量设置为小端序,并设置解集 774 | 775 | ```python 776 | scanf0_address = param0 777 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 778 | scanf1_address = param1 779 | self.state.memory.store(scanf1_address, scanf1) 780 | self.state.globals['solutions'] = (scanf0, scanf1) 781 | ``` 782 | 783 | 然后开始设置根据符号表函数名进行Hook操作 784 | 785 | ```python 786 | scanf_symbol = '__isoc99_scanf' 787 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 788 | ``` 789 | 790 | 然后我们需要一个验证函数求证我们求解出的状态是正确的输出状态,所以需要编写一个check_puts函数进行检查,我们主要是在检查puts函数调用时传入的参数s的值,这里有一个独特的地方我们检查的puts地址是PLT地址, 791 | 792 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200901223730.png) 793 | 794 | 因为有两个地方都调用了puts函数,而puts是一个外部导入函数,每次调用本质上都需要访问PLT表,所以我们直接捕获运行到puts的PLT地址的state做检查就行 795 | 796 | ```python 797 | def is_successful(state): 798 | puts_address = 0x8048370 799 | if state.addr == puts_address: 800 | return check_puts(state) 801 | else: 802 | return False 803 | ``` 804 | 805 | 接下来我们开始编写check函数,首先我们知道puts函数只有一个参数,那这个参数一定是存在栈上esp指针+4的位置(具体可以去参阅32位Linux函数传参格式) 806 | 807 | ``` 808 | esp + 7 -> /----------------\ 809 | esp + 6 -> | puts | 810 | esp + 5 -> | parameter | 811 | esp + 4 -> \----------------/ 812 | esp + 3 -> /----------------\ 813 | esp + 2 -> | return | 814 | esp + 1 -> | address | 815 | esp -> \----------------/ 816 | ``` 817 | 818 | 我们调用memory的load方法将这个数据提取出来看看是不是goodjob字符串所在的地址 819 | 820 | ```python 821 | def check_puts(state): 822 | puts_parameter = state.memory.load(state.regs.esp + 4, 4, endness=project.arch.memory_endness) 823 | if state.se.symbolic(puts_parameter): 824 | good_job_string_address = 0x594e4257 825 | is_vulnerable_expression = puts_parameter == good_job_string_address 826 | ``` 827 | 828 | 这里我们需要对当前状态做一个拷贝,方便操作状态而不对原来的状态产生影响干扰,然后给状态添加约束条件,如果地址相等则返回正确 829 | 830 | ```python 831 | copied_state = state.copy() 832 | copied_state.add_constraints(is_vulnerable_expression) 833 | 834 | if copied_state.satisfiable(): 835 | state.add_constraints(is_vulnerable_expression) 836 | return True 837 | else: 838 | return False 839 | else: 840 | return False 841 | ``` 842 | 843 | 接下来的部分都是大同小异,不再赘述 844 | 845 | ## 16_angr_arbitrary_write 846 | 847 | 这题如题就是学习如何任意写,老样子先检查一下文件 848 | 849 | ```bash 850 | syc@ubuntu:~/Desktop/TEMP$ checksec 16_angr_arbitrary_write 851 | [*] '/home/syc/Desktop/TEMP/16_angr_arbitrary_write' 852 | Arch: i386-32-little 853 | RELRO: Partial RELRO 854 | Stack: No canary found 855 | NX: NX enabled 856 | PIE: No PIE (0x8048000) 857 | ``` 858 | 859 | 用IDA打开检查一下函数 860 | 861 | ```c 862 | int __cdecl main(int argc, const char **argv, const char **envp) 863 | { 864 | char s; // [esp+Ch] [ebp-1Ch] 865 | char *dest; // [esp+1Ch] [ebp-Ch] 866 | 867 | dest = unimportant_buffer; 868 | memset(&s, 0, 0x10u); 869 | strncpy(password_buffer, "PASSWORD", 0xCu); 870 | print_msg(); 871 | printf("Enter the password: "); 872 | __isoc99_scanf("%u %20s", &key, &s); 873 | if ( key == 24173502 ) 874 | strncpy(dest, &s, 0x10u); 875 | else 876 | strncpy(unimportant_buffer, &s, 0x10u); 877 | if ( !strncmp(password_buffer, "DVTBOGZL", 8u) ) 878 | puts("Good Job."); 879 | else 880 | puts("Try again."); 881 | return 0; 882 | } 883 | ``` 884 | 885 | 一开始也是毫无头绪,记得我们之前铭记的做题核心就是输出“Good Job.",顺着这个思路往上走,第一步我们观察 886 | 887 | ```c 888 | !strncmp(password_buffer, "NDYNWEUJ", 8u) 889 | ``` 890 | 891 | 需要的条件是`password_buffer`的里面内容为`NDYNWEUJ`,接下来的问题是如何指定内容,我们发现并没有直接的渠道给我们去修改这里的内容,我们思路到哪些渠道可以提供给我们修改内存内容值,可以得知: 892 | 893 | ```c 894 | dest = unimportant_buffer; 895 | __isoc99_scanf("%u %20s", &key, &s); 896 | strncpy(dest, &s, 0x10u); 897 | strncpy(unimportant_buffer, &s, 0x10u); 898 | ``` 899 | 900 | 我们回顾一下`strncpy`函数: 901 | 902 | ```c 903 | char *strncpy(char *dest, const char *src, int n) 904 | ``` 905 | 906 | 表示把`src`所指向的字符串中以`src`地址开始的前n个字节复制到`dest`所指的数组中,并返回被复制后的`dest` 907 | 908 | ```c 909 | strncmp(password_buffer, "DVTBOGZL", 8u) 910 | ``` 911 | 912 | 可以想到我们可以将`dest`指向`password_buffer`,然后将`src`的内容修改为`DVTBOGZL`即可,然后我们知道一开始`dest`已经指向`unimportant_buffer`,我们如何修改`dest`呢? 913 | 914 | 回忆起上一题的手法,观察这个函数: 915 | 916 | ```c 917 | __isoc99_scanf("%u %20s", &key, &s); 918 | ``` 919 | 920 | ```c 921 | -0000001C s db ? 922 | -0000001B db ? ; undefined 923 | -0000001A db ? ; undefined 924 | -00000019 db ? ; undefined 925 | -00000018 db ? ; undefined 926 | -00000017 db ? ; undefined 927 | -00000016 db ? ; undefined 928 | -00000015 db ? ; undefined 929 | -00000014 db ? ; undefined 930 | -00000013 db ? ; undefined 931 | -00000012 db ? ; undefined 932 | -00000011 db ? ; undefined 933 | -00000010 db ? ; undefined 934 | -0000000F db ? ; undefined 935 | -0000000E db ? ; undefined 936 | -0000000D db ? ; undefined 937 | -0000000C dest dd ? ; offset 938 | -00000008 db ? ; undefined 939 | -00000007 db ? ; undefined 940 | -00000006 db ? ; undefined 941 | -00000005 db ? ; undefined 942 | ``` 943 | 944 | `s`和`dest`刚好只相差16字节,完全覆盖`dest`刚好需要20个字节,而`scanf`函数刚好给我们提供了20个字节,这里用上一题差不多的手法就行,当我们控制了dest的地址后,s的前16个字节又是我们可控的,于是我们就实现了任意地址写的功能 945 | 946 | 老样子先上EXP: 947 | 948 | ```python 949 | import angr 950 | import claripy 951 | import sys 952 | 953 | def Go(): 954 | path_to_binary = "./16_angr_arbitrary_write" 955 | project = angr.Project(path_to_binary) 956 | 957 | initial_state = project.factory.entry_state() 958 | 959 | class ReplacementScanf(angr.SimProcedure): 960 | def run(self, format_string, param0, param1): 961 | scanf0 = claripy.BVS('scanf0', 32) 962 | scanf1 = claripy.BVS('scanf1', 20*8) 963 | 964 | for char in scanf1.chop(bits=8): 965 | self.state.add_constraints(char >= 48, char <= 96) 966 | 967 | scanf0_address = param0 968 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 969 | scanf1_address = param1 970 | self.state.memory.store(scanf1_address, scanf1) 971 | 972 | self.state.globals['solutions'] = (scanf0, scanf1) 973 | 974 | scanf_symbol = '__isoc99_scanf' 975 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 976 | 977 | 978 | def check_strncpy(state): 979 | strncpy_src = state.memory.load(state.regs.esp + 8, 4, endness=project.arch.memory_endness) 980 | strncpy_dest = state.memory.load(state.regs.esp + 4, 4, endness=project.arch.memory_endness) 981 | strncpy_len = state.memory.load(state.regs.esp + 12, 4, endness=project.arch.memory_endness) 982 | 983 | src_contents = state.memory.load(strncpy_src, strncpy_len) 984 | 985 | if state.solver.symbolic(src_contents) and state.solver.symbolic(strncpy_dest): 986 | password_string = 'DVTBOGZL' 987 | buffer_address = 0x4655544c 988 | 989 | does_src_hold_password = src_contents[-1:-64] == password_string 990 | does_dest_equal_buffer_address = strncpy_dest == buffer_address 991 | 992 | if state.satisfiable(extra_constraints=(does_src_hold_password, does_dest_equal_buffer_address)): 993 | state.add_constraints(does_src_hold_password, does_dest_equal_buffer_address) 994 | return True 995 | else: 996 | return False 997 | else: 998 | return False 999 | 1000 | simulation = project.factory.simgr(initial_state) 1001 | 1002 | def is_successful(state): 1003 | strncpy_address = 0x8048410 1004 | if state.addr == strncpy_address: 1005 | return check_strncpy(state) 1006 | else: 1007 | return False 1008 | 1009 | simulation.explore(find=is_successful) 1010 | 1011 | if simulation.found: 1012 | solution_state = simulation.found[0] 1013 | 1014 | scanf0, scanf1 = solution_state.globals['solutions'] 1015 | solution0 = (solution_state.solver.eval(scanf0)) 1016 | solution1 = (solution_state.solver.eval(scanf1,cast_to=bytes)) 1017 | print("[+] Success! Solution is: {0} {1}".format(solution0, solution1)) 1018 | else: 1019 | raise Exception('Could not find the solution') 1020 | 1021 | if __name__ == '__main__': 1022 | Go() 1023 | ``` 1024 | 1025 | 运行一下查看结果: 1026 | 1027 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/QQ%E5%9B%BE%E7%89%8720200903172213.png) 1028 | 1029 | 接下来我们来分析一下脚本: 1030 | 1031 | 一开始的脚本和上一题都没有什么太大的区别,也是在hook我们的scanf函数然后做条件约束为可见字符之类的 1032 | 1033 | ```python 1034 | def Go(): 1035 | path_to_binary = "./16_angr_arbitrary_write" 1036 | project = angr.Project(path_to_binary) 1037 | 1038 | initial_state = project.factory.entry_state() 1039 | 1040 | class ReplacementScanf(angr.SimProcedure): 1041 | def run(self, format_string, param0, param1): 1042 | scanf0 = claripy.BVS('scanf0', 32) 1043 | scanf1 = claripy.BVS('scanf1', 20*8) 1044 | 1045 | for char in scanf1.chop(bits=8): 1046 | self.state.add_constraints(char >= 48, char <= 96) 1047 | 1048 | scanf0_address = param0 1049 | self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) 1050 | scanf1_address = param1 1051 | self.state.memory.store(scanf1_address, scanf1) 1052 | 1053 | self.state.globals['solutions'] = (scanf0, scanf1) 1054 | 1055 | scanf_symbol = '__isoc99_scanf' 1056 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 1057 | ``` 1058 | 1059 | 这里我们从检查`puts`函数变为了检查`strncpy`函数,我们需要检查的是`strncpy`的`dest`参数是否已经修改为`password_buffer`,且`src`参数是否为密码字符串,这里需要注意的是此时的参数栈结构: 1060 | 1061 | ``` 1062 | esp + 7 -> /----------------\ 1063 | esp + 6 -> | puts | 1064 | esp + 5 -> | parameter | 1065 | esp + 4 -> \----------------/ 1066 | esp + 3 -> /----------------\ 1067 | esp + 2 -> | return | 1068 | esp + 1 -> | address | 1069 | esp -> \----------------/ 1070 | ``` 1071 | 1072 | 我们利用memory的load方法把参数内容提取出来 1073 | 1074 | ```python 1075 | def check_strncpy(state): 1076 | strncpy_src = state.memory.load(state.regs.esp + 8, 4, endness=project.arch.memory_endness) 1077 | strncpy_dest = state.memory.load(state.regs.esp + 4, 4, endness=project.arch.memory_endness) 1078 | strncpy_len = state.memory.load(state.regs.esp + 12, 4, endness=project.arch.memory_endness) 1079 | ``` 1080 | 1081 | 这里需要注意的是我们在检查src参数是否正确的时候需要的是里面的字符串内容,然而我们第一次获取的是`src`字符串的地址,我们还需要再调用一次load方法把src真正的内容提取出来 1082 | 1083 | ```python 1084 | src_contents = state.memory.load(strncpy_src, strncpy_len) 1085 | ``` 1086 | 1087 | 然后就是正常的参数验证环节,首先验证src字符串是否为我们想要的字符串,因为机器是小端序,所以我们需要`[-1:-64]`这样来比较 1088 | 1089 | ```python 1090 | if state.solver.symbolic(src_contents) and state.solver.symbolic(strncpy_dest): 1091 | password_string = 'DVTBOGZL' 1092 | buffer_address = 0x4655544c 1093 | 1094 | does_src_hold_password = src_contents[-1:-64] == password_string 1095 | does_dest_equal_buffer_address = strncpy_dest == buffer_address 1096 | ``` 1097 | 1098 | 当`src`字符串的确为我们需要的时候,接下来判定`dest`是否为`password_buffe`的地址 1099 | 1100 | ```python 1101 | if state.satisfiable(extra_constraints=(does_src_hold_password, does_dest_equal_buffer_address)): 1102 | state.add_constraints(does_src_hold_password, does_dest_equal_buffer_address) 1103 | return True 1104 | else: 1105 | return False 1106 | ``` 1107 | 1108 | 接下来都是比较常规的套路了,不再赘述 1109 | 1110 | ## 17_angr_arbitrary_jump 1111 | 1112 | 如题目所示,这题主要是学会任意地址跳转,即利用Angr处理无约束状态,老样子先检查一下文件: 1113 | 1114 | ```bash 1115 | syc@ubuntu:~/Desktop/TEMP$ checksec 17_angr_arbitrary_jump 1116 | [*] '/home/syc/Desktop/TEMP/17_angr_arbitrary_jump' 1117 | Arch: i386-32-little 1118 | RELRO: Partial RELRO 1119 | Stack: No canary found 1120 | NX: NX enabled 1121 | PIE: No PIE (0x8048000) 1122 | ``` 1123 | 1124 | 然后用IDA打开检查一下函数 1125 | 1126 | ```c 1127 | int __cdecl main(int argc, const char **argv, const char **envp) 1128 | { 1129 | print_msg(); 1130 | printf("Enter the password: "); 1131 | read_input(); 1132 | puts("Try again."); 1133 | return 0; 1134 | } 1135 | ``` 1136 | 1137 | ```c 1138 | int print_msg() 1139 | { 1140 | return printf("%s", msg); 1141 | } 1142 | ``` 1143 | 1144 | ```c 1145 | int read_input() 1146 | { 1147 | char v1; // [esp+1Ah] [ebp-1Eh] 1148 | 1149 | return __isoc99_scanf("%s", &v1); 1150 | } 1151 | ``` 1152 | 1153 | 然后我们还可以发现存在一个没有被调用到的函数`print_good` 1154 | 1155 | ```c 1156 | void __noreturn print_good() 1157 | { 1158 | puts("Good Job."); 1159 | exit(0); 1160 | } 1161 | ``` 1162 | 1163 | 我们不难发现这题里面的read_input()函数里的scanf存在栈溢出漏洞,简单来说这题就是非常简单的ROP使得我们跳转到print_good函数 1164 | 1165 | 话不多说先上EXP: 1166 | 1167 | ```python 1168 | import angr 1169 | import claripy 1170 | import sys 1171 | 1172 | def Go(): 1173 | path_to_binary = "./17_angr_arbitrary_jump" 1174 | project = angr.Project(path_to_binary) 1175 | initial_state = project.factory.entry_state() 1176 | 1177 | class ReplacementScanf(angr.SimProcedure): 1178 | def run(self, format_string, input_buffer_address): 1179 | input_buffer = claripy.BVS( 1180 | 'input_buffer', 64 * 8) 1181 | for char in input_buffer.chop(bits=8): 1182 | self.state.add_constraints(char >= '0', char <= 'z') 1183 | 1184 | self.state.memory.store( 1185 | input_buffer_address, input_buffer, endness=project.arch.memory_endness) 1186 | self.state.globals['solution'] = input_buffer 1187 | 1188 | scanf_symbol = '__isoc99_scanf' 1189 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 1190 | 1191 | simulation = project.factory.simgr( 1192 | initial_state, 1193 | save_unconstrained=True, 1194 | stashes={ 1195 | 'active' : [initial_state], 1196 | 'unconstrained' : [], 1197 | 'found' : [], 1198 | 'not_needed' : [] 1199 | } 1200 | ) 1201 | 1202 | def check_vulnerable(state): 1203 | return state.solver.symbolic(state.regs.eip) 1204 | 1205 | def has_found_solution(): 1206 | return simulation.found 1207 | 1208 | def has_unconstrained_to_check(): 1209 | return simulation.unconstrained 1210 | 1211 | def has_active(): 1212 | return simulation.active 1213 | 1214 | while (has_active() or has_unconstrained_to_check()) and (not has_found_solution()): 1215 | for unconstrained_state in simulation.unconstrained: 1216 | def should_move(s): 1217 | return s is unconstrained_state 1218 | simulation.move('unconstrained', 'found', filter_func=should_move) 1219 | simulation.step() 1220 | 1221 | if simulation.found: 1222 | solution_state = simulation.found[0] 1223 | solution_state.add_constraints(solution_state.regs.eip == 0x4d4c4749) 1224 | solution = solution_state.solver.eval( 1225 | solution_state.globals['solution'], cast_to=bytes) 1226 | print(solution[::-1]) 1227 | else: 1228 | raise Exception('Could not find the solution') 1229 | 1230 | if __name__ == '__main__': 1231 | Go() 1232 | ``` 1233 | 1234 | 运行一下验证结果: 1235 | 1236 | ![](https://note-book.obs.cn-east-3.myhuaweicloud.com/Angr_CTF/%E5%9B%9B/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200903220756.png) 1237 | 1238 | 下面开始逐步讲解EXP 1239 | 1240 | 当一条指令有太多可能的分支时,就会出现无约束状态。当指令指针完全是符号指针时,就会发生这种情况,这意味着用户输入可以控制计算机执行的代码的地址 1241 | 1242 | ```assembly 1243 | mov user_input, eax 1244 | jmp eax 1245 | ``` 1246 | 1247 | 例如此题存在的栈溢出漏洞就可以让我们的程序进入无约束状态。一般情况下,当Angr遇到不受约束的状态时,它会将其抛出。在我们的例子中,我们希望利用无约束状态来跳转到我们选择的位置。我们将在稍后了解如何禁用Angr的默认行为 1248 | 1249 | 一开始的情况都是一样的 1250 | 1251 | ```python 1252 | def Go(): 1253 | path_to_binary = "./17_angr_arbitrary_jump" 1254 | project = angr.Project(path_to_binary) 1255 | initial_state = project.factory.entry_state() 1256 | ``` 1257 | 1258 | 然后老样子Hook掉我们的scanf函数,使得输入的信息都是可见字符串 1259 | 1260 | ```python 1261 | class ReplacementScanf(angr.SimProcedure): 1262 | def run(self, format_string, input_buffer_address): 1263 | input_buffer = claripy.BVS( 1264 | 'input_buffer', 64 * 8) 1265 | for char in input_buffer.chop(bits=8): 1266 | self.state.add_constraints(char >= '0', char <= 'z') 1267 | 1268 | self.state.memory.store( 1269 | input_buffer_address, input_buffer, endness=project.arch.memory_endness) 1270 | self.state.globals['solution'] = input_buffer 1271 | 1272 | scanf_symbol = '__isoc99_scanf' 1273 | project.hook_symbol(scanf_symbol, ReplacementScanf()) 1274 | ``` 1275 | 1276 | 然后我们将改变Angr的模拟引擎的默认设置,参数`save_unconstrained=True`时指定Angr不抛出不受约束的状态。相反,它会将它们移动到名为`simul.com unconstrained`的stashes 中。此外,我们将使用一些默认情况下不包含的stashes ,如'found'和'not_needed'。稍后将学习如何使用它们 1277 | 1278 | - active:程序仍能进一步执行 1279 | - deadended:程序结束 1280 | - errored:Angr执行中出现错误的状态 1281 | - unconstrained:不受约束的状态 1282 | - found:找到路径答案的状态 1283 | - not_needed:所有其它情况 1284 | 1285 | ```python 1286 | simulation = project.factory.simgr( 1287 | initial_state, 1288 | save_unconstrained=True, 1289 | stashes={ 1290 | 'active' : [initial_state], 1291 | 'unconstrained' : [], 1292 | 'found' : [], 1293 | 'not_needed' : [] 1294 | } 1295 | ) 1296 | ``` 1297 | 1298 | 接下来我们将定义四个函数来获得我们想要获得的程序状态 1299 | 1300 | ```python 1301 | #检查无约束状态是否可利用 1302 | def check_vulnerable(state): 1303 | return state.solver.symbolic(state.regs.eip) 1304 | 1305 | def has_found_solution(): 1306 | return simulation.found 1307 | #检查是否还有未受约束的状态需要检查 1308 | def has_unconstrained_to_check(): 1309 | return simulation.unconstrained 1310 | #active是可以进一步探索的所有状态的列表 1311 | def has_active(): 1312 | return simulation.active 1313 | ``` 1314 | 1315 | 我们之前一直使用的`simulation.explore`方法并不适合我们现在这种情况,因为`find`参数指定的方法不会在无约束状态下被调用,想要自己探索未约束情况下的二进制代码,我们需要自己编写解决方案 1316 | 1317 | ```python 1318 | while (has_active() or has_unconstrained_to_check()) and (not has_found_solution()): 1319 | for unconstrained_state in simulation.unconstrained: 1320 | def should_move(s): 1321 | return s is unconstrained_state 1322 | simulation.move('unconstrained', 'found', filter_func=should_move) 1323 | simulation.step() 1324 | ``` 1325 | 1326 | 上面这个解决方案的思路是,因为我们需要的是无约束状态,如果出现了约束状态下的解则求解失败,故有`and (not has_found_solution())`,且有待检查的状态才继续循环遍历所有的状态。最终的结果是找到了一个未约束状态 1327 | 1328 | 接下来的代码和之前的大同小异,就不再赘述 1329 | 1330 | > Tips:出现一些奇怪的问题,建议参考一下官方关于Angr在改用Python3之后的一些API变化: 1331 | > 1332 | > Migrating to angr 8 —— https://docs.angr.io/appendix/migration#deprecations-and-name-changes 1333 | 1334 | ## 参考文献 1335 | 1336 | 【1】angr官方文档—— https://docs.angr.io/core-concepts 1337 | 1338 | 【2】angr 系列教程(一)核心概念及模块解读—— https://xz.aliyun.com/t/7117#toc-14 1339 | 1340 | 【3】Enhancing Symbolic Execution with Veritesting —— Carnegie Mellon University 1341 | 1342 | 【4】angr 文档翻译(1-2):加载一个二进制文件——CLE和angr工程 —— https://www.jianshu.com/p/f660800bb70f 1343 | 1344 | -------------------------------------------------------------------------------- /笔记/04/图片/261737366704808.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/261737366704808.jpg -------------------------------------------------------------------------------- /笔记/04/图片/QQ图片20200901210631.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/QQ图片20200901210631.png -------------------------------------------------------------------------------- /笔记/04/图片/QQ图片20200903172213.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/QQ图片20200903172213.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200818201017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200818201017.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200818214519.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200818214519.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200819172721.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200819172721.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200819173153.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200819173153.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200819173251.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200819173251.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200819173332.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200819173332.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200819174508.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200819174508.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200819202222.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200819202222.png -------------------------------------------------------------------------------- /笔记/04/图片/微信图片_20200819220653.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信图片_20200819220653.png -------------------------------------------------------------------------------- /笔记/04/图片/微信截图_20200901091836.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信截图_20200901091836.png -------------------------------------------------------------------------------- /笔记/04/图片/微信截图_20200901092304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信截图_20200901092304.png -------------------------------------------------------------------------------- /笔记/04/图片/微信截图_20200901210802.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信截图_20200901210802.png -------------------------------------------------------------------------------- /笔记/04/图片/微信截图_20200901223730.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信截图_20200901223730.png -------------------------------------------------------------------------------- /笔记/04/图片/微信截图_20200903220756.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZERO-A-ONE/AngrCTF_FITM/a4f6bf8116322be361a97b22e99e173656297389/笔记/04/图片/微信截图_20200903220756.png --------------------------------------------------------------------------------