7 Nov 2019

angr从入门到放弃

angr从入门到放弃

angr-doc:https://github.com/angr/angr-doc

angr-api-doc:http://angr.io/api-doc/

基本操作

defcamp_r100

1

sub_4006F0检查password

2

4

希望执行到0x400844,不希望执行0x400855

p = angr.Project("r100")
simgr = p.factory.simulation_manager(p.factory.full_init_state())
simgr.explore(find=0x400844, avoid=0x400855)
print(simgr.found[0].posix.dumps(0))
#simgr.found[0].posix.dumps(0)代表该状态执行路径的所有输入
#simgr.found[0].posix.dumps(1)代表该状态执行路径的所有输出

3

defcon2016quals_baby-re

18

import angr
import claripy
proj = angr.Project('./baby-re', auto_load_libs=False)
state = proj.factory.entry_state(add_options={angr.options.LAZY_SOLVES})
sm = proj.factory.simulation_manager(state)
sm.explore(find=0x4028E9, avoid=0x402941)
print(sm.found[0].posix.dumps(0))
#0000000077000000009700000001160000000104000000003200000001050000000115000000003200000001040000000097000000011400000001000000000033
#Math is hard!

从命令行获取参数

ais3_crackme

6

verify

5

7

希望执行到0x400602,不希望执行0x40060e

使用claripy构造输入

claripy.BVS()创建位向量符号

claripy.BVV()创建位向量值

不能使用posix.dump(0)打印,使用found.solver.eval()获得符号执行结果

import angr
import claripy
p = angr.Project("./ais3_crackme")
argv1 = claripy.BVS("argv1",100*8)
state = p.factory.entry_state(args=["./crackme1",argv1])
simgr = p.factory.simulation_manager(state)
simgr.explore(find=0x400602,avoid=0x40060E)
solution = simgr.found[0].solver.eval(argv1, cast_to=bytes)
print(solution)

8

设置约束条件

csaw_wyvern

9

flag长度28,使用claripy构造28个变量,拼起来最后加上\n

flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(28)]
flag = claripy.Concat(*flag_chars + [claripy.BVV(b'\n')])

这是个C++程序,而angr只实现了C库,所以需要使用full_init_state方法和unicorn引擎

st = p.factory.full_init_state(
        args=['./wyvern'],
        add_options=angr.options.unicorn,
        stdin=flag,
)

设置条件约束,flag中没有\x00和\x0a

for k in flag_chars:
    st.solver.add(k != 0)
    st.solver.add(k != 10)
import angr
import claripy


p = angr.Project('wyvern')
flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(28)]
flag = claripy.Concat(*flag_chars + [claripy.BVV(b'\n')])
st = p.factory.full_init_state(
        args=['./wyvern'],
        add_options=angr.options.unicorn,
        stdin=flag,
)

for k in flag_chars:
    st.solver.add(k != 0)
    st.solver.add(k != 10)


sm = p.factory.simulation_manager(st)
sm.explore(find=0x40E02C)
print(sm.found[0].posix.dumps(0))

10

对结果进行约束

asisctffinals2015_fake

11

输入正确的值,输出flag

13

strtol将字符串转为整数,claripy构造的位向量符号无法转化,所以从0x4004AC开始,直接使rax为构造的位向量符号

state = p.factory.blank_state(addr=0x4004AC)
inp = state.solver.BVS('inp', 8*8)
state.regs.rax = inp

flag以ASIS{开头,}结尾

执行到0x400684时,flag在rdi中

对found状态下的rdi进行约束

flag_addr = found.regs.rdi
found.add_constraints(found.memory.load(flag_addr, 5) == int(binascii.hexlify(b"ASIS{"), 16))

flag = found.memory.load(flag_addr, 40)
for i in range(5, 5+32):
    cond_0 = flag.get_byte(i) >= ord('0')
    cond_1 = flag.get_byte(i) <= ord('9')
    cond_2 = flag.get_byte(i) >= ord('a')
    cond_3 = flag.get_byte(i) <= ord('f')
    cond_4 = found.solver.And(cond_0, cond_1)
    cond_5 = found.solver.And(cond_2, cond_3)
    found.add_constraints(found.solver.Or(cond_4, cond_5))

flag是v5,v6,v7,v8,v9,最多40字节,这个38我寻思能试出来

我一直在想怎么分析出来flag套着的是个16进制串,翻了一波这比赛其他的题,flag都是这格式

import angr
import binascii

p = angr.Project("fake", auto_load_libs=False)

state = p.factory.blank_state(addr=0x4004AC)
inp = state.solver.BVS('inp', 8*8)
state.regs.rax = inp

simgr= p.factory.simulation_manager(state)
simgr.explore(find=0x400684)
found = simgr.found[0]

flag_addr = found.regs.rdi
found.add_constraints(found.memory.load(flag_addr, 5) == int(binascii.hexlify(b"ASIS{"), 16))

