Angr从0到0的成长之路(二)
本文最后更新于525 天前,其中的信息可能已经过时!

前言

继续做题,冲冲冲,本文每一个章节都是先按照自己的思路做完然后看出题人的答案

每次进虚拟环境敲代码太麻烦了,在这存一下

cd angrfile
virtualenv env
source venv/bin/activate

0x0.03_angr_symbolic_registers

Emm…..不还是那一套嘛…..

import angr 
import sys

def main(argv):
    Path = argv[1]
    p = angr.Project(Path,auto_load_libs = False)
    init_state = p.factory.entry_state()
    sm = p.factory.simgr(init_state)
    
    target_address = 0x080495FA
    
    sm.explore(find = target_address)
    
    if sm.found:
        solution_state = sm.found[0]
        print(solution_state.posix.dumps(0))
      
if __name__ == '__main__':
    main(sys.argv)

也是出了答案,看一眼题解

OK啊,也是有很多新东西,开始学习出题人的思路:在通常情况下,想要从main函数入口点找到一条正确的路径是很耗时的,但是我们可以指定一个start_address,然后自己分析之前的部分,将此时的要求的值在哪个寄存器里搞明白,可以自己加限制来提高效率,用angr输出这时候的寄存器的值,再一步步向上逆

import angr 
import claripy
import sys

def main(argv):
    Path = argv[1]
    p = angr.Project(Path,auto_load_libs = False)
    start_address = 0x08049592
    init_state = p.factory.blank_state(addr = start_address , auto_load_libs =False)
    
    #创建三个变量,包括名字和大小,怎么一股z3的味道...
    password0_size_in_bits = 32
    password0 = claripy.BVS('password0',password0_size_in_bits)
    password1_size_in_bits = 32
    password1 = claripy.BVS('password1',password1_size_in_bits)
    password2_size_in_bits = 32
    password2 = claripy.BVS('password2',password2_size_in_bits)
    
    #将三个数字交给state状态下的这三个寄存器
    init_state.regs.eax = password0
    init_state.regs.ebx = password1
    init_state.regs.edx = password2
    
    sm = p.factory.simgr(init_state)
    
    def is_sucessful(state):
        stdout_output = state.posix.dumps(1)
        return True if  b'Good Job.' in stdout_output else False

    def should_abort(state):
        stdout_output = state.posix.dumps(1)
        return True if b'Try again.' in stdout_output else False
    
    sm.explore(find = is_sucessful,avoid = should_abort)
    
    if sm.found:
        solution_state = sm.found[0]
        solution0 = solution_state.solver.eval(password0)
        solution1 = solution_state.solver.eval(password1)
        solution2 = solution_state.solver.eval(password2)
        #这里不是dumps(0),我们要的值不是state的时候的输入,是寄存器的值
        print(hex(solution0),hex(solution1),hex(solution2))    
           
if __name__ == '__main__':
    main(sys.argv)

写本文时本人并不熟练python的格式化字符串和代码简写,代码有点史,能跑就行

0x1.04_angr_symbolic_stack

根据名字来看的话应该是跟栈有关,进去一看果然是把读的数放在了栈上,这个程序很简单,为了练习就不以做出来为目的了,直接往出题人的思路上去靠一下

然后我大概写了一个exp,但是因为不知道能不能直接对esp操作,再加上一些函数并不了解,直接学习一手

import angr,claripy
import sys

def main(argv):
    path = argv[1]
    p = angr.Project(path,auto_load_libs = False)
    start_address = 0x80493EF
    init_state = p.factory.blank_state(addr = start_address)
    password0 = claripy.BVS('password0',32)
    password1 = claripy.BVS('password1',32)
    
    init_state.regs.esp = ...  #初始化esp
    
    sm = p.factory.simgr(init_state)
    
    def is_sucessful(state):
        stdout_output = state.posix.dumps(1)
        return True if  b'Good Job.' in stdout_output else False

    def should_abort(state):
        stdout_output = state.posix.dumps(1)
        return True if b'Try again.' in stdout_output else False
    
    sm.explore(find = is_sucessful,avoid = should_abort)
    
    if sm.found:
        solution_state = sm.found[0]
        ...
        #得到当前的state的esp然后减一下
        print(solution)
           
           
if __name__ == '__main__':
    main(sys.argv)

这个题好像要比我想象的要复杂得多啊,为了还原栈在scanf前后的状态要分析一下栈的变化

先是将esp交给了ebp,为接下来的函数开辟了一部分的栈空间,然后esp下降,然后将ebp-0x10,ebp-0xc压入栈中,与格式化字符串一起传给scanf,然后可以看到在第一个加密函数对第一个输入进行了操作,所以password0就是第一个输入,现在对上面的代码进行一下改进

import angr,claripy
import sys

