angr符號執行用例解析——0ctf_momo_3


源碼及二進制文件鏈接:https://github.com/angr/angr-doc/tree/master/examples/0ctf_momo_3

該題目來自於2016年0ctf比賽的被混淆過的二進制文件。在使用angr進行分析前,作者通過調試工具確定了flag的生成方式。flag獲取代碼爲:.text:0804ABAC   mov  edx, dword_81FE260[edx*4]。隨後,我們對輸入進行了變換,並尋找與Qira的不同點。我們發現0x81fe6e0和0x81fe6e4內容一樣時即可。因爲movfuscator混淆後 “vm”訪問內存總是一樣的,所以我們可以通過尋找相同指令(相同順序)來確定angr執行的目標。

程序首先將momo文件加載進來;
然後計算了要分析的起始地址和大小;
通過loader.memory.read_bytes方法將要分析的數據讀取出來;
使用capstone框架對這段數據反彙編;
初始化一個capstoneblock;
在這段block中通過指令的字符串與順序匹配來尋找分析的目標地址targets;
指令的順序爲:
edx, dword ptr [edx*4 + 0x81fe260]
al, byte ptr [0x81fe6e0]
dl, byte ptr [0x81fe6e4]
發現一個記錄一個targets;
打印出發現的targets地址數目;
對於每一個target進行分析,分析內容如下:
初始化一個flag_arr=“0ctf{”
然後從string工具類中取出一個char,與剛剛的flag_arr拼接得到flag;
獲取程序入口狀態entry_state;
entry_state的輸入爲flag;
從這個狀態起explore,尋找所有包含target地址的路徑(按照順序來說,找到一個就返回了,所以滿足的路徑應該只有一個);
以這個路徑第一個狀態爲起始,執行到target,比較寄存器ax和dx的值,如果相等則說明這個trychar是對的,則在flag_arr後面添加這個字符;
直到所有的target都遍歷完成,就得到了正確的flag。

個人總結:
這個程序分析的過程僅利用了angr中反彙編,寄存器訪問,路徑探索explore過濾,模擬執行successors等功能。沒有用到符號執行,與約束求解器。而且抓取flag的思路實在是暴力,相當於依次暴力破解每個字符,找到滿足要求的字符後,再去遍歷下一位。
分析起來是真的慢。

#!/usr/bin/env python

# This is a movfuscated binary from 0ctf 2016, debugging we can see
# that every byte from the flag gets moved this way:
# .text:0804ABAC                 mov     edx, dword_81FE260[edx*4]
# after, varying a few times the input and looking for differences
# with Qira we can see that it is expected for the content of
# 0x81fe6e0 and 0x81fe6e4 to be the same
# since the way the movfuscator "vm" access memory is always the same
# we can search for the same instructions (in the same order) to
# establish the targets for angr execution

import sys
import string
import angr
from angr.block import CapstoneInsn, CapstoneBlock


ins_char = 0x81fe6e0
flag_char = 0x81fe6e4

after_fgets = 0x08049653
mov_congrats = 0x0805356E


def main():
    p = angr.Project('./momo', load_options={'auto_load_libs': False}) #load the binary files

    addr = after_fgets  
    size = mov_congrats - after_fgets

    # let's disasm with capstone to search targets
    insn_bytes = ''.join(
        p.loader.memory.read_bytes(addr, size))

    insns = []
    for cs_insn in p.arch.capstone.disasm(insn_bytes, addr):
        insns.append(CapstoneInsn(cs_insn))
    block = CapstoneBlock(addr, insns, 0, p.arch)

    targets = []

    # let's keep track of the state
    state = 0
    for ins in block.insns:
        if state == 0:
            if ins.op_str == 'edx, dword ptr [edx*4 + 0x81fe260]':
                state += 1
                continue
        if state == 1:
            if ins.op_str == 'al, byte ptr [0x81fe6e0]':
                state += 1
                continue
        if state == 2:
            if ins.op_str == 'dl, byte ptr [0x81fe6e4]':
                targets.append(ins.address + ins.size) #targets are addrs
                state = 0

    print "found {:d} targets".format(len(targets))
    assert len(targets) == 28

    flag_arr = ['0', 'c', 't', 'f', '{']

    for target in targets[5:]:
        print "\nexamining target {:#x}:".format(target)
        for trychar in string.printable:
            print trychar,
            sys.stdout.flush()
            flag = ''.join(flag_arr)+trychar
            state = p.factory.entry_state()
            state.posix.files[0].content.store(0, flag + "\n") #have no idea about posix.files

            e = p.surveyors.Explorer(start=state, find=(target,))#find target states from entry state
            e.run()

            assert len(e.found) == 1
            np = e.found[0] #find the first state with target

            while(True):
                nb_size = target - np.addr
                if nb_size <= 0:
                    break
                np = p.factory.successors(np, size=nb_size).flat_successors[0]
            assert nb_size == 0

            al = np.regs.eax[7:0]
            dl = np.regs.edx[7:0]
            al_val = al._model_concrete.value
            dl_val = dl._model_concrete.value

            if al_val == dl_val:
                flag_arr.append(trychar)
                break

    return ''.join(flag_arr)


def test():
    assert main() == '0ctf{m0V_I5_tUr1N9_c0P1Et3!}'

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章