angr使用[3]--使用技巧

0x08對探索結果添加限制

#跳過一些函數來求解

import angr
import claripy
import sys
import binascii

def main(argv):
  path_to_binary = "08_angr_constraints"
  project = angr.Project(path_to_binary)

  start_address = 0x8048625
  initial_state = project.factory.blank_state(addr=start_address)

  password = claripy.BVS('password', 0x10*8)

  password_address = 0x0804A050
  initial_state.memory.store(password_address, password)

  simulation = project.factory.simgr(initial_state)

  # Angr will not be able to reach the point at which the binary prints out
  # 'Good Job.'. We cannot use that as the target anymore.
  # (!)
  address_to_check_constraint = 0x0804866C
  simulation.explore(find=address_to_check_constraint)  #先讓程序運行到比較之前

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

    # Recall that we need to constrain the to_check parameter (see top) of the 
    # check_equals_ function. Determine the address that is being passed as the
    # parameter and load it into a bitvector so that we can constrain it.
    # (!)
    constrained_parameter_address = 0x0804A050        #獲取對應內存區域在運行到這裏的時候的值
    constrained_parameter_size_bytes = 0x10
    constrained_parameter_bitvector = solution_state.memory.load(
      constrained_parameter_address,
      constrained_parameter_size_bytes
    )

    # We want to constrain the system to find an input that will make
    # constrained_parameter_bitvector equal the desired value.
    # (!)
    constrained_parameter_desired_value = "MRXJKZYRKMKENFZB" # :string   約束這些數據得到符合要求的

    # Specify a claripy expression (using Pythonic syntax) that tests whether
    # constrained_parameter_bitvector == constrained_parameter_desired_value.
    # We will let z3 attempt to find an input that will make this expression
    # true.
    constraint_expression = constrained_parameter_bitvector == constrained_parameter_desired_value
    
    # Add the constraint to the state to instruct z3 to include it when solving
    # for input.
    solution_state.add_constraints(constraint_expression)#添加約束條件

    # Solve for the constrained_parameter_bitvector.
    solution = solution_state.se.eval(password)                                 #求解得到滿足上述條件的輸入
    solution="".join(map("{:x}".format,[solution]))

    print(binascii.a2b_hex(solution))
  else:
    raise Exception('Could not find the solution')

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

# solution_state.add_constraints(constrained_parameter_bitvector == constrained_parameter_desired_value)獲取到走到這裏的好多數據中滿足條件的輸入

'''
    constrained_parameter_bitvector = solution_state.memory.load(
      constrained_parameter_address,
      constrained_parameter_size_bytes
    )
    獲取一個地址處運行到這個時候的值
'''

核心代碼添加求解約束條件,運行到這裏已經得到了所有的結果,加上這個條件進一步過濾結果

  constraint_expression = constrained_parameter_bitvector == constrained_parameter_desired_value
    
    # Add the constraint to the state to instruct z3 to include it when solving
    # for input.
    solution_state.add_constraints(constraint_expression)#添加約束條件

0x09對一個地址hook

# This level performs the following computations:
#
# 1. Get 16 bytes of user input and encrypt it.
# 2. Save the result of check_equals_AABBCCDDEEFFGGHH (or similar)
# 3. Get another 16 bytes from the user and encrypt it.
# 4. Check that it's equal to a predefined password.
#
# The ONLY part of this program that we have to worry about is #2. We will be
# replacing the call to check_equals_ with our own version, using a hook, since
# check_equals_ will run too slowly otherwise.

'''
    claripy.If(條件,條件爲True時的返回值,條件爲False時的返回值) => 創建條件判斷

    claripy.BVV(值,值大小) => 創建一個數值


'''


import angr
import claripy
import sys

