angr 安装
angr 需要使用到 z3、pyvex ,但使用的版本会与原本有去区别,所以推荐在 virtualenv 虚拟 python 环境中安装 angr
自定义 docker
基于 debian 11 自定义制作的 docker 镜像,内含 aconda、jupyterlib、angr
1 2 3
| git clone --depth=1 [email protected]:skyedai910/anaconda_angr_docker.git cd anaconda_angr_docker docker-compose up -d --build
|
- 端口映射:8888:8888
- 卷轴挂载:./volumes:/root
angr ctf
用于训练 angr 的题目仓库:https://github.com/jakespringer/angr_ctf
00_angr_find
简单加密程序,找到校验成功的内存地址之后用 angr 找到路径即可,输入值就是明文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import angr import sys
binary_path = "00_angr_find"
def main(argv): project = angr.Project(binary_path) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state)
print_good_address = 0x804867d bad_address = 0x0804866B simulation.explore(find=print_good_address,avoid=bad_address)
if simulation.found: solution_state = simulation.found[0] print(solution_state.posix.dumps(sys.stdin.fileno()))
if __name__ == '__main__': main(sys.argv)
|
01_angr_avoid
这题学习使用 avoid 规避某些路径。这里存在疑问就是:一开始将 good_address 设置在函数开头 0x080485B5;bad_address 也是设置在函数头部 0x080485a8,但这样 fuzz 出来的密码不对。将 good_address 设置在 puts 输出成功提示符 0x080485e5 就能出来正确结果,然后进一步测试将其设置为 0x080485C9 等函数内部的地址只有部分能成功 fuzz 出正确结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import angr import sys
binary_path = "01_angr_avoid"
def main(argv): project = angr.Project(binary_path) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state)
print_good_address = 0x080485e5 bad_address = 0x080485a8 simulation.explore(find=print_good_address,avoid=bad_address)
if simulation.found: solution_state = simulation.found[0] print(solution_state.posix.dumps(sys.stdin.fileno()))
if __name__ == '__main__': main(sys.argv)
|
02_angr_find_condition
虽然汇编看不了,但是还是能查到输出成功和错误提示的地址,但利用上面两题脚本 fuzz 不出来。
这题不直接写成功路径的内存地址,而是用探索满足条件的状态(stdout 包含 Good Job )。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import angr import sys
binary_path = "02_angr_find_condition"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state)
simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] print(solution_state.posix.dumps(sys.stdin.fileno())) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
03_angr_symbolic_registers
操作 scanf 在寄存器上的值
程序用scanf("%x %x %x", &v1, &v2, &v3);
读取 3 个值放在 eax、ebx、edx ,三者经过复杂运算后全为 0 则成功。
angr 不支持使用 scanf 读取多个内容(scanf("%u %u)
),需要将程序起点设置在 scanf 之后,并手动将值传入寄存器
定义 BVS 与 z3 使用一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import angr import claripy import sys
binary_path = "03_angr_symbolic_registers"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) start_address = 0x80488d1 initial_state = project.factory.blank_state(addr=start_address) simulation = project.factory.simgr(initial_state)
password0 = claripy.BVS('password0', 32) password1 = claripy.BVS('password1', 32) password2 = claripy.BVS('password2', 32)
initial_state.regs.eax = password0 initial_state.regs.ebx = password1 initial_state.regs.edx = password2
simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] solution0 = solution_state.solver.eval(password0) solution1 = solution_state.solver.eval(password1) solution2 = solution_state.solver.eval(password2) print(hex(solution0), hex(solution1), hex(solution2)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
04_angr_symbolic_stack
操作 scanf 在栈上的值
这个程序 scanf 将内容存储在栈上,需要自行构造栈空间绕过 scanf 输入。
(32 位程序)call 后面调整栈顶 esp 也是算同一个栈帧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| .text:08048679 push ebp .text:0804867A mov ebp, esp .text:0804867C sub esp, 18h .text:0804867F sub esp, 4 .text:08048682 lea eax, [ebp+var_10] .text:08048685 push eax .text:08048686 lea eax, [ebp+var_C] .text:08048689 push eax .text:0804868A push offset aUU ; "%u %u" .text:0804868F call ___isoc99_scanf .text:08048694 add esp, 10h //scanf结束 .text:08048697 mov eax, [ebp+var_C] .text:0804869A sub esp, 0Ch .text:0804869D push eax .text:0804869E call complex_function0 .text:080486A3 add esp, 10h //complex_function0结束 .text:080486A6 mov [ebp+var_C], eax .text:080486A9 mov eax, [ebp+var_10] .text:080486AC sub esp, 0Ch .text:080486AF push eax .text:080486B0 call complex_function1 .text:080486B5 add esp, 10h //complex_function1结束
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| import angr import claripy import sys
binary_path = "04_angr_symbolic_stack"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) start_address = 0x8048697 initial_state = project.factory.blank_state(addr=start_address) password0 = claripy.BVS('p0', 4*8) password1 = claripy.BVS('p1', 4*8) initial_state.regs.ebp = initial_state.regs.esp initial_state.regs.esp -= 0x8 simulation = project.factory.simgr(initial_state) initial_state.stack_push(password0) initial_state.stack_push(password1)
simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] solution0 = solution_state.solver.eval(password0) solution1 = solution_state.solver.eval(password1) print(hex(solution0), hex(solution1)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
05_angr_symbolic_memory
操控 scanf 在全局变量上的值
memory.store 对内存操作需要指定 endness 为程序的端序(小端序),angr 默认是大端序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| import angr import claripy import sys
binary_path = "05_angr_symbolic_memory"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) start_address = 0x8048606 initial_state = project.factory.blank_state(addr=start_address) password0 = claripy.BVS('p0', 8*8) password1 = claripy.BVS('p1', 8*8) password2 = claripy.BVS('p2', 8*8) password3 = claripy.BVS('p3', 8*8)
password0_address = 0xa29faa0 initial_state.memory.store(password0_address, password0, endness=project.arch.memory_endness) password1_address = 0xa29faa8 initial_state.memory.store(password1_address, password1, endness=project.arch.memory_endness) password2_address = 0xa29fab0 initial_state.memory.store(password2_address, password2, endness=project.arch.memory_endness) password3_address = 0xa29fab8 initial_state.memory.store(password3_address, password3, endness=project.arch.memory_endness) simulation = project.factory.simgr(initial_state)
simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] solution0 = solution_state.solver.eval(password0) solution1 = solution_state.solver.eval(password1) solution2 = solution_state.solver.eval(password2) solution3 = solution_state.solver.eval(password3) print(hex(solution0), hex(solution1), hex(solution2), hex(solution3)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
06_angr_symbolic_dynamic_memory
操控 scanf 在堆上的值
- 向 chunk_list 写入 fake_heap_addr 。伪堆地址其他师傅文章说是可以随意找,我这里是关闭 aslr gdb 找堆地址
- 向堆写入内容
注意官方 exp 打出来答案是不能通过校验的,原因是向堆写入内容没有设置端序,记住使用 store 时一定要设置端序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| import angr import claripy import sys
binary_path = "06_angr_symbolic_dynamic_memory"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) start_address = 0x804869e initial_state = project.factory.blank_state(addr=start_address) password0 = claripy.BVS('p0', 8*8) password1 = claripy.BVS('p1', 8*8)
chunk_list = 0xa79A118 heap_addr = 0xa79b158 initial_state.memory.store(chunk_list, heap_addr, endness=project.arch.memory_endness) initial_state.memory.store(chunk_list+0x8, heap_addr+0x10, endness=project.arch.memory_endness) initial_state.memory.store(heap_addr, password0, endness=project.arch.memory_endness) initial_state.memory.store(heap_addr+0x10, password1, endness=project.arch.memory_endness) simulation = project.factory.simgr(initial_state)
simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] solution0 = solution_state.solver.eval(password0) solution1 = solution_state.solver.eval(password1) print(hex(solution0), hex(solution1)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
07_angr_symbolic_file
控制在 file 的值
程序先从 stdin 输入内容,将 stdin 内容写入到文件,然后再 file_read 从文件读取 stdin 输入的内容。程序开始绕过这个前面两步,直接使用 angr 虚拟文件,让程序读取虚拟文件内容
- 生成 angr 虚拟文件
- 注入到程序中
symbolic_file_backing_memory 在新版已被取消,官方 exp 可能打不通
由于不会设置控制 file 输入时的端序设置,所以下面 exp 结果是大端序,需要每 8 字节进行字节翻转。但是如果输出是字符串格式则是小端序(最后注释部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import angr import claripy import sys
binary_path = "07_angr_symbolic_file"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) start_address = 0x80488db initial_state = project.factory.blank_state(addr=start_address)
filename = 'WCEXPXBW.txt' symbolic_file_size_bytes = 64 password0 = claripy.BVS('p0', symbolic_file_size_bytes*8) password_file = angr.storage.SimFile(filename, content=password0, size=symbolic_file_size_bytes) initial_state.fs.insert(filename, password_file)
simulation = project.factory.simgr(initial_state)
simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] ''' 输出结果是小端序 solution0 = solution.solver.eval(password, cast_to=bytes) print(solution0) ''' solution0 = solution_state.solver.eval(password0) print(hex(solution0)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
08_angr_constraints
添加约束条件,避免路径爆炸
- 每个 if 判断 angr 会创建两条路径
- 如果使用 strcmp 等结合 for 和 if 对一段字符串逐个字节比较会产生路径爆炸。长度 16 字节的字符串,会产生 2**16 条路径
示例程序中 check_equals_AUPDNNPROEZRJWKB 就是使用逐字节比较,需要添加约束条件避免路径爆炸文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int __cdecl main(int argc, const char **argv, const char **envp) { signed int i;
qmemcpy(password, "AUPDNNPROEZRJWKB", sizeof(password)); memset(&buffer, 0, 0x11u); printf("Enter the password: "); __isoc99_scanf("%16s", &buffer); for ( i = 0; i <= 15; ++i ) *(_BYTE *)(i + 134520912) = complex_function(*(char *)(i + 134520912), 15 - i); if ( check_equals_AUPDNNPROEZRJWKB((int)&buffer, 0x10u) ) puts("Good Job."); else puts("Try again."); return 0; }
|
和前面题目一样绕过 scanf ,手动向内存注入 password ,运行到 0x8048671 进入 check 函数之前,添加约束条件:
- 提取复杂运算后的密文
- 添加约束条件:密文需要等于
BWYRUBQCMVSBRGFU
这里比较麻烦的还是大小端序的问题,记住这个规律大部分情况能分辨怎么配置。
假设程序是小端序:
angr 模拟量与程序内存值比较:需要将模拟量配置为小端序
例题:[第五题](# 05_angr_symbolic_memory)开始都是
angr 模拟量与已知值比较(自定义值、脚本里面写的):不一定需要配置为小端序,只要模拟量和已知值是同一端序即可
例题:[08_angr_constraints](# 08_angr_constraints)
小端序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| import angr import claripy import sys
binary_path = "08_angr_constraints"
def main(argv): project = angr.Project(binary_path) start_address = 0x804862a initial_state = project.factory.blank_state(addr=start_address) password0 = claripy.BVS('p0', 16*8) password0_address = 0x804a050 initial_state.memory.store(password0_address, password0, endness=project.arch.memory_endness)
simulation = project.factory.simgr(initial_state)
address_to_check_constraint = 0x8048671
simulation.explore(find=address_to_check_constraint)
if simulation.found: solution_state = simulation.found[0]
constrained_parameter_address = 0x804a050 constrained_parameter_size_bytes = 16 constrained_parameter_bitvector = solution_state.memory.load( constrained_parameter_address, constrained_parameter_size_bytes, endness=project.arch.memory_endness )
constrained_parameter_desired_value = 'BWYRUBQCMVSBRGFU'[::-1] solution_state.add_constraints(constrained_parameter_bitvector == constrained_parameter_desired_value)
solution0 = solution_state.solver.eval(password0) print(hex(solution0)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
大端序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| import angr import claripy import sys
binary_path = "08_angr_constraints"
def main(argv): project = angr.Project(binary_path) start_address = 0x804862a initial_state = project.factory.blank_state(addr=start_address) password0 = claripy.BVS('p0', 16*8) password0_address = 0x804a050 initial_state.memory.store(password0_address, password0)
simulation = project.factory.simgr(initial_state)
address_to_check_constraint = 0x8048671
simulation.explore(find=address_to_check_constraint)
if simulation.found: solution_state = simulation.found[0]
constrained_parameter_address = 0x804a050 constrained_parameter_size_bytes = 16 constrained_parameter_bitvector = solution_state.memory.load( constrained_parameter_address, constrained_parameter_size_bytes )
constrained_parameter_desired_value = 'BWYRUBQCMVSBRGFU' solution_state.add_constraints(constrained_parameter_bitvector == constrained_parameter_desired_value)
solution0 = solution_state.solver.eval(password0) print(hex(solution0)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
09_angr_hooks
通过地址 hook 函数
- 新 angr 可以支持 scanf 输入
- 两次输入,两次检查是否等于
XKSPZSJKJYQCQXZV
需要 hook check 函数替换为自定义比较函数,返回值需要根据原函数编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| import angr import claripy import sys
binary_path = "09_angr_hooks"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) initial_state = project.factory.entry_state() check_equals_called_address = 0x80486b8 instruction_to_skip_length = 5
@project.hook(check_equals_called_address, length=instruction_to_skip_length) def skip_check_equals_(state): user_input_buffer_address = 0x804a054 user_input_buffer_length = 16 user_input_string = state.memory.load( user_input_buffer_address, user_input_buffer_length ) check_against_string = 'XKSPZSJKJYQCQXZV' state.regs.eax = claripy.If( user_input_string == check_against_string, claripy.BVV(1, 32), claripy.BVV(0, 32) )
simulation = project.factory.simgr(initial_state) simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] solution0 = solution_state.posix.dumps(sys.stdin.fileno()) print((solution0)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
10_angr_simprocedures
通过符号名(函数名)hook 函数
通过汇编视图可以看到 check 调用关系极度复杂,已经不能地址 hook 函数解决(太多了),使用 SimProcedure
自定义函数并 hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| import angr import claripy import sys
binary_path = "10_angr_simprocedures"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) initial_state = project.factory.entry_state() class ReplacementCheckEquals(angr.SimProcedure): def run(self, to_check, length): user_input_buffer_address = to_check user_input_buffer_length = length user_input_string = self.state.memory.load( user_input_buffer_address, user_input_buffer_length ) check_against_string = 'WQNDNKKWAWOLXBAC' return claripy.If( user_input_string == check_against_string, claripy.BVV(1, 32), claripy.BVV(0, 32) ) check_equals_symbol = 'check_equals_WQNDNKKWAWOLXBAC' project.hook_symbol(check_equals_symbol, ReplacementCheckEquals())
simulation = project.factory.simgr(initial_state) simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] solution0 = solution_state.posix.dumps(sys.stdin.fileno()) print((solution0)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
11_angr_sim_scanf
需要 hook scanf 函数,不能直接将程序起点设置到 scanf 之后,因为 scanf 前面有秘钥生成过程,如果跳过秘钥为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import angr import claripy import sys
binary_path = "11_angr_sim_scanf"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) initial_state = project.factory.entry_state() class ReplacementCheckEquals(angr.SimProcedure): def run(self, format_string, param0, param1): scanf0 = claripy.BVS('scanf0', 32) scanf1 = claripy.BVS('scanf1', 32)
scanf0_address = param0 self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) scanf1_address = param1 self.state.memory.store(scanf1_address, scanf1, endness=project.arch.memory_endness) self.state.globals['solutions'] = (scanf0, scanf1)
check_equals_symbol = '__isoc99_scanf' project.hook_symbol(check_equals_symbol, ReplacementCheckEquals())
simulation = project.factory.simgr(initial_state) simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] stored_solutions = solution_state.globals['solutions'] solution = ' '.join(map(str, map(solution_state.se.eval, stored_solutions))) print((solution)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
12_angr_veritesting
开启Veritesting技术解决路径爆炸问题
创建 SimulationManager 时通过 veritesting=True 来开启Veritesting技术
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import angr import claripy import sys
binary_path = "12_angr_veritesting"
def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output
def is_bad(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output
def main(argv): project = angr.Project(binary_path) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state, veritesting=True) simulation.explore(find=is_successful,avoid=is_bad)
if simulation.found: solution_state = simulation.found[0] solution0 = solution_state.posix.dumps(sys.stdin.fileno()) print((solution0)) else: print("can't no find solve")
if __name__ == '__main__': main(sys.argv)
|
13_angr_static_binary
静态程序 hook 系统函数
由于是静态程序系统函数被包含在 elf 中,导致 angr 会对系统函数本身进行路径测试,而导致的效率低下、路径爆炸问题(可以用 00_angr_find exp 测试一下观察路径测试的内存地址就知道了)。
因此对于静态程序就需要 hook 系统函数提高效率,可以理解为告诉 angr 这是系统函数,这个函数不需要路径测试。
不是全部的系统函数都需要 hook ,只需要 hook 通向目标路径上所调用的系统函数。这题最后目标是 puts("Good Job.")
,涉及的系统调用顺序是:
_isoc99_scanf 原型是 scanf ,gcc 开启保护后会将 scanf 编译成 _isoc99_scanf
- __libc_start_main
- printf
- scanf
- strcmp
- puts
strcmp 可以不替换,因为 plt 表里面有 strcmp ,也可以双击 strcmp 进去,可以看到是一个跳转函数,而不是函数具体实现的代码,这里对比 strcmp 和其他函数就知道了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import angr import sys
binary_path = "13_angr_static_binary"
def main(argv): project = angr.Project(binary_path) initial_state = project.factory.entry_state()
project.hook(0x804f350, angr.SIM_PROCEDURES['libc']['puts']()) project.hook(0x804ed80, angr.SIM_PROCEDURES['libc']['scanf']()) project.hook(0x804ed40, angr.SIM_PROCEDURES['libc']['printf']()) project.hook(0x8048d10, angr.SIM_PROCEDURES['glibc']['__libc_start_main']())
simulation = project.factory.simgr(initial_state)
print_good_address = 0x080489E6 bad_address = 0x080489D4 simulation.explore(find=print_good_address,avoid=bad_address)
if simulation.found: solution_state = simulation.found[0] print(solution_state.posix.dumps(sys.stdin.fileno()))
if __name__ == '__main__': main(sys.argv)
|
14_angr_shared_library
fuzz pie 程序设置加载基地址
1 2 3 4 5 6
| base = 0 project = angr.Project(path_to_binary, load_options={ 'main_opts' : { 'custom_base_addr' : base } })
|
当 angr fuzz 开启 pie 程序时,默认基地址是 0x4000000 。除了小部分题目(在固定地址上申请空间那种虚拟机题目),加载地址在哪里没有影响,因为 angr 对于 pie 程序是用偏移地址去定位代码,基地址只是影响 text 段位置而已。
总结起来就是:当程序有对明确固定地址的操作时,需要注意加载基地址,其他不需要。
对于这条题目将校验放在了函数库(开启 pie ),函数库没有对固定地址读写操作,不需要加载基地址,直接对函数库进行 fuzz
官方 exp base 是 0x4000000 默认值,可以任意改都能出正确结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import angr import claripy import sys
binary_path = "lib14_angr_shared_library.so"
def main(argv): base = 0 project = angr.Project(binary_path, load_options={ 'main_opts' : { 'custom_base_addr' : base } })
buffer_pointer = claripy.BVV(0x3000000, 32) validate_function_address = base + 0x6d7 initial_state = project.factory.call_state(validate_function_address, buffer_pointer, claripy.BVV(8, 32))
password = claripy.BVS('password', 8*8) initial_state.memory.store(buffer_pointer, password)
simulation = project.factory.simgr(initial_state)
print_good_address = base + 0x783 simulation.explore(find=print_good_address)
if simulation.found: solution_state = simulation.found[0] solution_state.add_constraints(solution_state.regs.eax != 0) print(solution_state.se.eval(password,cast_to=bytes))
if __name__ == '__main__': main(sys.argv)
|
参考资料