Last updated on January 27, 2024 am
                  
                
              
            
            
              
                
                一道题学会angr和IDApython,甚至BFS(:
题目流程很简单,输入一个8个十六进制数,依次根据最低位选择一条路径走,走到会终点即可得到flag
这里是题目
IDApython
观察迷宫的流程图,会发现是很有规律的,每一个block都有两个successor,根据输入决定前往哪一个successor。还要注意一点,就是会有直接jmp块,需要做个小处理。通过IDApython 提取处迷宫的结构,再用BFS算法去走即可。
提取迷宫
大致分为三部分,第一部分是计算每个block的successor,存储为字典形式,这里不区分是普通跳转还是直接jmp跳转
第二部分是记录直接跳转块
第三部分是计算普通跳转是通过什么方式去跳,即是jz还是jnz,也是存储为字典形式
note:在提取迷宫的时候,最好将地址全部转为十进制的整型,否则后面比较会有各种问题(在这卡了好久)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
   | start_ea = 0x1500 end_ea = 0x23D9
  func = idaapi.get_func(start_ea) cfg = idaapi.FlowChart(func)
  Maze = dict() JmpMethod = dict() DirJmp = list() for block in cfg:     BlockStart = block.start_ea     BlockEnd = block.end_ea               Maze[BlockStart] = list()
           for succ in block.succs():                  if not GetDisasm(succ.start_ea).startswith('jmp'):             Maze[BlockStart].append(succ.start_ea)                  else:             Maze[BlockStart].append(int(GetDisasm(succ.start_ea)[-4::],base=16))               if GetDisasm(BlockStart).startswith('jmp'):         DirJmp.append(BlockStart)                   elif BlockStart!=start_ea and BlockStart!=end_ea:         JmpMethod[BlockStart] = list()                                    Dst = int(GetDisasm(prev_head(BlockEnd))[-4::],base=16)         JmpMethod[BlockStart].append(print_insn_mnem(prev_head(BlockEnd)))         JmpMethod[BlockStart].append(Dst)
  print("Maze:") print(Maze) print("***************************************************************") print("DirJmp:") print(DirJmp) print("***************************************************************") print("JmpMethod:") print(JmpMethod)     
 
  | 
 
BFS
得到迷宫后,就可以用BFS走,DFS应该是走不出来的,因为可能有环的存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
   |  import queue
  start = 0x1500 end = 0x23d9 q = queue.Queue() q.put(0x1500)
 
  def maze_walk():     visited = set()     father = {0x1500: None}      while not q.empty():         curr = q.get()          nxs = Maze[curr]         for dirc in nxs:             if dirc not in visited:                 visited.add(dirc)                 father[dirc] = curr                 q.put(dirc)
              if dirc == end:                 p = [dirc]                 while father[dirc] is not None:                     dirc = father[dirc]                     p.append(dirc)
                  return p[::-1] 
 
  path = maze_walk() p = []
  for i in path:     if i not in DirJmp:          p.append(i) p = p[1:] 
 
  ans = '' for i in range(len(p) - 1):      if JmpMethod[p[i]][0] == 'jnz' and JmpMethod[p[i]][1] == p[i + 1]:         ans += '1'     if JmpMethod[p[i]][0] == 'jz' and JmpMethod[p[i]][1] == p[i + 1]:         ans += '0'     if JmpMethod[p[i]][0] == 'jnz' and JmpMethod[p[i]][1] != p[i + 1]:         ans += '0'     if JmpMethod[p[i]][0] == 'jz' and JmpMethod[p[i]][1] != p[i + 1]:         ans += '1'
 
  ans = int(ans[::-1], 2) print(hex(ans))
 
 
  | 
 
Angr
主要思路就是模拟迷宫的走法,自定义一个ExplorationTechnique,避免路径爆炸的问题,毕竟 2^64次方还是很大的,里面的各种技术细节可以参考下之前的文章(就是因为这题,才写了这些文章 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
   | import angr import claripy from angr import SimState
  class Walk(angr.ExplorationTechnique):     """大体思路就是BFS"""     def __init__(self,MazeRange:tuple[int ,int],MazeFinal:int) -> None:         '''         MazeRange:整个迷宫范围,也就是起始地址到结束地址,注意是整个迷宫,不是起始到终点的范围         MazeFinal:迷宫终点         '''         self._MazeRange = MazeRange         self._MazeFinal = MazeFinal         self._Vistited = set()         
      def setup(self,simgr:angr.SimulationManager):         """         定义并初始化两个stash         初始化_Vistited         """         simgr.stashes['visited'] =[]         simgr.stashes['found'] =[]         self._Vistited.add(simgr.active[0].addr)
      def isInMaze(self,state:angr.SimState) -> bool:         return self._MazeRange[0] <= state.addr < self._MazeRange[1]             def StateSplitter(self,states:list[angr.SimState]) -> tuple[list[angr.SimState],list[angr.SimState]]:         """         此函数用于分拣State,如果访问过就不要让这个State加入下一次的循环了         类似于BFS那个核心要点         """             _keep =[]             _split =[]             for state in states:                 if self.isInMaze(state):                      if state.addr in self._Vistited:                         _split.append(state)                     else:                         self._Vistited.add(state.addr)                         _keep.append(state)                 else:                     _keep.append(state) 
              return _keep,_split          def isEnd(self,state:SimState) -> bool:             return state.addr == self._MazeFinal           def step(self,simgr:angr.SimulationManager,stash :str = 'active',**kwargs) -> angr.SimulationManager:                  simgr.step(stash=stash,**kwargs)
                   simgr.split(stash_splitter = self.StateSplitter,from_stash = 'active',to_stash = 'visited')
                   simgr.move(from_stash = 'active',to_stash = 'found',filter_func = self.isEnd)
          return simgr              def complete(self,simgr:angr.SimulationManager):                  return bool(simgr.found)     
 
  proj = angr.Project("./maze",auto_load_libs = False,main_opts={'base_addr': 0})
 
  SymMazeWalk = proj.loader.find_symbol("maze_walk") SymMazeFinal = proj.loader.find_symbol("maze_final")
  SymKey = proj.loader.find_symbol("key") SymSteps = proj.loader.find_symbol("steps")
  KeyValue = claripy.BVS("SymKey",64)
 
  State = proj.factory.call_state(SymMazeWalk.rebased_addr,prototype = 'void f()') State.options.add(angr.sim_options.ZERO_FILL_UNCONSTRAINED_REGISTERS)
 
  State.memory.store(SymKey.rebased_addr,KeyValue) State.memory.store(SymSteps.rebased_addr,KeyValue)
 
  tech = Walk(MazeRange=(SymMazeWalk.rebased_addr,SymMazeWalk.rebased_addr + SymMazeWalk.size),             MazeFinal=SymMazeFinal.rebased_addr)
 
  Simgr = proj.factory.simgr(State)
  Simgr.use_technique(tech)
 
  Simgr.run()
  Simgr.remove_technique(tech)
 
  SolveState = proj.factory.simgr(Simgr.found[0]) SolveState.run()
  print(SolveState.deadended[0].posix.dumps(1))
 
 
 
  |