├── 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 | 
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 | 
291 |
292 | 
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 | 
358 |
359 | 查看一下获得的源代码的main函数
360 |
361 | 
362 |
363 | 这题就是为了angr而生的题目,我们只需要让执行流只进入maybe_good函数,而避免进入avoid_me函数即可,现在需要拿到这两个函数的地址
364 |
365 | 
366 |
367 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
549 |
550 | 我们可以看到`malloc()`分配了两个缓冲区,因为`maclloc()`函数只有一个参数,通过`push 9`不难推测出两个缓冲区大小为9字节(IDA也能直接看到的说),并返回缓冲区地址到EAX寄存器中
551 |
552 | 实际上,可以看到在两次调用之后,根据`mov ds:buffer0, eax`和`mov ds:buffer1, eax`得知开辟后的缓冲区被复制到标识为`buffer0`和`buffer1`的两个存储区中
553 |
554 | 
555 |
556 | 且根据IDA可以得知`buffer0`的地址为**0xABCC8A4**,`buffer1`的地址为**0xABCC8AC**
557 |
558 | 回到我们最开始认识angr的时候,我们知道angr并没有真正“运行”二进制文件(至少到目前为止),它只是在模拟运行状态,因此它实际上不需要将内存分配到堆中,实际上可以伪造任何地址。我们所做的是我们在堆栈选择两个地址存放我们的缓冲区地址。之后我们告诉angr,将两个fake address分别保存到 `buffer0`,`buffer1` ,因为程序实际执行的时候就会把 **malloc**返回的地址保存到这里。最后我们把符号位向量保存到 伪造的地址里。
559 |
560 | 
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 | 
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 | 
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 | 
332 |
333 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
176 |
177 | 其实这题就是体验一下Veritesting的强大功能
178 |
179 | ## 13_angr_static_binary
180 |
181 | 这题如题就是主要学习如何使用angr解出静态编译的题目,学习如何Hook静态库函数
182 |
183 | ### 静态编译
184 |
185 | 不同于动态编译是将应用程序需要的模块都编译成动态链接库,启动程序(初始化)时,这些模块不会被加载,运行时用到哪个模块就调用哪个。静态编译就是在编译时,把所有模块都编译进可执行文件里,当启动这个可执行文件时,所有模块都被加载进来,反映在现实中就是程序体积会相对大一些,在IDA中会发现所有用到函数都是静态编译好的
186 |
187 | 
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 | 
312 |
313 | 这题解题真正需要用的函数也就`printf`,`scnaf`,`puts`,即完成了angr需要的输出、输入、路径选择的功能,我们手动找到这几个函数的地址
314 |
315 | 
316 |
317 | 
318 |
319 | 
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 | 
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 | 
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 | 
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 | 
647 |
648 | 发现程序中存在"Good Job."字符串验证了我们之前的想法,我们目前需要做的事情就是把s存储的地址修改为Good Job所在的地址即**0x594e4257**
649 |
650 | 那我们如何修改呢,视线回到充满着漏洞和内存泄漏的scanf函数,观察一下v4的栈结构
651 |
652 | 
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 | 
732 |
733 | 
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 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------