angr_ctf

最近想学 llvm + vmp,看到这两技术都有用到angr,遂来学习一下

做了下Github上angr_ctf 的逆向题目 https://github.com/jakespringer/angr_ctf ,感觉它比我想象中的要强大

代码如下

0x01 一般模板

#1、 angr 一般模板
import angr
import sys, time


def found(simgr):
    if simgr.found:
        solution_state = simgr.found[0]
        correct_input = solution_state.posix.dumps(sys.stdin.fileno())
        print(correct_input)
    else:
        raise Exception('not solution')


def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    initial_state = p.factory.entry_state(
        add_options={angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
                     angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )

    simgr = p.factory.simulation_manager(initial_state)

    good_address =  0x80485F7       #加上 0x400000 基质
    again_address = (0x8048575,0x8048609)  

    simgr.explore(find=good_address, avoid=again_address)
    found(simgr)


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

0x02  通过condition的方式选择分支

#2 、angr  通过condition的方式来选择find和avoid的分支 , 际上在选择分支的时候可以根据对应的输出结果来判断,在explore的时候传入函数,用于指定什么情况下选择该路径,什么时候跳过该路径
import angr
import sys, time


def found(simgr):
    if simgr.found:
        solution_state = simgr.found[0]
        correct_input = solution_state.posix.dumps(sys.stdin.fileno())
        print(correct_input)
    else:
        raise Exception('not solution')


def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    initial_state = p.factory.entry_state(
        add_options={angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
                     angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )

    simgr = p.factory.simulation_manager(initial_state)

    # good_address =  0x80485F7       #加上 0x400000 基质
    # again_address = (0x8048575,0x8048609)  
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 


    simgr.explore(find=good_address, avoid=again_address)
    found(simgr)


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

0x03 输入的参数存放在寄存器上

#3、 angr angr的入口位置不再是main函数入口,而是在程序的某一位置上  ### 传入的参数在寄存器中...
entry_state()    构造一个已经准备好从函数入口点(main)执行的状态
blank_state()    构造一个“空状态”,它的大多数数据都是未初始化的。当使用未初始化的的数据时,一个不受约束的符号值将会被返回,记住当需要从程序中任意一点执行的时候使用
call_state()    构造一个已经准备好执行某个函数的状态
full_init_state()    构造一个已经执行过所有与需要执行的初始化函数,并准备从函数入口点执行的状态。比如,共享库构造函数(constructor)或预初始化器。当这些执行完之后,程序将会跳到入口点
import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装

def found(simgr , ttt):
    passwd0 = ttt[0]
    passwd1 = ttt[1]
    passwd2 = ttt[2]
    if simgr.found:
        solution_state = simgr.found[0]
        solution0 = format(solution_state.solver.eval(passwd0), 'x')           # format(...,'x')是将16进制转换为字符串
        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))

    else:
        raise Exception('not solution')


def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    # 不再是entry_state()了,通过给定的起始地址,在该处创建一个空白状态
    start_address  = 0x80488c7     #这个地址是将输入值传入寄存器的起始地址
    initial_state = p.factory.blank_state(addr=start_address)

    

    passwd_size_in_bits = 32
    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
    simgr = p.factory.simulation_manager(initial_state)   #模拟器管理者
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 


    simgr.explore(find=good_address, avoid=again_address)
    found(simgr,(passwd0,passwd1,passwd2))


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

0x04 输入的参数存放在栈中

#4、传入的参数在栈中   我们需要在初始化入口点的时候修改一下angr的模拟堆栈

import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装

def found(simgr , ttt):
    passwd0 = ttt[0]
    passwd1 = ttt[1]
    if simgr.found:
        solution_state = simgr.found[0]
        solution0 = format(solution_state.solver.eval(passwd0), 'x')           # format(...,'x')是将16进制转换为字符串
        solution1 = format(solution_state.solver.eval(passwd1), 'x')
        solution = solution0 + " " + solution1
        print("[+] Success! Solution is: {}".format(solution))

    else:
        raise Exception('not solution')


