CTF逆向-[安洵杯 2019]game-使用deflat对主要混淆脱混淆后常规逻辑判断

CTF逆向-[安洵杯 2019]game-使用deflat对主要混淆脱混淆后常规逻辑判断

来源:https://buuoj.cn/

内容:无

附件: https://pan.baidu.com/s/1qq_64SNIRnnTTCNqNIKOiw?pwd=1iz9 提取码:1iz9

答案:KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J

总体思路

判断和输入相关的函数,然后对这些函数使用deflat.py对程序扁平化处理。

反向编写脚本得到答案

详细步骤

  • 检查文件信息

image-20220324224825993

  • 打开general_inspection以及trace等自定义方法的,程序发现控制流特别大,可能是进行了混淆

image-20220325080818724

使用deflat.py(见附件),在ida中查看main方法的地址为0x4006F0,分别deflat掉对输入有操作的函数,check1check3check2

  • check3函数中套了一个check2,该函数中是判断用户的输入值是否与全局变量相同,双击点开sodokuD0g3变量,shift+e按如图设置得到地图。其中D0g3变量会接收变更后的input值,即使得用户输入与其sodoku变量相同即可

    • image-20220517193131654
    • image-20220517194652212
  • 其中check1函数的实现是转换v_input:

    • v_input的0-len/2-1len/2-len互换
    • v_input[2n]和v_input[2n+1]互换
    • v_input[k] = (v_input[k] & 0xF3 | ~v_input[k] & 0xC) - 20
  • 通过使用z3创建一个 0-9的映射,即将每个数字都得到原来的输入

    • from typing import List
      import z3
      import struct
      
      
      def convert(raw: str) -> List[int]:
          raw = bytearray.fromhex(raw)
          raw = [raw[x*4:(x+1)*4] for x in range(int(len(raw)/4))]
          raw = [struct.unpack('<I', x)[0] for x in raw]
          return raw
      
      
      v_sudo = '010000000400000005000000030000000200000007000000060000000900000008000000080000000300000009000000060000000500000004000000010000000200000007000000060000000700000002000000080000000100000009000000050000000400000003000000040000000900000006000000010000000800000005000000030000000700000002000000020000000100000008000000040000000700000003000000090000000500000006000000070000000500000003000000020000000900000006000000040000000800000001000000030000000600000007000000050000000400000002000000080000000100000009000000090000000800000004000000070000000600000001000000020000000300000005000000050000000200000001000000090000000300000008000000070000000600000004000000'
      v_d0g3 = '010000000000000005000000030000000200000007000000000000000000000008000000080000000000000009000000000000000500000000000000000000000200000000000000000000000700000000000000000000000100000000000000050000000000000003000000040000000900000000000000010000000000000000000000030000000000000000000000000000000100000000000000000000000700000000000000090000000000000006000000070000000000000003000000020000000900000000000000040000000800000000000000000000000600000000000000050000000400000000000000080000000000000009000000000000000000000004000000000000000000000001000000000000000300000000000000000000000200000001000000000000000300000000000000070000000000000004000000'
      v_sudo = convert(v_sudo)
      v_d0g3 = convert(v_d0g3)
      print(f'v_sudo:{v_sudo}')
      print(f'v_d0g3:{v_d0g3}')
      s = z3.Solver()
      v_result = [index + 48 for index, x in enumerate([0] * 10)]  # 初始化数字的ascii
      v_input = [0] * len(v_result)
      v_input = [z3.BitVec(str(index), 16) for index, x in enumerate(v_input)]
      
      
      def exp(index: int):
          x = v_input[index]
          r = v_result[index]
          return (x & 0xf3 | ~x & 0xc) - 20 == r
      
      
      def get_result(index: int) -> int:
          r = None
          s.add(exp(index))
          result = s.check()
          if result == z3.sat:
              rs = s.model()
              r = rs[0]  # 获取最后结果key
              r = rs[r].as_long()  # 转换为value
          else:
              print(f'fail on {index}')
          s.reset()
          return r
      
      
      key_mapper = [get_result(index) for index, x in enumerate(v_result)]
      print(f'key_mapper:{key_mapper}')
      
  • 得到 key_mapper:[72, 73, 74, 75, 68, 69, 70, 71, 64, 65]

  • 发现在最后的input判断中是只判断当前数组中为0的项的,即输入的值是v_sudo和v_d0g3不一致的值

    • image-20220517212507739

    • # 只填入d0g3中为0的位置,使得v_sudo == v_d0g3
      is_invalid = -1
      v_encoded = [v_sudo[index] if x == 0 else is_invalid for index,
                   x in enumerate(v_d0g3)]  # 转换为输入
      v_encoded = list(filter(lambda x: x != is_invalid, v_encoded))
      v_encoded = [key_mapper[x] for x in v_encoded]
      v_encoded = [chr(x) for x in v_encoded]
      print(f'v_encoded:{v_encoded}')
      
    • 得到转换后的v_input v_encoded:['D', 'F', 'A', 'K', 'F', 'D', 'I', 'G', 'F', 'J', '@', 'A', 'D', 'F', '@', 'E', 'G', 'J', 'J', '@', 'D', 'K', 'E', 'E', 'F', 'I', 'K', 'G', 'J', 'I', 'A', '@', 'G', 'F', 'J', 'E', 'E', 'A', '@', 'F']

  • 最后将其通过key_mapper后,按:

    • v_input的0-len/2-1len/2-len互换
    • v_input[2n]和v_input[2n+1]互换
  • 最终的exp为

    • from typing import List
      import z3
      import struct
      
      
      def convert(raw: str) -> List[int]:
          raw = bytearray.fromhex(raw)
          raw = [raw[x*4:(x+1)*4] for x in range(int(len(raw)/4))]
          raw = [struct.unpack('<I', x)[0] for x in raw]
          return raw
      
      
      v_sudo = '010000000400000005000000030000000200000007000000060000000900000008000000080000000300000009000000060000000500000004000000010000000200000007000000060000000700000002000000080000000100000009000000050000000400000003000000040000000900000006000000010000000800000005000000030000000700000002000000020000000100000008000000040000000700000003000000090000000500000006000000070000000500000003000000020000000900000006000000040000000800000001000000030000000600000007000000050000000400000002000000080000000100000009000000090000000800000004000000070000000600000001000000020000000300000005000000050000000200000001000000090000000300000008000000070000000600000004000000'
      v_d0g3 = '010000000000000005000000030000000200000007000000000000000000000008000000080000000000000009000000000000000500000000000000000000000200000000000000000000000700000000000000000000000100000000000000050000000000000003000000040000000900000000000000010000000000000000000000030000000000000000000000000000000100000000000000000000000700000000000000090000000000000006000000070000000000000003000000020000000900000000000000040000000800000000000000000000000600000000000000050000000400000000000000080000000000000009000000000000000000000004000000000000000000000001000000000000000300000000000000000000000200000001000000000000000300000000000000070000000000000004000000'
      v_sudo = convert(v_sudo)
      v_d0g3 = convert(v_d0g3)
      print(f'v_sudo:{v_sudo}')
      print(f'v_d0g3:{v_d0g3}')
      s = z3.Solver()
      v_result = [index + 48 for index, x in enumerate([0] * 10)]  # 初始化数字的ascii
      v_input = [0] * len(v_result)
      v_input = [z3.BitVec(str(index), 16) for index, x in enumerate(v_input)]
      
      
      def exp(index: int):
          x = v_input[index]
          r = v_result[index]
          return (x & 0xf3 | ~x & 0xc) - 20 == r
      
      
      def get_result(index: int) -> int:
          r = None
          s.add(exp(index))
          result = s.check()
          if result == z3.sat:
              rs = s.model()
              r = rs[0]  # 获取最后结果key
              r = rs[r].as_long()  # 转换为value
          else:
              print(f'fail on {index}')
          s.reset()
          return r
      
      
      key_mapper = [get_result(index) for index, x in enumerate(v_result)]
      print(f'key_mapper:{key_mapper}')
      # 只填入d0g3中为0的位置,使得v_sudo == v_d0g3
      is_invalid = -1
      v_encoded = [v_sudo[index] if x == 0 else is_invalid for index,
                   x in enumerate(v_d0g3)]  # 转换为输入
      v_encoded = list(filter(lambda x: x != is_invalid, v_encoded))
      v_encoded = [key_mapper[x] for x in v_encoded]
      v_encoded = [chr(x) for x in v_encoded]
      print(f'v_encoded:{v_encoded}')
      
      
      result = v_encoded
      vel = len(result)  # result_len
      vehl = int(vel / 2) # result_half_len
      
      for i in range(vehl):
          (result[i], result[i+vehl]) = (result[i+vehl], result[i])
      
      for i in range(0, vel-1, 2):
          (result[i], result[i+1]) = (result[i+1], result[i])
      
      
      print(''.join(result))
      
      
    • 得到答案 KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J

其他文档

更多CTF逆向题通用性做法和常用工具下载参考该博文内容:CTF逆向Reverse题的玩法

相关逆向CTF题

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值