项目地址:

https://github.com/jakespringer/angr_ctf

https://github.com/Hustcw/Angr_Tutorial_For_CTF

00_angr_find

image-20231106174610873

image-20231106174626852

image-20231106174700614

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import angr

proj = angr.Project('./00_angr_find',auto_load_libs=False)#不加载共享文件库

state = proj.factory.entry_state()#构造一个已经准备好从函数入口点执行的状态

sm = proj.factory.simulation_manager(state) #创建一个模拟管理器

good = 0x0804867D

sm.explore(find = good)

if sm.found: #如果found不为空
find_state = sm.found[0] #把found分类中的state传递给find_state

flag = find_state.posix.dumps(0) #获取输入

print(str(flag,'utf-8'))

01_angr_avoid

image-20231106175043156

有很多avoid_me过程,为了避免浪费时间可以设置不执行

image-20231106175153406

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import angr

proj = angr.Project('./01_angr_avoid',auto_load_libs=False)

state = proj.factory.entry_state()

simgr = proj.factory.simulation_manager(state)

simgr.explore(find=0x080485E5,avoid=0x080485A8)
#avoid是需要避免的地址,即找到的状态需要满足 不执行此地址,保存在avoid分类中
if simgr.found:
found_state = simgr.found[0]

flag = found_state.posix.dumps(0)
print(flag)
print(str(flag,'utf-8'))

02_angr_find_condition

image-20231106175810413

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import angr

proj = angr.Project('./02_angr_find_condition',auto_load_libs=False)

state = proj.factory.entry_state()

simgr = proj.factory.simulation_manager(state)

good = lambda s:b'Good Job.' in s.posix.dumps(1)
# 意为寻找输出为'Good Job.'的状态,注意这里为字节型数据

simgr.explore(find=good)

if simgr.found:
found_state = simgr.found[0]

flag = found_state.posix.dumps(0)

print(str(flag,'utf-8'))

03_angr_symbolic_registers

image-20231106180022133

image-20231106180030534

这个scanf需要输入3个,并且是通过eax,ebx,edx三个寄存器传参的。

所以需要对三个寄存器进行设置值,所以利用到了claripy

image-20231106180353008

所以通过设置空状态来跳过这个scanf输入,设置起始地址为0x08048937

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


proj = angr.Project('./03_angr_symbolic_registers',auto_load_libs=False)

#state = proj.factory.entry_state()
start_addr = 0x08048980
good = lambda s:b'Good Job.' in s.posix.dumps(1)

state = proj.factory.blank_state(addr = start_addr) #构造空状态跳过scanf


p1 = claripy.BVS('p1',32) #创建符号变量替代eax,ebx,edx三个参数
p2 = claripy.BVS('p2',32)
p3 = claripy.BVS('p3',32)

state.regs.eax = p1
state.regs.ebx = p2
state.regs.edx = p3

simgr = proj.factory.simulation_manager(state)

simgr.explore(find=good)

if simgr.found:
found_state = simgr.found[0]

flag1 = found_state.solver.eval(p1)
flag2 = found_state.solver.eval(p2)
flag3 = found_state.solver.eval(p3)

print("flag:{} {} {}".format(hex(flag1),hex(flag2),hex(flag3)))
else:
print("no find")

04_angr_symbolic_stack

image-20231106180627544

输入两个参数,是通过栈传参的。

image-20231106180719842

image-20231106180948981

这一步是恢复scanf函数的栈空间。所以要从0x8048697处开始,并且复制栈中的数据。可以通过gdb调出偏移。

image-20231106181353764

可以模仿开辟这个栈,复制其中的数据。

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

proj = angr.Project('./04_angr_symbolic_stack',auto_load_libs=False)

start_addr = 0x08048697

good = lambda s:b'Good Job.' in s.posix.dumps(1)

state = proj.factory.blank_state(addr = start_addr)

state.regs.ebp = state.regs.esp #复制开辟栈

state.regs.esp = state.regs.esp - 8 #复制栈中数据

p1 = claripy.BVS('p1',32)
p2 = claripy.BVS('p2',32)

state.stack_push(p1)
state.stack_push(p2)

simgr = proj.factory.simulation_manager(state)

simgr.explore(find=good)

if simgr.found:
found_state = simgr.found[0]

flag1 = found_state.solver.eval(p1)
flag2 = found_state.solver.eval(p2)
print("flag:{} {}".format(flag1,flag2))