def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    # 不再是entry_state()了,通过给定的起始地址,在该处创建一个空白状态
    start_address  = 0x80486AE     #这个地址是scanf函数刚刚结束的位置 两个参数分别在 ebp - c  和 ebp - 0x10的位置
    initial_state = p.factory.blank_state(addr=start_address)

    # # 由于要对栈做操作,需要先将esp指向ebp
    initial_state.regs.esp = initial_state.regs.ebp

    passwd_size_in_bits = 32
    passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
    passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)

    # 下面需要将符号变量注入栈中,为此需要先腾出两个变量的空间,即2*4=8字节
    padding_length_in_bytes = 2*4  # :integer
    initial_state.regs.esp -= padding_length_in_bytes    #esp + 

    # 将变量压入栈中
    initial_state.stack_push(passwd0)  # :bitvector (claripy.BVS, claripy.BVV, claripy.BV) 
    initial_state.stack_push(passwd1)  # :bitvector (claripy.BVS, claripy.BVV, claripy.BV)

    simgr = p.factory.simulation_manager(initial_state)   #模拟器管理者
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simgr.explore(find=good_address, avoid=again_address)
    found(simgr,(passwd0,passwd1))


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

0x05 传入的参数被存放在全局区中

import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装

from Crypto.Util.number import long_to_bytes

def found(simgr , ttt):
    passwd0 = ttt[0]
    passwd1 = ttt[1]
    passwd2 = ttt[2]
    passwd3 = ttt[3]
    if simgr.found:
        solution_state = simgr.found[0]
        solution0 = solution_state.solver.eval(passwd0)           # format(...,'x')是将16进制转换为字符串
        solution1 = solution_state.solver.eval(passwd1)
        solution2 = solution_state.solver.eval(passwd2)
        solution3 = solution_state.solver.eval(passwd3)
        solution = long_to_bytes(solution0) + long_to_bytes(solution1) + long_to_bytes(solution2) + long_to_bytes(solution3)
        print("[+] Success! Solution is: {}".format(solution))

    else:
        raise Exception('not solution')


def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    # 不再是entry_state()了,通过给定的起始地址,在该处创建一个空白状态
    start_address  = 0x8048618     #这个地址是scanf函数刚刚结束的位置
    initial_state = p.factory.blank_state(addr=start_address)

    
    initial_state.regs.esp = initial_state.regs.ebp

    passwd_size_in_bits = 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)

    passwd_address =0xAB232C0
    initial_state.memory.store(passwd_address, passwd0)                 #  store(addr, ...): 向指定内存写入数据    #load(addr,...): 读取指定地址的内存
    initial_state.memory.store(passwd_address + 0x08, passwd1)
    initial_state.memory.store(passwd_address + 0x10, passwd2)
    initial_state.memory.store(passwd_address + 0x18, passwd3)

    simgr = p.factory.simulation_manager(initial_state)   #模拟器管理者
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simgr.explore(find=good_address, avoid=again_address)
    found(simgr,(passwd0,passwd1,passwd2,passwd3))


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

 

0x06 输入的数据属于 malloc分配的堆区,且返回值储存在全局变量中

#6、 输入的数据属于 malloc分配的堆区,且返回值储存在全局变量中 , 这一关要注意小端序的存放
#全局变量是有固定地址的,另外他的值存储了malloc返回的地址,malloc返回的地址存储了string字符串,对应关系即为: 全局变量 -> malloc随机分配的地址fake_addr -> string
import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装

from Crypto.Util.number import long_to_bytes

def found(simgr , ttt):
    passwd0 = ttt[0]
    passwd1 = ttt[1]
    if simgr.found:
        solution_state = simgr.found[0]
        solution0 = solution_state.solver.eval(passwd0)           # format(...,'x')是将16进制转换为字符串
        solution1 = solution_state.solver.eval(passwd1)
        solution = long_to_bytes(solution0) + long_to_bytes(solution1)
        print("[+] Success! Solution is: {}".format(solution))

    else:
        raise Exception('not solution')


