freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

angr在逆向中的运用
2022-06-03 22:06:56
所属地 四川省

Angr

angr在现在的ctf并不常见,因为angr具有很强的不确定性,但在最近几天的ctf中是存在题可以使用angr进行跑的,所以我们花费两天的时间系统的学习一下angr,以下的题,我们全部用angr来解决。

00_angr_find

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+1Ch] [ebp-1Ch]
char s1[9]; // [esp+23h] [ebp-15h] BYREF
unsigned int v6; // [esp+2Ch] [ebp-Ch]

v6 = __readgsdword(0x14u);
printf("Enter the password: ");
__isoc99_scanf("%8s", s1);
for ( i = 0; i <= 7; ++i )
s1[i] = complex_function(s1[i], i);
if ( !strcmp(s1, "ILIUFVJF") )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

comlex函数中也就是一个if函数和return,并不难,如何用angr写脚本是一个难的问题

angr的使用也比较麻烦,因为angr带有z3,如果直接安装,会出现问题,这个z3与python的z3是由一点去别的,所以我们每次用都要搞一个虚拟环境,我们每次使用先打开虚拟环境:

其实只用后两个命令都行

cd
sudo apt-get install python-dev libffi-dev build-essential virtualenvwrapper
export WORKON_HOME=~/HOME
VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/share/virtualenvwrapper/virtualenvwrapper.sh
mkvirtualenv --python=$(which python3) angr && pip install angr

这样就行了

import angr
import sys
def main(argv):
#目标文件路径
path_to_binary = './00_angr_find/00_angr_find'
#创建项目
project = angr.Project(path_to_binary)
#设置项目起点,entry_state是程序的入口处,相当于main
initial_state = project.factory.entry_state()
#设置模拟器
simulation = project.factory.simgr(initial_state)
#设置目标地址
print_good_addr = 0x08048694
simulation.explore(find=print_good_addr)

#如果到达目标地址,打印此时符号向量
if simulation.found:
solution_state = simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))

#无解或出错抛出异常
else:
raise Exception('Could not find solution')

if __name__ == '__main__':
main(sys.argv)
(angr) ┌─(~/syclover/angr/decode)───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────(jonathan@DESKTOP-4L3BSKM:pts/0)─┐
└─(12:51:45)──> python3 angr0.py
WARNING | 2022-03-12 12:53:13,212 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-12 12:53:13,212 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-12 12:53:13,212 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-12 12:53:13,212 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-12 12:53:13,212 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-12 12:53:13,213 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80486c1 (__libc_csu_init+0x1 in 00_angr_find (0x80486c1))
WARNING | 2022-03-12 12:53:14,270 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff50 with 4 unconstrained bytes referenced from 0x8186b80 (strcmp+0x0 in libc.so.6 (0x86b80))
b'IICLTGRK'

得到结果。

sys.argv

传递给Python脚本的命令行参数列表。argv[0]是脚本名称(依赖于操作系统,无论这是否是完整路径名)。如果使用-c解释器的命令行选项执行命令,argv[0]则将其设置为字符串’-c’。如果没有脚本名称传递给Python解释器,argv[0]则为空字符串。

这个就是命令行输入

if name== 'main':

不能在import引用的脚本中执行。一个python文件通常有两种使用方法,第一是作为脚本直接执行,第二是 import 到其他的 python 脚本中被调用(模块重用)执行。因此 if name== 'main': 的作用就是控制这两种情况执行代码的过程,在 if name== 'main': 下的代码只有在第一种情况下

factory

angr中有许多使用时需要一个实例化的project的类,可以使用project.factory而不用把project到处传递。

states

Project对象只代表程序的一个初始化镜像,当你使用angr来执行时,你在使用一个表示模拟程序状态的一个特殊对象SimState

01_angr_avoid

通过字符串搜索到good job

这一题是明显让我们使用avoid,那么avoid其实就是告诉脚本不要走哪边。

主函数太大,无法反编译,估计是有很多别的路,所以要加avoid,我们可以用上一个脚本,但稍微改一下。

from doctest import FAIL_FAST
import angr
import sys

def main(argv):
path_to_binary = '../solutions/01_angr_avoid/01_angr_avoid'
project = angr.Project(path_to_binary)

initial_state = project.factory.entry_state()

simulation = project.factory.simgr(initial_state)
print_good_address = 0x080485FC
fail_address = 0x0804860E
simulation.explore(find=print_good_address,avoid=fail_address)
if simulation.found:
solution_state = simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))

else:
raise Exception('Could not find solution')

if __name__ == '__main__':
main(sys.argv)

我们只是在模拟器中添加了avoid。

WARNING | 2022-03-12 14:12:22,299 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-12 14:12:22,299 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-12 14:12:22,299 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-12 14:12:22,299 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-12 14:12:22,299 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-12 14:12:22,300 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80d45b1 (__libc_csu_init+0x1 in 01_angr_avoid (0x80d45b1))
WARNING | 2022-03-12 14:12:51,054 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff1d with 11 unconstrained bytes referenced from 0x8187170 (strncmp+0x0 in libc.so.6 (0x87170))
WARNING | 2022-03-12 14:12:51,054 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff40 with 4 unconstrained bytes referenced from 0x8187170 (strncmp+0x0 in libc.so.6 (0x87170))
b'JLVUSGJZ'

02_angr_find_condition

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+18h] [ebp-40h]
int j; // [esp+1Ch] [ebp-3Ch]
char s1[20]; // [esp+24h] [ebp-34h] BYREF
char s2[20]; // [esp+38h] [ebp-20h] BYREF
unsigned int v8; // [esp+4Ch] [ebp-Ch]

v8 = __readgsdword(0x14u);
for ( i = 0; i <= 19; ++i )
s2[i] = 0;
qmemcpy(s2, "CAWMCZTB", 8);
printf("Enter the password: ");
__isoc99_scanf("%8s", s1);
for ( j = 0; j <= 7; ++j )
s1[j] = complex_function(s1[j], j + 8);
if ( !strcmp(s1, s2) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

这个题哦,看上去没啥区别,当我们找puts时,就会出现问题

1654264622_629a132e34c545d6a72e7.png!small?1654264621354

这么多puts:good,应该怎么办,解决方法是通过判断控制台输出数据是不是"Good Job" 和"Try again" 来确认执行到了成功还是失败分支.即我们不再确认地址,我们只看输出是不是我们想要的

import sys
import angr
def main(argv):
path = '../solutions/02_angr_find_condition/02_angr_find_condition'
project = angr.Project(path)
state = project.factory.entry_state()
simulation = project.factory.simgr(state)

def good_job(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())

return 'Good Job' in str(stdout_output)
#含义就是在所有输出里找try again
def try_again(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())

return 'Try again' in str(stdout_output)

simulation.explore(find=good_job,avoid=try_again)

if simulation.found:
solution_state = simulation.found[0]
#下面是打印输入流,输出是stdout
print(solution_state.posix.dumps(sys.stdin.fileno()))
else:
raise Exception('NOooooooo!')

if __name__ == '__main__':
main(sys.argv)
WARNING | 2022-03-12 14:41:20,664 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-12 14:41:20,664 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-12 14:41:20,664 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-12 14:41:20,664 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-12 14:41:20,664 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-12 14:41:20,664 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x804d2a1 (__libc_csu_init+0x1 in 02_angr_find_condition (0x804d2a1))
WARNING | 2022-03-12 14:41:21,644 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff0d with 11 unconstrained bytes referenced from 0x8186b80 (strcmp+0x0 in libc.so.6 (0x86b80))
WARNING | 2022-03-12 14:41:21,644 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff30 with 4 unconstrained bytes referenced from 0x8186b80 (strcmp+0x0 in libc.so.6 (0x86b80))
b'OHYJUMBE'

03_angr_symbolic_registers

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;
}

三个complex,里面全是运算,有一点这三个函数只有两个有参数,但我们看汇编发现其实是有三个值,那么我们只需要求出eax,ebx,edx的值就行了

1654264629_629a133555bb9b0854934.png!small?1654264628383

那么我们就要学会如何从寄存器中获得输入

import sys
import angr
import claripy
def main():
path = '../solutions/03_angr_symbolic_registers/03_angr_symbolic_registers'
project = angr.Project(path)
#自己设置项目起始地址
start_addr = 0x080488C7
state = project.factory.blank_state(addr=start_addr)

bit_lenth = 32
psd0 = claripy.BVS('psd0',bit_lenth)
psd1 = claripy.BVS('psd1',bit_lenth)
psd2 = claripy.BVS('psd2',bit_lenth)

state.regs.eax = psd0
state.regs.ebx = psd1
state.regs.edx = psd2

simulation = project.factory.simgr(state)

good_add = 0x080489 2 f
fail_add = 0x0804891D
simulation.explore(find=good_add,avoid=fail_add)

if simulation.found:
solution_state = simulation.found[0]
#如何进行寄存器输出
solution0 =solution_state.se.eval(psd0)
solution1 =solution_state.se.eval(psd1)
solution2 =solution_state.se.eval(psd2)
solution = ' '.join(map('{:x}'.format,[solution0,solution1,solution2]))
print(solution)
else:
raise Exception('Noooooooo!')
if __name__ == '__main__':
main()


最主要的是,如果我们要从寄存器中找数据,那么我们一定要自己把函数位置找到,因为寄存器是会变的。

初始化寄存器操作:

bit_length = 32
psd0 = claripy.BVS('psd0', bit_length)
initial_state.regs.eax = psd0

eval

是Python的一个内置函数,功能十分强大,这个函数的作用是,返回传入字符串的表达式的结果。就是说:将字符串当成有效的表达式 来求值 并 返回计算结果。

formamt

fotmat作为Python的的格式字符串函数,主要通过字符串中的花括号{},来识别替换字段,从而完成字符串的格式化。

上面主要是变成十六进制再连起来

WARNING | 2022-03-12 15:33:58,675 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-12 15:33:58,676 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-12 15:33:58,676 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-12 15:33:58,676 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-12 15:33:58,676 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-12 15:33:58,676 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x80488c7 (main+0x26 in 03_angr_symbolic_registers (0x80488c7))
CRITICAL | 2022-03-12 15:33:59,119 | angr.sim_state | The name state.se is deprecated; please use state.solver.
e9b37483 7aab5fde 8f5b48ea

总结:

project.factory.blank_state(addr=start_address) => 创建自定义入口的状态上下文

initial_state.regs => 操作状态上下文的寄存器

claripy.BVS('变量名', 变量大小) => 创建求解变量

solution_state.se.eval(变量) => 求解符号变量


04_angr_symbolic_stack

这个题又不一样了,这个是局部变量

int __cdecl main(int argc, const char **argv, const char **envp)
{
printf("Enter the password: ");
handle_user();
return 0;
}
int handle_user()
{
int v1; // [esp+8h] [ebp-10h] BYREF
int v2[3]; // [esp+Ch] [ebp-Ch] BYREF

__isoc99_scanf("%u %u", v2, &v1);
v2[0] = complex_function0(v2[0]);
v1 = complex_function1(v1);
if ( v2[0] == 2083606899 && v1 == -99478518 )
return puts("Good Job.");
else
return puts("Try again.");
}

我们知道局部变量是储存在栈中的,所以我们要构造栈上的内存

from mimetypes import init
import sys
import angr
import claripy


def main():
binary_path = '../solutions/04_angr_symbolic_stack/04_angr_symbolic_stack'
project = angr.Project(binary_path)

start_addr = 0x080486AE
initial_state = project.factory.blank_state(addr=start_addr)

initial_state.regs.ebp = initial_state.regs.esp # 初始化栈,令ebp等于esp

password0 = claripy.BVS('password0', 32) # 初始化两个位向量
password1 = claripy.BVS('password1', 32)

padding_length_in_bytes = 0x8 # 填充栈 integer * 2
initial_state.regs.esp -= padding_length_in_bytes

initial_state.stack_push(password0) # 将位向量压入栈中
initial_state.stack_push(password1)

simulation = project.factory.simgr(initial_state)

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

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

simulation.explore(find=is_successful, avoid=should_abort)

if simulation.found:
solution_state = simulation.found[0]

solution0 = solution_state.se.eval(password0)
solution1 = solution_state.se.eval(password1)

solution = '%u %u' % (solution0, solution1)
print(solution)
else:
raise Exception('could not find the solution')

if __name__ == '__main__':
main()

符号化栈空间的运用:

initial_state.regs.ebp = initial_state.regs.esp # 初始化栈,令ebp等于esp

padding_length_in_bytes = 0xC # 填充栈
initial_state.regs.esp -= padding_length_in_bytes

initial_state.stack_push(password0) # 将位向量压入栈中

05_angr_symbolic_memory

这个题考的是bss段的输入,我们要构造bss段符号表示

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_AB232C8, &unk_AB232D0, &unk_AB232D8);
for ( i = 0; i <= 31; ++i )
*(_BYTE *)(i + 179450560) = complex_function(*(char *)(i + 179450560), i);
if ( !strncmp(user_input, "OSIWHBXIFOQVSBZBISILSCLBIAXSEWUT", 0x20u) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}
int __cdecl complex_function(int a1, int a2)
{
if ( a1 <= 64 || a1 > 90 )
{
puts("Try again.");
exit(1);
}
return (9 * a2 + a1 - 65) % 26 + 65;
}

给出脚本:

import angr
import sys
import claripy

def main(agrv):
path = '../solutions/05_angr_symbolic_memory/05_angr_symbolic_memory'
project = angr.Project(path)
adds = 0x08048618#scanf后
state = project.factory.blank_state(add=adds)
# 64 = 8(8个字符) * 1(每个字符一字节) * 8(每个字节8比特)
psd0 =claripy.BVS('psd0',64)
psd1 =claripy.BVS('psd1',64)
psd2 =claripy.BVS('psd2',64)
psd3 =claripy.BVS('psd3',64)

psd0_add = 0x0AB232C0
psd1_add = 0x0AB232C8
psd2_add = 0x0AB232D0
psd3_add = 0x0AB232D8

state.memory.store(psd0_add,psd0)#把位向量存入内存
state.memory.store(psd1_add,psd1)
state.memory.store(psd2_add,psd2)
state.memory.store(psd3_add,psd3)

simulation = project.factory.simgr(state)

def good_add(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)

def fail_add(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)

simulation.explore(find=good_add,avoid=fail_add)

if simulation.found:
solution_state = simulation.found[0]
solution0 = solution_state.se.eval(psd0)
solution1 = solution_state.se.eval(psd1)
solution2 = solution_state.se.eval(psd2)
solution3 = solution_state.se.eval(psd3)
solution = ' '.join(map('{:x}'.format, [ solution0, solution1,solution2,solution3 ]))
print(solution)
else:
raise Exception('Noooooooo!')

if __name__ == '__main__':
main(sys.argv)
WARNING | 2022-03-12 20:06:00,666 | angr.sim_state | Unused keyword arguments passed to SimState: add
WARNING | 2022-03-12 20:06:00,673 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-12 20:06:00,674 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-12 20:06:00,674 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-12 20:06:00,674 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-12 20:06:00,674 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-12 20:06:00,674 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fff0000 with 4 unconstrained bytes referenced from 0x8048432 (_start+0x2 in 05_angr_symbolic_memory (0x8048432))
WARNING | 2022-03-12 20:06:00,675 | angr.storage.memory_mixins.default_filler_mixin | Filling register eax with 4 unconstrained bytes referenced from 0x8048438 (_start+0x8 in 05_angr_symbolic_memory (0x8048438))
WARNING | 2022-03-12 20:06:00,676 | angr.storage.memory_mixins.default_filler_mixin | Filling register edx with 4 unconstrained bytes referenced from 0x804843a (_start+0xa in 05_angr_symbolic_memory (0x804843a))
WARNING | 2022-03-12 20:06:00,746 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80486a1 (__libc_csu_init+0x1 in 05_angr_symbolic_memory (0x80486a1))
CRITICAL | 2022-03-12 20:06:05,988 | angr.sim_state | The name state.se is deprecated; please use state.solver.
0 0 0 0

06_angr_symbolic_dynamic_memory

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+Ch] [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);
for ( i = 0; i <= 7; ++i )
{
buffer0[i] = complex_function(buffer0[i], i);
buffer1[i] = complex_function(buffer1[i], i + 32);
}
if ( !strncmp(buffer0, "OSIWHBXI", 8u) && !strncmp(buffer1, "FOQVSBZB", 8u) )
puts("Good Job.");
else
puts("Try again.");
free(buffer0);
free(buffer1);
return 0;
}

这次scanf() 有两个输入参数,数据保存的位置是通过全局变量的char* 指针来保存到buffer 中,大小为8 字节.我们主要是对buffer两个内存进行求解。我们如何将两块动态申请来的内存符号化,malloc申请的空间是动态的,但存放malloc的buffer是再bss段,属于静态,我们选择伪造指针使其指向一片可写的内存,将内存符号化


import sys
import angr
import claripy
from operator import is_

def main():
path = '../solutions/06_angr_symbolic_dynamic_memory/06_angr_symbolic_dynamic_memory'
project = angr.Project(path)

start_address = 0x080486AF
state = project.factory.blank_state(addr=start_address)

psd0 = claripy.BVS('psd0',64)
psd1 = claripy.BVS('psd1',64)
fake_address0 = 0x0A2DEF30# 伪造malloc得来的内存,在bss上随便找的
pointer_to_address0 = 0x0A2DEF74# 指向伪造内存的指针
state.memory.store(pointer_to_address0,fake_address0,endness=project.arch.memory_endness)# 将指针指向伪造的内存
fake_address1 = 0x0A2DEF40
pointer_to_address1 = 0x0A2DEF7C
state.memory.store(pointer_to_address1,fake_address1,endness=project.arch.memory_endness)
state.memory.store(fake_address0,psd0)# 将伪造的内存符号化
state.memory.store(fake_address1,psd1)
simulation = project.factory.simgr(state)
simulation.explore(find=0x0804877A,avoid=0x08048768)

if simulation.found:
solution_state = simulation.found[0]
solution0 = solution_state.se.eval(psd0)
solution1 = solution_state.se.eval(psd1)
solution = ' '.join(map('{:x}'.format,[solution0,solution1]))
print(solution)
else:
raise Exception('Nooooooooo!')

if __name__ == '__main__':
main()

WARNING | 2022-03-13 15:07:11,666 | angr.storage.memory_mixins.bvv_conversion_mixin | Unknown size for memory data 0xa2def30. Default to arch.bits.
WARNING | 2022-03-13 15:07:11,667 | angr.storage.memory_mixins.bvv_conversion_mixin | Unknown size for memory data 0xa2def40. Default to arch.bits.
WARNING | 2022-03-13 15:07:11,672 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-13 15:07:11,672 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-13 15:07:11,672 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-13 15:07:11,672 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-13 15:07:11,672 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-13 15:07:11,672 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x80486af (main+0x8c in 06_angr_symbolic_dynamic_memory (0x80486af))
CRITICAL | 2022-03-13 15:07:15,521 | angr.sim_state | The name state.se is deprecated; please use state.solver.
4f46494a484f5856 46425149534f5a4f

这节学到了符号化申请动态内存,通过伪造内存方式:

initial_state.memory.store(buffer0_addr, fake0_addr, endness=project.arch.memory_endness) # 将指针指向伪造的内存
initial_state.memory.store(fake0_addr, password0) # 将伪造的内存符号化

endness的参数是小端写入,因为angr默认大端。

07_angr_symbolic_file

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [esp-14h] [ebp-30h]
int v4; // [esp-10h] [ebp-2Ch]
int v5; // [esp-Ch] [ebp-28h]
int v6; // [esp-8h] [ebp-24h]
int v7; // [esp-4h] [ebp-20h]
int v8; // [esp+0h] [ebp-1Ch]
int i; // [esp+0h] [ebp-1Ch]

memset(buffer, 0, sizeof(buffer));
printf("Enter the password: ");
__isoc99_scanf("%64s", buffer, v3, v4, v5, v6, v7, v8);
ignore_me((int)buffer, 0x40u);
memset(buffer, 0, sizeof(buffer));
fp = fopen("FOQVSBZB.txt", "rb");
fread(buffer, 1u, 0x40u, fp);
fclose(fp);
unlink("FOQVSBZB.txt");
for ( i = 0; i <= 7; ++i )
*(_BYTE *)(i + 134520992) = complex_function(*(char *)(i + 134520992), i);
if ( strcmp(buffer, "OSIWHBXI") )
{
puts("Try again.");
exit(1);
}
puts("Good Job.");
exit(0);
}

这个是将输入写入文件,然后再打开文件进行加密

所以要构建文件。

import sys
import claripy
import angr
import binascii
from libnum import n2s
def main():
path = '../solutions/07_angr_symbolic_file/07_angr_symbolic_file'
project = angr.Project(path)

start_address = 0x080488BC
initial_state = project.factory.blank_state(add=start_address)

filename = 'FOQVSBZB.txt'
symbolic_file_size_bytes = 0x40#64位

psd = claripy.BVS('psd',symbolic_file_size_bytes * 8)
psd_file = angr.SimFile(filename, content=psd, size=symbolic_file_size_bytes) # 符号化文件

initial_state = project.factory.blank_state(addr=start_address, fs={filename: psd_file}) # 再初始状态中添加一个虚拟的文件系统
simulation = project.factory.simgr(initial_state)
def good_job(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())

return 'Good Job' in str(stdout_output)

def try_again(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())

return 'Try again' in str(stdout_output)

simulation.explore(find=good_job,avoid=try_again)
if simulation.found:
solution_state = simulation.found[0]
solution = n2s(solution_state.solver.eval(psd))
print(solution)
else:
raise Exception('Nooooooo!!!')

if __name__ == '__main__':
main()
WARNING | 2022-03-13 15:50:33,858 | angr.sim_state | Unused keyword arguments passed to SimState: add
WARNING | 2022-03-13 15:50:33,908 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-13 15:50:33,909 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-13 15:50:33,909 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-13 15:50:33,909 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-13 15:50:33,909 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-13 15:50:33,909 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048922 (main+0xc4 in 07_angr_symbolic_file (0x8048922))
OBAXRUZT

总结:

angr.storage.SimFile(文件名,文件内容, size = 文件大小) => 创建一个模拟文件,当有被执行的程序fopen 打开文件时,我们可以控制其里面的内容

initial_state.posix.fs => 状态上下文的文件系统对象

要注意点,但我们创建符号good_job其参数是state,所以我们在创建项目时,不能用state,可以用initial_state

08_angr_constraints

这道题的特点就是会一个一个字符比较,所以不能用之前的方法,否则会有2的16次方中可能,会爆炸的,我们要做的就是符号化buffer,然后进行加密加密后获得控制权,从而检查psd是否与对比的一致

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+Ch] [ebp-Ch]

qmemcpy(&password, "OSIWHBXIFOQVSBZB", 16);
memset(&buffer, 0, 0x11u);
printf("Enter the password: ");
__isoc99_scanf("%16s", &buffer);
for ( i = 0; i <= 15; ++i )
*(_BYTE *)(i + 134520896) = complex_function(*(char *)(i + 134520896), 15 - i);
if ( check_equals_OSIWHBXIFOQVSBZB(&buffer, 16) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

给出脚本:

import angr
import sys
import claripy
from libnum import n2s
def main():
path = '../solutions/08_angr_constraints/08_angr_constraints'
project = angr.Project(path)
start_add = 0x0804863C #scanf后面
initial_state = project.factory.blank_state(add=start_add)
buffer = claripy.BVS('buffer',0x10*8)#buffer的大小是0x10 字节,也就是0x10 * 8 比特
buffer_address = 0x0804A040
initial_state.memory.store(buffer_address,buffer)#buffer 是全局变量,地址是0x804A040
simulation = project.factory.simgr(initial_state)
address_to_check_constraint = 0x08048683
simulation.explore(find=address_to_check_constraint)# compilex_function() 计算结束位置
if simulation.found:
solution_state = simulation.found[0]
constrained_parameter_address = 0x0804A040 # 计算约束的内存位置
constrained_parameter_size_bytes = 0x10 # 计算约束的内存大小
constrained_parameter_bitvector = solution_state.memory.load(  constrained_parameter_address,constrained_parameter_size_bytes) # 加载内存

constrained_parameter_desired_value = "OSIWHBXIFOQVSBZB"# Key-String
solution_state.add_constraints(constrained_parameter_bitvector == constrained_parameter_desired_value )# 添加约束
solution = solution_state.se.eval(buffer)
print(n2s(solution))
else:
raise Exception('nooooooooo!')

if __name__ == '__main__':
main()

学到:

solution_state.memory.load(内存地址,内存大小) => 加载内存

solution_state.add_constraints(约束条件) => 添加约束条件

09_angr_hooks

这个题主要是对比的时候在遍历分支的时候会用很长时间,我们直接hook掉,然后替换成我们自己对比函数就行

import sys
import claripy
import angr
def main():
path = '../solutions/09_angr_hooks/09_angr_hooks'
project = angr.Project(path)
initial_state = project.factory.entry_state()

check_equals = 0x080486CA
instruction_to_skip_lenth = 5
@project.hook(check_equals, length=instruction_to_skip_lenth)
def skip_check_equals(state):
user_input_buffer_addr = 0x0804A044#buffer地址
user_input_buffer_lenth = 16

user_input_string = state.memory.laod( user_input_buffer_addr,user_input_buffer_lenth)
check_against_string = 'OSIWHBXIFOQVSBZB'
state.regs.eax = claripy.If(user_input_string == check_against_string,claripy.BVV(1,32),claripy.BVV(0,32))
simulation = project.factory.simgr(initial_state)

def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)

def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)

simulation.explore(find=is_successful, avoid=should_abort)  # Try Explore Execute Path

if simulation.found:
solution_state = simulation.found[0]
solution = solution_state.posix.dumps(sys.stdin.fileno())  # Get data ..
print(solution)
else:
raise Exception('Could not find the solution')

if __name__ == '__main__':
main()

上面脚本不如下面脚本:

import sys
import claripy
import angr
def main():
path = '../solutions/09_angr_hooks/09_angr_hooks'
project = angr.Project(path)

initial_state = project.factory.entry_state()

class ReplacementCheckEquals(angr.SimProcedure):
def run(self, check_data_address, check_data_length):
check_input_string = self.state.memory.load(check_data_address,check_data_length)
check_against_string = 'OSIWHBXIFOQVSBZB'

return claripy.If(check_input_string == check_against_string,claripy.BVV(1,32),claripy.BVV(0,32))

check_equals_symbol = 'check_equals_OSIWHBXIFOQVSBZB'
project.hook_symbol(check_equals_symbol,ReplacementCheckEquals())

simulation = project.factory.simgr(initial_state)

def good_job(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job' in str(stdout_output)

def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again' in str(stdout_output)

simulation.explore(find=good_job,avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]

solution = solution_state.posix.dumps(sys.stdin.fileno())
print(solution)
if __name__ == '__main__':
main()
WARNING | 2022-03-14 15:40:43,538 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-14 15:40:43,538 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-14 15:40:43,538 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-14 15:40:43,538 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-14 15:40:43,538 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-14 15:40:43,539 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80487a1 (__libc_csu_init+0x1 in 09_angr_hooks (0x80487a1))
WARNING | 2022-03-14 15:40:46,867 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
b'QREPXOHPJPOQKQLKNOBMULEMGMLNHNIH'

总结:

Hook回调函数格式:

@project.hook(Hook地址,执行完Hook函数后指令往后跳转n字节) def skip_check_equals_(state):

pass

claripy.If(条件,条件为True时的返回值,条件为False时的返回值) => 创建条件判断

claripy.BVV(值,值大小) => 创建一个数值

10_angr_simprocedures

还是一样,先输入flag然后循环运算,接下来函数中对比。逐字节比较太麻烦,我们仍需对比

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+20h] [ebp-28h]
char s[17]; // [esp+2Bh] [ebp-1Dh] BYREF
unsigned int v6; // [esp+3Ch] [ebp-Ch]

v6 = __readgsdword(0x14u);
memcpy(&password, "OSIWHBXIFOQVSBZB", 0x10u);
memset(s, 0, sizeof(s));
printf("Enter the password: ");
__isoc99_scanf("%16s", s);
for ( i = 0; i <= 15; ++i )
s[i] = complex_function(s[i], 18 - i);
if ( check_equals_OSIWHBXIFOQVSBZB(s, 16) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

本题跟9题的区别就在于,本题call equal指令太多,不像9题只有一个,所以我们需要多个hook

import sys
import claripy
import angr
def main():
path = '../solutions/10_angr_simprocedures/10_angr_simprocedures'
project = angr.Project(path)

initial_state = project.factory.entry_state()

class ReplacementCheckEquals(angr.SimProcedure):
def run(self, check_data_address, check_data_length):
check_input_string = self.state.memory.load(check_data_address,check_data_length)
check_against_string = 'OSIWHBXIFOQVSBZB'

return claripy.If(check_input_string == check_against_string,claripy.BVV(1,32),claripy.BVV(0,32))

check_equals_symbol = 'check_equals_OSIWHBXIFOQVSBZB'
project.hook_symbol(check_equals_symbol,ReplacementCheckEquals())

simulation = project.factory.simgr(initial_state)

def good_job(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job' in str(stdout_output)

def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again' in str(stdout_output)

simulation.explore(find=good_job,avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]

solution = solution_state.posix.dumps(sys.stdin.fileno())
print(solution)
if __name__ == '__main__':
main()
WARNING | 2022-03-14 15:02:40,607 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-14 15:02:40,607 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-14 15:02:40,607 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-14 15:02:40,607 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-14 15:02:40,607 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-14 15:02:40,607 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x804a9d1 (__libc_csu_init+0x1 in 10_angr_simprocedures (0x804a9d1))
WARNING | 2022-03-14 15:02:43,850 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
b'MTMDRONBBNSAAMNS'

这个题hook整个函数。采用SimProcedure类

Hook 回调函数格式:

class ReplacementCheckEquals(angr.SimProcedure):

def run(self, Hook的函数参数列表):

….

return 函数返回值 # 如果是void函数可以省略

project.hook_symbol(要Hook的函数名,SimProcedure类实例)

通过这个题,我在想上个题是否也能用这个回调函数呢,这个脚本会省去很多事,成功了!

11_angr_sim_scanf

本题加密的是对比数据,加密完再输入,然后对比

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+20h] [ebp-28h]
char s[20]; // [esp+28h] [ebp-20h] BYREF
unsigned int v7; // [esp+3Ch] [ebp-Ch]

v7 = __readgsdword(0x14u);
memset(s, 0, sizeof(s));
qmemcpy(s, "LOGMHXHI", 8);
for ( i = 0; i <= 7; ++i )
s[i] = complex_function(s[i], i);
printf("Enter the password: ");
__isoc99_scanf("%u %u", buffer0, buffer1);
if ( !strncmp(buffer0, s, 4u) && !strncmp(buffer1, &s[4], 4u) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

这道题是让我们模拟scanf

import sys
import angr
import claripy

def main():
path = '../solutions/11_angr_sim_scanf/11_angr_sim_scanf'
project = angr.Project(path)

initial_state = project.factory.entry_state()

class ReplacementScanf(angr.SimProcedure):
def run(self,format_string,scanf0_address,scanf1_address):
scanf0 = claripy.BVS('scanf0', 32)
scanf1 = claripy.BVS('scanf1', 32)

self.state.memory.store(scanf0_address,scanf0,endness=project.arch.memory_endness)
self.state.memory.store(scanf1_address,scanf1,endness=project.arch.memory_endness)

self.state.globals['solution0'] = scanf0
self.state.globals['solution1'] = scanf1

scanf_symbol = '__isoc99_scanf'
project.hook_symbol(scanf_symbol, ReplacementScanf())
simulation = project.factory.simgr(initial_state)
def good_job(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again' in str(stdout_output)

simulation.explore(find=good_job,avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
store_solution0 = solution_state.globals['solution0']
store_solution1 = solution_state.globals['solution1']
solution0 = solution_state.se.eval(store_solution0)
solution1 = solution_state.se.eval(store_solution1)
print(solution0,solution1)
if __name__ =='__main__':
main()

WARNING | 2022-03-14 16:09:33,831 | angr.project | Address is already hooked, during hook(0x81513a0, <SimProcedure ReplacementScanf>). Re-hooking.
WARNING | 2022-03-14 16:09:33,903 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-14 16:09:33,903 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-14 16:09:33,903 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-14 16:09:33,903 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-14 16:09:33,903 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-14 16:09:33,903 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x804fce1 (__libc_csu_init+0x1 in 11_angr_sim_scanf (0x804fce1))
WARNING | 2022-03-14 16:09:34,137 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff40 with 4 unconstrained bytes referenced from 0x8187170 (strncmp+0x0 in libc.so.6 (0x87170))
CRITICAL | 2022-03-14 16:09:34,334 | angr.sim_state | The name state.se is deprecated; please use state.solver.
1447907916 1146768724

这个题学到了构造scanf的hook:

#将位向量储存到当前状态并且获取位向量的方法
self.state.globals['solution0'] = scanf0
#保存当前状态
solution0 = solution_state.globals['solution0']
#去出scanf

12_angr_veritesting

如果说这个题跟第一题有什么区别的话,就是这个输入32位,很容易爆炸,所以有veritesting参数来表示是否合并路径

import sys
import angr
def main():
path = '../solutions/12_angr_veritesting/12_angr_veritesting'
project = angr.Project(path)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state,veritesting = True)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)  # :boolean
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)

simulation.explore(find=is_successful,avoid=should_abort)
if simulation.found:
solution = simulation.found[0]
print(solution.posix.dumps(sys.stdin.fileno()))
if __name__ =='__main__':
main()
WARNING | 2022-03-14 16:30:57,745 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-14 16:30:57,745 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-14 16:30:57,745 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-14 16:30:57,745 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-14 16:30:57,745 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-14 16:30:57,746 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80486e1 (__libc_csu_init+0x1 in 12_angr_veritesting (0x80486e1))
WARNING | 2022-03-14 16:30:57,757 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:57,758 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
Deprecation warning: Use self.model.get_any_node() instead of get_any_node
WARNING | 2022-03-14 16:30:57,789 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:57,790 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:57,852 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:57,853 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:57,887 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:57,887 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:58,001 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:58,001 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:58,098 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:58,099 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:58,209 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:58,209 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:30:58,322 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:30:58,564 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:30:58,807 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:30:59,033 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:30:59,259 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:30:59,507 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:30:59,751 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:00,001 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:00,253 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:00,526 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:00,801 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:01,072 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:01,352 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:01,645 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:01,934 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:02,240 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:02,541 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:02,851 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:03,179 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:03,504 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:03,826 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:04,170 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:04,519 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:04,869 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:05,314 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:05,676 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:06,060 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:06,456 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:06,859 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:07,269 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:07,704 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:31:08,188 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
b'YNCRGVKZODSHWLAPETIXMBQFUJYNCRGV'

学习了如何自动合并路径:

simulation = project.factory.simgr(initial_state, veritesting=True) 

13_angr_static_binary

看起来很简单,用第一问的脚本就行了,实际不行,因为这个是用静态链接编译的,我们还要实现或者引用一些函数的hook

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+1Ch] [ebp-3Ch]
int j; // [esp+20h] [ebp-38h]
char v6[20]; // [esp+24h] [ebp-34h] BYREF
char v7[20]; // [esp+38h] [ebp-20h] BYREF
unsigned int v8; // [esp+4Ch] [ebp-Ch]

v8 = __readgsdword(0x14u);
for ( i = 0; i <= 19; ++i )
v7[i] = 0;
qmemcpy(v7, "ELZXQOOQ", 8);
printf("Enter the password: ");
_isoc99_scanf("%8s", v6);
for ( j = 0; j <= 7; ++j )
v6[j] = complex_function(v6[j], j);
if ( j_strcmp_ifunc(v6, v7) )
puts("Try again.");
else
puts("Good Job.");
return 0;
}
import angr
import sys

def main():
path = '../solutions/13_angr_static_binary/13_angr_static_binary'
project = angr.Project(path)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state,veritesting = True)

project.hook(0x0804FAB0,angr.SIM_PROCEDURES['libc']['printf']())
project.hook(0x0804FB10,angr.SIM_PROCEDURES['libc']['scanf']())
project.hook(0x080503F0,angr.SIM_PROCEDURES['libc']['puts']())
project.hook(0x08048D60,angr.SIM_PROCEDURES['glibc']['__libc_start_main']())
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)  # :boolean
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)

simulation.explore(find=is_successful,avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))
if __name__ == '__main__':
main()
WARNING | 2022-03-14 16:55:19,180 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-14 16:55:19,180 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-14 16:55:19,180 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-14 16:55:19,180 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-14 16:55:19,181 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-14 16:55:19,181 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80497b1 (__libc_csu_init+0x1 in 13_angr_static_binary (0x80497b1))
WARNING | 2022-03-14 16:55:19,185 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,186 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
Deprecation warning: Use self.model.get_any_node() instead of get_any_node
WARNING | 2022-03-14 16:55:19,255 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,255 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,279 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,279 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,323 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,325 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,406 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,406 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,422 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,422 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,502 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,502 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,528 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,528 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,566 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:19,566 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:20,020 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:20,021 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:20,310 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:20,311 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:20,391 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:20,392 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:21,303 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:21,304 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:24,416 | angr.sim_manager | Cannot find states with common history line to merge. Fall back to the naive merging strategy and merge all states.
WARNING | 2022-03-14 16:55:25,412 | angr.storage.memory_mixins.default_filler_mixin | Filling register es with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
WARNING | 2022-03-14 16:55:25,412 | angr.storage.memory_mixins.default_filler_mixin | Filling register gs with 4 unconstrained bytes referenced from 0x0 (not part of a loaded object)
b'EADQYLAR'

我们学到了获取angr内部系统函数:

angr.SIM_PROCEDURES[ 系统库名 ] [ 系统函数名 ] () => 获取Angr 内部实现的系统函数

14_angr_shared_library

int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[16]; // [esp+1Ch] [ebp-1Ch] BYREF
unsigned int v5; // [esp+2Ch] [ebp-Ch]

v5 = __readgsdword(0x14u);
memset(s, 0, sizeof(s));
printf("Enter the password: ");
__isoc99_scanf("%8s", s);
if ( validate(s, 8) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

好像很简单,但我们发现validate函数在另一个so文件,该题考的是动态链接的符号执行,我们只要搞出validate的符号执行就ok

import sys
import angr
import claripy

def main():
path = '../solutions/14_angr_shared_library/lib14_angr_shared_library.so'
base = 0x40000000#随意
project = angr.Project(path,load_options={'main_opts' : {'custom_base_addr' : base}})
buffer_pointer = claripy.BVV(0x30000000,32)#创建buffer指针
validate_function_address = base + 0x670 #在该文件中的调用地址
initial_state = project.factory.call_state(validate_function_address,buffer_pointer,claripy.BVV(8,32))
# 调用validate_function,因为函数声明validata_function(buffer_point,buffer_length) ,所以我们构造出调用validata_function(0x3000000,0x8) .
password = claripy.BVS('password', 64)

initial_state.memory.store(buffer_pointer,password)
simulation = project.factory.simgr(initial_state)
simulation.explore(find=base+0x71C)# 执行到validate 函数的RETN 指令
if simulation.found:
solution_state = simulation.found[0]
solution_state.add_constraints(solution_state.regs.eax !=0)
solution = solution_state.se.eval(password)
print(solution)
if __name__ == '__main__':
main()
CRITICAL | 2022-03-14 17:38:36,605 | cle.backends | Deprecation warning: the custom_base_addr parameter has been renamed to base_addr
WARNING | 2022-03-14 17:38:36,862 | angr.calling_conventions | Guessing call prototype. Please specify prototype.
WARNING | 2022-03-14 17:38:36,868 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-14 17:38:36,869 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-14 17:38:36,869 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-14 17:38:36,869 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-14 17:38:36,869 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-14 17:38:36,869 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x40000670 (validate+0x0 in lib14_angr_shared_library.so (0x670))
WARNING | 2022-03-14 17:38:36,870 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x40000673 (validate+0x3 in lib14_angr_shared_library.so (0x673))
WARNING | 2022-03-14 17:38:37,842 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x30000008 with 121 unconstrained bytes referenced from 0x40186b80 (strcmp+0x0 in libc.so.6 (0x86b80))
WARNING | 2022-03-14 17:38:37,911 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeffe8 with 4 unconstrained bytes referenced from 0x40186b80 (strcmp+0x0 in libc.so.6 (0x86b80))
WARNING | 2022-03-14 17:38:37,911 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fff0000 with 77 unconstrained bytes referenced from 0x40186b80 (strcmp+0x0 in libc.so.6 (0x86b80))
CRITICAL | 2022-03-14 17:38:38,105 | angr.sim_state | The name state.se is deprecated; please use state.solver.
6076275416825026394

学习到了:

如何加载共享库
base = 0x40000000#随意
project = angr.Project(path,load_options={'main_opts' : {'custom_base_addr' : base}})
initial_state = project.factory.call_state(validate_function_address,buffer_pointer,claripy.BVV(8,32))
# 构造指针给函数使用.

15_angr_arbitrary_read

当if成立,s输出,以为会输出tryagain,但v4要写入20字节,就会有溢出,因为s到v4只有16字节

int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+Ch] [ebp-1Ch] BYREF
char *s; // [esp+1Ch] [ebp-Ch]

s = try_again;
printf("Enter the password: ");
__isoc99_scanf("%u %20s", &key, &v4);
if ( key == 2358019 )
puts(s);
else
puts(try_again);
return 0;
}

脚本如下:

import sys
import angr
import claripy
def main():
path = '../solutions/15_angr_arbitrary_read/15_angr_arbitrary_read'
project = angr.Project(path)

initial_state = project.factory.entry_state()

class ReplacementScanf(angr.SimProcedure):# 实现Scanf Hook 函数

def run(self,format_string, check_key_address,input_buffer_address):
scanf0 = claripy.BVS('scanf0',32)#check_key
scanf1 = claripy.BVS('scanf1',160)#inputbuffer

for char in scanf1.chop(bits=8):
self.state.add_constraints(char >= '0', char <= 'z')# 对input_buffer 的输入约束

self.state.memory.store(check_key_address,scanf0 ,endness =project.arch.memory_endness)# 保存求解变量到指定的内存中

self.state.memory.store(input_buffer_address,scanf1 ,endness =project.arch.memory_endness)

self.state.globals['solution0'] = scanf0# 保存这两个变量到state 中,后续求解需要用到
self.state.globals['solution1'] = scanf1

scanf_symbol = '__isoc99_scanf'
project.hook_symbol(scanf_symbol,ReplacementScanf())
def check_puts(state):
puts_parameter = state.memory.load(state.regs.esp + 4,4,endness=project.arch.memory_endness)