else:
print("not found")

05_angr_symbolic_memory

image-20231106205626389

数据存储在内存中。

image-20231106205855398

所以就是利用claripy生成四个符号向量,其中scanf中的每个值为%8s,就是8个字符,而char是一个字节(8 bit),所以BVS设置设置为8*8

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

proj = angr.Project('./05_angr_symbolic_memory',auto_load_libs=False)

start_addr = 0x080485FE

state = proj.factory.blank_state(addr=start_addr)

good = lambda s:b'Good Job.' in s.posix.dumps(1)


input1 = 0x0A1BA1C0 #数据存储的地址
input2 = 0x0A1BA1C8
input3 = 0x0A1BA1D0
input4 = 0x0A1BA1D8

p1 = claripy.BVS('p1',8*8) #生成符号变量
p2 = claripy.BVS('p2',8*8)
p3 = claripy.BVS('p3',8*8)
p4 = claripy.BVS('p4',8*8)

state.memory.store(input1,p1) #存储到对应地址
state.memory.store(input2,p2)
state.memory.store(input3,p3)
state.memory.store(input4,p4)

simgr = proj.factory.simulation_manager(state)

simgr.explore(find=good)

if simgr.found:
found_state = simgr.found[0]
s1 = found_state.solver.eval(p1,cast_to=bytes).decode()
s2 = found_state.solver.eval(p2,cast_to=bytes).decode()
s3 = found_state.solver.eval(p3,cast_to=bytes).decode()
s4 = found_state.solver.eval(p4,cast_to=bytes).decode()
print("flag:{} {} {} {}".format(s1,s2,s3,s4))
else:
print("not found")

因为最后读取的是字符串,但是eval出来的是bytes类型的,所以需要进行个类型转换。

06_angr_symbolic_dynamic_memory

image-20231107082449619

首先malloc分配两块动态内存。buffer0buffer1用来存储这两块内存的地址。

image-20231107082725955

所以可以先设置两个fake地址,再设置符号变量。

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

proj = angr.Project('./06_angr_symbolic_dynamic_memory',auto_load_libs=False)

start_addr = 0x08048696

good = lambda s:b'Good Job.' in s.posix.dumps(1)

butter0 = 0x0ABCC8A4
butter1 = 0x0ABCC8AC


state = proj.factory.blank_state(addr = start_addr)

fake_addr1 = 0x11111111
fake_addr2 = 0x22222222

b0 = claripy.BVS('b0',8*8)
b1 = claripy.BVS('b1',8*8)
state.memory.store(butter0,fake_addr1,endness=proj.arch.memory_endness,size=4)#指定字节存储序
state.memory.store(butter1,fake_addr2,endness=proj.arch.memory_endness,size=4)
state.memory.store(fake_addr1,b0)
state.memory.store(fake_addr2,b1)

simgr = proj.factory.simulation_manager(state)
simgr.explore(find=good)
if simgr.found:
found_state = simgr.found[0]
flag1 = found_state.solver.eval(b0,cast_to=bytes).decode()
flag2 = found_state.solver.eval(b1,cast_to=bytes).decode()
print("flag:{} {}".format(flag1,flag2))

else:
print("not found")

07_angr_symbolic_file

image-20231107090959714

这题需要符号化整个文件,然后再设置符号化文件中的内容。

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

proj = angr.Project('./07_angr_symbolic_file',auto_load_libs=False)

good = lambda s:b'Good Job.' in s.posix.dumps(1)

start_addr = 0x080488D6
buffer = 0x0804A0A0

state = proj.factory.blank_state(addr = start_addr)

file_name = "OJKSQYDP.txt"
size_file = 64
password = claripy.BVS('p1',size_file*8)
#创建一个用于符号化文件操作的SimFile的对象,content是一个符号变量,相当于将文件中的内容符号化
file_sym = angr.storage.SimFile(file_name,content=password)
state.fs.insert(file_name,file_sym)

simgr = proj.factory.simulation_manager(state)

simgr.explore(find=good)
if simgr.found:
found_state = simgr.found[0]
flag1 = found_state.solver.eval(password,cast_to=bytes).decode()
print("flag:{}".format(flag1))
else:
print("not found")

08_angr_constraints

image-20231107122042667

image-20231107122057107