def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    # 不再是entry_state()了,通过给定的起始地址,在该处创建一个空白状态
    start_address  = 0x80486AF     #这个地址是scanf函数刚刚结束的位置
    initial_state = p.factory.blank_state(addr=start_address)

    passwd_size_in_bits = 64
    passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
    passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)

    buffer0 = 0xA2DEF74
    buffer1 = 0xA2DEF7C
    fake_addr_buffer0 = 0xffffc93c
    fake_addr_buffer1 = 0xffffc94c
    initial_state.memory.store(buffer0,fake_addr_buffer0,endness=p.arch.memory_endness)   #to specify to use the endianness of your architecture, use the parameter endness=project.arch.memory_endness. On x86, this is little-endian
    initial_state.memory.store(buffer1,fake_addr_buffer1,endness=p.arch.memory_endness)
    initial_state.memory.store(fake_addr_buffer0, passwd0)                 #  store(addr, ...): 向指定内存写入数据    #load(addr,...): 读取指定地址的内存
    initial_state.memory.store(fake_addr_buffer1, passwd1)

    simgr = p.factory.simulation_manager(initial_state)   #模拟器管理者
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simgr.explore(find=good_address, avoid=again_address)
    found(simgr,(passwd0,passwd1))


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

# [+] Success! Solution is: b'OFIJHOXVFBQISOZO'
# 6.683165550231934

 

0x07 文件处理

## 7、 文件处理
# 在angr中,对文件系统进行模拟需要通过angr.storage.SimFile(name, content, size)对象,接收三个参数:
# name: 要模拟的文件名称
# content:Optional:模拟的文件内容,通常传入的是一个BV(bitvector)对象,表示符号化的变量,也可以传入string
# size:Optional:文件的大小
# 当完成文件的模拟后,就像我们生成一个符号变量之后需要将其添加到state中的指定位置(如寄存器,内存,栈)一样(将符号化变量添加到初始化状态中),生成的模拟文件也需要添加到指定的state当中,添加的方式有两种:
# 利用state.fs.insert(filename, simfile)方法:传入文件名和相应的模拟文件对象(SimFile),类似于之前state.memory.store(fake_heap_address0, passwd0)这部分操作
# 利用state.pofix.fs选项以文件名的字典来预配置SimFile
def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    # 不再是entry_state()了,通过给定的起始地址,在该处创建一个空白状态
    start_address  = 0x80488BC     #这个地址是ignore函数刚刚结束的位置
    initial_state = p.factory.blank_state(addr=start_address,
    add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
                    angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )
    # 文件名
    filename = 'FOQVSBZB.txt'
    #输入64字节
    symbolic_file_size_bytes  = 64
    #创建BV
    password = claripy.BVS('password', symbolic_file_size_bytes * 8)  #字节 = 位数 x 8
    # 模拟文件,将符号变量作为content添加进去
    simfile = angr.storage.SimFile(filename, content=password)
    #  # 将模拟文件插入到初始化状态中
    initial_state.fs.insert(filename,simfile)
    simgr = p.factory.simgr(initial_state)   #模拟器管理者

    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simgr.explore(find=good_address, avoid=again_address)

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

        solution = solution_state.solver.eval(password,cast_to=bytes).decode()

        print(f"[+] Success! Solution is: {solution}")
    else:
        raise Exception('Could not find the solution')


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

#[+] Success! Solution is: OBAXRUZT
#8.293952941894531

0x08 利用约束进行路径优化

#8、路径优化    #这里,我们输入了16个字符,经过一些简单运算后,逐个与固定字符串比较,若比较时有一处不一样的话,则带循环结束后,返回0,输出Try again
# 如果,我们继续用之前的方法,探寻存在Good Job的路径,那我们输入的每一个字符都会增加两倍的路径探寻,16个字符将会是 65536条路径,将会浪费大量资源,
#但,实际需求上我们只需要把程序运行到字符串进行比较的位置,然后添加约束(字符串相等的约束),直接求解即可,我们压根不用执行到 Good Job !!!
import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装