def main(argv):
  path_to_binary = "09_angr_hooks"
  project = angr.Project(path_to_binary)

  # Since Angr can handle the initial call to scanf, we can start from the
  # beginning.
  initial_state = project.factory.entry_state()

  # Hook the address of where check_equals_ is called.
  # (!)
  check_equals_called_address = 0x80486B3        #這個地址是call的地址參數沒有改變,只改變call

  # The length parameter in angr.Hook specifies how many bytes the execution
  # engine should skip after completing the hook. This will allow hooks to
  # replace certain instructions (or groups of instructions). Determine the
  # instructions involved in calling check_equals_, and then determine how many
  # bytes are used to represent them in memory. This will be the skip length.
  # (!)
  instruction_to_skip_length = 0x05     #不同系統下call指令的大小不同
  @project.hook(check_equals_called_address, length=instruction_to_skip_length)
  def skip_check_equals_(state):      #hook成這個函數
    # Determine the address where user input is stored. It is passed as a
    # parameter ot the check_equals_ function. Then, load the string. Reminder:
    # int check_equals_(char* to_check, int length) { ...
    user_input_buffer_address = 0x0804A054 # :integer, probably hexadecimal
    user_input_buffer_length = 0x10

    # Reminder: state.memory.load will read the stored value at the address
    # user_input_buffer_address of byte length user_input_buffer_length.
    # It will return a bitvector holding the value. This value can either be
    # symbolic or concrete, depending on what was stored there in the program.
    user_input_string = state.memory.load(       #加載這個地址處的值
      user_input_buffer_address, 
      user_input_buffer_length
    )
    
    # Determine the string this function is checking the user input against.
    # It's encoded in the name of this function; decompile the program to find
    # it.
    check_against_string = 'MRXJKZYRKMKENFZB'# :string

  #因爲check_equals_MRXJKZYRKMKENFZB函數的返回值放在eax寄存器裏面
    state.regs.eax = claripy.If(
      user_input_string == check_against_string, 
      claripy.BVV(1, 32), 
      claripy.BVV(0, 32)
    )

  simulation = project.factory.simgr(initial_state)

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

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

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

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

    # Since we are allowing Angr to handle the input, retrieve it by printing
    # the contents of stdin. Use one of the early levels as a reference.
    solution = solution_state.posix.dumps(sys.stdin.fileno())
    print (solution)
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)
#用python 自定義函數來hook程序的函數跳過一些已知函數
#solution = solution_state.posix.dumps(sys.stdin.fileno()) 獲取滿足條件的格式化輸入的數據

核心代碼

 check_equals_called_address = 0x80486B3        #這個地址是call的地址參數沒有改變,只改變call

  instruction_to_skip_length = 0x05     #不同系統下call指令的大小不同
  @project.hook(check_equals_called_address, length=instruction_to_skip_length)
  def skip_check_equals_(state):      #hook成這個函數
  
  check_equals_called_address爲要hook的地址
  def skip_check_equals_(state): 這個函數會代替這個地址處原來的函數
  claripy.If(條件,條件爲True時的返回值,條件爲False時的返回值) => 創建條件判斷

    claripy.BVV(值,值大小) => 創建一個數值


state.regs.eax = claripy.If(
      user_input_string == check_against_string, 
      claripy.BVV(1, 32), 
      claripy.BVV(0, 32)
    )

0x10通過函數名字hook

# This challenge is similar to the previous one. It operates under the same
# premise that you will have to replace the check_equals_ function. In this 
# case, however, check_equals_ is called so many times that it wouldn't make 
# sense to hook where each one was called. Instead, use a SimProcedure to write
# your own check_equals_ implementation and then hook the check_equals_ symbol 
# to replace all calls to scanf with a call to your SimProcedure.
#
# You may be thinking: 
#   Why can't I just use hooks? The function is called many times, but if I hook
#   the address of the function itself (rather than the addresses where it is 
#   called), I can replace its behavior everywhere. Furthermore, I can get the
#   parameters by reading them off the stack (with memory.load(regs.esp + xx)),
#   and return a value by simply setting eax! Since I know the length of the 
#   function in bytes, I can return from the hook just before the 'ret'
#   instruction is called, which will allow the program to jump back to where it
#   was before it called my hook.
# If you thought that, then congratulations! You have just invented the idea of
# SimProcedures! Instead of doing all of that by hand, you can let the already-
# implemented SimProcedures do the boring work for you so that you can focus on
# writing a replacement function in a Pythonic way.
# As a bonus, SimProcedures allow you to specify custom calling conventions, but
# unfortunately it is not covered in this CTF.

import angr
import claripy
import sys