如果不设置其它约束直接跑的话,可能会导致路径爆炸。循环里的分支呈指数增长的。

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

proj = angr.Project('./08_angr_constraints',auto_load_libs=False)

start_addr = 0x8048625
good = lambda s:b'Good Job.' in s.posix.dumps(1)
state = proj.factory.blank_state(addr = start_addr)

b1 = claripy.BVS('b1',16*8)

password = 0x0804A050

state.memory.store(password,b1)
simgr = proj.factory.simulation_manager(state)

check_addr = 0x08048673

simgr.explore(find=check_addr) #为防止路径爆炸,需要在判断之后手动添加限制条件,所以指定find为check地址

if simgr.found:
found_state = simgr.found[0]
constrain_addr = password
byte_size = 16
load_symbols = found_state.memory.load(constrain_addr,byte_size) #从constrain_addr(即password)处加载16字节的数据到load_symbols
string = "AUPDNNPROEZRJWKB"
found_state.add_constraints(load_symbols == string)#添加约束,要求load_symbols == "AUPDNNPROEZRJWKB"
flag = found_state.solver.eval(b1,cast_to=bytes).decode()
print("flag:{}".format(flag))
else:
print("not found")

09_angr_hooks

image-20231107143701640

image-20231107143726234

这个函数会引起路径爆炸,所以这里采用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
import angr
import claripy

proj = angr.Project('./09_angr_hooks',auto_load_libs=False)

state = proj.factory.entry_state()
good = lambda s:b'Good Job.' in s.posix.dumps(1)
check_addr = 0x080486B3
skip_len = 5
#设置为自己的函数,需要从check前跳过
@proj.hook(check_addr,length=skip_len)
#自己实现的函数
def check_equal(state):
buffer_addr = 0x0804A054
load_buffer_symbol = state.memory.load(buffer_addr,16)
check_str = "XYMKBKUHNIQYNQXE"
#根据判断load_buffer_symbol和XYMKBKUHNIQYNQXE是否相等来返回1或者0
state.regs.eax = claripy.If(load_buffer_symbol == check_str,claripy.BVV(1,32),claripy.BVV(0,32))

simgr = proj.factory.simulation_manager(state)

simgr.explore(find = good)
if simgr.found:
found_state = simgr.found[0]
flag = found_state.posix.dumps(0)
print(str(flag,'utf-8'))
else:
print("not found")

10_angr_simprocedures

image-20231107154842859

跟前面一个差不多,但是这个check被调用的次数多。

image-20231107154946282

所以不再是根据函数地址去hook了,直接根据函数名称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
import angr
import claripy

def exp():
proj = angr.Project('./10_angr_simprocedures',auto_load_libs=False)
state = proj.factory.entry_state()

class my_symbol(angr.SimProcedure):
def run(self,check_addr,length):
angr_input = self.state.memory.load(check_addr,length)
#要比较的字符串
check_string = "ORSDDWXHZURJRBDH"
return claripy.If(angr_input == check_string,claripy.BVV(1,32),claripy.BVV(0,32))
#要=hook的函数名称
check_symbol = 'check_equals_ORSDDWXHZURJRBDH'
proj.hook_symbol(check_symbol,my_symbol())
simgr = proj.factory.simulation_manager(state)

simgr.explore(find = is_good,avoid = is_bad)
if simgr.found:
found_state = simgr.found[0]
flag = found_state.posix.dumps(0)
print(str(flag,'utf-8'))
else:
print("not found")

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)

if __name__ == '__main__':
exp()

11_angr_sim_scanf

image-20231107174725126

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

def exp():
proj = angr.Project('./11_angr_sim_scanf',auto_load_libs=False)
start_addr = 0x08048714
state = proj.factory.entry_state()
class my_symbol(angr.SimProcedure):
def run(self,format_string,addr1,addr2):
buffer0 = claripy.BVS('buffer0',32)
buffer1 = claripy.BVS('buffer1',32)
self.state.memory.store(addr1,buffer0,endness=proj.arch.memory_endness,size = 4)
self.state.memory.store(addr2,buffer1,endness=proj.arch.memory_endness,size = 4)
self.state.globals['solutions'] = (buffer0,buffer1)#为了让函数外部的simgr也能获得到
scanf_symbol = "__isoc99_scanf"
proj.hook_symbol(scanf_symbol,my_symbol())
simgr = proj.factory.simulation_manager(state)
simgr.explore(find=is_good,avoid=is_bad)
if simgr.found:
found_state = simgr.found[0]
flag = found_state.globals['solutions']
flag1 = found_state.solver.eval(flag[0])
flag2 = found_state.solver.eval(flag[1])
print("flag:{} {}".format(flag1,flag2))
else:
print("not found")

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)

