跟着AngrCTF学Angr(1)

最近想跟着angr_ctf学习了一下符号执行(用来在做逆向的时候偷懒),还是挺有收获的

PS:

angr == 8.20.1.7

二进制文件和脚本存储于https://github.com/ycdxsb/Challenges/tree/master/angr_ctf

基本使用#

常规使用#

  • 00_angr_find
  • 01_angr_avoid

简单设置目标地址和不想达到的地址即可,可以是单个地址也可以是很多地址,所以这里都用列表统一了一下

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

def main(filepath):
project = angr.Project(filepath)
init_state = project.factory.entry_state()
sim = project.factory.simgr(init_state)
find = [0x8048678]
avoid = []
sim.explore(find=find,avoid=avoid)
if sim.found:
solution_state = sim.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))
else:
raise Exception('Could not find the solution')


if __name__=="__main__":
if(len(sys.argv)!=2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

设置判断函数#

  • 02_angr_find_condition

对于一些情况,我们不需要去仔细分析要避免或者搜索哪些分支,只知道正确会输出什么,错误会输出什么,这个时候可以使用判断函数代替之前的地址列表

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 sys

def main(filepath):
project = angr.Project(filepath)
init_state = project.factory.entry_state()
sim = project.factory.simgr(init_state)

def find(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Good Job." in stdout_output

def avoid(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Try again." in stdout_output

sim.explore(find=find,avoid=avoid)
if sim.found:
solution_state = sim.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))
else:
raise Exception('Could not find the solution')


if __name__=="__main__":
if(len(sys.argv)!=2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

符号化读入#

由于angr不能处理复杂的scanf读入情况,因此要自己越过scanf对scanf后的状态赋值,并从scanf后开始执行

符号化寄存器#

  • 03_angr_symbolic_registers
1
2
3
4
5
6
7
.text:08048882                 call    _printf
.text:08048887 add esp, 10h
.text:0804888A call get_user_input
.text:0804888F mov [ebp+var_14], eax
.text:08048892 mov [ebp+var_10], ebx
.text:08048895 mov [ebp+var_C], edx
.text:08048898 sub esp, 0Ch

可以看到在get_user_input函数执行后,读入的三个数字分别存在eax,ebx和edx中,因此我们越过get_user_input函数开始执行,并且将其中的eax,ebx,edx赋值成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
37
38
39
40
41
42
43
import angr
import sys
import claripy

def main(filepath):
project = angr.Project(filepath)
start_address = 0x804888F

init_state = project.factory.blank_state(addr = start_address)
password_bits = 32
password0 = claripy.BVS('password0', password_bits)
password1 = claripy.BVS('password1', password_bits)
password2 = claripy.BVS('password2', password_bits)
init_state.regs.eax = password0
init_state.regs.ebx = password1
init_state.regs.edx = password2

sim = project.factory.simgr(init_state)

def find(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Good Job." in stdout_output

def avoid(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Try again." in stdout_output

sim.explore(find=find,avoid=avoid)
if sim.found:
solution_state = sim.found[0]
solution0 = format(solution_state.se.eval(password0),'x')
solution1 = format(solution_state.se.eval(password1),'x')
solution2 = format(solution_state.se.eval(password2),'x')
print(solution0,solution1,solution2)
else:
raise Exception('Could not find the solution')


if __name__=="__main__":
if(len(sys.argv)!=2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

符号化栈#

  • 04_angr_symbolic_stack
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int handle_user()
{
int result; // eax
int v1; // [esp+8h] [ebp-10h]
int v2; // [esp+Ch] [ebp-Ch]

__isoc99_scanf("%u %u", &v2, &v1);
v2 = complex_function0(v2);
v1 = complex_function1(v1);
if ( v2 == 887024739 && v1 == 1261126168 )
result = puts("Good Job.");
else
result = puts("Try again.");
return result;
}

可以看到,以%u格式读入后的数据v1和v2存储在栈上,那么当我们越过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
43
import angr
import sys
import claripy


def main(filepath):
project = angr.Project(filepath)
start_address = 0x8048697

init_state = project.factory.blank_state(addr=start_address)
init_state.regs.ebp = init_state.regs.esp

init_state.regs.esp -= 8
password0 = claripy.BVS('password0', 32)
password1 = claripy.BVS('password1', 32)
init_state.stack_push(password0)
init_state.stack_push(password1)

sim = project.factory.simgr(init_state)

def find(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Good Job." in stdout_output

def avoid(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Try again." in stdout_output

sim.explore(find=find, avoid=avoid)
if sim.found:
solution_state = sim.found[0]
solution0 = solution_state.se.eval(password0)
solution1 = solution_state.se.eval(password1)
print(solution0, solution1)
else:
raise Exception('Could not find the solution')


if __name__ == "__main__":
if(len(sys.argv) != 2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

符号化bss#

  • 05_angr_symbolic_memory
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_B368DA8, &unk_B368DB0, &unk_B368DB8);
for ( i = 0; i <= 31; ++i )
*(_BYTE *)(i + 0xB368DA0) = complex_function(*(char *)(i + 0xB368DA0), i);
if ( !strncmp(user_input, "IIZAUPRCZIZQJGKOJGDEAPHFFOBMNITD", 0x20u) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

可以看到是以字符串形式读入四个8字节的字符串,然后存到bss段上,所以越过scanf,并且在bss段上赋上符号数据即可

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


def main(filepath):
project = angr.Project(filepath)
start_address = 0x8048601

init_state = project.factory.blank_state(addr=start_address)
passwords = [claripy.BVS("password%d" % i, 64) for i in range(4)]
passwords_address = [0xB368DA0, 0xB368DA8, 0xB368DB0, 0xB368DB8]
for i in range(4):
init_state.memory.store(passwords_address[i], passwords[i])

sim = project.factory.simgr(init_state)

def find(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Good Job." in stdout_output

def avoid(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Try again." in stdout_output

sim.explore(find=find, avoid=avoid)
if sim.found:
solution_state = sim.found[0]
solutions = []
for i in range(4):
solutions.append(solution_state.se.eval(
passwords[i], cast_to = bytes))
print(b" ".join(solutions))
else:
raise Exception('Could not find the solution')


if __name__ == "__main__":
if(len(sys.argv) != 2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

符号化堆#

  • 06_angr_symbolic_dynamic_memory
1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // ebx
char *v4; // ebx
int v6; // [esp-10h] [ebp-1Ch]
signed int i; // [esp+0h] [ebp-Ch]

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, v6);

在这里,buffer0buffer1.bss段的指针,在运行过程中申请了内存块,scanf读入的数据也是存在申请的内存块内的,所以需要自己符号化malloc的内存

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


def main(filepath):
project = angr.Project(filepath)
start_address = 0x8048699

init_state = project.factory.blank_state(addr=start_address)
password0 = claripy.BVS('password0',64)
password1 = claripy.BVS('password1',64)

fake_heap_address0 = 0xffffc93c
fake_heap_address1 = 0xffffc94c

pointer_to_malloc_memory_address0 = 0x8135468
pointer_to_malloc_memory_address1 = 0x8135470

init_state.memory.store(pointer_to_malloc_memory_address0,fake_heap_address0,endness=project.arch.memory_endness)
init_state.memory.store(pointer_to_malloc_memory_address1,fake_heap_address1,endness=project.arch.memory_endness)

init_state.memory.store(fake_heap_address0,password0)
init_state.memory.store(fake_heap_address1,password1)

sim = project.factory.simgr(init_state)

def find(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Good Job." in stdout_output

def avoid(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Try again." in stdout_output

sim.explore(find=find, avoid=avoid)
if sim.found:
solution_state = sim.found[0]
sulution0=solution_state.se.eval(password0,cast_to=bytes)
sulution1=solution_state.se.eval(password1,cast_to=bytes)
print(b" ".join([sulution0,sulution1]))
else:
raise Exception('Could not find the solution')


if __name__ == "__main__":
if(len(sys.argv) != 2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

符号化文件#

  • 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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
signed int i; // [esp+Ch] [ebp-Ch]

memset(buffer, 0, 0x40u);
printf("Enter the password: ");
__isoc99_scanf("%64s", buffer);
ignore_me((int)buffer, 0x40u);
memset(buffer, 0, 0x40u);
fp = fopen("JKOYZJZV.txt", "rb");
fread(buffer, 1u, 0x40u, fp);
fclose(fp);
unlink("JKOYZJZV.txt");
for ( i = 0; i <= 7; ++i )
*(_BYTE *)(i + 134520992) = complex_function(*(char *)(i + 0x804A0A0), i);
if ( strncmp(buffer, "QAIIDABN", 9u) )
{
puts("Try again.");
exit(1);
}
puts("Good Job.");
exit(0);
}

可以看到这里需要从文件读取内容,所以需要自己符号化文件

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


def main(filepath):
project = angr.Project(filepath)

start_address = 0x80488D6
init_state = project.factory.blank_state(addr=start_address)

filename = "JKOYZJZV.txt"
symbolic_file_size_bytes = 0x40

password = claripy.BVS('password', symbolic_file_size_bytes * 8)

#file_options = 'r'
password_file = angr.storage.SimFile(filename, content=password,size = symbolic_file_size_bytes)

init_state.fs.insert(filename,password_file)

sim = project.factory.simgr(init_state)

def find(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Good Job." in stdout_output

def avoid(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b"Try again." in stdout_output

sim.explore(find=find, avoid=avoid)
if sim.found:
solution_state = sim.found[0]
solution = solution_state.se.eval(password,cast_to=bytes)
print(solution)
else:
raise Exception('Could not find the solution')


if __name__ == "__main__":
if(len(sys.argv) != 2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

评论