flag = found.memory.load(flag_addr, 40)
for i in range(5, 5+32):
    cond_0 = flag.get_byte(i) >= ord('0')
    cond_1 = flag.get_byte(i) <= ord('9')
    cond_2 = flag.get_byte(i) >= ord('a')
    cond_3 = flag.get_byte(i) <= ord('f')
    cond_4 = found.solver.And(cond_0, cond_1)
    cond_5 = found.solver.And(cond_2, cond_3)
    found.add_constraints(found.solver.Or(cond_4, cond_5))

found.add_constraints(flag.get_byte(32+5) == ord('}'))

flag_str = found.solver.eval(flag, cast_to=bytes)
#num = found.solver.eval(inp)
#pirnt(num)
print(flag_str)

12

hook

defcamp_r100

import angr
project = angr.Project('r100', auto_load_libs=False)
@project.hook(0x400844)
def print_flag(state):
    print(state.posix.dumps(0))
    project.terminate_execution()
project.execute()

hook 0x400844,project.execute()开始执行,执行到0x400844时执行print_flag,遇到terminate_execution()时结束

14

hook符号表

tumctf2016_zwiebel

15

self-ModifyingCode

有ptrace反调试,需要绕一下

//ptrace_disable.c
int ptrace(int request, int pid, int addr, int data){
    return 0;
}
//gcc -shared -fPIC  -o ptrace_disable.so ptrace_disable.c

进gdb

pwndbg> set environment LD_PRELOAD=./ptrace_disable.so
pwndbg> break main

16

pwndbg> break *0x400875

17

si进去

pwndbg> si
pwndbg> x/128gi 0x7fffff790000
=> 0x7fffff790000:      lea    esi,[rsi+0x0]
   0x7fffff790006:      lea    esi,[rsi+riz*1+0x0]
   0x7fffff79000a:      lea    esi,[rsi+riz*1+0x0]
   0x7fffff790011:      lea    edi,[rdi+riz*1+0x0]
   0x7fffff790018:      mov    rax,rbx
   0x7fffff79001b:      mov    al,BYTE PTR [rax+0x0]
   0x7fffff79001e:      and    al,0x40
   0x7fffff790020:      je     0x7fffff790038
   0x7fffff790022:      lea    rsi,[rip+0x34]        # 0x7fffff79005d
   0x7fffff790029:      lods   eax,DWORD PTR ds:[rsi]
   0x7fffff79002a:      mov    rcx,rax
   0x7fffff79002d:      lods   eax,DWORD PTR ds:[rsi]
   0x7fffff79002e:      xor    DWORD PTR [rsi],eax
   0x7fffff790030:      add    rsi,0x4
   0x7fffff790034:      loop   0x7fffff79002e
   0x7fffff790036:      jmp    0x7fffff790065
   0x7fffff790038:      push   0xa283a
   0x7fffff79003d:      mov    eax,0x1
   0x7fffff790042:      mov    edi,0x0
   0x7fffff790047:      mov    rsi,rsp
   0x7fffff79004a:      mov    edx,0x3
   0x7fffff79004f:      syscall
   0x7fffff790051:      mov    eax,0x3c
   0x7fffff790056:      mov    edi,0x1
   0x7fffff79005b:      syscall
   0x7fffff79005d:      or     dl,BYTE PTR [rbx+0x28eb0000]
   0x7fffff790063:      rex.RX cmp al,0x66
   0x7fffff790066:      pop    rsp
   0x7fffff790067:      (bad)
   0x7fffff790068:      cmp    al,0xa3
   0x7fffff79006a:      movabs eax,ds:0x3e9f3e6235abb69e
   0x7fffff790073:      (bad)
   0x7fffff790074:      mov    cl,0xde
   0x7fffff790076:      sbb    al,0x46
   0x7fffff790078:      cmp    al,0xeb
   0x7fffff79007a:      test   DWORD PTR [rsi],ecx
   0x7fffff79007c:      mov    ch,0x2a
   0x7fffff79007e:      test   DWORD PTR [rdi+0x3a],esi
   0x7fffff790081:      movabs ds:0x8311add0093880ab,eax
   0x7fffff79008a:      adc    ch,BYTE PTR [rsi+0x36]
   0x7fffff79008d:      jmp    0x7fffff79001f
   0x7fffff79008f:      rex.RXB cmp al,0xeb
   0x7fffff790092:      sub    cl,bh
   0x7fffff790094:      cmp    al,0xeb
   0x7fffff790096:      sub    BYTE PTR [rsi+0x74],al
   0x7fffff790099:      (bad)
   0x7fffff79009a:      (bad)
   0x7fffff79009b:      cld
   0x7fffff79009c:      (bad)
   0x7fffff79009d:      jmp    0x7fffff7900c7
   0x7fffff79009f:      rex.RX xor r13d,esi
   0x7fffff7900a2:      nop
   0x7fffff7900a3:      jp     0x7fffff7900e1
   0x7fffff7900a5:      jmp    0x7fffff7900cf
   0x7fffff7900a7:      stc
   0x7fffff7900a8:      cmp    eax,0x334628eb
   0x7fffff7900ad:      out    dx,al
   0x7fffff7900ae:      fst    st(4)
   0x7fffff7900b0:      cmp    al,0xeb
   0x7fffff7900b2:      mov    ebp,0x30e00501
   0x7fffff7900b7:      mov    bh,0x5
   0x7fffff7900b9:      loopne 0x7fffff790078
   0x7fffff7900bb:      add    DWORD PTR [rax+0x5019a5c],ecx
   0x7fffff7900c1:      loopne 0x7fffff790080
   0x7fffff7900c3:      mov    WORD PTR [rcx-0x3a],?
   0x7fffff7900c6:      mov    ebp,0xbdc6b18c
   0x7fffff7900cb:      add    DWORD PTR [rip+0xffffffffdd88f5e0],eax        # 0x7fffdd01f6b1
   0x7fffff7900d1:      push   0xfffffffffffffffd
   0x7fffff7900d3:      adc    al,0x21
   0x7fffff7900d5:      lock leave
   0x7fffff7900d7:      (bad)
   0x7fffff7900d8:      rex.WRB ins DWORD PTR es:[rdi],dx
   0x7fffff7900da:      mov    BYTE PTR [rip+0xffffffffacbde005],dh        # 0x7fffac36e0e5
   0x7fffff7900e0:      imul   r15,QWORD PTR [r12+rbp*4+0x34],0xffffffffc382f5e6
   0x7fffff7900e9:      in     al,0x5f
   0x7fffff7900eb:      stc
   0x7fffff7900ec:      out    dx,al
   0x7fffff7900ed:      int    0xd5
   0x7fffff7900ef:      cmp    ebp,DWORD PTR [rip+0x4b9bdea]        # 0x80000432bedf
   0x7fffff7900f5:      loopne 0x7fffff7900b4
   0x7fffff7900f7:      add    DWORD PTR [rdx+0x501bde0],edi
   0x7fffff7900fd:      test   al,0x34
   0x7fffff7900ff:      out    0xbf,eax
   0x7fffff790101:      jrcxz  0x7fffff7900c0
   0x7fffff790103:      add    DWORD PTR [rip+0x39b9b8ef],eax        # 0x80003932b9f8
   0x7fffff790109:      loopne 0x7fffff7900c8
   0x7fffff79010b:      add    DWORD PTR [rdx+0x501bde1],edi
   0x7fffff790111:      out    dx,eax
   0x7fffff790112:      mov    eax,0xbde097da
   0x7fffff790117:      jrcxz  0x7fffff7900fc
   0x7fffff790119:      rcl    DWORD PTR [rcx+0x6e],1
   0x7fffff79011c:      xchg   ebp,eax
   0x7fffff79011d:      rcr    esp,1
   0x7fffff79011f:      pop    rdi

