Angr 从入门到放弃(三)

之前我们已经学习了angr的基础操作,比如怎样建立约束模拟器,怎样添加避免执行地址,怎样把创建符号化变量,怎样读取或指定寄存器的值,怎样设置程序符号执行的目标地址,怎样hook一个地址或者直接重写某一个函数,怎样添加寄存器约束条件的,本篇文章将继续学习一些angr的基础操作。

通过标准输出来指定目标地址

试想如果一个程序会打印good或者bad,但是有很多个地方都打印了good,也有很多个地方都打印了bad,这就无法通过指定某一个地址来让模拟器跑,至于avoid,虽然也可以手动的将所有打印bad的地址都加进去,但是很麻烦,这个时候可以怎么来做呢,就是根据标准输出来判断是target还是avoid

import sys
import angr

def main(argv):
    path_to_binary = '../program/02_angr_find_condition'
    project = angr.Project(path_to_binary)
    initial_state = project.factory.entry_state()
    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]
        print(solution_state.posix.dumps(sys.stdin.fileno()))
    else:
        raise Exception('Could not find solution')
    
if __name__ == '__main__':
    main(sys.argv)

可以发现,find和avoid都是支持函数参数的,可以直接将一个返回True或False的函数赋给这两个参数,而state.posix.dumps(1)抓取的就是标准输出所以两个函数直接判断想要的字符串是否在标准输出中出现即可完成判断

指定运行起始地址并指定寄存器的值

有些时候函数会输入一些参数,然后根据这些参数执行一些加解密逻辑,此时我们直接将程序地址指定到加解密逻辑之前,然后把用到的寄存器赋予一些符号化变量,然后直接执行即可

import sys
import angr
import claripy

def main():
    binary_path = '../program/03_angr_symbolic_registers'
    project = angr.Project(binary_path)
	
    # 设置项目开始地址
    start_addr = 0x0804890E
    initial_state = project.factory.blank_state(addr=start_addr)
	
    
   # 将寄存器符号化
    bit_length = 32
    psd0 = claripy.BVS('psd0', bit_length)
    psd1 = claripy.BVS('psd1', bit_length)
    psd2 = claripy.BVS('psd2', bit_length)

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

    simulation = project.factory.simgr(initial_state)

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

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

    simulation.explore(find=good_job, avoid=fail)
    
    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 = '%x %x %x' % (solution0, solution1, solution2)
        print(solution)
    else:
        raise Exception('Could not find the solution')
            
if __name__ == '__main__':
    main()

本道题就是用到了eax,ebx,edx三个寄存器,所以直接符号化好三个变量然后给到三个寄存器,同时不再采用以前常用的entry_state,而是通过project.factory.blank_state(addr=start_addr)直接指定开始模拟的地址为start_addr

符号化栈空间

如果还是上面那种情况,只不过参数从在寄存器里变成了在栈上,该如何符号化呢?
核心操作如下

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) # 将位向量压入栈中

还是新建一个blank_state,初始化栈空间其实就是让esp等于ebp,然后再拉高esp的过程,程序在真实运行的时候也是这样做的,然后将所需参数符号化以后压进栈的指定位置中,只要和程序真实的栈布局相同即可

符号化内存

如果又是上面那种情况,只不过参数从栈上变成了在bss段上,该如何符号化呢?
核心操作如下:

password0 = claripy.BVS('password0', 64) # 64 = 8(8个字符) * 1(每个字符一字节) * 8(每个字节8比特)
    password1 = claripy.BVS('password1', 64)
    password2 = claripy.BVS('password2', 64)
    password3 = claripy.BVS('password3', 64)

    password0_addr = 0x09FD92A0
    password1_addr = 0x09FD92A8
    password2_addr = 0x09FD92B0
    password3_addr = 0x09FD92B8

    initial_state.memory.store(password0_addr, password0) # 将位向量存入内存
    initial_state.memory.store(password1_addr, password1)
    initial_state.memory.store(password2_addr, password2)
    initial_state.memory.store(password3_addr, password3)

这里可以看到,其实就是把符号化好的变量通过memory.store存到指定的内存地址上即可

符号化文件

有时候程序会打开某个文件并读取内容,所以还需要知道如何来符号化一个文件

filename = 'MRXJKZYR.txt' # 文件名称
    symbolic_file_size_bytes = 64 # 文件大小(字节)

    password = claripy.BVS('password', symbolic_file_size_bytes * 8) # 初始化位向量
    password_file = angr.SimFile(filename, content=password, size=symbolic_file_size_bytes) # 符号化文件

    initial_state = project.factory.blank_state(addr=start_addr, fs={filename: password_file}) # 再初始状态中添加一个虚拟的文件系统

可以看到这里其实angr是有提供接口的,设置好文件的大小和内容,然后再创建blank_state的时候指定一个文件系统fs,然后以字典的形式存储文件名和由SimFile创建出来的文件对象即可。

如何自动合并路径

当路径很多很复杂的时候,可以开启一个angr的选项
simulation = project.factory.simgr(initial_state, veritesting=True)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值