angr學習[5]--代碼整理(總結)

0x01探索模板


import angr
import claripy
import sys

def main(argv):
  path_to_binary = "15_angr_arbitrary_read"
  project = angr.Project(path_to_binary)

  # You can either use a blank state or an entry state; just make sure to start
  # at the beginning of the program.
  # (!)
  initial_state = project.factory.entry_state()

  # Again, scanf needs to be replaced.
  class ReplacementScanf(angr.SimProcedure):     #鉤取scanf函數重新定義,獲取到它的參數
    #定義的輸入第二個輸入的數據爲可見字符
    # Hint: scanf("%u %20s")
    def run(self, format_string, checksec_key_address,input_address):
      scanf0 = claripy.BVS('scanf0', 4*8)
      scanf1 = claripy.BVS('scanf1', 20*8)
      for char in scanf1.chop(bits=8):      #把整個數據分割每8位爲一組

        self.state.add_constraints(char >= '0', char <= 'z')#添加限制,__isoc99_scanf進來的v4的所有字符爲可見字符


      scanf0_address = checksec_key_address
      self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness)
      self.state.memory.store(input_address, scanf1, endness=project.arch.memory_endness)    #保存求解變量到內存中


      self.state.globals['solution0'] = scanf0
      self.state.globals['solution1'] = scanf1    #保存這兩個變量到state,結束的時候可以查看這裏對應的輸入

  scanf_symbol =  '__isoc99_scanf'# :string
  project.hook_symbol(scanf_symbol, ReplacementScanf())#在這裏執行hook

  # We will call this whenever puts is called. The goal of this function is to
  # determine if the pointer passed to puts is controllable by the user, such
  # that we can rewrite it to point to the string "Good Job."
  def check_puts(state):      #執行到這裏是剛執行完puts函數    獲取現在puts函數的參數1.檢查符號化  2.複製一份執行流添加限制判斷在這個限制之下是否有解(添加的限制不能取消)
    puts_parameter = state.memory.load(state.regs.esp+4,4, endness=project.arch.memory_endness)     #情景是剛剛調用了puts,它的參數在esp+4位置處

    if state.se.symbolic(puts_parameter):#檢查這個參數是否爲符號化對象

      good_job_string_address = 0x4D52584B # :integer, probably hexadecimal   Good Job.的地址
      is_vulnerable_expression = puts_parameter==good_job_string_address # :boolean bitvector expression

      #運行到這裏所有輸入得到的狀態都已經有了,現在要做的是從這些輸入中判斷是否有一種輸入滿足輸出的內容爲Good job.
      copied_state = state.copy()        #複製執行狀態的上下文
      copied_state.add_constraints(is_vulnerable_expression)    #複製的新進程添加限制爲 puts參數爲Good job.的地址
      if copied_state.satisfiable():#  判斷添加了上面這個約束是否有解
        state.add_constraints(is_vulnerable_expression)    #  如果有解的話就保存到我們執行的那個狀態對象
        return True
      #複製一個執行狀態對象用來探路,直到探到正確的路才讓真正的執行流走到這裏
      else:
        return False
    else: # not state.se.symbolic(???)
      return False

  simulation = project.factory.simgr(initial_state)

  def is_successful(state):         #到這裏剛剛執行完puts函數   相當於下斷點

    puts_address = 0x8048370     #puts@plt的地址(已經執行過了call)
    if state.addr == puts_address:    #程序執行到這裏(也就是路徑探測到這裏)
      # Return True if we determine this call to puts is exploitable.
      return check_puts(state)
    else:
      # We have not yet found a call to puts; we should continue!
      return False

  simulation.explore(find=is_successful)

  if simulation.found:      #如果找到說明滿足條件1.能夠走到puts函數   2.走到puts函數的參數爲格式化對象    3.判斷輸出的內容是否爲Good job.
    solution_state = simulation.found[0]

    solution0 = solution_state.se.eval(solution_state.globals['solution0'],cast_to=bytes)
    solution1 = solution_state.se.eval(solution_state.globals['solution1'], cast_to=bytes)  # 輸出字符串序列化的內容

    print(solution0, solution1)
  else:
    raise Exception('Could not find the solution')

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

0x02獲取輸入(能運行到這裏的輸入)(解析結果)

獲取滿足條件的格式化輸入

solution_state.posix.dumps(sys.stdin.fileno())

獲取設置變量的值
password1=claripy.BVS('password1',password_size_in_bits)結合使用獲取滿足條件的設置的變量的值

solution0=solution_state.se.eval(password0)

在hook函數中的變量獲取能走到某處的輸入

self.state.globals['solution0'] = scanf0			#在hook函數裏面聲明這是個最終要解析的變量

    stored_solutions0 = solution_state.globals['solution0']
    solution0 = solution_state.se.eval(stored_solutions0)					#最終的解析

解析結果字符串序列化
輸出結果爲字符串

    solution0 = solution_state.se.eval(solution_state.globals['solution0'],cast_to=bytes)

添加限制條件

     constrained_parameter_desired_value = "MRXJKZYRKMKENFZB" # :string   約束這些數據得到符合要求的
    constraint_expression = constrained_parameter_bitvector == constrained_parameter_desired_value
    solution_state.add_constraints(constraint_expression)#添加約束條件
    )

0x03獲取輸出(運行到這裏的時候的狀態)
獲取運行到某處程序的格式化輸出

stdout_output=state.posix.dumps(sys.stdout.fileno())

獲取運行到某處的某內存地址的內容

    constrained_parameter_address = 0x0804A050        #獲取對應內存區域在運行到這裏的時候的值
    constrained_parameter_size_bytes = 0x10
    constrained_parameter_bitvector = solution_state.memory.load(
      constrained_parameter_address,
      constrained_parameter_size_bytes
    )

