angr學習[1]簡單加載程序分析,設置寄存器

0x01簡介
之前學了一些但是後來沒有跟進,慢慢忘了,最近找到一個比較好的學習github項目,系統地重新學一下

angr是一種模糊執行工具,不是太清楚怎麼做的但是比直接爆破要好很多就對了。
並且angr提供了一個強大的功能,可以加載分析人以平臺下的二進制文件。

原github項目地址
https://github.com/jakespringer/angr_ctf

下面的代碼會給出兩部分
1.官網給出的代碼,但是需要填寫一些內容。 我這裏會填上之後放進來
2.我自己跟着寫的。 這上面會加上我的理解

0x02環境搭建
需要pypy來運行generate.py 編譯出來二進制文件。
需要給pypy安裝pip 用來安裝庫 這東西好像跟python沒多大區別

python2
需要安裝angr python2 -m pip install angr

編譯命令 pypy generate.py 1 1


0x01簡單加載分析
github上並沒有給地址,這裏是我自己填上的,程序不同地址可能會不同

填上官網給的腳本的地址空缺

#coding=utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
# Before you begin, here are a few notes about these capture-the-flag
# challenges.
#
# Each binary, when run, will ask for a password, which can be entered via stdin
# (typing it into the console.) Many of the levels will accept many different
# passwords. Your goal is to find a single password that works for each binary.
#
# If you enter an incorrect password, the program will print "Try again." If you
# enter a correct password, the program will print "Good Job."
#
# Each challenge will be accompanied by a file like this one, named
# "scaffoldXX.py". It will offer guidance as well as the skeleton of a possible
# solution. You will have to edit each file. In some cases, you will have to
# edit it significantly. While use of these files is recommended, you can write
# a solution without them, if you find that they are too restrictive.
#
# Places in the scaffoldXX.py that require a simple substitution will be marked
# with three question marks (???). Places that require more code will be marked
# with an ellipsis (...). Comments will document any new concepts, but will be
# omitted for concepts that have already been covered (you will need to use
# previous scaffoldXX.py files as a reference to solve the challenges.) If a
# comment documents a part of the code that needs to be changed, it will be
# marked with an exclamation point at the end, on a separate line (!).

import angr
import sys

def main(argv):
  # Create an Angr project.
  # If you want to be able to point to the binary from the command line, you can
  # use argv[1] as the parameter. Then, you can run the script from the command
  # line as follows:
  # python ./scaffold00.py [binary]
  # (!)
  path_to_binary = "1"  # :string
  project = angr.Project(path_to_binary)

  # Tell Angr where to start executing (should it start from the main()
  # function or somewhere else?) For now, use the entry_state function
  # to instruct Angr to start from the main() function.
  initial_state = project.factory.entry_state()

  # Create a simulation manager initialized with the starting state. It provides
  # a number of useful tools to search and execute the binary.
  simulation = project.factory.simgr(initial_state)

  # Explore the binary to attempt to find the address that prints "Good Job."
  # You will have to find the address you want to find and insert it here. 
  # This function will keep executing until it either finds a solution or it 
  # has explored every possible path through the executable.
  # (!)
  print_good_address = 0x08048675  # :integer (probably in hexadecimal)
  simulation.explore(find=print_good_address)

  # Check that we have found a solution. The simulation.explore() method will
  # set simulation.found to a list of the states that it could find that reach
  # the instruction we asked it to search for. Remember, in Python, if a list
  # is empty, it will be evaluated as false, otherwise true.
  if simulation.found:
    # The explore method stops after it finds a single state that arrives at the
    # target address.
    solution_state = simulation.found[0]

    # Print the string that Angr wrote to stdin to follow solution_state. This 
    # is our solution.
    print solution_state.posix.dumps(sys.stdin.fileno())
  else:
    # If Angr could not find a path that reaches print_good_address, throw an
    # error. Perhaps you mistyped the print_good_address?
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

加上我的理解

# -*- coding:utf-8 -*-
#https://github.com/jakespringer/angr_ctf
"""
運行步驟:
    1.創建project載入要分析的文件
    2.告訴angr從哪裏加載
    3.告訴angr到哪裏結束,angr會探索這些結果
    4.判斷是否有想要的結果然後輸出

"""
import angr
import sys
reload(sys)
sys.setdefaultencoding('utf8')


def main(argv):
    path_to_binary = "1"  # :string
    project = angr.Project(path_to_binary)#要分析的二進制文件

    # 告訴ange從哪裏開始執行entry_state() 表示從main函數開始
    initial_state=project.factory.entry_state()

    #創建一個使用起始狀態初始化的模擬管理器。它提供
    #一些有用的工具來搜索和執行二進制文件。
    simulation=project.factory.simgr(initial_state)

    #函數會一直運行直到運行到0x08048675這個地質處,或者探索了每條可能路徑
    print_good_address = 0x08048675  # :integer (probably in hexadecimal)
    simulation.explore(find=print_good_address)
    #找到的方案會存放到simulation.found列表裏面(列表裏面存放的是地址)
    if simulation.found:
        solution_state=simulation.found[0]
        #用這種方式輸出內容
        print solution_state.posix.dumps(sys.stdin.fileno())
    else:
        raise Exception("Could not find the solution")
