首先,下载angr_ctf,打开dist文件夹,这里才是练习题,然后solution是答案
00.find
01.avoid
前面两个是基础操作
复习一下流程:
1.项目路径
2.进入状态
3.模拟器模拟进入状态时的环境
4.模拟器explore,find一个地址,avoid一些地址
5.simulation.found[]数组存储成功的输入
6.print(solution_state.posix.dumps(sys.stdin.fileno()))
这个语句中,posix指的是访问模拟状态下的文件描述符,
dumps指的是获取该文件描述符下的数据并返回为byte对象,
sys.stdin.fileno()指的是获取键盘输出的文件描述符,值为0;
02.find_condition
这个操作是指不以地址为成功指示,而是以输出,如果输出‘Good Job.’则是成功的
具体如下:
定义is_successful,should_abort两个函数,分别作为find和avoid的参数,
本质上就是另一种寻找方式
03.fill registers
这里是从不同的地方进入程序,使用了寄存器存放输入的参数
1.设置入口,angr.options指示使用符号填充寄存器
这里找入口要找准,刚开始使用寄存器的值的时候最好,这时刚好用上符号
符号对象要使用位向量:
填充:
然后按照一般步骤求解。
这里有个知识点,可以使用eval来返回多解的其中一个解
1.{:x}: 格式说明符,用于将整数以小写的十六进制格式打印。
2.format(flag1, flag2, flag3): 将 flag1、flag2 和 flag3 这三个整数传递给 format 方法,对应地填充到三个 {} 占位符中。
04.change symbolic in stack
这里其实就是把符号存进栈里面,而不是寄存器
1.定入口,这里是scanf过后,想要把栈里面通过scanf得到的变量替换掉,所以在恢复scanf调用前的栈的地址插入入口就好了,然后再自己在栈添加多出的两个变量
2.初始化
initial_state.regs.ebp = initial_state.regs.esp#初始化esp,ebp
3.初始符号
4.开辟空间
5.入栈
后面的操作不变
solution = ' '.join(map(str, [ solution0, solution1 ]))
这个拼接很丝滑
05.symbolic in memory
1.找入口
2.在ida里面找到对应push的参数到具体内存地址
3.后面操作相同,处理字符串的方式: