文章目录
前言
根据大佬们的angr教程的学习,作个人总结
参考内容1:https://www.anquanke.com/post/id/212816#h2-16
参考内容2:https://www.bilibili.com/video/BV167411o7WK/?spm_id_from=333.788.videocard.0
一、基本模板
1.以地址的形式寻找
"""
步骤:
1.加载文件
2.初始化入口
3.给入口 开始设置模拟执行
4.告诉程序要执行到的地方
5.出结果
6.输出结果
"""
import angr
import sys
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False) # 加载文件
initial_state = project.factory.entry_state() # 初始化,二进制文件入口
simulation = project.factory.simgr(initial_state) # 模拟执行,给参数:入口
avoid_me_address = 0x080485A8
maybe_good_address = 0x080485E0
simulation.explore(find=maybe_good_address, avoid=avoid_me_address)
#想要执行到及避免的地方,find=addr avoid
if simulation.found:
solution_state = simulation.found[0]
solution = solution_state.posix.dumps(sys.stdin.fileno())
# 等价于 dump(0)
print("[+] Success! Solution is: {}".format(solution.decode("utf-8")))
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
2.以字节对象的形式寻找
import angr
import sys
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in stdout_output:
return True
# 对象要使用的是b'string'不能缺b
# 该对象是字节对象 而不是字符串
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
solution = solution_state.posix.dumps(sys.stdin.fileno())
# 可用dumps(0)
print("[+] Success! Solution is: {}".format(solution.decode("utf-8")))
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
二、绕过scanf
1.寄存器符号化
import angr
import sys
import claripy
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
start_address = 0x08048980
initial_state = project.factory.blank_state(addr=start_address)
# 自定义入口 而不再是从二进制文件入口直接进入
passwd_size_in_bits = 32
# 生成符号位向量,BVS(ame,size)
passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
passwd2 = claripy.BVS('passwd2', passwd_size_in_bits)
initial_state.regs.eax = passwd0
initial_state.regs.ebx = passwd1
initial_state.regs.edx = passwd2
# state.regs.xxx 取寄存器
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
solution0 = format(solution_state.solver.eval(passwd0), 'x')
# 约束求解 求值 而不是直接dump(0)
solution1 = format(solution_state.solver.eval(passwd1), 'x')
solution2 = format(solution_state.solver.eval(passwd2), 'x')
solution = solution0 + " " + solution1 + " " + solution2
print("[+] Success! Solution is: {}".format(solution))
# print(simgr.found[0].posix.dumps(0))
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
2.栈符号化
import angr
import sys
import claripy
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
start_address = 0x8048697
initial_state = project.factory.blank_state(addr=start_address)
initial_state.regs.ebp = initial_state.regs.esp
# mov ebp,esp
passwd_size_in_bits = 32
passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
padding_length_in_bytes = 0x8
initial_state.regs.esp -= padding_length_in_bytes
# esp_addr
initial_state.stack_push(passwd0)
initial_state.stack_push(passwd1)
# push 两个变量
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(1)
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(1)
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
solution0 = (solution_state.solver.eval(passwd0))
solution1 = (solution_state.solver.eval(passwd1))
print("[+] Success! Solution is: {0} {1}".format(solution0, solution1))
#print(solution0, solution1)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
3.内存符号化
3.1 固定内存地址符号化
import angr
import sys
import claripy
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
start_address = 0x8048601
initial_state = project.factory.blank_state(addr=start_address)
passwd_size_in_bits = 64
# 因为是4个8字节即64比特大小的字符串
passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
passwd2 = claripy.BVS('passwd2', passwd_size_in_bits)
passwd3 = claripy.BVS('passwd3', passwd_size_in_bits)
passwd0_address = 0xA1BA1C0
#passwd1_address = 0xA1BA1C8
#passwd2_address = 0xA1BA1D0
#passwd3_address = 0xA1BA1D8
initial_state.memory.store(passwd0_address, passwd0)
# state.memory.store(addr,value)
# 数据存在了某一固定地址
initial_state.memory.store(passwd0_address + 0x8, passwd1)
initial_state.memory.store(passwd0_address + 0x10, passwd2)
initial_state.memory.store(passwd0_address + 0x18, passwd3)
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(1)
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(1)
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
solution0 = solution_state.solver.eval(passwd0,cast_to=bytes)
solution1 = solution_state.solver.eval(passwd1,cast_to=bytes)
solution2 = solution_state.solver.eval(passwd2,cast_to=bytes)
solution3 = solution_state.solver.eval(passwd3,cast_to=bytes)
solution = solution0 + b" " + solution1 + b" " + solution2 + b" " + solution3
print("[+] Success! Solution is: {}".format(solution.decode("utf-8")))
#print(solution0, solution1, solution2, solution3)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
3.2 动态内存符号化
import angr
import sys
import claripy
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
start_address = 0x8048699
initial_state = project.factory.blank_state(addr=start_address)
passwd_size_in_bits = 64
# 缓冲区大小为 8字节,所以64bit
passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
fake_heap_address0 = 0xffffc93c
pointer_to_malloc_memory_address0 = 0xabcc8a4
fake_heap_address1 = 0xffffc94c
pointer_to_malloc_memory_address1 = 0xabcc8ac
initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness)
initial_state.memory.store(pointer_to_malloc_memory_address1, fake_heap_address1, endness=project.arch.memory_endness)
initial_state.memory.store(fake_heap_address0, passwd0)
initial_state.memory.store(fake_heap_address1, passwd1)
# fake_heap_address放入了pointer_to_malloc_memory_addresss(fake即伪装的动态地址faker),之后pass放入这伪装的动态地址中。
# 实现:"pass->fake->pointer"
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(1)
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(1)
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
solution0 = solution_state.solver.eval(passwd0, cast_to=bytes)
solution1 = solution_state.solver.eval(passwd1, cast_to=bytes)
print("[+] Success! Solution is: {0} {1}".format(solution0.decode('utf-8'), solution1.decode('utf-8')))
#print(solution0, solution1)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
scanf绕过总结
state.regs.xxx
state.memory.store
BVV(value,size) 和 BVS( name, size)
要注意大小 是用bit的,所以要注意各种转换(1字节=8比特)
在后边有直接hook scanf,不选择绕过 而选择模拟的操作方式。哪种更好尚且不知。
针对不同情况下的不同符号化,并非只是绕过scanf时才会用到。要点在于 输入是否需要符号化,再根据输入的情境不同 使用不同的符号化。
hook
hook单一程序 使用地址hook
import angr
import sys
import claripy
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
initial_state = project.factory.entry_state()
# 因为使用了hook 所以 可直接从entry处进行操作
check_equals_called_address = 0x80486B3
instruction_to_skip_length = 5
# project.hook(要hook的函数地址,该地址所占长度)
@project.hook(check_equals_called_address, length=instruction_to_skip_length)
def skip_check_equals_(state):
user_input_buffer_address = 0x804A054
user_input_buffer_length = 16
user_input_string = state.memory.load(
user_input_buffer_address,
user_input_buffer_length
)
# 固定内存地址 的 操作方式
check_against_string = 'XKSPZSJKJYQCQXZV'
register_size_bit = 32
state.regs.eax = claripy.If(
user_input_string == check_against_string, # 如果相等
claripy.BVV(1, register_size_bit), # 成功返回1
claripy.BVV(0, register_size_bit) # 不成功返回0
)
# 题目中,eax是作为函数计算后返回值存放的地方 成功则返回1,不成功则返回0
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(1)
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(1)
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
solution = solution_state.posix.dumps(0)
print("[+] Success! Solution is: {0}".format(solution.decode('utf-8')))
#print(solution0)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
hook函数名
import angr
import claripy
import sys
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
initial_state = project.factory.entry_state()
# 同上,可直接从entry进入
# class 继承自angr.SimProcedure
# 作用:使得函数被我们写的函数代替 并在run(self),开始用self
class ReplacementCheckEquals(angr.SimProcedure):
def run(self, to_check, length):
user_input_buffer_address = to_check
user_input_buffer_length = length
user_input_string = self.state.memory.load(
user_input_buffer_address,
user_input_buffer_length
)
check_against_string = 'ORSDDWXHZURJRBDH'
return claripy.If(
user_input_string == check_against_string,
claripy.BVV(1, 32),
claripy.BVV(0, 32)
)
# 同上 与之不同的是:hook函数名是对所有的该名函数作hook,所以需要定义一个模拟函数的类,在里边作模拟程序 def run;
check_equals_symbol = 'check_equals_ORSDDWXHZURJRBDH'
project.hook_symbol(check_equals_symbol, ReplacementCheckEquals())
# project.hook_symbol(要hook的函数名, 用于代替该函数的类)
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(1)
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(1)
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
solution = solution_state.posix.dumps(0)
print("[+] Success! Solution is: {0}".format(solution.decode('utf-8')))
#print(solution0)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
hook scanf
import angr
import claripy
import sys
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
initial_state = project.factory.entry_state()
# 与hook 函数名一样 模拟scanf函数
class ReplacementScanf(angr.SimProcedure):
def run(self, format_string, param0, param1):
scanf0 = claripy.BVS('scanf0', 32)
scanf1 = claripy.BVS('scanf1', 32)
# 两个scnaf的调用
scanf0_address = param0
self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness)
scanf1_address = param1
self.state.memory.store(scanf1_address, scanf1, endness=project.arch.memory_endness)
self.state.globals['solutions'] = (scanf0, scanf1)
#这里的关键我们都知道Python的变量生存周期,在这里scanf0和scanf1是函数ReplacementScanf的局 部变量,为了让函数外部也能获得我们输入的符号位向量,从而调用求解器获得答案,需要将这两个符号位向量变为全局变量,这里我们需要调用带有全局状态的globals插件中“保存”对我们的符号值的引用。globals插件允许使用列表,元组或多个键的字典来存储多个位向量(作者原话)
# 在局部函数里定义了scanf,但是scanf事实上是被很多地方所使用的,所以需要state.globals
scanf_symbol = '__isoc99_scanf' # scanf的函数名
project.hook_symbol(scanf_symbol, ReplacementScanf())
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(1)
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(1)
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
stored_solutions = solution_state.globals['solutions']
# 两个值 以列表形式存储了
scanf0_solution = solution_state.solver.eval(stored_solutions[0])
scanf1_solution = solution_state.solver.eval(stored_solutions[1])
print("[+] Success! Solution is: {0} {1}".format(scanf0_solution,scanf1_solution))
#print(scanf0_solution, scanf1_solution)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
hook 静态库
hook 外部文件导入动态库
hook总结
hook针对不同内容,会有不同写法。说不出个所以然。。。
路径爆炸问题
路径爆炸:当程序进行逐位比较时,每次比较都会有对或错两种结果,假设有16个字符,那么会有2^16种路径,即路径爆炸。不利于angr分析
hook
上方已写 – hook单一程序 使用地址hook
约束条件:使用z3求解结果
import angr
import sys
import claripy
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
start_address = 0x8048625
buff_addr = 0x0804A050
address_to_check_constraint = 0x08048565
initial_state = project.factory.blank_state(addr=start_address)
# 自定义地址 跳过scanf处
char_size_in_bits = 8
passwd_len = 16
passwd0 = claripy.BVS('passwd0', char_size_in_bits*passwd_len)
initial_state.memory.store(buff_addr, passwd0)
simulation = project.factory.simgr(initial_state)
simulation.explore(find=address_to_check_constraint)
# 模拟执行到check函数处 在下方开始使用约束求解,来求解出值
if simulation.found:
solution_state = simulation.found[0]
constrained_parameter_address = buff_addr
constrained_parameter_size_bytes = 16
constrained_parameter_bitvector = solution_state.memory.load( # check函数的
constrained_parameter_address,
constrained_parameter_size_bytes
)
constrained_parameter_desired_value = 'AUPDNNPROEZRJWKB'
solution_state.solver.add(constrained_parameter_bitvector == constrained_parameter_desired_value)
# state.solver.add() -- 添加约束 就z3 之后求解出来
solution0 = solution_state.solver.eval(passwd0,cast_to=bytes)
print("[+] Success! Solution is: {0}".format(solution0))
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
veritesting
import angr
import claripy
import sys
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state, veritesting=True)
# 简而言之,veritesting的开启使得angr同时使用"动态符号执行" 和 "静态分析" 减少路径爆炸的影响,直接跑angr
def is_successful(state):
stdout_output = state.posix.dumps(1)
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(1)
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
solution = solution_state.posix.dumps(0)
print("[+] Success! Solution is: {0}".format(solution))
#print(scanf0_solution, scanf1_solution)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
文件模拟
import angr
import sys
import claripy
def Go(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary, auto_load_libs=False)
start_address = 0x80488EA
initial_state = project.factory.blank_state(addr=start_address)
filename = 'OJKSQYDP.txt'
symbolic_file_size_bytes = 64
passwd0 = claripy.BVS('password', symbolic_file_size_bytes * 8)
passwd_file = angr.storage.SimFile(filename, content=passwd0, size=symbolic_file_size_bytes)
initial_state.fs.insert(filename, passwd_file)
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(1)
if b'Good Job.\n' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(1)
if b'Try again.\n' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
for i in simulation.found:
solution_state = i
solution0 = solution_state.solver.eval(passwd0, cast_to=bytes)
print("[+] Success! Solution is: {0}".format(solution0.decode('utf-8')))
#print(solution0)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
Go(sys.argv)
PWN
任意读
任意写
任意跳转
总结
人比较笨,总觉得没有理解得很好。