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)
接下來會持續更新這個再接再厲!