def main(argv):
  path_to_binary = "10_angr_simprocedures"
  project = angr.Project(path_to_binary)

  initial_state = project.factory.entry_state()

  # Define a class that inherits angr.SimProcedure in order to take advantage
  # of Angr's SimProcedures.
  class ReplacementCheckEquals(angr.SimProcedure):

    def run(self, to_check,check_data_length):       #to_check,check_data_length代表着被hook的函數的兩個參數
      # We can almost copy and paste the solution from the previous challenge.
      # Hint: Don't look up the address! It's passed as a parameter.
      # (!)
      user_input_buffer_address = to_check
      user_input_buffer_length = check_data_length

      # Note the use of self.state to find the state of the system in a 
      # SimProcedure.
      user_input_string = self.state.memory.load(
        user_input_buffer_address,
        user_input_buffer_length
      )

      check_against_string = 'MRXJKZYRKMKENFZB'
      
      # Finally, instead of setting eax, we can use a Pythonic return statement
      # to return the output of this function. 
      # Hint: Look at the previous solution.
      return claripy.If(
        check_against_string==user_input_string,
        claripy.BVV(1,32),
        claripy.BVV(0,32)
      )


  # Hook the check_equals symbol. Angr automatically looks up the address 
  # associated with the symbol. Alternatively, you can use 'hook' instead
  # of 'hook_symbol' and specify the address of the function. To find the 
  # correct symbol, disassemble the binary.
  # (!)
  check_equals_symbol = 'check_equals_MRXJKZYRKMKENFZB' # :string   要hook的函數名字
  project.hook_symbol(check_equals_symbol, ReplacementCheckEquals())    #僅僅指定函數名字和設置參數獲取的變量,能自動獲取到參數和函數
#和9不同這個二進制文件有很多call check_equals_MRXJKZYRKMKENFZB指令  不知道要hook哪一個
  simulation = project.factory.simgr(initial_state)

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

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

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

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

    solution = solution_state.posix.dumps(sys.stdin.fileno())
    print (solution)
  else:
    raise Exception('Could not find the solution')

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

核心代碼
通過函數名hook一堆函數,並且獲取這些函數的參數

class ReplacementCheckEquals(angr.SimProcedure):

    def run(self, to_check,check_data_length):       #to_check,check_data_length代表着被hook的函數的兩個參數
 project.hook_symbol(check_equals_symbol, ReplacementCheckEquals())    #僅僅指定函數名字和設置參數獲取的變量,能自動獲取到參數和函數
#和9不同這個二進制文件有很多call check_equals_MRXJKZYRKMKENFZB指令  不知道要hook哪一個

0x11hook scanf函數,聲明最終要解析的變量

# This time, the solution involves simply replacing scanf with our own version,
# since Angr does not support requesting multiple parameters with scanf.

import angr
import claripy
import sys
import binascii

def main(argv):
  path_to_binary = "11_angr_sim_scanf"
  project = angr.Project(path_to_binary)

  initial_state = project.factory.entry_state()

  class ReplacementScanf(angr.SimProcedure):
    # Finish the parameters to the scanf function. Hint: 'scanf("%u %u", ...)'.
    # (!)
    def run(self, format_string, scanf0_address, scanf1_address):
      scanf0 = claripy.BVS('scanf0', 4*8)
      scanf1 = claripy.BVS('scanf1', 4 * 8)

      # The scanf function writes user input to the buffers to which the 
      # parameters point.
      self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness)
      self.state.memory.store(scanf1_address, scanf1, endness=project.arch.memory_endness)

      # Now, we want to 'set aside' references to our symbolic values in the
      # globals plugin included by default with a state. You will need to
      # store multiple bitvectors. You can either use a list, tuple, or multiple
      # keys to reference the different bitvectors.
      # (!)
      self.state.globals['solution0'] = scanf0
      self.state.globals['solution1'] = scanf1

  scanf_symbol = "__isoc99_scanf"
  project.hook_symbol(scanf_symbol, ReplacementScanf())

  simulation = project.factory.simgr(initial_state)

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

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

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

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

    # Grab whatever you set aside in the globals dict.
    stored_solutions0 = solution_state.globals['solution0']
    stored_solutions1 = solution_state.globals['solution1']
    solution0 = solution_state.se.eval(stored_solutions0)
    solution1 = solution_state.se.eval(stored_solutions1)

    solution = "".join(map("{:x}".format,[solution0,solution1]))

    print (binascii.a2b_hex(solution))
  else:
    raise Exception('Could not find the solution')

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