if __name__=="__main__":
    main(sys.argv)

0x02這裏添加了avoid用來排除錯誤的位置

git給出

import angr
import sys
reload(sys)
sys.setdefaultencoding('utf8')

def main(argv):
  path_to_binary = "1"
  project = angr.Project(path_to_binary)
  initial_state = project.factory.entry_state()
  simulation = project.factory.simgr(initial_state)

  # Explore the binary, but this time, instead of only looking for a state that
  # reaches the print_good_address, also find a state that does not reach 
  # will_not_succeed_address. The binary is pretty large, to save you some time,
  # everything you will need to look at is near the beginning of the address 
  # space.
  # (!)
  print_good_address = 0x080485E0
  will_not_succeed_address = 0x080485F2
  simulation.explore(find=print_good_address, avoid=will_not_succeed_address)

  if simulation.found:
    solution_state = simulation.found[0]
    print solution_state.posix.dumps(sys.stdin.fileno())
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

我的理解

# -*- coding:utf-8 -*-
import angr
import sys
reload(sys)
sys.setdefaultencoding("utf8")

def main(argv):

    project=angr.Project("1")
    initial_state=project.factory.entry_state()
    simulation=project.factory.simgr(initial_state)

    print_good_address=0x080485E0
    will_not_succeed_address=0x080485F2
    #這裏除了找得到一個正確的地方還要找到一個不正確的地方,這樣可以減少探索時間
    simulation.explore(find=print_good_address,avoid=will_not_succeed_address)

    if simulation.found:
        solution_astate=simulation.found[0]
        print solution_astate.posix.dumps(sys.stdin.fileno())
    else:
        raise Exception('Could not find the solution')

if __name__ == '__main__':
    main(sys.argv)

0x03如何避免在代碼裏面寫入地址
git給出

# It is very useful to be able to search for a state that reaches a certain
# instruction. However, in some cases, you may not know the address of the
# specific instruction you want to reach (or perhaps there is no single
# instruction goal.) In this challenge, you don't know which instruction
# grants you success. Instead, you just know that you want to find a state where
# the binary prints "Good Job."
#
# Angr is powerful in that it allows you to search for a states that meets an
# arbitrary condition that you specify in Python, using a predicate you define
# as a function that takes a state and returns True if you have found what you
# are looking for, and False otherwise.

import angr
import sys
reload(sys)
sys.setdefaultencoding('utf8')

def main(argv):
  path_to_binary = argv[1]
  project = angr.Project(path_to_binary)
  initial_state = project.factory.entry_state()
  simulation = project.factory.simgr(initial_state)

  # Define a function that checks if you have found the state you are looking
  # for.
  def is_successful(state):
    # Dump whatever has been printed out by the binary so far into a string.
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    #print(stdout_output)
    # Return whether 'Good Job.' has been printed yet.
    # (!)
    return 'Good Job.' in stdout_output# :boolean


  # Same as above, but this time check if the state should abort. If you return
  # False, Angr will continue to step the state. In this specific challenge, the
  # only time at which you will know you should abort is when the program prints
  # "Try again."
  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())

    return 'Try again.' in stdout_output


  # Tell Angr to explore the binary and find any state that is_successful identfies
  # as a successful state by returning True.
  simulation.explore(find=is_successful, avoid=should_abort)

  if simulation.found:
    solution_state = simulation.found[0]
    print solution_state.posix.dumps(sys.stdin.fileno())
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

我的理解

# -*- coding:utf-8 -*-

import angr
import sys
reload(sys)
sys.setdefaultencoding("utf8")

#和上一個不同這裏用is_successful和should_abort兩個bool型函數代替了原本的地址。
#也就是說simulation.explore()函數會接收find 和avoid的值,在find爲True的時候結束探索。 在avoid爲True的時候會重新開始新一輪探索

#使用條件是:有明確輸出正確或錯誤。
#優勢:省去了找兩個地址的時間,也會兼容更多編譯出來的代碼

def main(argv):
    project=angr.Project("1")
    initial_state=project.factory.entry_state()
    simulation=project.factory.simgr(initial_state)

    def is_successful(state):
        stdout_output=state.posix.dumps(sys.stdout.fileno())

        return "Good Job." in stdout_output

    def should_abort(state):
        stdout_output=state.posix.dumps(sys.stdout.fileno())
        return "Try agarn." in stdout_output

    simulation.explore(find=is_successful,avoid=should_abort)

    if simulation.found:
        solution_state=simulation.found[0]
        print solution_state.posix.dumps(sys.stdin.fileno())
    else:
        raise Exception("Could not find the  solution")

if __name__=='__main__':
    main(sys.argv)

0x04設置載入條件 —— 寄存器的值
填上git給出

# -*- coding:UTF-8 -*-
# Angr doesn't currently support reading multiple things with scanf (Ex:
# scanf("%u %u).) You will have to tell the simulation engine to begin the
# program after scanf is called and manually inject the symbols into registers.