if __name__ == '__main__':
exp()

12_angr_veritesting

简单说一下Veritesting

由于动态符号执行和静态符号执行的优缺点不同,Veritesting结合了静态符号执行与动态符号执行,减少路径爆炸的影响。

image-20231107195025006

红框处的循环内有着混淆和判断,所以如果继续循环下去的话,分支会呈指数式增长,会导致路径爆炸。所以需要增加条件约束和Hook函数避免路径爆炸。也可以使用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
import angr
import claripy

def exp():
proj = angr.Project('./12_angr_veritesting',auto_load_libs=False)
state = proj.factory.entry_state()
simgr = proj.factory.simulation_manager(state,veritesting=True)

simgr.explore(find=is_good,avoid=is_bad)

if simgr.found:
found_state = simgr.found[0]
flag = found_state.posix.dumps(0)
print(flag.decode())
else:
print("not found")

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)

if __name__ == '__main__':
exp()

13_angr_static_binary

image-20231107211537389

这道题用的是静态编译。通常情况下,Angr会自动地用工作速度快的多的simporcedure代替标准库函数,但是这一题中由于用的是静态编译,所以都编译为静态函数,angr没有自动替换。所以需要手动hook所有使用标准库的C函数,angrsimprocedure中提供了这些静态函数。

1
2
3
4
5
6
7
8
9
10
11
angr.SIM_PROCEDURES['libc']['malloc']
angr.SIM_PROCEDURES['libc']['fopen']
angr.SIM_PROCEDURES['libc']['fclose']
angr.SIM_PROCEDURES['libc']['fwrite']
angr.SIM_PROCEDURES['libc']['getchar']
angr.SIM_PROCEDURES['libc']['strncmp']
angr.SIM_PROCEDURES['libc']['strcmp']
angr.SIM_PROCEDURES['libc']['scanf']
angr.SIM_PROCEDURES['libc']['printf']
angr.SIM_PROCEDURES['libc']['puts']
angr.SIM_PROCEDURES['libc']['exit']

只需要找到静态函数的地址进行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
import angr
import claripy

def exp():
proj = angr.Project('./13_angr_static_binary',auto_load_libs=False)
state = proj.factory.entry_state()
proj.hook(0x0804ED40,angr.SIM_PROCEDURES['libc']['printf']())
proj.hook(0x0804ED80,angr.SIM_PROCEDURES['libc']['scanf']())
proj.hook(0x0804F350,angr.SIM_PROCEDURES['libc']['puts']())
proj.hook(0x08048D10,angr.SIM_PROCEDURES['glibc']['__libc_start_main']())

simgr = proj.factory.simulation_manager(state,veritesting=True)
simgr.explore(find=is_good,avoid=is_bad)
if simgr.found:
found_state = simgr.found[0]
flag = found_state.posix.dumps(0)
print(flag.decode())
else:
print("not found")

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)


if __name__ == '__main__':
exp()

14_angr_shared_library

image-20231108152253418

validate是一个外部导入函数

image-20231108152402367

.so文件中找到函数的定义

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

def exp():
base = 0x4000000
proj = angr.Project('./lib14_angr_shared_library.so',load_options={'main_opts':{'custom_base_addr':base}})
buffer_pointer = claripy.BVV(0x3000000,32)
validate_addr = base + 0x6d7
#构造准备好执行validate函数的状态
state = proj.factory.call_state(validate_addr,buffer_pointer,claripy.BVV(8,32))

password = claripy.BVS('password',8*8)
state.memory.store(buffer_pointer,password)

simgr = proj.factory.simulation_manager(state)
success_addr = base + 0x783
simgr.explore(find=success_addr)
if simgr.found:
found_state = simgr.found[0]
#添加约束函数正确执行返回值不等于0
found_state.add_constraints(found_state.regs.eax != 0)
flag = found_state.solver.eval(password,cast_to=bytes).decode()
print("flag:{}".format(flag))

else:
print("not found")


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)

if __name__ == '__main__':
exp()