def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    # 不再是entry_state()了,通过给定的起始地址,在该处创建一个空白状态
    start_address  = 0x804863C     
    address_to_check_constraint = 0x8048683
    initial_state = p.factory.blank_state(addr=start_address,
    add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
                    angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )

    #输入64字节
    symbolic_file_size_bytes  = 16
    #创建BV
    password = claripy.BVS('password', symbolic_file_size_bytes * 8)  #字节 = 位数 x 8

    buff_address = 0x804A040
    initial_state.memory.store(buff_address,password)

    simgr = p.factory.simgr(initial_state)   #模拟器管理者

    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simgr.explore(find=address_to_check_constraint, avoid=again_address)

    if simgr.found:
        solution_state = simgr.found[0]
        solution = solution_state.solver.eval(password,cast_to=bytes).decode()
        print(f"[+] Success! Solution is: {solution}")
        constrained_parameter_address  = buff_address
        constrained_parameter_size_bytes = 16

        constrained_parameter_bitvector = solution_state.memory.load(
        constrained_parameter_address,
        constrained_parameter_size_bytes
        )
        constrained_parameter_desired_value = b'OSIWHBXIFOQVSBZB' # :string (encoded)
        solution_state.add_constraints(constrained_parameter_bitvector == constrained_parameter_desired_value)      ##为向量增加约束
        solution = solution_state.solver.eval(password,cast_to=bytes).decode()
        print(f"[+] Success! Solution is: {solution}")
    else:
        raise Exception('Could not find the solution')


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

0x09 angr Users Hook

#9、 angr Hook 实现
# 这次输入了两次,拥有两次字符串比较,我们可以用上面的做法来做,但这里提供了更简单的方法 -- Hook
# angr 中的Hook分为了Uesr Hook 和 Symbols Hook     参考 https://docs.angr.io/extending-angr/simprocedures#user-hooks
# 这里我们使用  Uesr Hook Hook掉 080486CA 位置的字符串检查函数,然后自行比较,并将返回值传递给eax  

import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装



def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    initial_state = p.factory.entry_state(
        add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
        angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )  #直接入口点
    check_equals_called_address = 0x80486ca
    instruction_to_skip_length = 5
    @p.hook(check_equals_called_address, length=instruction_to_skip_length)
    def skip_check_equals_(state):          
        user_input_buffer_address = 0x804a044 # :integer, probably hexadecimal
        user_input_buffer_length = 16
        user_input_string = state.memory.load(
        user_input_buffer_address,
        user_input_buffer_length
        )
        check_against_string = 'OSIWHBXIFOQVSBZB'.encode() # :string
        state.regs.eax = claripy.If(                                #   claripy.If(expression, ret_if_true, ret_if_false)   返回值要设置好宽度之类的再返回
        user_input_string == check_against_string, 
        claripy.BVV(1, 32),                 # claripy.BVS 创建符号向量    claripy.BVV创建位向量
                                                                # 符号表示
                                                                # BV:bitvector
                                                                # 构造符号值或具体值的一个比特容器,它具有大小。

                                                                # 例子:

                                                                # 建立一个32bit的符号值容器 "x":
                                                                # claripy.BVS('x',32)
                                                                # 建立一个32bit的具体值(0xc001b3475)容器:
                                                                # claripy.BVV(0xc001b3a75,32)
                                                                # 建立一个32bit的步进值,从1000到2000能被10整除的数:
                                                                # claripy.SI(name='x',bits=32,lower_bound=1000,upper_bound=2000,stride=10)
        claripy.BVV(0, 32)
        )

    simulation = p.factory.simgr(initial_state)


    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simulation.explore(find=good_address, avoid=again_address)

    if simulation.found:
        solution_state = simulation.found[0]
        solution = solution_state.posix.dumps(sys.stdin.fileno()).decode()
        print(f"[+] Success! Solution is: {solution}")
    else:
        raise Exception('Could not find the solution')


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)
# [+] Success! Solution is: QREPXOHPJPOQKQLKNOBMULEMGMLNHNIH
# 3.748581886291504

0x0A 符号Hook

# angr 10、 这道题和第八题一样,我们采取符号名 Hook  第九题Hook地址,第十题Hook符号   ,这里符号Hook有点覆写的感觉         
#参考 https://docs.angr.io/extending-angr/simprocedures#user-hooks

import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装



def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    initial_state = p.factory.entry_state(
        add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
        angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )  #直接入口点

    class ReplacementCheckEquals(angr.SimProcedure):
        def run(self,to_check,length):            #self参数  原程序第一次参数 原程序第二个参数
            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 = 'OSIWHBXIFOQVSBZB'.encode()
            
            return claripy.If(
            user_input_string == check_against_string,
            claripy.BVV(1, 32),
            claripy.BVV(0, 32)
            )
    check_equals_symbol = 'check_equals_OSIWHBXIFOQVSBZB' # :string
    p.hook_symbol(check_equals_symbol, ReplacementCheckEquals())
    simulation = p.factory.simgr(initial_state)
    
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simulation.explore(find=good_address, avoid=again_address)

    if simulation.found:
        solution_state = simulation.found[0]
        solution = solution_state.posix.dumps(sys.stdin.fileno()).decode()
        print(f"[+] Success! Solution is: {solution}")
    else:
        raise Exception('Could not find the solution')


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)
# [+] Success! Solution is: MTMDRONBBNSAAMNS
# 1.842383623123169

 