'''
stored_solutions1 = solution_state.globals['solution1']
solution0 = solution_state.se.eval(stored_solutions0)

獲取全局變量
self.state.globals['solution1'] = scanf1
這樣聲明全局變量
'''
  

核心代碼
生成二位向量,把變量放入內存,聲明這個變量最終求解時用到

     scanf0 = claripy.BVS('scanf0', 4*8)
      scanf1 = claripy.BVS('scanf1', 4 * 8)

      # The scanf function writes user input to the buffers to which the 
      # parameters point.
      self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness)
      self.state.memory.store(scanf1_address, scanf1, endness=project.arch.memory_endness)

      # Now, we want to 'set aside' references to our symbolic values in the
      # globals plugin included by default with a state. You will need to
      # store multiple bitvectors. You can either use a list, tuple, or multiple
      # keys to reference the different bitvectors.
      # (!)
      self.state.globals['solution0'] = scanf0
      self.state.globals['solution1'] = scanf1

求解聲明的變量
stored_solutions0 = solution_state.globals['solution0']
solution0 = solution_state.se.eval(stored_solutions0)

0x12對比較長的變量求解

import angr
import sys
def main(argv):
    path_to_binary="12_angr_veritesting"
    project=angr.Project(path_to_binary)
    initial_state=project.factory.entry_state()
    simulation=project.factory.simgr(initial_state,veritesting=True)
    def is_successful(state):
        stdout_output=state.posix.dumps(sys.stdout.fileno())
        return 'Good Job'.encode() in stdout_output
    def should_abord(state):
        should_output=state.posix.dumps(sys.stdout.fileno())
        return 'Try again'.encode() in should_output

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

    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)

核心代碼
用veritesting=True選項來解決比較長的變量求解

simulation=project.factory.simgr(initial_state,veritesting=True)

0x04針對自定義的庫函數求解
把函數hook掉(如果不hook會很難求解)

# This challenge is the exact same as the first challenge, except that it was
# compiled as a static binary. Normally, Angr automatically replaces standard
# library functions with SimProcedures that work much more quickly.
#
# Here are a few SimProcedures Angr has already written for you. They implement
# standard library functions. You will not need all of them:
# angr.SIM_PROCEDURES['libc']['malloc']
# angr.SIM_PROCEDURES['libc']['fopen']
# angr.SIM_PROCEDURES['libc']['fclose']
# angr.SIM_PROCEDURES['libc']['fwrite']
# angr.SIM_PROCEDURES['libc']['getchar']
# angr.SIM_PROCEDURES['libc']['strncmp']
# angr.SIM_PROCEDURES['libc']['strcmp']
# angr.SIM_PROCEDURES['libc']['scanf']
# angr.SIM_PROCEDURES['libc']['printf']
# angr.SIM_PROCEDURES['libc']['puts']
# angr.SIM_PROCEDURES['libc']['exit']
#
# As a reminder, you can hook functions with something similar to:
# project.hook(malloc_address, angr.SIM_PROCEDURES['libc']['malloc'])
#
# There are many more, see:
# https://github.com/angr/angr/tree/master/angr/procedures/libc
#
# Additionally, note that, when the binary is executed, the main function is not
# the first piece of code called. In the _start function, __libc_start_main is 
# called to start your program. The initialization that occurs in this function
# can take a long time with Angr, so you should replace it with a SimProcedure.
# angr.SIM_PROCEDURES['glibc']['__libc_start_main']
# Note 'glibc' instead of 'libc'.
import angr
import sys
def main(argv):
    path_to_binary="13_angr_static_binary"
    project=angr.Project(path_to_binary)
    initial_state=project.factory.entry_state()
    simulation=project.factory.simgr(initial_state,veritesting=True)


    project.hook(0x804ed40,angr.SIM_PROCEDURES['libc']['printf']())
    project.hook(0x804ed80, angr.SIM_PROCEDURES['libc']['scanf']())
    project.hook(0x804f350, angr.SIM_PROCEDURES['libc']['puts']())
    project.hook(0x8048d10, angr.SIM_PROCEDURES['glibc']['__libc_start_main']())

    def is_successful(state):
        stdout_output=state.posix.dumps(sys.stdout.fileno())
        return 'Good Job'.encode() in stdout_output
    def should_abord(state):
        should_output=state.posix.dumps(sys.stdout.fileno())
        return 'Try again'.encode() in should_output

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

    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)

