早就听闻了angr,之前看wp也有大佬使用了angr,上一次打开angrctf一头雾水,还要装环境啥的,就先搁置了。现在趁着军训开始angr的学习。
tnnd两天都没配置好一个虚拟环境,还是在朋友的帮助下搞了个docker,woc真难绷,或许以后有能力了可以搞一个虚拟机,包含所有的逆向需要的环境hhh。一天后来考古,那个docker用起来着实别扭,不知道出了什么问题,不能将主机的文件拷贝到容器中,还有就是用了docker start 容器id也启动不了容器,只好另寻他路,本来想放弃的,可是一想到被这环境折磨三四天了,哎。终于,在wsl的虚拟环境成功搭建!!!
记录一下启动步骤
1 2 3 4 5 # $ source myenv/bin/activate# $ deactivate
(15条消息) 在wsl上安装angr框架_wsl2下安装angr workon_ljahum的博客-CSDN博客
Angr介绍 看一看官方文档的解释
angr is a multi-architecture binary analysis toolkit, with the capability to perform dynamic symbolic execution (like Mayhem, KLEE, etc.) and various static analyses on binaries.
angr是一个多架构二进制分析工具包,具有执行动态符号执行(例如Mayhem,KLEE等)和各种静态分析的能力。
什么叫符号执行 呢?
符号执行 符号执行是一种静态分析技术,是一种计算机科学领域的程序分析技术,通过采用抽象的符号代替精确值作为程序输入变量,得出每个路径抽象的输出结果。 这一技术在硬件、底层程序测试中有一定的应用,能够有效的发现程序中的漏洞。符号执行就是给程序传递一个符号而不是具体的值,让符号伴随程序运行,当遇到分支时angr会保留所有分支以及进入分支的约束条件,最后根据约束条件对我们传递的符号约束求解。这听着有点像全自动z3。
概念有点抽象,不如直接做题。
Angr_CTF 参考链接:angr符号执行练习 00_angr_find_哔哩哔哩_bilibili
angr从入门到精通
angr核心概念即模块解读
使用步骤
创建project
设置state
新建符号量 : BVS (bitvector symbolic ) 或 BVV (bitvector value)
把符号量设置到内存或者其他地方
设置 Simulation Managers , 进行路径探索的对象
运行,探索满足路径需要的值
约束求解,获取执行结果
00_angr_find 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 import angrimport sysdef angr00 (): path_to_binary="./home/mzyy/AngerCTF/00_angr_find/00_angr_find" project=angr.Project(path_to_binary) initial_state=project.factory.entry_state() simulation=project.factory.simgr(initial_state) print_good_address=0x8048678 simulation.explore(find=print_good_address) if simulation.found: solution_state=simulation.found[0 ] print (solution_state.posix.dumps(0 )) if __name__ == "__main__" : angr00()
下面是用ipython写的截的图,是跟着B站的一位up主来的。
01_angr_avoid
反编译main函数时说函数过大无法反编译(可以通过修改ida的设置文件,提高最多分析长度来解决此问题)。这一题实际上也不需要,根据函数名,我们找到avoid_me的地址加到参数中即可。
查看avoid_me的交叉引用,发现巨多。我们可以看到全是main函数在引用,这就是main函数巨大的原因吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 import angrdef angr01 (): path="./angrctf/01_angr_avoid" p=angr.Project(path) init_state=p.factory.entry_state() sm=p.factory.simgr(init_state) sm.explore(find=0x080485FC ,avoid=0x080485BF ) if sm.found: solution_state=sm.found[0 ] print (solution_state.posix.dumps(0 )) if __name__ == "__main__" : angr01()
这一题就是想让我们知道,避开一些错误的路径可以提高效率,我觉得将上面的avoid删掉一样可以达到目的,不过时间可能会很夸张。哎呀被打脸了,跑了十分钟左右跑出来一个killed。
好吧电脑内存不足,进程被系统杀死了,那如果内存足够大还是能跑出来的吧。提出问题和回答问题的人都好耐心好有礼貌^_^
02_angr_find_condition 很显然这次不能通过输出good job的地址来解题了,因为这一题故意设置了很多跳转,使用了多次put good job和多次put try again。根据作者的注释,在一些情况下我们可能不知道要达到的指令的地址,或者没有特定的指令目标。在这种情况下,我们只要知道一种状态,例如在某状态下二进制文件打印出“Good Job”。angr提供了一种功能强大的方法:允许搜索满足任意条件的状态。具体来讲,我们可以使用一个函数来定义一个状态,该函数接收一个state作为参数,并返回True或false表示该状态是否满足要求。当程序找到一个符合条件的状态时,他就会停止搜索。具体看下面这函数,它检查状态的标准输出是否包含字符串 “Good Job.”。
1 2 3 4 5 def is_successful_state (state ): if b"Good Job." in state.posix.dumps(1 ): return True else : return False
find的用法 除了地址之外,find
参数还可以是一个函数,该函数接受一个路径(path
)作为参数,并返回一个布尔值。当该函数返回 True
时,路径组将停止探索。例如,以下代码使用 find
方法搜索二进制文件中所有包含 win_function
函数的路径,并打印出对应的输入数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 import angrproj = angr.Project('/path/to/binary' ) state = proj.factory.entry_state() pg = proj.factory.path_group(state) def win_function (path ): return "Congratulations!" in path.state.posix.dumps(1 ) pg.explore(find=win_function) for found in pg.found: print (found.state.posix.dumps(0 ))
在这个示例中,win_function
是一个用于检查路径是否包含特定输出的函数。在探索过程中,每当发现一个路径包含 win_function
函数并生成相应的结论时,路径组将停止探索,并输出相应的输入数据。
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 import angrimport sysdef main (argv ): path=argv[1 ] p=angr.Project(path) init_state=p.factory.entry_state() sm=p.factory.simgr(init_state) def isGood (sm ): if b"Good Job." in sm.posix.dumps(1 ): return True else : return False def isBad (sm ): if b"Try again." in sm.posix.dumps(1 ): return True else : return False sm.explore(find=isGood,avoid=isBad) if sm.found: solution_state=sm.found[0 ] print (solution_state.posix.dumps(0 )) if __name__ =='__main__' : main(sys.argv)
03_angr_symbolic_registers 根据当时作者的说明,Angr目前不支持使用scanf一次读取多个变量(例如:# scanf(“%u %u))。您需要告诉仿真引擎在调用scanf后开始程序,并手动将符号注入寄存器。据说现在可以了,但学一下总没有坏处,能从中体会到angr的灵活。
首先呢确定进入的地址,就在scanf之后,0x080488C7
进去之后呢因为我们跳过了scanf所以把它的参数放到该有的位置,也就是寄存器eax,ebx,edx中
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 sysimport claripydef main (argv ): path=argv[1 ] p=angr.Project(path) start_address=0x080488C7 init_state=p.factory.blank_state(addr=start_address) pass0=claripy.BVS('pass0' ,32 ) pass1=claripy.BVS('pass1' ,32 ) pass2=claripy.BVS('pass2' ,32 ) init_state.regs.eax=pass0 init_state.regs.ebx=pass1 init_state.regs.edx=pass2 sm=p.factory.simgr(init_state) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) sm.explore(find=is_good,avoid=is_bad) if sm.found: soulution_state=sm.found[0 ] password0=soulution_state.solver.eval (pass0) password1=soulution_state.solver.eval (pass1) password2=soulution_state.solver.eval (pass2) print ("{:x} {:x} {:x}" .format (password0,password1,password2)) else : print ("no found" ) if __name__=='__main__' : main(sys.argv)
04_angr_symbolic_stack 做这一题之前需要回顾一下栈帧,在看《逆向工程核心原理》的时候了解过。
栈帧 函数调用
通过push指令传参
将call指令的下一条指令的地址压入栈中作为返回地址
push ebp 保存ebp的原始值 ebp稍后会被用作栈帧指针
mov ebp,esp 直到函数返回前ebp中的值都是esp的初始值 我们可以通过ebp安全的访问栈中的函数参数与局部变量
.sub esp,x这里的x依局部变量而变,如果局部变量为两个long类型(4字节)则此处的x应该为8
借助mov指令和ebp创建局部变量
删除栈帧 mov esp,ebp(恢复栈指针),pop ebp(恢复ebp)
retn
返回原来位置后,add esp,x测出的x根据步骤0穿入的参数而定,这一步的目的是将参数从栈中清理
上一个题目的scanf是单独使用的,就是将我们的输入放入栈中然后再传到寄存器,所以我们只要跳到scanf执行完之后将输入放入寄存器即可。而这一题,是直接将我们的输入当作临时变量使用,即通过栈指针访问,那么我们跳过scanf之后,函数预留的那两个临时变量的位置是空的,所以我们要对栈进行操作,将函数正确的放入栈中。看上面的图,调用scanf的时候利用寄存器从右往左传参,var_10和var_c分别是第二个参数和第一个参数,所以我们将创建的符号变量依次放入ebp+var_C和ebp+var_10处即可。
从ida我们可以看出,这个函数第二行sub esp, 18h,实际上堆栈空间是0x18,但是在实际的做题中我们只是用到了那两个参数,我们就恢复了0x8.
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 import angrimport sysimport claripydef main (argv ): path=argv[1 ] project=angr.Project(path) start_address=0x080486AE init_state=project.factory.blank_state(addr=start_address) pass0=claripy.BVS('pass0' ,32 ) pass1=claripy.BVS('pass1' ,32 ) init_state.regs.ebp=init_state.regs.esp padding_size=0x8 init_state.regs.esp-=padding_size init_state.stack_push(pass0) init_state.stack_push(pass1) sm=project.factory.simulation_manager(init_state) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) sm.explore(find=is_good,avoid=is_bad) if sm.found: solution_state=sm.found[0 ] password0=solution_state.solver.eval (pass0) password1=solution_state.solver.eval (pass1) print ("{} {} " .format (password0,password1)) else : print ("no found" ) if __name__=='__main__' : main(sys.argv)
疑惑 eval
solver.eval(expression)
将会解出一个可行解
solver.eval_one(expression)
将会给出一个表达式的可行解,若有多个可行解,则抛出异常。
solver.eval_upto(expression, n)
将会给出最多n个可行解,如果不足n个就给出所有的可行解。
solver.eval_exact(expression, n)
将会给出n个可行解,如果解的个数不等于n个,将会抛出异常。
solver.min(expression)
将会给出最小可行解
solver.max(expression)
将会给出最大可行解
05_angr_symbolic_memory 修改了寄存器,修改了栈,这一次开始修改内存了。
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 sysimport claripydef main (argv ): path=argv[1 ] project=angr.Project(path) start_address=0x08048618 init_state=project.factory.blank_state(addr=start_address) passwd0=claripy.BVS('passwd0' ,8 *8 ) passwd1=claripy.BVS('passwd1' ,8 *8 ) passwd2=claripy.BVS('passwd2' ,8 *8 ) passwd3=claripy.BVS('passwd3' ,8 *8 ) passwd0_address=0x0AB232C0 passwd1_address=0x0AB232C8 passwd2_address=0x0AB232D0 passwd3_address=0x0AB232D8 init_state.memory.store(passwd0_address,passwd0) init_state.memory.store(passwd1_address,passwd1) init_state.memory.store(passwd2_address,passwd2) init_state.memory.store(passwd3_address,passwd3) sm=project.factory.simgr(init_state) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) sm.explore(find=is_good,avoid=is_bad) if sm.found: solution_state=sm.found[0 ] password0=solution_state.solver.eval (passwd0,cast_to=bytes ).decode() password1=solution_state.solver.eval (passwd1,cast_to=bytes ).decode() password2=solution_state.solver.eval (passwd2,cast_to=bytes ).decode() password3=solution_state.solver.eval (passwd3,cast_to=bytes ).decode() print ("{} {} {} {} " .format (password0,password1,password2,password3)) else : print ("no found" ) if __name__=='__main__' : main(sys.argv)
06_angr_symbolic_dynamic_memory 新知识点:符号化动态内存。
作者的解释文档这样写着:我们可以不告诉二进制程序将数据写入使用malloc()
分配的内存地址,而是直接伪造一个未使用的内存块的地址,并覆盖指向数据的指针。
思路是这样的:malloc函数返回值是一个地址,储存到了buffer里,我们伪造一个地址放到buffer即可,然后在我们伪造的地址处填入符号。
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 angr import sysimport claripydef main (argv ): path=argv[1 ] start_address=0x080486AF project=angr.Project(path) init_state=project.factory.blank_state(addr=start_address) passwd_size=8 *8 passwd0=claripy.BVS('passwd0' ,passwd_size) passwd1=claripy.BVS('passwd1' ,passwd_size) fake_heap_address0=0x0804A144 fake_heap_address1=0x0804A154 real_address0=0x0A2DEF74 real_address1=0x0A2DEF7C init_state.memory.store(real_address0,fake_heap_address0,endness=project.arch.memory_endness) init_state.memory.store(real_address1,fake_heap_address1,endness=project.arch.memory_endness) init_state.memory.store(fake_heap_address0,passwd0) init_state.memory.store(fake_heap_address1,passwd1) sm=project.factory.simgr(init_state) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) sm.explore(find=is_good,avoid=is_bad) if sm.found: solution_state=sm.found[0 ] password0=solution_state.solver.eval (passwd0,cast_to=bytes ).decode() password1=solution_state.solver.eval (passwd1,cast_to=bytes ).decode() print ("{} {} " .format (password0,password1)) else : print ("no found" ) if __name__=='__main__' : main(sys.argv)
1 2 3 init_state.memory.store(real_address0,fake_heap_address0,endness=project.arch.memory_endness) (原地址,我们指定的假地址,端序),原地址指的是存放malloc返回值的变量的地址,此处endness是和本项目相同
endness可选项
1 2 3 LE – 小端序BE – 大端序 ME – 中间序
07_angr_symbolic_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 30 31 32 33 34 35 36 37 import angrimport sysimport claripydef main (argv ): path=argv[1 ] project=angr.Project(path) start_address=0x080488BC init_state=project.factory.blank_state(addr=start_address) filename='FOQVSBZB.txt' filesize=0x40 passwd0=claripy.BVS('passwd0' ,filesize*8 ) passwdfile=angr.storage.SimFile(filename,content=passwd0,size=filesize) init_state.fs.insert(filename,passwdfile) sm=project.factory.simgr(init_state) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) sm.explore(find=is_good,avoid=is_bad) if sm.found: solution_state=sm.found[0 ] password0=solution_state.solver.eval (passwd0,cast_to=bytes ).decode() print (password0) else : print ("no found" ) if __name__=='__main__' : main(sys.argv)
新操作,创建虚拟文件并将其放入仿真文件系统
1 2 3 4 5 6 7 8 9 10 filename='FOQVSBZB.txt' filesize_byte=0x40 passwd0=claripy.BVS('passwd0' ,filesize*8 ) passwdfile=angr.storage.SimFile(filename,content=passwd0,size=filesize) init_state.fs.insert(filename,passwdfile)
08_angr_constraints 开始之前先了解一下路径爆炸。因为这次的新知识点就是:通过添加约束解决路径爆炸问题。
路径爆炸 路径爆炸(Path explosion)指的是在有限状态机或接收器的设计过程中,状态数或路径数呈指数增长的现象。当程序经历的所有可能路径数量超过计算机的处理能力时,就会出现路径爆炸的问题。
路径爆炸是软件测试和验证中一个重要的问题。在对程序进行测试或验证时,需要覆盖程序的所有可能路径,以确保程序的正确性和安全性。但是,当程序中存在复杂的控制流结构时,这个任务就变得非常艰巨。
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 import angrimport sysimport claripydef main (argv ): path=argv[1 ] project=angr.Project(path) start_address=0x0804863C buffer_address=0x0804A040 ckeckfun_address=0x0804857C init_state=project.factory.blank_state(addr=start_address) passwd_len=16 passwd0=claripy.BVS('passwd0' ,passwd_len*8 ) init_state.memory.store(buffer_address,passwd0) simulation=project.factory.simulation_manager(init_state) simulation.explore(find=ckeckfun_address) if simulation.found: solution_state=simulation.found[0 ] parameter_address=buffer_address parameter_size_bytes=16 parameter_bitvector=solution_state.memory.load(parameter_address,parameter_size_bytes) compare_valve='OSIWHBXIFOQVSBZB' solution_state.solver.add(parameter_bitvector==compare_valve) solution0=solution_state.solver.eval (passwd0,cast_to=bytes ).decode() print (solution0) else : print ('no found' ) if __name__=='__main__' : main(sys.argv)
如果按照程序之前的逻辑,按字节进行比对,16byte长度的数据就会产生2^16个分支,分支呈指数级增长,因此我们不按照他的逻辑进行比对,我们的输入经过一些操作之后还是被存储在buffer里,我们直接拿处理过后的buffer与comparedata进行比较,这样这样就变成了简单的爆破,不会造成路径爆炸。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ckeckfun_address=0x0804857C simulation.explore(find=ckeckfun_address) if simulation.found: solution_state=simulation.found[0 ] parameter_address=buffer_address parameter_size_bytes=16 parameter_bitvector=solution_state.memory.load(parameter_address,parameter_size_bytes) compare_valve='OSIWHBXIFOQVSBZB' solution_state.solver.add(parameter_bitvector==compare_valve) solution0=solution_state.solver.eval (passwd0,cast_to=bytes ).decode()
09_angr_hooks 新知识点:hook。
wiki:
钩子编程(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术 。 处理被拦截的函数调用、事件、消息的代码,被称为钩子(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 import angrimport sysimport claripydef main (argv ): path=argv[1 ] project=angr.Project(path) init_state=project.factory.entry_state() checkfun_address=0x080486CA jump_len=5 @project.hook(checkfun_address,length=jump_len ) def fake_checkfun (state ): buffer_address=0x0804A044 buffer_len=16 usr_input_string=state.memory.load(buffer_address,buffer_len) compare_data='OSIWHBXIFOQVSBZB' .encode() state.regs.eax=claripy.If(usr_input_string==compare_data,claripy.BVV(1 , 32 ),claripy.BVV(0 , 32 ) ) sm=project.factory.simgr(init_state) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) sm.explore(find=is_good,avoid=is_bad) if sm.found: solution_state=sm.found[0 ] solution = solution_state.posix.dumps(0 ) print (solution) else : raise Exception('Could not find the solution' ) if __name__=='__main__' : main(sys.argv)
利用地址hook,最后部分模仿函数返回值,返回值储存在寄存器eax中。
在ida查看命令字节码长度。
1 2 3 4 5 6 7 8 9 10 11 jump_len=5 @project.hook(checkfun_address,length=jump_len ) def fake_checkfun (state ): buffer_address=0x0804A044 buffer_len=16 usr_input_string=state.memory.load(buffer_address,buffer_len) compare_data='OSIWHBXIFOQVSBZB' .encode() state.regs.eax=claripy.If(usr_input_string==compare_data,claripy.BVV(1 , 32 ),claripy.BVV(0 , 32 ) )
10_angr_simprocedures 仍然是利用hook解决路径爆炸的问题,上一题是利用地址比较麻烦,现在学习利用函数名来hook,有点像最开始的时候用函数替换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 40 41 import angrimport sysimport claripydef main (argv ): path=argv[1 ] project=angr.Project(path) init_state=project.factory.entry_state() class Replacefun (angr.SimProcedure): def run (self,to_check,length ): buffer_address=to_check buffer_length=length user_input_string=self.state.memory.load(buffer_address,buffer_length) comparedata='OSIWHBXIFOQVSBZB' .encode() return claripy.If( user_input_string==comparedata, claripy.BVV(1 ,32 ), claripy.BVV(0 ,32 ) ) check_equals_symbol='check_equals_OSIWHBXIFOQVSBZB' project.hook_symbol(check_equals_symbol,Replacefun()) sm=project.factory.simgr(init_state) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) sm.explore(find=is_good,avoid=is_bad) if sm.found: solution_state=sm.found[0 ] solution = solution_state.posix.dumps(0 ) print (solution) else : raise Exception('Could not find the solution' ) if __name__=='__main__' : main(sys.argv)
把新知识点放下面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Replacefun (angr.SimProcedure): def run (self,to_check,length ): buffer_address=to_check buffer_length=length user_input_string=self.state.memory.load(buffer_address,buffer_length) comparedata='OSIWHBXIFOQVSBZB' .encode() return claripy.If( user_input_string==comparedata, claripy.BVV(1 ,32 ), claripy.BVV(0 ,32 ) ) check_equals_symbol='check_equals_OSIWHBXIFOQVSBZB' project.hook_symbol(check_equals_symbol,Replacefun())
11_angr_sim_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 import angrimport sysimport claripydef main (argv ): path=argv[1 ] project=angr.Project(path) init_state=project.factory.entry_state() class myscanf (angr.SimProcedure): def run (self,format_string,para0,para1 ): input0=claripy.BVS('input0' ,4 *8 ) input1=claripy.BVS('input1' ,4 *8 ) self.state.memory.store(para0,input0,endness=project.arch.memory_endness) self.state.memory.store(para1,input1,endness=project.arch.memory_endness) self.state.globals ['solution0' ]=input0 self.state.globals ['solution1' ]=input1 scanf_symbol='__isoc99_scanf' project.hook_symbol(scanf_symbol,myscanf()) sm=project.factory.simgr(init_state) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) sm.explore(find=is_good,avoid=is_bad) if sm.found: solution_state=sm.found[0 ] stored_solutions0 = solution_state.globals ['solution0' ] stored_solutions1 = solution_state.globals ['solution1' ] solution = f'{solution_state.solver.eval (stored_solutions0)} {solution_state.solver.eval (stored_solutions1)} ' print (solution) else : raise Exception('Could not find the solution' ) if __name__=='__main__' : main(sys.argv)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class myscanf (angr.SimProcedure): def run (self,format_string,para0,para1 ): input0=claripy.BVS('input0' ,4 *8 ) input1=claripy.BVS('input1' ,4 *8 ) self.state.memory.store(para0,input0,endness=project.arch.memory_endness) self.state.memory.store(para1,input1,endness=project.arch.memory_endness) self.state.globals ['solution0' ]=input0 self.state.globals ['solution1' ]=input1 scanf_symbol='__isoc99_scanf' project.hook_symbol(scanf_symbol,myscanf())
12_angr_veritesting 之前使用hook或者添加约束来解决路径爆炸问题,现在直接在创建虚拟管理器的时候加上一个参数,simulation=project.factory.simgr(init_state,veritesting=True)
简单来说就是Veritesting结合了静态符合执行与动态符号执行,减少了路径爆炸的影响,在angr里我们只要在构造模拟管理器时,启用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 import angrimport sysimport claripydef main (argv ): path=argv[1 ] project=angr.Project(path) init_state=project.factory.entry_state() simulation=project.factory.simgr(init_state,veritesting=True ) def is_good (state ): return b'Good Job.' in state.posix.dumps(1 ) def is_bad (state ): return b'Try again.' in state.posix.dumps(1 ) simulation.explore(find=is_good,avoid=is_bad) if simulation.found: solution_state=simulation.found[0 ] solution=solution_state.posix(0 ) print (solution) else : print ('no found' ) if __name__=='__main__' : main(sys.argv)
13_angr_static_binary
作者文档里的内容:
这个挑战与第一个挑战完全相同,只是它被编译为静态二进制文件。通常,Angr会自动使用SimProcedures替换标准库函数,以实现更快的运行速度。
为了解决这个挑战,需要手动hook任何使用的标准库c函数。
什么叫静态什么叫动态?
拖进ida里很容易看出来,题目13function那一栏里比12多得多,这是因为静态链接 库将所有依赖项都包含在目标二进制文件中,反观动态链接 ,同台链接是指程序在训醒时才需要加载所依赖的库,当我们使用动态链接库来编译程序时,编译器并不会将所有库函数的代码都合并为一个单独的可执行文件。相反,它只是在可执行文件中留下一些标记,以便在运行时从系统或其他位置加载动态链接库。
在静态链接库中,没有动态链接库来提供符号,我们需要手动hook任何使用的标准库c函数,并确保从main函数的开头开始执行。
在动态链接库中,动态链接器会提供符号,我们不需要手动hook标准库c函数。
这一题我们就要将main函数里所用到的标准库函数hook住,用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 import angrimport sysdef main (argv ): path=argv[1 ] project=angr.Project(path) init_state=project.factory.entry_state() printf_address=0x0804FAB0 scanf_address=0x0804FB10 strcmp_address=0x08048228 puts_address=0x080503F0 __libc_start_main_address=0x08048D60 project.hook(printf_address,angr.SIM_PROCEDURES['libc' ]['printf' ]()) project.hook(scanf_address,angr.SIM_PROCEDURES['libc' ]['scanf' ]()) project.hook(strcmp_address,angr.SIM_PROCEDURES['libc' ]['strcmp' ]()) project.hook(puts_address,angr.SIM_PROCEDURES['libc' ]['puts' ]()) project.hook(__libc_start_main_address,angr.SIM_PROCEDURES['glibc' ]['__libc_start_main' ]()) def isgood (state ): return b'Good Job.' in state.posix.dumps(1 ) def isbad (state ): return b'Try again.' in state.posix.dumps(1 ) simulation=project.factory.simgr(init_state) simulation.explore(find=isgood,avoid=isbad) if simulation.found: solution_state=simulation.found[0 ] solution=solution_state.posix.dumps(0 ) print (solution) else : print ('no found' ) if __name__=='__main__' : main(sys.argv)
14_angr_shared_library