前言

最近在突破学习angr使用

这儿就拿这个例题来写写, 挺好的

方便我复习

构造堆栈的核心: 找到函数调用前的exp情况和调用后的esp的情况以及输入的地址

例题:https://github.com/angr/angr-doc/tree/master/examples/flareon2015_2

直接开整

? 我以为是elf文件

image-20230308095329342

查下壳

image-20230308095409904

拖入ida分析

函数这么少??
image-20230308095500362

决定用angr来解题 那么 想想构造堆栈的核心

那就找呗

image-20230308180816232

可以先看下 官方exp

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
#!/usr/bin/env python
import angr

def main():
b = angr.Project("very_success", load_options={"auto_load_libs":False})
# 创建一个angr项目并且金庸CLE自动解析共享库依赖关系(说白了就是不自动加载c函数的库文件)
# 因为这是一个win的二进制文件, 为了避免调用win的api
# 我们需要把0小01084设为其实状态, 并且设置好传入的参数
s = b.factory.blank_state(addr=0x401084)
# 设置堆栈上的参数
s.memory.store(s.regs.esp+12, s.solver.BVV(40, s.arch.bits))
s.mem[s.regs.esp+8:].dword = 0x402159
s.mem[s.regs.esp+4:].dword = 0x4010e4
s.mem[s.regs.esp:].dword = 0x401064
# 为输入存储一个符号字符串, 因为此时的输入是空白的
s.memory.store(0x402159, s.solver.BVS("ans", 8*40))
# 创建模拟执行器
sm = b.factory.simulation_manager(s)
sm.explore(find=0x40106b, avoid=0x401072)
# 打印字符串
found_state = sm.found[0]
return found_state.solver.eval(found_state.memory.load(0x402159, 40), cast_to=bytes).strip(b'\0')

def test():
assert main() == b'a_Little_b1t_harder_plez@flare-on.com'

if __name__ == '__main__':
print(main())

为了必满调用win的api, 我们需要把入口设为0小01084

0x401084为函数sub-401084校验输入的地方

但是这样的话就会跳过sub-401000函数, 所以我们要模仿此函数

函数调用前后的堆栈

这道题的关键就在这里

1
2
3
4
5
# 设置堆栈上的参数
s.memory.store(s.regs.esp+12, s.solver.BVV(40, s.arch.bits))
s.mem[s.regs.esp+8:].dword = 0x402159
s.mem[s.regs.esp+4:].dword = 0x4010e4
s.mem[s.regs.esp:].dword = 0x401064

至于这一句

1
s.memory.store(s.regs.esp+12, s.solver.BVV(40, s.arch.bits))

这个创建了一个值40 大小为s.arch.bits的BVV(位向量值 也就是输入)

其中s.arch.bits的值位32(因为这是一个32位的程序)

接着的话 把该值载入到exp+12的位置上

为什么呢? (逆出来的 图方便 - 抽象 哈哈

因为这个地址对此程序无影响 所以就拿来当作参照物

保存call sub401084调用后的堆栈情况

image-20230308182132349

这儿需要知道[ebp-10h]地址存的啥

可以动调(笔者这种懒人最喜欢做的事了)

image-20230308182304092

也可以往回逆

image-20230308182400030

接下来就简单了

构造模拟器 设置find以及avoid

1
2
sm = b.factory.simulation_manager(s)
sm.explore(find=0x40106b, avoid=0x401072)

最后输出就好了

1
2
found_state = sm.found[0]
print(found_state.solver.eval(found_state.memory.load(0x402159, 40), cast_to=bytes).strip(b'\0')

整理一下exp

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 angr
import claripy

def main():
p = angr.Project('./very_success', auto_load_libs=False)
state = p.factory.blank_state(addr=0x401084)

state.memory.store(state.regs.esp+12, state.solver.BVV(40, state.arch.bits))
# 输入的数据的地址
state.mem[state.regs.esp+8:].dword = 0x402159
# 函数调用前[ebp - 10]存放的地址
state.mem[state.regs.esp+4:].dword = 0x4010E4
# call之后eip的地址
state.mem[state.regs.esp:].dword = 0x401064

state.memory.store(0x402159, state.solver.BVS("ans", 40*8))

simgr = p.factory.simulation_manager(state)
simgr.explore(find=0x40106B, avoid=0x401072)

found = simgr.found[0]
return found.solver.eval(found.memory.load(0x402159, 40), cast_to=bytes).strip(b'\0')

if __name__ == '__main__':
print(main())

image-20230308182704771

ok

总结

构造堆栈的核心: 找到函数调用前的exp情况和调用后的esp的情况以及输入的地址

本题结束 感谢观看

Orz