import angr
import claripy
import sys
reload(sys)
sys.setdefaultencoding('utf8')

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

  # Sometimes, you want to specify where the program should start. The variable
  # start_address will specify where the symbolic execution engine should begin.
  # Note that we are using blank_state, not entry_state.
  # (!)
  start_address = 0x08048916  # :integer (probably hexadecimal)
  initial_state = project.factory.blank_state(addr=start_address)

  # Create a symbolic bitvector (the datatype Angr uses to inject symbolic
  # values into the binary.) The first parameter is just a name Angr uses
  # to reference it. 
  # You will have to construct multiple bitvectors. Copy the two lines below
  # and change the variable names. To figure out how many (and of what size)
  # you need, dissassemble the binary and determine the format parameter passed
  # to scanf.
  # (!)
  password0_size_in_bits = 32  # :integer

  password0 = claripy.BVS('password0', password0_size_in_bits)
  password1 = claripy.BVS('password1', password0_size_in_bits)
  password2 = claripy.BVS('password1', password0_size_in_bits)


  #...

  # Set a register to a symbolic value. This is one way to inject symbols into
  # the program.
  # initial_state.regs stores a number of convenient attributes that reference
  # registers by name. For example, to set eax to password0, use:
  #
  # initial_state.regs.eax = password0
  #
  # You will have to set multiple registers to distinct bitvectors. Copy and
  # paste the line below and change the register. To determine which registers
  # to inject which symbol, dissassemble the binary and look at the instructions
  # immediately following the call to scanf.
  # (!)

  initial_state.regs.eax = password0
  initial_state.regs.ebx = password1
  initial_state.regs.edx = password2
  #...

  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return "Good " in stdout_output

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return "Try" in stdout_output

  simulation.explore(find=is_successful, avoid=should_abort)

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

    # Solve for the symbolic values. If there are multiple solutions, we only
    # care about one, so we can use eval, which returns any (but only one)
    # solution. Pass eval the bitvector you want to solve for.
    # (!)
    solution0 = solution_state.se.eval(password0)
    solution1 = solution_state.se.eval(password1)
    solution2 = solution_state.se.eval(password2)

    # ...

    # Aggregate and format the solutions you computed above, and then print
    # the full string. Pay attention to the order of the integers, and the
    # expected base (decimal, octal, hexadecimal, etc).
    solution = str(solution0)+","+str(solution1)+","+str(solution2)  # :string
    print hex(solution0),hex(solution1),hex(solution2)
    print solution
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

添加我的理解

# -*- coding:utf-8 -*-
import angr
import claripy
import sys
reload(sys)
sys.setdefaultencoding("utf8")

#設置寄存器值

"""
angr只支持一次scanf,多次scanf需要繞過。
這裏用 claripy.BVS 生成32位的數字    因爲是32位寄存器所以需要32位

initial_state.regs.eax = password0   可以設置寄存器的值

solution0 = solution_state.se.eval(password0)  可以獲取解決方案,但是隻有一個位向量的



加載程序,設置開始地址,設置寄存器值(設置初始狀態)   設置正確和錯誤的結果探索   取出跑出來的結果    

"""

def main(argv):
    project=angr.Project("./1")

    #這個地址是從ida裏面找出來的,讀入了數據之後的值
    """
.text:08048911                 call    get_user_input
.text:08048916                 mov     [ebp+var_14], eax
.text:08048919                 mov     [ebp+var_10], ebx
.text:0804891C                 mov     [ebp+var_C], edx
    """
    start_address=0x08048916
    initial_state=project.factory.blank_state(addr=start_address) #用blank_state 來實現從某個地址處載入

    #因爲32位程序寄存器最多放32位
    password0_size_in_bits=32
    #用來生成位向量   這個東西應該是類似列表之類的東西
    password0=claripy.BVS('password0',password0_size_in_bits)
    password1 = claripy.BVS('password1', password0_size_in_bits)
    password2 = claripy.BVS('password2', password0_size_in_bits)

    #給寄存器賦值   ida裏面可以看到這三個寄存器是決定程序的值是否走到 good job的值
    initial_state.regs.eax=password0
    initial_state.regs.ebx = password1
    initial_state.regs.edx = password2

    simulation=project.factory.simgr(initial_state)

    def is_successful(state):
        stdout_output=state.posix.dumps(sys.stdout.fileno())
        return "Good" in stdout_output

    def should_abort(state):
        stdout_output=state.posix.dumps(sys.stdout.fileno())
        return "Try" in stdout_output

    simulation.explore(find=is_successful,avoid=should_abort)

    if simulation.found:
        solution_state=simulation.found[0]
        #找到了之後想要拿出來需要一個一個取  應該是字典之類的東東西
        solution0=solution_state.se.eval(password0)
        solution1=solution_state.se.eval(password1)
        solution2=solution_state.se.eval(password2)


        solution= str(solution0)+","+str(solution1)+","+str(solution2)
        print hex(solution0), hex(solution1), hex(solution2)
        print solution

    else:
        raise Exception("Could not find the solution")


if __name__ == '__main__':
    main(sys.argv)

接下來會持續更新這個再接再厲!

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