if state.se.symbolic(puts_parameter):
good_job_string_address = 0x08048562#利用try again的函数调用
copied_state = state.copy() # 复制执行状态上下文进行约束求解,不影响原理的执行上下文
copied_state.add_constraints(puts_parameter == good_job_string_address)# puts 的参数地址是否可以被指定为 0x08048562 ,如果可以的话,那就证明这个值是可控的

if copied_state.satisfiable():# 判断添加了上面这个约束是否有解
state.add_constrains(puts_parameter == good_job_string_address) # 如果有解的话就保存到我们执行的那个状态对象
return True
else:
return False
else:
return False

simulation = project.factory.simgr(initial_state)
def is_successful(state):
puts_address = 0x0804854F# 当程序执行到puts() 函数时,我们就认为路径探索到了这里,然后再去通过check_puts() 判断这里是否存在漏洞,告诉Angr这是不是我们需要找的那条执行路径

if state.addr == puts_address:
return check_puts(state)
else:
return False
simulation.explore(find=is_successful)
if simulation.found:
solution_state = simulation.found[0]
solution0 = solution_state.se.eval(solution_state.globals['solution0'])
solution1 = solution_state.se.eval(solution_state.globals['solution1'],cast_to=bytes)
print(solution0,solution1)

if __name__ == '__main__':
main()
state.copy() => 复制状态上下文

state.satisfiable() => 判断当前的所有约束是否有解

solution_state.se.eval(求解变量,cast_to=bytes) => 序列化变量内容为字符串

16_angr_arbitrary_write

这个题很明显,password是不等于NEDVTNOP,所以我们可以用s溢出覆盖dest的值,使其指向password,这样就能修改其内容了

int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[16]; // [esp+Ch] [ebp-1Ch] BYREF
char *dest; // [esp+1Ch] [ebp-Ch]