使用angr跑完所有分支,可以得到正确输入

添加support_selfmodifying_code=True参数使符号执行使可以selfmodifying

使用hook绕过ptrace

p = angr.Project("zwiebel", support_selfmodifying_code=True)
p.hook_symbol('ptrace', angr.SIM_PROCEDURES['stubs']['ReturnUnconstrained'](return_value=0))

angr.SIM_PROCEDURES是angr对符号化函数的字典调用,angr.SIM_PROCEDURES[模块][库函数]()

import angr

def main():

    # Uncomment the following two lines if you want to have logging output from
    # SimulationManager
    # import logging
    # logging.getLogger('angr.manager').setLevel(logging.DEBUG)

    p = angr.Project("zwiebel", support_selfmodifying_code=True) # this is important! this binary unpacks its code
    p.hook_symbol('ptrace', angr.SIM_PROCEDURES['stubs']['ReturnUnconstrained'](return_value=0))

    # unicorn support makes execution, especially code unpacking, way faster
    state = p.factory.full_init_state(add_options=angr.options.unicorn)
    sm = p.factory.simulation_manager(state)

    while sm.active:
        # in order to save memory, we only keep the recent 20 deadended or
        # errored states
        #print(len(sm.active))
        sm.run(n=20)
        if 'deadended' in sm.stashes and sm.deadended:
            sm.stashes['deadended'] = sm.deadended[-20:]
        if sm.errored:
            sm.errored = sm.errored[-20:]

    assert sm.deadended
    flag = sm.deadended[-1].posix.dumps(0).split(b"\n")[0]
    import ipdb; ipdb.set_trace()
    return flag

def test():
    flag = main()
    assert flag.startswith(b'hxp{1_h0p3_y0u_d1dnt_p33l_th3_0ni0n_by_h4nd}')

if __name__ == "__main__":
    print(main())

# Here is the output (after 2 hours and 31 minutes on my machine running Pypy):
# 
# ipdb> print(sm)
# <PathGroup with 20 errored, 21 deadended>
# ipdb> print(sm.deadended[-1])
# <Path with 160170 runs (at 0x20001e0)>
# ipdb> print(sm.deadended[-1].state.posix.dumps(0))
# hxp{1_h0p3_y0u_d1dnt_p33l_th3_0ni0n_by_h4nd}
# :)

每次跑20个,正确的输入在最后一个deadended

没想明白为啥,我寻思还是验一下deadended里面有没有flag,没有就扔


Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).