def main(argv):
    path = argv[1]
    p = angr.Project(path,auto_load_libs = False)
    start_address = 0x080493F2
    init_state = p.factory.blank_state(addr = start_address)
    password0 = claripy.BVS('password0',32)
    password1 = claripy.BVS('password1',32)
    
    init_state.regs.ebp = init_state.regs.esp
    init_state.regs.esp -= 0x8
    init_state.stack_push(password0)
    init_state.stack_push(password1)
    
    sm = p.factory.simgr(init_state)
    
    def is_sucessful(state):
        stdout_output = state.posix.dumps(1)
        return True if  b'Good Job.' in stdout_output else False

    def should_abort(state):
        stdout_output = state.posix.dumps(1)
        return True if b'Try again.' in stdout_output else False
    
    sm.explore(find = is_sucessful,avoid = should_abort)
    
    if sm.found:
        solution_state = sm.found[0]
        solution0 = solution_state.solver.eval(password0)
        solution1 = solution_state.solver.eval(password1)
        print(solution0,solution1)
    else:
        print('Not Found')
           
if __name__ == '__main__':
    main(sys.argv)

要特别注意取的起始地址start_address,地址不同,栈的情况也不一样,要注意还原,stack_push函数可以完全模拟入栈操作的寄存器和操作数的变化

我调试了一下,在add esp,0x10结束后esp 并不在我们设计的ebp-0x10的位置,但是之后的操作栈只会向下开辟,不污染我们的password所以能出答案

0x2.05_angr_symbolic_memory

很显然,这题我们的思路就是要去初始化四个变量,然后将他们交给scanf之后的state的那四个特定地址user_input, &unk_9BFC3C8, &unk_9BFC3D0, &unk_9BFC3D8,怎么交给这四个地址肯定是有个函数的,但是之前的代码中并未提到

看了一下函数的格式是这样的

initial_state.memory.store(password0_address, password0)

并且最后为了得到字符串而不是一串数字,还进行了如下操作

solution0 = solution_state.solver.eval(password0,cast_to=bytes).decode()

好了,现在可以写出我们的exp了

import angr
import claripy
import sys

def main(argv):
    path = argv[1]
    p = angr.Project(path,auto_load_libs = False)
    start_address = 0x08049315
    init_state = p.factory.blank_state(addr = start_address)

    password0 = claripy.BVS('password0',64)
    password1 = claripy.BVS('password1',64)
    password2 = claripy.BVS('password2',64)
    password3 = claripy.BVS('password3',64)
    init_state.memory.store(0x09BFC3C0, password0)
    init_state.memory.store(0x09BFC3C8, password1)
    init_state.memory.store(0x09BFC3D0, password2)
    init_state.memory.store(0x09BFC3D8, password3)
    
    sm = p.factory.simgr(init_state)
    
    def is_sucessful(state):
        stdout_output = state.posix.dumps(1)
        return True if  b'Good Job.' in stdout_output else False

    def should_abort(state):
        stdout_output = state.posix.dumps(1)
        return True if b'Try again.' in stdout_output else False
    
    sm.explore(find = is_sucessful,avoid = should_abort)
    
    if sm.found:
        solution_state = sm.found[0]
        solution0 = solution_state.solver.eval(password0,cast_to = bytes).decode()
        solution1 = solution_state.solver.eval(password1,cast_to = bytes).decode()
        solution2 = solution_state.solver.eval(password2,cast_to = bytes).decode()
        solution3 = solution_state.solver.eval(password3,cast_to = bytes).decode()
        print(solution0,solution1,solution2,solution3)
    else:
        print('Not Found')
           
if __name__ == '__main__':
    main(sys.argv)

0x3.06_angr_symbolic_dynamic_memory

与上个题不同的是这个不是静态的地址,是从堆上开辟的一段地址,所以在还原环境的时候我们也要在堆上开辟地址,交给buffer0和buffer1

出题人的exp中提到angr是默认大段排序,所以要用 endness = p.arch.memory_endness 改为小段存储数据,直接上exp了:

import angr 
import claripy
import sys

def main(argv):
    path = argv[1]
    p = angr.Project(path,auto_load_libs = False)
    start_address = 0x0804938C
    init_state = p.factory.blank_state(addr = start_address)
    
    #print('ESP:',init_state.regs.esp)
    esp = 0x7fff0000
    password0 = claripy.BVS('password0',64)
    password1 = claripy.BVS('password1',64)
    heap_0 = esp - 0x200
    buffer0 = 0x093D3B38
    init_state.memory.store(buffer0, heap_0,endness = p.arch.memory_endness)
    init_state.memory.store(heap_0,password0)
    heap_1 = esp - 0x300
    buffer1 = 0x093D3B40
    init_state.memory.store(buffer1, heap_1,endness = p.arch.memory_endness)
    init_state.memory.store(heap_1,password1)
    
    sm = p.factory.simgr(init_state)
    
    def is_sucessful(state):
        stdout_output = state.posix.dumps(1)
        return True if  b'Good Job.' in stdout_output else False

    def should_abort(state):
        stdout_output = state.posix.dumps(1)
        return True if b'Try again.' in stdout_output else False
    
    sm.explore(find = is_sucessful,avoid = should_abort)
    
    if sm.found:
        solution_state = sm.found[0]
        solution0 = solution_state.solver.eval(password0,cast_to = bytes).decode()
        solution1 = solution_state.solver.eval(password1,cast_to = bytes).decode()

        print(solution0,solution1)
    else:
        print('Not Found')
           
if __name__ == '__main__':
    main(sys.argv)   

今天的题就先做到这吧 : )

如果本文对您有帮助,能否给个一键三连 (bushi)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