獲取運行到某處寄存器的值
用於判斷運行結果是否符合條件,或者添加限制條件

solution_state.regs.eax != 0

運行到某處獲取棧的數據

puts_parameter = state.memory.load(state.regs.esp+4,4, endness=project.arch.memory_endness) #情景是剛剛調用了puts,它的參數在esp+4位置處

獲取運行到哪個地址
個人理解應該是類似下斷點,不知道可不可以繼續往下運行程序

 strncpy_address = 0x08048410
    if state.addr == strncpy_address:

0x04探索
設置探索成功與失敗運行到的地址
這裏開啓了pie

  print_good_address = 0x400000+0x000136D
  will_not_succeed_address = 0x400000+0x000137E
  simulation.explore(find=print_good_address, avoid=will_not_succeed_address)

設置成功和失敗函數

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return 'Good Job.'.encode() in stdout_output# :boolean
  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return 'Try again.'.encode() in stdout_output
  simulation.explore(find=is_successful, avoid=should_abort)

複製一份狀態去探測看是否有滿足限制條件的值

      #運行到這裏所有輸入得到的狀態都已經有了,現在要做的是從這些輸入中判斷是否有一種輸入滿足輸出的內容爲Good job.
      copied_state = state.copy()        #複製執行狀態的上下文
      copied_state.add_constraints(is_vulnerable_expression)    #複製的新進程添加限制爲 puts參數爲Good job.的地址
      if copied_state.satisfiable():#  判斷添加了上面這個約束是否有解
        state.add_constraints(is_vulnerable_expression)    #  如果有解的話就保存到我們執行的那個狀態對象
        return True
      #複製一個執行狀態對象用來探路,直到探到正確的路才讓真正的執行流走到這裏

簡單的虛擬探測功能和上一個一樣
兩個限制條件爲and的關係,兩個同時成立

     if state.satisfiable(extra_constraints=(does_src_hold_password, does_dest_equal_buffer_address)):     #嘗試求解滿足兩個條件1.strncpy函數的目的地址爲
        #if ( !strncmp(password_buffer, "KZYRKMKE", 8u) )中password_buffer的地址  2.strncpy中的前8位數據的內容爲KZYRKMKE
        state.add_constraints(does_src_hold_password, does_dest_equal_buffer_address)# 嘗試求解成功添加真正的限制
        return True

0x05設置初始化變量

設置加載開始地址

    start_address=0x804890E
    initial_state=project.factory.blank_state(addr=start_address)  #設置加載地址  blank_state

設置變量
是一堆變量

    password_size_in_bits=32
    password0=claripy.BVS('password0',password_size_in_bits)       #設置二維向量三個

設置寄存器

initial_state.regs.eax=password0 

設置棧數據

ida中的代碼

  int v1; // [esp+8h] [ebp-10h]
  int v2; // [esp+Ch] [ebp-Ch]

  __isoc99_scanf("%u %u", &v2, &v1);

在這裏插入圖片描述

  initial_state.regs.ebp = initial_state.regs.esp
  padding_length_in_bytes = 8  # :integer
  initial_state.regs.esp -= padding_length_in_bytes                  #放入棧中
  initial_state.stack_push(password0)  # :bitvector (claripy.BVS, claripy.BVV, claripy.BV)
  initial_state.stack_push(password1)

設置bss段內容(已知內存地址)

  password0_address = 0x09FD92A0                       #把數據放到
  initial_state.memory.store(password0_address, password0)

從scanf0_address加載數據存到scanf0_address 最後一個參數應該表示讀取多少

self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness)

設置堆內容(運行地址未知)

  fake_heap_address0 = 0x40000
  pointer_to_malloc_memory_address0 =0x09FD92AC
  initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness)        #寫一個任意的堆地址,把這個地址寫入到變量
  initial_state.memory.store(fake_heap_address0, password0)		#設置堆具體內容

設置文件內容

  password_file = angr.storage.SimFile(filename, content=password)       #把內容放入文件
  initial_state.fs.insert(filename, password_file)

0x06hook方法

hook一個地址
def skip_check_equals_(state):下面定義的代碼就是要把check_equals_called_address地址處的難書hook成什麼

  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成這個函數

hook一組函數
根據函數名字hook函數,直接獲取被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()) #開始hook

0x07特殊操作

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

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

分割數據給claripy.BVS(‘scanf0’, 4*8)生成的二位向量添加限制

      for char in scanf1.chop(bits=8):      #把整個數據分割每8位爲一組

        self.state.add_constraints(char >= '0', char <= 'z')#添加限制,__isoc99_scanf進來的v4的所有字符爲可見字符

判斷是否爲符號化對象(運行結束找出一個數據進行檢測要加的條件)

state.se.symbolic(puts_parameter):#檢查這個參數是否爲符號化對象

0x08解析特殊文件
輸入有很多(比如30個字節)

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

對自己寫的c庫函數

用angr自帶的hook函數hook爲庫函數

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

用angr分析動態鏈接庫文件
加載動態鏈接庫,庫函數

  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))#要調用的庫函數和他的參數


0x09注意點
用 state.memory.load(strncpy_src, strncpy_len)函數獲取到的值
1.是倒序存儲需要完全逆過來還原才能進行字符串比較

		src_contents = state.memory.load(strncpy_src, strncpy_len)



      password_string = "KZYRKMKE"# :string

      does_src_hold_password = src_contents[-1:-64] == password_string     #小端字節序存儲用這種方式反轉數據

2.需要檢查是否是符號化對象

if state.se.symbolic(strncpy_dest):
	是符號化對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章