前言:最近对自动化二进制漏洞挖掘哼感兴趣,百度了下,发现自动化二进制漏洞挖掘的一个核心技术之一Angr符号执行,感觉很有意思再加上之前比赛也有很多关于Angr的题目,angr也可以对抗代码混淆ollvm这些,感觉以后用处蛮大的,于是就学了学,这里记录下笔记,发到博客里和微信公众号里供大家学习,学了一天,才刚学到07文件符号执行化,就分别总结成了一、二、三篇,太菜了
一、00
我们要用到两个模块,一个angr和一个sys
angr进行符号执行,sys来进行用户输入输出
我们第一步要进行angr脚本去跑约束逆向的时候,
第一步:
1.创建文件项目:
proj=angr.Project(binary,auto_load_libs=False),不加载libc
2.初始化地址块
state=proj.factory.entry_state() 这里的块初始化到了程序的start 块
3.初始化以后,我们要对这个块进行模拟
sim=proj.factory.simgr(state)
4.模拟后,开始寻找正确的路径
ok_address=0x8048675
sim.explore(find=ok_address) 这里是我们输出正确的路径
5.搜索到路径后,把状态放到sim.found[0]里,然后从这个状态里取出来即可
if sim.found:
solution_state=sim.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()).decode())
import angr
import sys
def solver():
binary="./00_angr_find"
proj=angr.Project(binary,auto_load_libs=False)
state=proj.factory.entry_state()
sim=proj.factory.simgr(state)
ok_address=0x8048675
sim.explore(find=ok_address)
if sim.found:
solution_state=sim.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()).decode())
else:
print("no!!!!")
if __name__=='__main__':
solver()
二、01
这个案例,前面部分跟00一样,在寻找路径的时候,explore的时候加个avoid参数,这个参数就避开错误代码块的参数
import angr
import sys
def solver():
binary="./01_angr_avoid"
proj=angr.Project(binary)
state=proj.factory.entry_state()
sim=proj.factory.simgr(state)
ok_address=0x80485E0
bi_kai=0x80485A8
sim.explore(find=ok_address,avoid=bi_kai)
if sim.found:
solution_state=sim.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()).decode())
else:
print("no !!!!!")
if __name__=='__main__':
solver()
三、02
这里刚上面一样,唯一的区别就是,自己写了一个避开的函数和正确的函数,赋给了find和avoid,效果都是一样的
import angr
import sys
def find_path(state):
return b'Good Job.' in state.posix.dumps(sys.stdout.fileno())
def avoid_path(state):
return b'Try again.' in state.posix.dumps(sys.stdout.fileno())
def solver():
binary="./02_angr_find_condition"
proj=angr.Project(binary)
init_state=proj.factory.entry_state()
sim=proj.factory.simgr(init_state)
ok_address=0x08048715
sim.explore(find=find_path,avoid=avoid_path)
if sim.found:
solution_state=sim.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()).decode())
else:
raise Exception('Could not find the solution')
if __name__=='__main__':
solver()
四、03 regs 寄存器
这里就有特点了,对寄存器的操作
我们看题目代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 user_input; // rax
int v5; // [esp+4h] [ebp-14h]
int v6; // [esp+8h] [ebp-10h]
int v7; // [esp+Ch] [ebp-Ch]
int v8; // [esp+Ch] [ebp-Ch]
printf("Enter the password: ");
user_input = get_user_input();
v7 = HIDWORD(user_input);
v5 = complex_function_1(user_input);
v6 = complex_function_2();
v8 = complex_function_3(v7);
if ( v5 || v6 || v8 )
puts("Try again.");
else
puts("Good Job.");
return 0;
}
get_user_input:
int get_user_input()
{
int v1; // [esp+0h] [ebp-18h] BYREF
int v2; // [esp+4h] [ebp-14h] BYREF
int v3[4]; // [esp+8h] [ebp-10h] BYREF
v3[1] = __readgsdword(0x14u);
__isoc99_scanf("%x %x %x", &v1, &v2, v3);
return v1;
}
通过上面两行代码我们发现,在getuserinput这个函数里,对v1、v2、v3进行了输入,然后只用了v1,然后看汇编
0804892A 68 93 8A 04 08 push offset aXXX ; "%x %x %x"
.text:0804892F E8 9C FA FF FF call ___isoc99_scanf
.text:0804892F
.text:08048934 83 C4 10 add esp, 10h
.text:08048937 8B 4D E8 mov ecx, [ebp+var_18]
.text:0804893A 89 C8 mov eax, ecx
.text:0804893C 8B 4D EC mov ecx, [ebp+var_14]
.text:0804893F 89 CB mov ebx, ecx
.text:08048941 8B 4D F0 mov ecx, [ebp+var_10]
.text:08048944 89 CA mov edx, ecx
.text:08048946 90 nop
发现三个值传给 了eax ebx ecx,我们可以在输入函数之后,利用向量给 initial_state.regs.eax ebx edx,进行向量的赋值,在后面进行状态查看即可,分析完后,我这里对关键的exp 代码进行分析
(1).start_address=0x08048980
initial_state=proj.factory.blank_state(addr=start_address) 这里在输入函数之后开始空白块,我这里理解的空白块是从这里开始进行向下执行,并且绕过程序自己的输入函数,我们自己进行对reg来操作赋值
(2).
passwd_0=claripy.BVS('passwd_0',32)
passwd_1=claripy.BVS('passwd_1',32)
passwd_2=claripy.BVS('passwd_2',32)
initial_state.regs.eax=passwd_0
initial_state.regs.ebx=passwd_1
initial_state.regs.edx=passwd_2
用claripy进行向量的创建,创建3个向量,因为是int32位,所以这里后面写成了32,然后再把三个向量导入到eax ebx edx
(3).
simgr=proj.factory.simgr(initial_state)
simgr.explore(find=find_path,avoid=avoid_path)
然后进行模拟执行并且查询
(4) .
然后通过simgr.found获取解决后的state(状态),
solution0=solution_state.solver.eval(passwd_0)
solution1=solution_state.solver.eval(passwd_1)
solution2=solution_state.solver.eval(passwd_2)
然后再用solution_state.solver.eval获取状态值,获取到状态值打印即可
import angr
import sys
import claripy
def find_path(state):
return b'Good Job.' in state.posix.dumps(sys.stdout.fileno())
def avoid_path(state):
return b'Try again.' in state.posix.dumps(sys.stdout.fileno())
def solver():
binary="./03_angr_symbolic_registers"
proj=angr.Project(binary)
start_address=0x08048980
initial_state=proj.factory.blank_state(addr=start_address)
passwd_0=claripy.BVS('passwd_0',32)
passwd_1=claripy.BVS('passwd_1',32)
passwd_2=claripy.BVS('passwd_2',32)
initial_state.regs.eax=passwd_0
initial_state.regs.ebx=passwd_1
initial_state.regs.edx=passwd_2
simgr=proj.factory.simgr(initial_state)
simgr.explore(find=find_path,avoid=avoid_path)
if simgr.found:
solution_state=simgr.found[0]
solution0=solution_state.solver.eval(passwd_0)
solution1=solution_state.solver.eval(passwd_1)
solution2=solution_state.solver.eval(passwd_2)
print('passwd0:{}'.format(hex(solution0)))
print('passwd1:{}'.format(hex(solution1)))
print('passwd2:{}'.format(hex(solution2)))
else:
raise Exception("Could not find the solution!")
if __name__=='__main__':
solver()
五、04 stack
这里是对stack的一个符号化,对栈的化,就得注意栈的分布了,esp和ebp寄存器这些
我们还是选关键代码进行分析
(1)。
passwd0=claripy.BVS("passwd0",32) #这里创建了向量
passwd1=claripy.BVS("passwd1",32) #
initial_state.regs.ebp=initial_state.regs.esp
initial_state.regs.esp-=0x8 #scanf
initial_state.stack_push(passwd0)
initial_state.stack_push(passwd1)
第一步先把寄存器esp给了ebp,然后让esp-0x8,这里减0x8的原因,我推测有两个原因,一是联系上下文、二是开辟栈空间
然后把我们创建的向量放到stack上即可,下面代码还是老一套,solver.eval(向量状态)
import angr
import sys
import claripy
def solver():
binary="./04_angr_symbolic_stack"
proj=angr.Project(binary)
start_address=0x8048697
initial_state=proj.factory.blank_state(addr=start_address)
passwd0=claripy.BVS("passwd0",32)
passwd1=claripy.BVS("passwd1",32)
initial_state.regs.ebp=initial_state.regs.esp
initial_state.regs.esp-=0x8 #scanf
initial_state.stack_push(passwd0)
initial_state.stack_push(passwd1)
#initial_state.regs.esp-=12 #.text:08048682 8D 45 F0 lea eax, [ebp+var_10]the relative position of esp when return from scanf()
simgr=proj.factory.simgr(initial_state)
simgr.explore(find=0x80486E1,avoid=0x80486CF)
if simgr.found:
solution_state=simgr.found[0]
solution0=solution_state.solver.eval(passwd0)
solution1=solution_state.solver.eval(passwd1)
print("passwd0:{}".format(hex(solution0)))
print("passwd1:{}".format(hex(solution1)))
else:
raise Exception("Colindd not !!!!")
if __name__=='__main__':
solver()
六、05 me'mory
这道题是对memory内存进行符号化
我们看下源代码:
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_A1BA1C8, &unk_A1BA1D0, &unk_A1BA1D8);
for ( i = 0; i <= 31; ++i )
*(_BYTE *)(i + 0xA1BA1C0) = complex_function(*(char *)(i + 0xA1BA1C0), i);
if ( !strncmp(user_input, "NJPURZPCDYEAXCSJZJMPSOMBFDDLHBVN", 0x20u) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}
通过以上代码,我们发现是往内存bss段里读入4个8bytes,所以这里我们利用一个为内存赋值的语句
init_state.memory.store(address,passwd0) 这个是把passwd0赋值给address这个地址的里
然后我们创建4个变量并且把这个四个变量放到读入的内存里
address=0xA1BA1C0
init_state.memory.store(address,passwd0) #放到
init_state.memory.store(address+0x8,passwd1)
init_state.memory.store(address+0x10,passwd2)
init_state.memory.store(address+0x18,passwd3)
这里注意字节的增长
然后下面就是按照之前的方法获取相应的状态值即可
import angr
import sys
import claripy
def solver():
binary="./05_angr_symbolic_memory"
proj=angr.Project(binary)
start_address=0x8048601
init_state=proj.factory.blank_state(addr=start_address)
passwd0=claripy.BVS("passwd0",64)
passwd1=claripy.BVS("passwd1",64)
passwd2=claripy.BVS("passwd2",64)
passwd3=claripy.BVS("passwd3",64)
address=0xA1BA1C0
init_state.memory.store(address,passwd0)
init_state.memory.store(address+0x8,passwd1)
init_state.memory.store(address+0x10,passwd2)
init_state.memory.store(address+0x18,passwd3)
simgr=proj.factory.simgr(init_state)
simgr.explore(find=0x804866A,avoid=0x8048658)
if simgr.found:
solution_state=simgr.found[0]
solution0=solution_state.solver.eval(passwd0,cast_to=bytes)
solution1=solution_state.solver.eval(passwd1,cast_to=bytes)
solution2=solution_state.solver.eval(passwd2,cast_to=bytes)
solution3=solution_state.solver.eval(passwd3,cast_to=bytes)
print("passwd0:{}".format(solution0))
print("passwd1:{}".format(solution1))
print("passwd2:{}".format(solution2))
print("passwd3:{}".format(solution3))
raise Exception("couln not !!!!!!!")
if __name__=='__main__':
solver()
七、06 动态memory
这里是动态的chunk,malloc出来的,这里我们在动态里进行伪造fakechunk,然后再往这个伪造的chunk进行放入向量即可
先看下源代码:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // ebx
char *v4; // ebx
int v6; // [esp-10h] [ebp-1Ch]
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);
for ( i = 0; i <= 7; ++i )
{
v3 = &buffer0[i];
*v3 = complex_function(buffer0[i], i);
v4 = &buffer1[i];
*v4 = complex_function(buffer1[i], i + 32);
}
if ( !strncmp(buffer0, "UODXLZBI", 8u) && !strncmp(buffer1, "UAORRAYF", 8u) )
puts("Good Job.");
else
puts("Try again.");
free(buffer0);
free(buffer1);
return 0;
}
获取buffer0和buffer1的地址对这个地址进行fake即可
以下是我伪造的代码:
buf0=0xABCC8A4
buf1=0xABCC8AC
fake_buf0=0xAAAAA10
fake_buf1=0xAAAAA20
p1=claripy.BVS("p1",64)
p2=claripy.BVS("p2",64)
init_state.memory.store(buf0,fake_buf0,endness=proj.arch.memory_endness)
init_state.memory.store(buf1,fake_buf1,endness=proj.arch.memory_endness)
init_state.memory.store(fake_buf0,p1)
init_state.memory.store(fake_buf1,p2)
然后下一步一把梭获取状态值即可
import angr
import sys
import claripy
def find_path(state):
return b'Good Job.' in state.posix.dumps(sys.stdout.fileno())
def avoid_path(state):
return b'Try again.' in state.posix.dumps(sys.stdout.fileno())
def solver():
binary="./06_angr_symbolic_dynamic_memory"
proj=angr.Project(binary)
start_address=0x8048699
init_state=proj.factory.blank_state(addr=start_address)
buf0=0xABCC8A4
buf1=0xABCC8AC
fake_buf0=0xAAAAA10
fake_buf1=0xAAAAA20
p1=claripy.BVS("p1",64)
p2=claripy.BVS("p2",64)
init_state.memory.store(buf0,fake_buf0,endness=proj.arch.memory_endness)
init_state.memory.store(buf1,fake_buf1,endness=proj.arch.memory_endness)
init_state.memory.store(fake_buf0,p1)
init_state.memory.store(fake_buf1,p2)
simgr=proj.factory.simgr(init_state)
simgr.explore(find=find_path,avoid=avoid_path)
if simgr.found:
sol_state=simgr.found[0]
p1=sol_state.solver.eval(p1,cast_to=bytes)
p2=sol_state.solver.eval(p2,cast_to=bytes)
print("p1:{}".format(p1))
print("p2:{}".format(p2))
else:
raise Exception("Colud not!!!!!!")
if __name__=='__main__':
solver()
八、07 符号fiile文件
这个题是对file文件进行符号化
我们看下题目代码:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int i; // [esp+Ch] [ebp-Ch]
memset(buffer, 0, sizeof(buffer));
printf("Enter the password: ");
__isoc99_scanf("%64s", buffer);
ignore_me((int)buffer, 0x40u);
memset(buffer, 0, sizeof(buffer));
fp = fopen("OJKSQYDP.txt", "rb");
fread(buffer, 1u, 0x40u, fp);
fclose(fp);
unlink("OJKSQYDP.txt");
for ( i = 0; i <= 7; ++i )
*(_BYTE *)(i + 0x804A0A0) = complex_function(*(char *)(i + 0x804A0A0), i);
if ( strncmp(buffer, "AQWLCTXB", 9u) )
{
puts("Try again.");
exit(1);
}
puts("Good Job.");
exit(0);
}
我们看到,这道打开了一个文件。在这道题的基础上,我们可以使用angr模拟文件从而进行文件内容符号化,我们用到了几个关键语句
file_size=0x40
filename='OJKSQYDP.txt'
password=claripy.BVS('password',file_size*8)
file_name=angr.storage.SimFile(filename,content=password,size=file_size)
init_state.fs.insert(filename,file_name)
我们利用angr.storage.SimFile模拟文件,第一个参数为文件名,第二个为我们的向量,第三个为题目本身的读入的size,然后模拟完后,我们再对文件系统进行载入init_state.fs.insert(filename,file_name)即可实现文件符号化
然后剩下就是用angr进行模拟,获取状态,获取向量状态值,一把梭
import angr
import sys
import claripy
def isGood(state):
return b'Good Job.' in state.posix.dumps(1)
def isBad(state):
return b'Try again.' in state.posix.dumps(1)
def solver():
binary="./07_angr_symbolic_file"
proj=angr.Project(binary)
start_address=0x80488EA
init_state=proj.factory.blank_state(addr=start_address)
file_size=0x40
filename='OJKSQYDP.txt'
password=claripy.BVS('password',file_size*8)
file_name=angr.storage.SimFile(filename,content=password,size=file_size)
init_state.fs.insert(filename,file_name)
simgr=proj.factory.simgr(init_state)
simgr.explore(find=isGood,avoid=isBad)
if simgr.found:
sol_state=simgr.found[0]
passwd=sol_state.solver.eval(password,cast_to=bytes)
print("passwd:{}".format(passwd))
else:
raise Exception("Colund not!!!!!!")
if __name__=="__main__":
solver()
未完持续