0x0B Hook scanf函数或通过Hook scanf函数读取堆栈

# angr 11 、Hook scanf函数;Hook scanf函数并读取堆栈  这里采用了两种做法
import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装



def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    initial_state = p.factory.entry_state(
        add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
        angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )  #直接入口点

    class ReplacementCheckEquals(angr.SimProcedure):
        def run(self,format_string, scanf0_address,scanf1_address):
            ebp_addr = self.state.regs.ebp
            s_arr_addr =  ebp_addr - 0x20
            constrained_parameter_address = s_arr_addr
            constrained_parameter_size_bytes = 4
            constrained_parameter_bitvector = self.state.memory.load(
            constrained_parameter_address,
            constrained_parameter_size_bytes
            )
            print("~~~~~~ ",constrained_parameter_bitvector)   ### 直接Hook 出堆栈也可以, 主意好小端序即可

            # 定义两个符号变量,用于写入buffer
            scanf0 = claripy.BVS('scanf0', 32)
            scanf1 = claripy.BVS('scanf1', 32)
            
            # 写入内存,最后一个参数指定字节序
            self.state.memory.store(scanf0_address, scanf0, endness=p.arch.memory_endness)
            self.state.memory.store(scanf1_address, scanf1, endness=p.arch.memory_endness)
             # 保存变量到全局,方便后续约束求解
            self.state.globals['solution0'] = scanf0
            self.state.globals['solution1'] = scanf1
            
    scanf_symbol = '__isoc99_scanf'
    p.hook_symbol(scanf_symbol, ReplacementCheckEquals())
    simulation = p.factory.simgr(initial_state)
    
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simulation.explore(find=good_address, avoid=again_address)

    if simulation.found:
        solution_state = simulation.found[0]
        stored_solutions0 = solution_state.globals['solution0']
        stored_solutions1 = solution_state.globals['solution1']
        solution = f'{solution_state.solver.eval(stored_solutions0)} {solution_state.solver.eval(stored_solutions1)}'
        print(f"[+] Success! Solution is: {solution}")
    else:
        raise Exception('Could not find the solution')


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)


# ~~~~~~  <BV32 0x4c524d56>
# [+] Success! Solution is: 1447907916 1146768724
# 0.769453763961792

 

0x0C 利用 Veritesting 技术解决简单的路径爆破问题

# angr 12、Veritesting 技术解决路径爆炸问题  正常解法的话 2 ^ 32 条路径,直接起飞
# 这里Veritesting 静态符合执行与动态符号结合执行,减少了路径爆炸的影响 , 原理: https://www.wangan.com/p/7fygfgbcefb38cfc#Veritesting
# 简而言之,Veritest 从 DSE 开始,一旦遇到一些简单的代码,就切换到 SSE 的模式。简单的代码指的是不含系统调用,间接跳转或者其他很难精确推断的语句。在 SSE 模式下,首先动态恢复控制流图,找到 SSE 容易分析的语句和难以分析的语句。然后 SSE 算法推断出从易分析的节点到难分析节点路径的影响。最后,Veritesting 切换回 DSE 模式去处理静态不好解决的情况。

import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装



def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    initial_state = p.factory.entry_state(
        add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
        angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )  #直接入口点


    simulation = p.factory.simgr(initial_state,veritesting = True)          #开启veritesting
    
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simulation.explore(find=good_address, avoid=again_address)

    if simulation.found:
        solution_state = simulation.found[0]
        solution = solution_state.posix.dumps(sys.stdin.fileno()).decode()
        print(f"[+] Success! Solution is: {solution}")
    else:
        raise Exception('Could not find the solution')


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

#[+] Success! Solution is: YNCRGVKZODSHWLAPETIXMBQFUJYNCRGV
#4.00534200668335


 