# project.hook(0x804ed40,angr.SIM_PROCEDURES['libc']['printf']())
#獲取angr內部實現的函數

angr已經實現了系統函數,想hook很簡單
不過這裏好像只需要hook輸入輸出函數就行了還有這個函數__libc_start_main

用來hook系統函數,注意庫函數不同,這個地址爲函數被call過之後的開始

    project.hook(0x804f350, angr.SIM_PROCEDURES['libc']['puts']())
    project.hook(0x8048d10, angr.SIM_PROCEDURES['glibc']['__libc_start_main']())

0x14分析動態鏈接庫

import angr
import claripy
import sys
import binascii

def main(argv):
  path_to_binary ="lib14_angr_shared_library.so"

  base = 0x400000
  project = angr.Project(path_to_binary, load_options={ 
    'main_opts' : { 
      'custom_base_addr' : base 
    } 
  })    #加載動態鏈接庫



  buffer_pointer=claripy.BVV(0x3000000,32)#創造一個32位的指針,值是任意的但這個值指向輸入數據
  validate_function_address = base+0x000006D7
  initial_state = project.factory.call_state(validate_function_address, buffer_pointer,claripy.BVV(8,32))#要調用的庫函數和他的參數

  # You will need to add code to inject a symbolic value into the program at the
  # end of the function that constrains eax to equal true (value of 1) just
  # before the function returns. There are multiple ways to do this:
  # 1. Use a hook.
  # 2. Search for the address just before the function returns and then
  #    constrain eax (this may require putting code elsewhere)
  password=claripy.BVS('password',8*8)
  initial_state.memory.store(buffer_pointer,password)   #向0x30000000地址處存入數據


  simulation = project.factory.simgr(initial_state)

  success_address = base+0x0000783        #函數結束的地址
  simulation.explore(find=success_address)

  if simulation.found:
    solution_state = simulation.found[0]
    solution_state.add_constraints(solution_state.regs.eax != 0)       #添加限制條件

    # Determine where the program places the return value, and constrain it so
    # that it is true. Then, solve for the solution and print it.
    # (!)
    solution = solution_state.se.eval(password)
    solution="".join(map("{:x}".format,[solution]))

    print (binascii.a2b_hex(solution))
  else:
    raise Exception('Could not find the solution')

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


'''
 base = 0x400000
  project = angr.Project(path_to_binary, load_options={ 
    'main_opts' : { 
      'custom_base_addr' : base 
    } 
  })    #加載動態鏈接庫
  加載動態鏈接庫
  
  
  buffer_pointer=claripy.BVV(0x3000000,32)#創造一個32位的指針,值是任意的但這個值指向輸入數據
  validate_function_address = base+0x000006D7
  initial_state = project.factory.call_state(validate_function_address, buffer_pointer,claripy.BVV(8,32))#要調用的庫函數和他的參數
  設置要調用的庫函數

獲取運行到這裏時寄存器的值

solution = solution_state.se.eval(password)
獲取運行到這裏時滿足的輸入
'''

對動態庫的操作

 base = 0x400000
  project = angr.Project(path_to_binary, load_options={ 
    'main_opts' : { 
      'custom_base_addr' : base 
    } 
  })    #加載動態鏈接庫
  加載動態鏈接庫
  
  
  buffer_pointer=claripy.BVV(0x3000000,32)#創造一個32位的指針,值是任意的但這個值指向輸入數據
  validate_function_address = base+0x000006D7
  initial_state = project.factory.call_state(validate_function_address, buffer_pointer,claripy.BVV(8,32))#要調用的庫函數和他的參數
  設置要調用的庫函數
發佈了130 篇原創文章 · 獲贊 35 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章