最近想跟着angr_ctf学习了一下符号执行(用来在做逆向的时候偷懒),还是挺有收获的
PS :
angr == 8.20.1.7
二进制文件和脚本存储于https://github.com/ycdxsb/Challenges/tree/master/angr_ctf
基本使用 常规使用 00_angr_find
01_angr_avoid
简单设置目标地址和不想达到的地址即可,可以是单个地址也可以是很多地址,所以这里都用列表统一了一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import angrimport sysdef main (filepath) : project = angr.Project(filepath) init_state = project.factory.entry_state() sim = project.factory.simgr(init_state) find = [0x8048678 ] avoid = [] sim.explore(find=find,avoid=avoid) if sim.found: solution_state = sim.found[0 ] print(solution_state.posix.dumps(sys.stdin.fileno())) else : raise Exception('Could not find the solution' ) if __name__=="__main__" : if (len(sys.argv)!=2 ): print('usage:python angr_basic.py filepath' ) filepath = sys.argv[1 ] main(filepath)
设置判断函数 对于一些情况,我们不需要去仔细分析要避免或者搜索哪些分支,只知道正确会输出什么,错误会输出什么,这个时候可以使用判断函数代替之前的地址列表
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 import angrimport sysdef main (filepath) : project = angr.Project(filepath) init_state = project.factory.entry_state() sim = project.factory.simgr(init_state) def find (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output def avoid (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output sim.explore(find=find,avoid=avoid) if sim.found: solution_state = sim.found[0 ] print(solution_state.posix.dumps(sys.stdin.fileno())) else : raise Exception('Could not find the solution' ) if __name__=="__main__" : if (len(sys.argv)!=2 ): print('usage:python angr_basic.py filepath' ) filepath = sys.argv[1 ] main(filepath)
符号化读入 由于angr不能处理复杂的scanf读入情况,因此要自己越过scanf对scanf后的状态赋值,并从scanf后开始执行
符号化寄存器 03_angr_symbolic_registers
1 2 3 4 5 6 7 .text:08048882 call _printf .text:08048887 add esp, 10h .text:0804888A call get_user_input .text:0804888F mov [ebp+var_14], eax .text:08048892 mov [ebp+var_10], ebx .text:08048895 mov [ebp+var_C], edx .text:08048898 sub esp, 0Ch
可以看到在get_user_input函数执行后,读入的三个数字分别存在eax,ebx和edx中,因此我们越过get_user_input函数开始执行,并且将其中的eax,ebx,edx赋值成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 import angrimport sysimport claripydef main (filepath) : project = angr.Project(filepath) start_address = 0x804888F init_state = project.factory.blank_state(addr = start_address) password_bits = 32 password0 = claripy.BVS('password0' , password_bits) password1 = claripy.BVS('password1' , password_bits) password2 = claripy.BVS('password2' , password_bits) init_state.regs.eax = password0 init_state.regs.ebx = password1 init_state.regs.edx = password2 sim = project.factory.simgr(init_state) def find (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output def avoid (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output sim.explore(find=find,avoid=avoid) if sim.found: solution_state = sim.found[0 ] solution0 = format(solution_state.se.eval(password0),'x' ) solution1 = format(solution_state.se.eval(password1),'x' ) solution2 = format(solution_state.se.eval(password2),'x' ) print(solution0,solution1,solution2) else : raise Exception('Could not find the solution' ) if __name__=="__main__" : if (len(sys.argv)!=2 ): print('usage:python angr_basic.py filepath' ) filepath = sys.argv[1 ] main(filepath)
符号化栈 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int handle_user () { int result; int v1; int v2; __isoc99_scanf("%u %u" , &v2, &v1); v2 = complex_function0(v2); v1 = complex_function1(v1); if ( v2 == 887024739 && v1 == 1261126168 ) result = puts ("Good Job." ); else result = puts ("Try again." ); return result; }
可以看到,以%u
格式读入后的数据v1和v2存储在栈上,那么当我们越过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 import angrimport sysimport claripydef main (filepath) : project = angr.Project(filepath) start_address = 0x8048697 init_state = project.factory.blank_state(addr=start_address) init_state.regs.ebp = init_state.regs.esp init_state.regs.esp -= 8 password0 = claripy.BVS('password0' , 32 ) password1 = claripy.BVS('password1' , 32 ) init_state.stack_push(password0) init_state.stack_push(password1) sim = project.factory.simgr(init_state) def find (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output def avoid (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output sim.explore(find=find, avoid=avoid) if sim.found: solution_state = sim.found[0 ] solution0 = solution_state.se.eval(password0) solution1 = solution_state.se.eval(password1) print(solution0, solution1) else : raise Exception('Could not find the solution' ) if __name__ == "__main__" : if (len(sys.argv) != 2 ): print('usage:python angr_basic.py filepath' ) filepath = sys.argv[1 ] main(filepath)
符号化bss 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __cdecl main (int argc, const char **argv, const char **envp) { int i; memset (user_input, 0 , 0x21 u); printf ("Enter the password: " ); __isoc99_scanf("%8s %8s %8s %8s" , user_input, &unk_B368DA8, &unk_B368DB0, &unk_B368DB8); for ( i = 0 ; i <= 31 ; ++i ) *(_BYTE *)(i + 0xB368DA0 ) = complex_function(*(char *)(i + 0xB368DA0 ), i); if ( !strncmp (user_input, "IIZAUPRCZIZQJGKOJGDEAPHFFOBMNITD" , 0x20 u) ) puts ("Good Job." ); else puts ("Try again." ); return 0 ; }
可以看到是以字符串形式读入四个8字节的字符串,然后存到bss段上,所以越过scanf,并且在bss段上赋上符号数据即可
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 import angrimport sysimport claripydef main (filepath) : project = angr.Project(filepath) start_address = 0x8048601 init_state = project.factory.blank_state(addr=start_address) passwords = [claripy.BVS("password%d" % i, 64 ) for i in range(4 )] passwords_address = [0xB368DA0 , 0xB368DA8 , 0xB368DB0 , 0xB368DB8 ] for i in range(4 ): init_state.memory.store(passwords_address[i], passwords[i]) sim = project.factory.simgr(init_state) def find (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output def avoid (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output sim.explore(find=find, avoid=avoid) if sim.found: solution_state = sim.found[0 ] solutions = [] for i in range(4 ): solutions.append(solution_state.se.eval( passwords[i], cast_to = bytes)) print(b" " .join(solutions)) else : raise Exception('Could not find the solution' ) if __name__ == "__main__" : if (len(sys.argv) != 2 ): print('usage:python angr_basic.py filepath' ) filepath = sys.argv[1 ] main(filepath)
符号化堆 06_angr_symbolic_dynamic_memory
1 2 3 4 5 6 7 8 9 10 11 12 13 int __cdecl main (int argc, const char **argv, const char **envp) { char *v3; char *v4; int v6; signed int i; buffer0 = (char *)malloc (9u ); buffer1 = (char *)malloc (9u ); memset (buffer0, 0 , 9u ); memset (buffer1, 0 , 9u ); printf ("Enter the password: " ); __isoc99_scanf("%8s %8s" , buffer0, buffer1, v6);
在这里,buffer0
和buffer1
是.bss
段的指针,在运行过程中申请了内存块,scanf
读入的数据也是存在申请的内存块内的,所以需要自己符号化malloc
的内存
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 import angrimport sysimport claripydef main (filepath) : project = angr.Project(filepath) start_address = 0x8048699 init_state = project.factory.blank_state(addr=start_address) password0 = claripy.BVS('password0' ,64 ) password1 = claripy.BVS('password1' ,64 ) fake_heap_address0 = 0xffffc93c fake_heap_address1 = 0xffffc94c pointer_to_malloc_memory_address0 = 0x8135468 pointer_to_malloc_memory_address1 = 0x8135470 init_state.memory.store(pointer_to_malloc_memory_address0,fake_heap_address0,endness=project.arch.memory_endness) init_state.memory.store(pointer_to_malloc_memory_address1,fake_heap_address1,endness=project.arch.memory_endness) init_state.memory.store(fake_heap_address0,password0) init_state.memory.store(fake_heap_address1,password1) sim = project.factory.simgr(init_state) def find (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output def avoid (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output sim.explore(find=find, avoid=avoid) if sim.found: solution_state = sim.found[0 ] sulution0=solution_state.se.eval(password0,cast_to=bytes) sulution1=solution_state.se.eval(password1,cast_to=bytes) print(b" " .join([sulution0,sulution1])) else : raise Exception('Could not find the solution' ) if __name__ == "__main__" : if (len(sys.argv) != 2 ): print('usage:python angr_basic.py filepath' ) filepath = sys.argv[1 ] main(filepath)
符号化文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { signed int i; memset (buffer , 0 , 0x40 u); printf ("Enter the password: " ); __isoc99_scanf("%64s" , buffer ); ignore_me((int )buffer , 0x40 u); memset (buffer , 0 , 0x40 u); fp = fopen("JKOYZJZV.txt" , "rb" ); fread(buffer , 1u , 0x40 u, fp); fclose(fp); unlink("JKOYZJZV.txt" ); for ( i = 0 ; i <= 7 ; ++i ) *(_BYTE *)(i + 134520992 ) = complex_function(*(char *)(i + 0x804A0A0 ), i); if ( strncmp (buffer , "QAIIDABN" , 9u ) ) { puts ("Try again." ); exit (1 ); } puts ("Good Job." ); exit (0 ); }
可以看到这里需要从文件读取内容,所以需要自己符号化文件
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 angrimport sysimport claripydef main (filepath) : project = angr.Project(filepath) start_address = 0x80488D6 init_state = project.factory.blank_state(addr=start_address) filename = "JKOYZJZV.txt" symbolic_file_size_bytes = 0x40 password = claripy.BVS('password' , symbolic_file_size_bytes * 8 ) password_file = angr.storage.SimFile(filename, content=password,size = symbolic_file_size_bytes) init_state.fs.insert(filename,password_file) sim = project.factory.simgr(init_state) def find (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Good Job." in stdout_output def avoid (state) : stdout_output = state.posix.dumps(sys.stdout.fileno()) return b"Try again." in stdout_output sim.explore(find=find, avoid=avoid) if sim.found: solution_state = sim.found[0 ] solution = solution_state.se.eval(password,cast_to=bytes) print(solution) else : raise Exception('Could not find the solution' ) if __name__ == "__main__" : if (len(sys.argv) != 2 ): print('usage:python angr_basic.py filepath' ) filepath = sys.argv[1 ] main(filepath)