dest = unimportant_buffer;
memset(s, 0, sizeof(s));
strncpy(password_buffer, "PASSWORD", 0xCu);
printf("Enter the password: ");
__isoc99_scanf("%u %20s", &key, s);
if ( key == 6712341 )
strncpy(dest, s, 0x10u);
else
strncpy(unimportant_buffer, s, 0x10u);
if ( !strncmp(password_buffer, "NEDVTNOP", 8u) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

我们输入20个字节是正好覆盖s的16个字节+的dest的字节的

import sys
import angr
import claripy

def main():
path = '../solutions/16_angr_arbitrary_write/16_angr_arbitrary_write'
project = angr.Project(path)
initial_state = project.factory.entry_state()

class ReplacementScanf(angr.SimProcedure):
def run(self,format_string,check_key,input_buffer):
scanf0 = claripy.BVS('scanf0',32)
scanf1 = claripy.BVS('scanf1',160)

for char in scanf1.chop(bits=8):
self.state.add_constraints(char >= 'a',char <= 'z')

self.state.memory.store(check_key,scanf0,endness=project.arch.memory_endness)
self.state.memory.store(input_buffer,scanf1,endness=project.arch.memory_endness)

self.state.globals['solution0'] = scanf0
self.state.globals['solution1'] = scanf1

scanf_symbol = '__isoc99_scanf'
project.hook_symbol(scanf_symbol,ReplacementScanf())
def check_strnpy(state):
strncpy_dest = state.memory.load(state.regs.esp + 4,4,endness=project.arch.memory_endness)
strncpy_src = state.memory.load(state.regs.esp + 8, 4, endness=project.arch.memory_endness)
strncpy_len = state.memory.load(state.regs.esp + 12, 4, endness=project.arch.memory_endness)
src_contents = state.memory.load(strncpy_src, strncpy_len)
# 因为参数中只保存了地址,我们需要根据这个地址去获取内容
if state.se.symbolic(strncpy_dest) and state.se.symbolic(src_contents):
# 判断dest 和src 的内容是不是符号化对象
if state.satisfiable(extra_constraints=(src_contents[-1:-64] == 'NEDVTNOP',strncpy_dest == 0x4D43523C)):
# 尝试求解,其中strncpy_dest == 0x4D43523C 的意思是判断dest 是否可控为password 的地址;src_contents[ -1 : -64 ] == 'KZYRKMKE' 是判断input_buffer 的内容是否可控为'KZYRKMKE' ,因为这块内存是倒序,所以需要通过[ -1 : -64 ] 倒转(contentes 的内容是比特,获取8 字节的大小为:8*8 = 64),然后判断该值是否为字符串'KZYRKMKE'
state.add_constraints(src_contents[ -1 : -64 ] == 'NEDVTNOP',strncpy_dest == 0x4D43523C)
return True
else:
return False
else:
return False
simulation = project.factory.simgr(initial_state)
def good_job(state):
strncpy_address = 0x0804860D
if state.addr == strncpy_address:
return check_strnpy(state)
else:
return False
simulation.explore(find=good_job)
if simulation.found:
solution_state = simulation.found[0]
solution0 = solution_state.se.eval(solution_state.globals['solution0'])
solution1 = solution_state.se.eval(solution_state.globals['solution1'],cast_to=bytes)
print(solution0,solution1)
if __name__ =='__main__':
main()

总结:state.satisfiable(extra_constraints=(条件1,条件2)) => 合并多个条件计算是否存在满足约束的解(注意两个或多个条件之间是And 合并判断,不是Or )

17_angr_arbitrary_jump

最后一题,在read_print中检查返回地址是否是godjob

int __cdecl main(int argc, const char **argv, const char **envp)
{
printf("Enter the password: ");
read_input();
puts("Try again.");
return 0;
}


import angr
import claripy

def main():
binary_path = '../solutions/17_angr_arbitrary_jump/17_angr_arbitrary_jump'
project = angr.Project(binary_path)

initial_state = project.factory.entry_state()
simulation = project.factory.simgr(
initial_state,
save_unconstrained=True,
stashes={
'active' : [initial_state],
'unconstrained' : [],
'found' : [],
'not_needed' : []
}
)

class ReplacementScanf(angr.SimProcedure):

def run(self, fmt, string_addr):
exploit = claripy.BVS('exploit', 8 * 64)

for char in exploit.chop(bits=8):
self.state.add_constraints(char >= '0', char <= 'z')

exploit_addr = string_addr
self.state.memory.store(exploit_addr, exploit)

self.state.globals['solution'] = exploit

scanf_symbol = '__isoc99_scanf'
project.hook_symbol(scanf_symbol, ReplacementScanf())

while (simulation.active or simulation.unconstrained) and (not simulation.found):  
for unconstrained_state in simulation.unconstrained:
def should_move(s):
return s is unconstrained_state

simulation.move('unconstrained', 'found', filter_func=should_move)


simulation.step()

if simulation.found:
solution_state = simulation.found[0]

solution_state.add_constraints(solution_state.regs.eip == 0x4D435250)

solution = solution_state.se.eval(solution_state.globals['solution'],cast_to = bytes)
print(solution)
else:
raise Exception('Could not find the solution')


if __name__ == '__main__':
main()
WARNING | 2022-03-15 13:47:38,088 | angr.project | Address is already hooked, during hook(0x4d5513a0, <SimProcedure ReplacementScanf>). Re-hooking.
WARNING | 2022-03-15 13:47:38,226 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-03-15 13:47:38,226 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-03-15 13:47:38,226 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-03-15 13:47:38,226 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-03-15 13:47:38,226 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-03-15 13:47:38,226 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x4d4352e1 (__libc_csu_init+0x1 in 17_angr_arbitrary_jump (0x4d4352e1))
WARNING | 2022-03-15 13:47:38,229 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x4d4352e1 (__libc_csu_init+0x1 in 17_angr_arbitrary_jump (0x4d4352e1))
WARNING | 2022-03-15 13:47:38,340 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,340 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,341 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,342 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,343 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,344 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,345 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,346 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,347 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,348 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,349 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,350 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,350 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,350 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,350 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,350 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,350 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,351 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,351 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,351 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,351 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,351 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,351 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,352 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,353 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,354 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,354 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,354 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,354 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,393 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,394 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,394 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,394 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,394 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,394 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,395 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,396 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,397 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,398 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,399 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,399 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,399 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,399 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,399 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,399 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,399 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,399 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,400 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,401 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,402 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,403 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,404 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,405 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,406 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,407 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,407 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,407 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,407 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,407 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,407 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,407 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,407 | claripy.ast.bv | BVV value is being coerced from a unicode string, encoding as utf-8
WARNING | 2022-03-15 13:47:38,766 | angr.engines.successors | Exit state has over 256 possible solutions. Likely unconstrained; skipping. <BV32 Reverse(exploit_11_512[135:104])>
WARNING | 2022-03-15 13:47:39,144 | angr.engines.successors | Exit state has over 256 possible solutions. Likely unconstrained; skipping. <BV32 Reverse(exploit_12_512[135:104])>
CRITICAL | 2022-03-15 13:47:39,181 | angr.sim_state | The name state.se is deprecated; please use state.solver.
b'00000000000000000000000000000000000000000000000PRCM00@0000000000'

总结:

class ReplacementScanf(angr.SimProcedure):class ReplacementScanf(angr.SimProcedure):import angr
#导入文件
path = ''
#打开二进制文件
project = angr.Project(path)
#接下来两种选择
#第一种
initial_state = project.factory.entry_state()#直接创建空白的执行上下文环境
#第二种
start_address = xxxx
initial_state = project.factory.blank_state(addr=start_address)
#创建模拟器,第二个参数是指定是否合并路径,路径多会爆炸
simulation = project.factory.simgr(initial_state,veritesting = True)
#good和try有两种方法,第一个是找到call puts的地址不再说了
#第二种是进行函数定义
def is_successful(state):
#从输出中提取
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job' in str(stdout_output) # :boolean
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again' in str(stdout_output)  # :boolean
#执行搜索路径(里面是要到达的地址)
simulation.explore(find=print_good_address,avoid=print_try_avoid)
#打印出结果,搜索结果的集合,simulation.found是python的list集合
if simulation.found:
solution_state = simulation.found[0]
#获取标准输入
print(solution_state.posix.dumps(sys.stdin.fileno()))

#寄存器类,比如输入了好几个参数,我们能从寄存器下手找到输入源
#创建求解变量
password0 = claripy.BVS('password0', password0_size_in_bits)
#告诉符号执行,执行的寄存器是什么
initial_state.regs.eax = password0
#在if中进行求解符号变量
solution0 = solution_state.se.eval(password0)
#栈类,如果结果保存在栈中
#将创建的变量从stack中取出来
initial_state.stack_push(password0)
#变量存在bss段,符号化内存
#第一个参数是变量的地址
initial_state.memory.store(password0_address, password0)
#数字字节顺序,可以表示为:project.arch.memory_endness
initial_state.memory.store(地址,数据,endness = 数据字节顺序) => 设置初始化内存数据
#有关文件就是第七题
#多分支比较,建立比较机制
#先在if里进行约束
constrained_parameter_address = 0x804A050  # 计算约束的内存位置
constrained_parameter_size_bytes = 0x10    # 计算约束的内存大小
constrained_parameter_bitvector = solution_state.memory.load(  # 加载内存
constrained_parameter_address,
constrained_parameter_size_bytes
)
#再用下述的进行字符串比较
solution_state.memory.load(内存地址,内存大小) => 加载内存
solution_state.add_constraints(约束条件) => 添加约束条件
#hook的写法
#连个参数分别是,hook的地址,执行后往后跳过几个字节
@project.hook(check_equals_called_address, length=instruction_to_skip_length)
def skip_check_equals_(state):
加载内存
claripy.If(条件,条件为True时的返回值,条件为False时的返回值) => 创建条件判断
claripy.BVV(值,值大小) => 创建一个数值
#另一种常见hook方法,常见于想hook的函数太多,无法定位情况
class ReplacementCheckEquals(angr.SimProcedure):
def run(self, Hook的函数参数列表):
加载内存
return 用claripy.If对比来返回值# 如果是void函数可以省略
#hook的函数名是真实函数名,第二个参数是ReplacementCheckEquals()
project.hook_symbol(要Hook的函数名,SimProcedure类实例)
#scanf太多,hookscanf
class ReplacementScanf(angr.SimProcedure):
def run(self, format_string, scanf0_address, scanf1_address ):
#创建求解变量
scanf0 = claripy.BVS('scanf0', 4 * 8)
#初始化内存
self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness)
#保存当前状态
self.state.globals['solution0'] = scanf0
project.hook_symbol(要Hook的函数名,SimProcedure类实例)
#用angr自带hook程序
#用在simgr后
project.hook(0x804ed40, angr.SIM_PROCEDURES['libc']['printf']())
angr.SIM_PROCEDURES[ 系统库名 ] [ 系统函数名 ] () => 获取Angr 内部实现的系统函数
#遇到函数是动态链接的
#现设基地址
base = 0x40000000(随便)
#我们的path是指那个so文件
project = angr.Project(path_to_binary, load_options={
'main_opts' : {
'custom_base_addr' : base
}
})
initial_state = project.factory.call_state(validate_function_address, buffer_pointer,claripy.BVV(8, 32))  # 调用validate_function,因为函数声明validata_function(buffer_point,buffer_length) ,所以我们构造出调用validata_function(0x3000000,0x8)
#对于溢出而覆盖的题
#下面是约束的写法
for char in scanf1.chop(bits=8):
self.state.add_constraints(char >= '0', char <= 'z')  # 对input_buffer 的输入约束
#约束完,存入内存,获取想要的puts
def check_puts(state):
puts_parameter = state.memory.load(state.regs.esp + 4, 4, endness=project.arch.memory_endness)  
#检查这个参数是否为符号化对象
if state.se.symbolic(puts_parameter):
#复制执行状态上下文进行约束求解,不影响原理的执行上下文
copied_state = state.copy()
if copied_state.satisfiable():  # 判断添加了上面这个约束是否有解
state.add_constraints(puts_parameter == good_job_string_address)  # 如果有解的话就保存到我们执行的那个状态对象
return True
else:
return False
else:
return False
#序列化变量内容为字符串
solution_state.se.eval(求解变量,cast_to=bytes)
#合并多个条件计算是否存在满足约束的解(注意两个或多个条件之间是And 合并判断,不是Or )
state.satisfiable(extra_constraints=(条件1,条件2))
# CTF
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录