前言 尝试学习angr在混淆下的应用,示例eg.exe源于封装好的现成solutions下
项目:https://github.com/jakespringer/angr_ctf
又是一个re✌:https://github.com/ZERO-A-ONE/AngrCTF_FITM/tree/master
ubuntu配置32位环境:
1 2 3 4 sudo dpkg --add-architecture i386sudo apt install libc6:i386 libstdc++6:i386sudo apt-get updatesudo apt install libncurses5-dev lib32z1
去除烦人警告
1 2 import logging logging.getLogger('angr.storage.memory_mixins.default_filler_mixin' ).setLevel(logging.CRITICAL)
00_find
1 2 3 4 5 6 7 8 9 10 11 12 13 import angr project=angr.Project('00_angr_find' ) initial_state = project.factory.entry_state( add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS} ) simulation = project.factory.simgr(initial_state) simulation.explore(find=0x804868C ,avoid=0x804867A )if simulation.found: solution_state = simulation.found[0 ] print (solution_state.posix.dumps(0 ).decode())else : assert 'fail'
01_avoid
find地址附近即可,avoid改为avoid_me函数地址,绕过减少solver时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import angr project = angr.Project('./01_angr_avoid' ) initial_state = project.factory.entry_state( add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS} ) simgr = project.factory.simulation_manager() find_address = 0x80485F4 avoid_address = 0x80485BF simgr.explore(find=find_address, avoid=avoid_address)if simgr.found: solution_state = simgr.found[0 ] print (solution_state.posix.dumps(0 ).decode())
02_def_addr 上一关调用大量avoid_me,此关则是两个对应输出语句被大量调用,采用下述写法处理没有具体地址情况
eg.
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 angrdef success (state ): output=state.posix.dumps(1 ) return b'Good Job' in outputdef failure (state ): output=state.posix.dumps(1 ) return b'Try again' in output project=angr.Project('./02_angr_find_condition' ) initial_state=project.factory.entry_state( add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS} ) simulation=project.factory.simgr(initial_state) simulation.explore(find=success,avoid=failure)if simulation.found: state=simulation.found[0 ] print (state.posix.dumps(0 ).decode()) else : assert False
03_register angr不支持多个scanf输入,scanf调用结束后指定angr注入地址
1 2 3 4 5 6 7 8 9 10 int get_user_input () { int v1; int v2; _DWORD v3[4 ]; v3[1 ] = __readgsdword(0x14u ); __isoc99_scanf("%x %x %x" , &v1, &v2, v3); return v1; }
此关新增将符号值注入寄存器
补充:
方法
说明
entry_state()
从程序入口开始
blank_state(addr==…)
从指定地址开始,完全空白
full_init_state()
执行到 main 前的所有初始化(如构造函数),比 entry_state() 更完整
call_state(addr,arg11,arg2,…)
专门用于调用某个函数,自动设置参数和返回地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 user_input; int v5; int v6; int v7; int v8; printf ("Enter the password: " ); user_input = get_user_input(); v7 = HIDWORD(user_input); v5 = complex_function_1(user_input); v6 = complex_function_2(); v8 = complex_function_3(v7); if ( v5 || v6 || v8 ) puts ("Try again." ); else puts ("Good Job." ); return 0 ; }
wk.
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 import angrimport claripy project=angr.Project('./03_angr_symbolic_registers' ) start_address=0x080488C7 initial_state=project.factory.blank_state( addr=start_address, add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS} ) hoge0_size=32 hoge1_size=32 hoge2_size=32 hoge0=claripy.BVS('hoge0' ,hoge0_size) hoge1=claripy.BVS('hoge1' ,hoge1_size) hoge2=claripy.BVS('hoge2' ,hoge2_size) initial_state.regs.eax=hoge0 initial_state.regs.ebx=hoge1 initial_state.regs.edx=hoge2 simulation=project.factory.simgr(initial_state) simulation.explore(find=0x0804892A ,avoid=0x08048918 )if simulation.found: state=simulation.found[0 ] a0=state.solver.eval (hoge0) a1=state.solver.eval (hoge1) a2=state.solver.eval (hoge2) a='' .join(map ('{:08x}' .format ,[a0,a1,a2])) print (a)else : assert False
04_stack 此关跳过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
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 claripy project=angr.Project('./04_angr_symbolic_stack' ) start_address=0x080486AE initial_state=project.factory.blank_state( addr=start_address, add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } ) initial_state.regs.ebp=initial_state.regs.esp hoge0=claripy.BVS('hoge0' ,32 ) hoge1=claripy.BVS('hoge1' ,32 ) initial_state.regs.esp-=8 initial_state.stack_push(hoge0) initial_state.stack_push(hoge1) simulation=project.factory.simgr(initial_state)def success (state ): output=state.posix.dumps(1 ) return 'Good Job.' .encode() in outputdef fail (state ): output=state.posix.dumps(1 ) return 'Try again.' .encode() in output simulation.explore(find=success,avoid=fail)if simulation.found: solution=simulation.found[0 ] a0=solution.solver.eval (hoge0) a1=solution.solver.eval (hoge1) s=' ' .join(map (str ,[a0,a1])) print (s)else : print ('fail' )
05_memory_store 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; // [esp+Ch] [ebp-Ch] memset(user_input, 0 , 0x21u); printf("Enter the password: " ); __isoc99_scanf("%8s %8s %8s %8s" , user_input, &unk_AB232C8, &unk_AB232D0, &unk_AB232D8); for ( i = 0 ; i <= 31 ; ++i ) *(_BYTE *)(i + 0xAB232C0 ) = complex_function(*(char *)(i + 0xAB232C0 ), i); if ( !strncmp(user_input, "OSIWHBXIFOQVSBZBISILSCLBIAXSEWUT" , 0x20u) ) puts("Good Job." ); else puts("Try again." ); return 0 ; }
wk.
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 import angrimport claripy project=angr.Project('./05_angr_symbolic_memory' ) start_address=0x08048618 initial_state=project.factory.blank_state( addr=start_address, add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } ) a=claripy.BVS('a' ,8 *8 ) b=claripy.BVS('b' ,64 ) c=claripy.BVS('c' ,64 ) d=claripy.BVS('d' ,64 ) a_addr = 0xab232c0 initial_state.memory.store(a_addr, a) b_addr = 0xab232c8 initial_state.memory.store(b_addr, b) c_addr = 0xab232d0 initial_state.memory.store(c_addr, c) d_addr = 0xab232d8 initial_state.memory.store(d_addr, d) simulation=project.factory.simgr(initial_state)def success (state ): output=state.posix.dumps(1 ) return 'Good Job.' .encode() in outputdef fail (state ): output=state.posix.dumps(1 ) return 'Try again.' .encode() in output simulation.explore(find=success,avoid=fail)if simulation.found: solution=simulation.found[0 ] q=solution.solver.eval (a,cast_to=bytes ).decode() w=solution.solver.eval (b,cast_to=bytes ).decode() e=solution.solver.eval (c,cast_to=bytes ).decode() r=solution.solver.eval (d,cast_to=bytes ).decode() s=' ' .join([q,w,e,r]) print (s)else : print ('fail' )
06_malloc_memory 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 main (int argc, const char **argv, const char **envp) { 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); for ( i = 0 ; i <= 7 ; ++i ) { buffer0[i] = complex_function(buffer0[i], i); buffer1[i] = complex_function(buffer1[i], i + 32 ); } if ( !strncmp (buffer0, "OSIWHBXI" , 8u ) && !strncmp (buffer1, "FOQVSBZB" , 8u ) ) puts ("Good Job." ); else puts ("Try again." ); free (buffer0); free (buffer1); return 0 ; }
改用地址指针,此关模拟heap堆
补充:
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 程序中调用了 malloc(9), 但 malloc 返回的内存块通常会按 16 字节对齐(在 32 位或 64 位系统上), 所以即使申请 8 字节,实际占用的空间是 16 字节(0x10)。 heap 是进程内存中的一块专门用于动态内存分配的区域。 特点:大小不固定,可以随着 malloc / free 动态扩展或收缩 由程序员手动管理(不像栈那样自动释放) 起始地址较低,向上增长(与栈相反) Heap 区域(初始空闲) +--------------------------------------------------+ | | | Free Space | | | +--------------------------------------------------+ ↑ malloc(8) 请求 8 字节 Heap 分配后 +------------------+-------------------------------+ | malloc(8) | Remaining Free | | 返回 ptr | | +------------------+-------------------------------+ ↑ ptr → 指向这里
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 import angrimport claripy project=angr.Project('./06_angr_symbolic_dynamic_memory' ) start_address=0x080486AF initial_state=project.factory.blank_state( addr=start_address, add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } ) a=claripy.BVS('a' ,8 *8 ) b=claripy.BVS('b' ,64 ) heap_addr0=0x1111111 pointer0=0xa2def74 initial_state.memory.store(pointer0,heap_addr0,endness=project.arch.memory_endness, size=4 ) heap_addr1=0x1111121 pointer1=0xa2def7c initial_state.memory.store(pointer1,heap_addr1,endness=project.arch.memory_endness, size=4 ) initial_state.memory.store(heap_addr0, a) initial_state.memory.store(heap_addr1, b) simulation=project.factory.simgr(initial_state)def success (state ): output=state.posix.dumps(1 ) return 'Good Job.' .encode() in outputdef fail (state ): output=state.posix.dumps(1 ) return 'Try again.' .encode() in output simulation.explore(find=success,avoid=fail)if simulation.found: solution=simulation.found[0 ] q=solution.solver.eval (a,cast_to=bytes ).decode() w=solution.solver.eval (b,cast_to=bytes ).decode() s=' ' .join([q,w]) print (s)else : print ('fail' )
07_file 模拟文件读取
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 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; int v4; int v5; int v6; int v7; int v8; int i; memset (buffer, 0 , sizeof (buffer)); printf ("Enter the password: " ); __isoc99_scanf("%64s" , buffer, v3, v4, v5, v6, v7, v8); ignore_me(buffer, 0x40u ); memset (buffer, 0 , sizeof (buffer)); fp = fopen("FOQVSBZB.txt" , "rb" ); fread(buffer, 1u , 0x40u , fp); fclose(fp); unlink("FOQVSBZB.txt" ); for ( i = 0 ; i <= 7 ; ++i ) *(_BYTE *)(i + 0x804A0A0 ) = complex_function(*(char *)(i + 134520992 ), i); if ( strcmp (buffer, "OSIWHBXI" ) ) { puts ("Try again." ); exit (1 ); } puts ("Good Job." ); exit (0 ); }
wk.
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 angrimport claripy project=angr.Project('./07_angr_symbolic_file' ) start_address=0x080488BC initial_state=project.factory.blank_state( addr=start_address, add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } ) filename='FOQVSBZB.txt' symbolic_file_size_bytes = 8 password = claripy.BVS('password' , symbolic_file_size_bytes * 8 ) file=angr.storage.SimFile(filename,content=password) initial_state.fs.insert(filename,file) simulation=project.factory.simgr(initial_state)def success (state ): output=state.posix.dumps(1 ) return 'Good Job.' .encode() in outputdef fail (state ): output=state.posix.dumps(1 ) return 'Try again.' .encode() in output simulation.explore(find=success,avoid=fail)if simulation.found: solution=simulation.found[0 ] s=solution.solver.eval (password,cast_to=bytes ).decode() print (s)else : print ('fail' )
补充:
概念
类型
含义
是否符号化
典型用途
BVS
claripy.BVS(name, size)
符号变量(Symbolic Variable)
是
表示未知输入(如密码、密钥)
BVV
claripy.BVV(value, size)
具体值(Concrete Value)
否
表示已知常量(如 “hello”)
dumps(0)
state.posix.dumps(0)
获取程序从 stdin 读入的数据
可能包含符号,但输出 concrete
查看输入内容、调试
solver.eval(...)
state.solver.eval(expr)
求解符号表达式 的具体值
输入是符号,输出是 concrete
得到密码、密钥等答案
08_constrain 处理字符串
符号执行16轮相当于2^16次,手动给出指定地址绕过字符串处理
跳过字符串处理
blank设置具体参数
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 import angrimport claripy project=angr.Project('./08_angr_constraints' ) start_addr=0x0804863C initial_state=project.factory.blank_state( addr=start_addr, add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } ) passwd=claripy.BVS('passwd' ,8 *16 ) passwd_addr=0x804a040 initial_state.memory.store(passwd, passwd_addr) simulation=project.factory.simgr(initial_state) check_addr=0x8048683 simulation.explore(addr=check_addr)if simulation.found: solution_state=simulation.found[0 ] cmp_addr=0x804a040 bytes_size=16 target_bitvector=solution_state.load(addr=check_addr,size=bytes_size) aim_value='OSIWHBXIFOQVSBZB' .encode() solution_state.add(target_bitvector==aim_value) solution=solution_state.solver.eval (passwd,cast_to=bytes ).decode() print (solution)else : raise Exception('No solution found' )
entry自行填充
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 import angrimport claripy project=angr.Project('./08_angr_constraints' ) initial_state=project.factory.entry_state( add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } ) simulation=project.factory.simgr(initial_state) address_to_check_constraint = 0x8048683 simulation.explore(find=address_to_check_constraint)if simulation.found: solution=simulation.found[0 ] constrained_parameter_address = 0x804a040 constrained_parameter_size_bytes = 16 constrained_parameter_bitvector = solution.memory.load( constrained_parameter_address, constrained_parameter_size_bytes ) constrained_parameter_desired_value = 'OSIWHBXIFOQVSBZB' .encode() solution.add_constraints(constrained_parameter_bitvector == constrained_parameter_desired_value) print (solution.posix.dumps(0 ).decode())else : print ('fail' )
09_hook angr的hook方法
主逻辑如下,两次输入以及校验,angr 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 int __cdecl main (int argc, const char **argv, const char **envp) { _BOOL4 equals; int i; int j; qmemcpy(password, "OSIWHBXIFOQVSBZB" , 16 ); memset (buffer, 0 , 0x11u ); printf ("Enter the password: " ); __isoc99_scanf("%16s" , buffer); for ( i = 0 ; i <= 15 ; ++i ) *(_BYTE *)(i + 0x804A044 ) = complex_function(*(char *)(i + 0x804A044 ), 18 - i); ::equals = check_equals_OSIWHBXIFOQVSBZB(buffer, 16 ); for ( j = 0 ; j <= 15 ; ++j ) *(_BYTE *)(j + 0x804A034 ) = complex_function(*(char *)(j + 0x804A034 ), j + 9 ); __isoc99_scanf("%16s" , buffer); equals = ::equals && !strncmp (buffer, password, 0x10u ); ::equals = equals; if ( equals ) puts ("Good Job." ); else puts ("Try again." ); return 0 ; }
关于此处字符串比较不用绕过的解释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int strncmp (const char *s1, const char *s2, size_t n) { for (size_t i = 0 ; i < n; i++) { if (s1[i] != s2[i]) { return s1[i] - s2[i]; } if (s1[i] == '\0' ) { return 0 ; } } return 0 ; }
wk.
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 import angrimport claripy project=angr.Project('./09_angr_hooks' ) initial_state=project.factory.entry_state( add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } ) check_equal_addr=0x080486CA instruction_len=5 @project.hook(check_equal_addr,length=instruction_len ) def skip_check_equal (state ): inputbuff_addr=0x804A044 len =16 input_string=state.memory.load(inputbuff_addr,len ) check_string='OSIWHBXIFOQVSBZB' .encode() state.regs.eax=claripy.If( input_string==check_string, claripy.BVV(1 ,32 ), claripy.BVV(0 ,32 ) ) simulation=project.factory.simgr(initial_state)def success (state ): output=state.posix.dumps(1 ) return 'Good Job.' .encode() in outputdef fail (state ): output = state.posix.dumps(1 ) return 'Try again.' .encode() in output simulation.explore(find=success,avoid=fail)if simulation.found: solution_state=simulation.found[0 ] solution=solution_state.posix.dumps(0 ).decode() print (solution)else : print ('fail' )
python装饰器
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 check_equal_addr=0x080486CA instruction_len=5 @project.hook(check_equal_addr,length=instruction_len ) def skip_check_equal (state ): inputbuff_addr=0x804A044 len =16 input_string=state.memory.load(inputbuff_addr,len ) check_string='OSIWHBXIFOQVSBZB' .encode() state.regs.eax=claripy.If( input_string==check_string, claripy.BVV(1 ,32 ), claripy.BVV(0 ,32 ) )def skip_check_equal (state ): inputbuff_addr = 0x804A044 len = 16 input_string = state.memory.load(inputbuff_addr, len ) check_string = 'OSIWHBXIFOQVSBZB' .encode() state.regs.eax = claripy.If( input_string == check_string, claripy.BVV(1 , 32 ), claripy.BVV(0 , 32 ) ) project.hook(check_equal_addr, skip_check_equal, length=instruction_len)
10_simprocedures 多轮加密,利用simprocedures简化处理,替换调用函数,原理不变:
1.hook 函数本身的地址
2.memory.load(regs.esp + xx)读取栈中函数
3.设eax返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int __cdecl main (int argc, const char **argv, const char **envp) { int i; _BYTE s[17 ]; unsigned int v6; v6 = __readgsdword(0x14u ); memcpy (&password, "OSIWHBXIFOQVSBZB" , 0x10u ); memset (s, 0 , sizeof (s)); printf ("Enter the password: " ); __isoc99_scanf("%16s" , s); for ( i = 0 ; i <= 15 ; ++i ) s[i] = complex_function((char )s[i], 18 - i); if ( check_equals_OSIWHBXIFOQVSBZB((int )s, 0x10u ) ) puts ("Good Job." ); else puts ("Try again." ); return 0 ; }
wk.
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 claripy project=angr.Project('./10_angr_simprocedures' ) initial_state=project.factory.entry_state( add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } )class replacement_check_equal (angr.SimProcedure): def run (self,to_check,len ): input_addr=to_check input_len=len string=self .state.memory.load(input_addr,input_len) aim_string='OSIWHBXIFOQVSBZB' .encode() return claripy.If( string==aim_string, claripy.BVV(1 ,32 ), claripy.BVV(0 ,32 ) ) check_equal_symbol='check_equals_OSIWHBXIFOQVSBZB' project.hook_symbol(check_equal_symbol,replacement_check_equal()) simulation=project.factory.simgr(initial_state)def success (state ): output=state.posix.dumps(1 ) return 'Good Job.' .encode() in outputdef fail (state ): output = state.posix.dumps(1 ) return 'Try again.' .encode() in output simulation.explore(find=success,avoid=fail)if simulation.found: solution_state=simulation.found[0 ] solution=solution_state.posix.dumps(0 ).decode() print (solution)else : print ('fail' )
11_sim_scanf simprocedures处理多个输入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .text:08048711 04 C 83 EC 04 sub esp, 4 .text:08048714 050 68 F8 8 A 06 08 push offset buffer1 .text:08048719 054 68 0 C 3 A 06 08 push offset buffer0 .text:0804871 E 058 68 83 FD 04 08 push offset aUU ; "%u %u" .text:08048723 05 C E8 28 FD FF FF call ___isoc99_scanf .text:08048728 05 C 83 C4 10 add esp, 10 h objdump -d ./11 _angr_sim_scanf | grep scanf #-d disassemble ./11 _angr_sim_scanf: 文件格式 elf32-i38608048450 <__isoc99_scanf@plt>: 8048723 : e8 28 fd ff ff call 8048450 <__isoc99_scanf@plt> 804878 e: e8 bd fc ff ff call 8048450 <__isoc99_scanf@plt> 8048802 : e8 49 fc ff ff call 8048450 <__isoc99_scanf@plt> 804886 d: e8 de fb ff ff call 8048450 <__isoc99_scanf@plt> 80488 ee: e8 5 d fb ff ff call 8048450 <__isoc99_scanf@plt> 8048959 : e8 f2 fa ff ff call 8048450 <__isoc99_scanf@plt>
wk.
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 claripy project=angr.Project('./11_angr_sim_scanf' ) initial_state=project.factory.entry_state( add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS } )class replacement_scanf (angr.SimProcedure): def run (self,format_string,string1_addr,string2_addr ): string1=claripy.BVS('string1' ,32 ) string2=claripy.BVS('string2' ,32 ) self .state.memory.store(string1_addr,string1,endness=project.arch.memory_endness) self .state.memory.store(string2_addr,string2,endness=project.arch.memory_endness) self .state.globals ['solution1' ]=string1 self .state.globals ['solution2' ]=string2 scanf_symbol='__isoc99_scanf' project.hook_symbol(scanf_symbol,replacement_scanf()) simulation=project.factory.simgr(initial_state)def success (state ): output=state.posix.dumps(1 ) return 'Good Job.' .encode() in outputdef fail (state ): output = state.posix.dumps(1 ) return 'Try again.' .encode() in output simulation.explore(find=success,avoid=fail)if simulation.found: solution_state=simulation.found[0 ] s1=solution_state.globals ['solution1' ] s2=solution_state.globals ['solution2' ] solution=f'{solution_state.solver.eval (s1)} {solution_state.solver.eval (s2)} ' print (solution)else : print ('fail' )
12_veritesting 主要引入veritesting概念
“Enhancing Symbolic Execution with Veritesting” (Avgerinos et al., ICSE 2014)
核心思想:结合动态符号执行(concolic execution)与静态路径展开(static path enumeration) ,在遇到复杂控制流(如循环、深层嵌套条件)时,自动“展开”部分代码区域,生成多条路径,从而避免过早陷入某一条路径而错过其他可能的解。
eg.
1 2 3 4 5 6 7 8 9 10 11 simgr = project.factory.simgr(initial_state, veritesting=True ) simgr = project.factory.simgr( initial_state, veritesting=True , veritesting_options={ 'enable_function_inlining' : False , 'max_iterations' : 10 , 'loop_unrolling_limit' : 5 } )
wk. 自己这里本地没跑通,github上跑通的为2020年样例,等佬研究
附官方的
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 import angrimport sysdef main (argv ): path_to_binary = argv[1 ] project = angr.Project(path_to_binary) initial_state = project.factory.entry_state( add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS} ) simulation = project.factory.simgr(initial_state, veritesting=True ) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Good Job.' .encode() in stdout_output def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Try again.' .encode() in stdout_output simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: solution_state = simulation.found[0 ] print (solution_state.posix.dumps(sys.stdin.fileno()).decode()) else : raise Exception('Could not find the solution' )if __name__ == '__main__' : main(sys.argv)
13_static_binary 未打包库函数的静态elf,手动加载
留意_libc_start_main函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void __usercall __noreturn start (int stack_end_@<eax>, void (*rtld_fini)(void )@<edx>) { int argc; int stack_end__1; char *ubp_av_; argc = stack_end__1; stack_end__1 = stack_end_; _libc_start_main( (int (*)(int , char **, char **))main, argc, &ubp_av_, (void (*)(void ))_libc_csu_init, (void (*)(void ))_libc_csu_fini, rtld_fini, &stack_end__1); } .text:080487F 0 ; void __usercall __noreturn start (int stack_end_@<eax>, void (*rtld_fini)(void )@<edx>)
wk.
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 import angr project = angr.Project('./13_angr_static_binary' ) initial_state = project.factory.entry_state( add_options={angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS} ) project.hook(0x804fab0 , angr.SIM_PROCEDURES['libc' ]['printf' ]()) project.hook(0x804fb10 , angr.SIM_PROCEDURES['libc' ]['scanf' ]()) project.hook(0x80503f0 , angr.SIM_PROCEDURES['libc' ]['puts' ]()) project.hook(0x8048d60 , angr.SIM_PROCEDURES['glibc' ]['__libc_start_main' ]()) simulation = project.factory.simgr(initial_state)def success (state ): output = state.posix.dumps(1 ) return 'Good Job.' .encode() in output def fail (state ): output = state.posix.dumps(1 ) return 'Try again.' .encode() in output simulation.explore(find=success, avoid=fail)if simulation.found: solution_state = simulation.found[0 ] print (solution_state.posix.dumps(0 ).decode())else : raise Exception('Could not find the solution' )
14_so 学起来跟frida思路很想啊,包括后面的地址偏移
so文件下的validate函数
1 2 3 (base) ma5k@ma5k-Ubuntu24:~/angr_ctf-master/solutions/14_angr_shared_library$ checksec --file=14_angr_shared_library RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY FortifiedFortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH RW-RUNPATH 69 Symbols No 0 214_angr_shared_library
no pie,基址不变
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 angrimport claripy path_to_binary = "./lib14_angr_shared_library.so" base = 0x4000000 project = angr.Project(path_to_binary, load_options={ 'main_opts' : { 'custom_base_addr' : base } }) buffer_pointer = claripy.BVV(0x3000000 , 32 ) validate_function_address = base + 0x670 initial_state = project.factory.call_state( validate_function_address, buffer_pointer, claripy.BVV(8 , 32 ), add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS} ) password = claripy.BVS('password' , 8 *8 ) initial_state.memory.store(buffer_pointer, password) simulation = project.factory.simgr(initial_state) success_address = base + 0x71c simulation.explore(find=success_address)if simulation.found: solution_state = simulation.found[0 ] solution_state.add_constraints(solution_state.regs.eax != 0 ) solution = solution_state.solver.eval (password, cast_to=bytes ).decode() print (solution)else : raise Exception('Could not find the solution' )
pre-binary 选项:
如果你想要对一个特定的二进制对象设置一些选项,CLE也能满足你的需求在加载二进制文件时可以设置特定的参数,使用 main_opts 和 lib_opts 参数进行设置。
backend - 指定 backend
base_addr - 指定基址
entry_point - 指定入口点
arch - 指定架构
示例如下:
1 2 >>> angr.Project('examples/fauxware/fauxware', main_opts={'backend': 'blob', 'arch': 'i386'}, lib_opts={'libc.so.6': {'backend': 'elf'}}) <Project examples/fauxware/fauxware>
参数main_opts和lib_opts接收一个以python字典形式存储的选项组。main_opts接收一个形如{选项名1:选项值1,选项名2:选项值2……}的字典,而lib_opts接收一个库名到形如{选项名1:选项值1,选项名2:选项值2……}的字典的映射。
lib_opts是二级字典,原因是一个二进制文件可能加载多个库,而main_opts指定的是主程序加载参数,而主程序一般只有一个,因此是一级字典。
这些选项的内容因不同的后台而异,下面是一些通用的选项:
backend —— 使用哪个后台,可以是一个对象,也可以是一个名字(字符串)
custom_base_addr —— 使用的基地址
custom_entry_point —— 使用的入口点
custom_arch —— 使用的处理器体系结构的名字
后记 后续关卡为无条件约束以及溢出类pwn利用了,不是很感兴趣,暂时学到这,完工