[RoarCTF 2019]polyre

最后更新于 2024-08-15 556 字 预计阅读时间: 3 分钟


ELF64位程序放进DIE发现有LLVM混淆

ida打开后发现程序流程图相当复杂

使用deflat.py去除控制流平坦化

python deflat.py -f ./polyre --addr 0x400620

有很多while循环但是(dword_603054 - 1) * dword_603054) & 1只能等与0或1,有许多假循环可以nop掉

或者写ida的python脚本,原理和上述一样,不过脚本将jnz改为jmp

st = 0x0000000000400620 #main开始
end = 0x0000000000402144 #main结束
 
def patch_nop(start,end):
    for i in range(start,end):
        ida_bytes.patch_byte(i, 0x90)		#修改指定地址处的指令  0x90是最简单的1字节nop
 
def next_instr(addr):
    return addr+idc.get_item_size(addr)		#获取指令或数据长度,这个函数的作用就是去往下一条指令

addr = st
while(addr<end):
    next = next_instr(addr)
    if "ds:dword_603054" in GetDisasm(addr):	#GetDisasm(addr)得到addr的反汇编语句
        while(True):
            addr = next
            next = next_instr(addr)
            if "jnz" in GetDisasm(addr):
                dest = idc.get_operand_value(addr, 0)		#得到操作数,就是指令后的数
                ida_bytes.patch_byte(addr, 0xe9)     #0xe9 jmp后面的四个字节是偏移
                ida_bytes.patch_byte(addr+5, 0x90)   #nop第五个字节
                offset = dest - (addr + 5)  #调整为正确的偏移地址 也就是相对偏移地址 - 当前指令后的地址
                ida_bytes.patch_dword(addr + 1, offset) #把地址赋值给jmp后
                print("patch bcf: 0x%x"%addr)
                addr = next
                break
    else:
        addr = next

得到简洁的代码

分析代码,过程中有许多无关的变量但是最后只用到了v3进行变化后再与所给数据进行比较

qword指针,每次拿输入的64位进行操作,当大于0时左移一位,小于0时异或一个数后左移一位,鉴定为CRC64

还原:在左移一位时每个数二进制的最后一位都为0,因为多项式0xB0004B7679FA26B3二进制最后一位为1,故和他异或后最后一位会变成0,只有负数与他异或,故可以用 x&1==1来判断操作前是否为负数

exp

from ctypes import *
a = [0xBC8FF26D43536296, 0x520100780530EE16, 0x4DC0B5EA935F08EC,
    0x342B90AFD853F450, 0x8B250EBCAA2C3681, 0x55759F81A2C68AE4]
num=0xB0004B7679FA26B3
flag=""
for x in a:
  for y in range(64):
    sign=x&1
    if sign==1:
      x^=num
    x//=2
    if sign == 1:
      x |= 0x8000000000000000
  for z in range(8):
    flag+=chr(x&0xff)
    x=x>>8
print(flag)
此作者没有提供个人介绍。
最后更新于 2024-08-15