0x0D angr解静态编译题目

# angr 13、angr解静态编译题目  题目中的标准函数如strcmp函数是静态编译的,能直接点开看到源码 ,通常,Angr在遇到标准函数库的时候会自动用工作速度快得多的simprocedure 库进行替代
# 但现在,题目中的程序没办法被自动替换,要解决这个问题,我们需要手动Hook所有使用标准库的C函数  ,我们只需要手动找到程序中用到静态函数的地址,将其利用simprocedure提供的函数Hook掉即可

import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装



def main(argv):
    path_to_binary = argv[1]
    p = angr.Project(path_to_binary)

    initial_state = p.factory.entry_state(
        add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
        angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
    )  #直接入口点

    p.hook(0x804fab0, angr.SIM_PROCEDURES['libc']['printf']())
    p.hook(0x804fb10, angr.SIM_PROCEDURES['libc']['scanf']())
    p.hook(0x80503f0, angr.SIM_PROCEDURES['libc']['puts']())
    p.hook(0x8048d60, angr.SIM_PROCEDURES['glibc']['__libc_start_main']())
    simulation = p.factory.simgr(initial_state)          #开启veritesting
    
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simulation.explore(find=good_address, avoid=again_address)

    if simulation.found:
        solution_state = simulation.found[0]
        solution = solution_state.posix.dumps(sys.stdin.fileno()).decode()
        print(f"[+] Success! Solution is: {solution}")
    else:
        raise Exception('Could not find the solution')


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

#[+] Success! Solution is: EADQYLAR
#13.491897106170654

 

0x0E angr处理依赖共享库.so文件的程序

 

# 14、 angr处理依赖共享库.so文件的程序, 主程序中的作者自定义的函数 -- validate 被放到了一个.so文件中
# 要做这道题,我们需要处理好.so文件中函数的偏移, 我们使用call_state 空降.so文件库中的validate函数,并在最后find的时候用根据eax的值求约束
# 这里只处理.so文件就可以解出flag

import angr
import sys, time
import claripy    ## claripy模块实际上是对python z3库的封装



def main(argv):
    path_to_binary = argv[1]
    so_binary_name = "lib14_angr_shared_library.so" 
    base = 0x4000000            #指定共享文件的基地址
    
    p = angr.Project(so_binary_name,load_options={
        'main_opts' : {
            'custom_base_addr' : base 
        }
    })
    buffer_pointer = claripy.BVV(0x3000000, 32)     #  0x3000000 在下面用作地址保存 BVS向量
    validate_function_address = base + 0x670
    initial_state = p.factory.call_state(validate_function_address, buffer_pointer, claripy.BVV(8, 32), #函数地址 函数的第一个参数 函数的第二个参数 ##call_state的具体用法 参考 https://api.angr.io/angr.html?highlight=call_state
    add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
                    angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
                    ) 
    password = claripy.BVS('password', 8*8)     
    initial_state.memory.store(buffer_pointer, password)

    simulation = p.factory.simgr(initial_state)          #开启veritesting
    success_address = base + 0x71c
    def good_address(state):        # 我们在路径探寻的时候的输出进行判断
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Good Job.' in stdout_output else False
    def again_address(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return True if b'Try again.' in stdout_output else False 

    simulation.explore(find=success_address)

    if simulation.found:
        solution_state = simulation.found[0]
        solution_state.add_constraints(solution_state.regs.eax != 0)
        solution = solution_state.solver.eval(password,cast_to=bytes).decode()
        print(f"[+] Success! Solution is: {solution}")
    else:
        raise Exception('Could not find the solution')


if __name__ == '__main__':
    start = time.time()
    main(sys.argv)
    end = time.time()
    total = end - start
    print(total)

[+] Success! Solution is: TSDLQKWZ
# 1.3254785537719727
# 这道题做完,我还是有一些疑惑,这里是直接加载了.so文件,也就是说,直接处理的.so文件,根本没有管主程序的事,那该怎么同时加载.so文件和主程序呢?????

 

参考链接

https://github.com/jakespringer/angr_ctf     #angr_ctf 题目链接
posted @ 2022-08-31 22:56  TLSN  阅读(154)  评论(0编辑  收藏  举报