|
from pwn import constants as cst, context |
|
import struct |
|
import collections |
|
|
|
context.arch = 'amd64' |
|
|
|
class Matcher: |
|
def _match(self, vals, ctx): |
|
raise ValueError('Abstract method') |
|
def __repr__(self): return str(self) |
|
|
|
class FieldsDict(Matcher): |
|
def __init__(self, **fields): |
|
self.fields = fields |
|
def _match(self, val, ctx): |
|
for key, fldPatt in self.fields.items(): |
|
fld = val[key] |
|
if not match(fld, fldPatt, ctx): return False |
|
return True |
|
def __str__(self): return 'MI' + str(self.fields) |
|
|
|
|
|
class Fields(Matcher): |
|
def __init__(self, **fields): |
|
self.fields = fields |
|
def _match(self, val, ctx): |
|
for key, fldPatt in self.fields.items(): |
|
fld = getattr(val, key) |
|
if not match(fld, fldPatt, ctx): return False |
|
return True |
|
def __str__(self): return 'M' + str(self.fields) |
|
|
|
class List(Matcher): |
|
def __init__(self, *vals): |
|
self.vals = vals |
|
def __len__(self): |
|
return len(self.vals) |
|
def _match(self, vals, ctx): |
|
if len(vals) != len(self.vals): |
|
raise ValueError('Unexhaustable pattern matching') |
|
for v, p in zip(vals, self.vals): |
|
if not match(v, p, ctx): return False |
|
return True |
|
|
|
def __str__(self): return 'M' + str(self.vals) |
|
|
|
class Wildcard(Matcher): |
|
def __init__(self): pass |
|
def _match(self, val, ctx): return True |
|
def __str__(self): return '_' |
|
|
|
class Var(Matcher): |
|
def __init__(self, name): |
|
self.name = name |
|
def _match(self, val, ctx): |
|
if self.name in ctx: |
|
if ctx[self.name] != val: |
|
return False |
|
else: |
|
ctx[self.name] = val |
|
return True |
|
def __str__(self): return 'var_' + self.name |
|
|
|
def match(val, pattern, ctx=None): |
|
if ctx == None: |
|
ctx = {'_matched': True} |
|
|
|
if isinstance(pattern, Matcher) and val != None: |
|
ret = pattern._match(val, ctx) |
|
else: |
|
ret = (val == pattern) |
|
|
|
if ret: return ctx |
|
else: return None |
|
|
|
|
|
sys_consts = {} |
|
for name in dir(cst): |
|
if name.startswith('SYS_'): |
|
sys_consts[getattr(cst, name)] = name |
|
sys_args = {} |
|
sys_useless = set() |
|
|
|
sys_args[cst.SYS_set_tid_address] = 1 |
|
sys_args[cst.SYS_prctl] = 5 |
|
sys_args[cst.SYS_read] = 3 |
|
sys_args[cst.SYS_write] = 3 |
|
sys_args[cst.SYS_rt_sigaction] = 3 |
|
sys_ops = { |
|
cst.SYS_getpgrp: '*', |
|
cst.SYS_getpid: '+', |
|
cst.SYS_getuid: '>>', |
|
cst.SYS_getegid: '-', |
|
cst.SYS_getgid: '&', |
|
cst.SYS_geteuid: '^', |
|
cst.SYS_gettid: '|', |
|
cst.SYS_getppid: '<<', |
|
cst.SYS_fork: '/', |
|
} |
|
|
|
class Bits: |
|
def __init__(self, val): |
|
self.val = val |
|
def get(self, size): |
|
ret = self.val & ((1<<size) - 1) |
|
self.val >>= size |
|
return ret |
|
|
|
class Arg: |
|
def __init__(self, pc, bits): |
|
self.mode = bits.get(2) |
|
if self.mode == 0: # REG |
|
self.val = bits.get(4) |
|
if self.val == 15: |
|
self.mode = 2 |
|
self.val = pc |
|
self._repr = hex(self.val) + '(pc)' |
|
else: |
|
self._repr = 'r' + str(self.val) |
|
elif self.mode == 1: # REG REF |
|
self.val = bits.get(4) |
|
if self.val == 15: |
|
self._repr = '&pc' |
|
else: |
|
self._repr = f'&r{self.val}' |
|
elif self.mode == 2: # IMM |
|
sz = bits.get(5) |
|
self.val = bits.get(sz + 1) |
|
self._repr = hex(self.val) |
|
else: |
|
self.val = 0 |
|
self._repr = 'PASS' |
|
|
|
def __eq__(self, other): return type(self) == type(other) and self._repr == other._repr |
|
|
|
def __str__(self): return self._repr |
|
def __repr__(self): return self._repr |
|
|
|
class Oper: |
|
def __init__(self, op, a1, a2, a3): |
|
self.sysnum = -2 |
|
self.op = op |
|
self.a1 = a1 |
|
self.a2 = a2 |
|
self.a3 = a3 |
|
self._repr = f'PUSH {self.a1} {self.op} {self.a2}, SHL {self.a3}' |
|
|
|
def __str__(self): return self._repr |
|
def __repr__(self): return self._repr |
|
|
|
def lvalOpStr(lval): |
|
if lval.mode == 0: |
|
lval = '*' + str(lval) |
|
elif lval.mode == 1: |
|
lval = str(lval)[1:] |
|
else: |
|
lval = f'*({lval})' |
|
return lval |
|
|
|
class Bne(collections.namedtuple('Bne', ['reg', 'target'])): |
|
def __init__(self, *args, **kwargs): |
|
self.sysnum = -6 |
|
self._repr = f'bne {self.reg}, 0x{self.target:04x}' |
|
|
|
def __str__(self): return self._repr |
|
def __repr__(self): return self._repr |
|
|
|
class OperAssign(collections.namedtuple('OperAssign', ['cast', 'op', 'dst', 'src1', 'src2'])): |
|
def __init__(self, *args, **kwargs): |
|
self.sysnum = -3 |
|
self._repr = f'{lvalOpStr(self.dst)} = {self.cast}{self.src1} {self.op} {self.src2}' |
|
|
|
def __str__(self): return self._repr |
|
def __repr__(self): return self._repr |
|
|
|
class Copy(collections.namedtuple('OperAssign', ['lval', 'rval'])): |
|
def __init__(self, *args, **kwargs): |
|
self.sysnum = -4 |
|
self._repr = f'memcpy({self.lval}, {self.rval}, 0x10)' |
|
|
|
def __str__(self): return self._repr |
|
def __repr__(self): return self._repr |
|
|
|
class Assign(collections.namedtuple('Assign', ['lval', 'rval'])): |
|
def __init__(self, *args, **kwargs): |
|
self.sysnum = -1 |
|
self._repr = f'{lvalOpStr(self.lval)} = {self.rval}' |
|
|
|
def __str__(self): return self._repr |
|
def __repr__(self): return self._repr |
|
|
|
class Special(): |
|
def __init__(self, _name, *args, **kwargs): |
|
self.sysnum = -5 |
|
self.name = _name |
|
self.args = args |
|
self.kwargs = kwargs |
|
|
|
lst = list(map(str, self.args)) + [f'{k}={v}' for k, v in kwargs.items()] |
|
lst = ', '.join(lst) |
|
self._repr = f'{self.name}({lst})' |
|
def __str__(self): return self._repr |
|
def __repr__(self): return self._repr |
|
|
|
Insn_ = collections.namedtuple('Insn', ['sysnum', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6']) |
|
class Insn(Insn_): |
|
def __str__(self): |
|
sys = sys_consts[self.sysnum] |
|
args = self[1:][:sys_args.get(self.sysnum, 6)] |
|
args = ', '.join(map(str, args)) |
|
return f'{sys}({args})' |
|
|
|
def fetchInsn(pc, insn): |
|
b = Bits(insn) |
|
return Insn(b.get(8), *[Arg(pc, b) for i in range(6)]) |
|
|
|
def p_opasgn(dst, src1, op, src2): |
|
return Fields(sysnum=-3, op=op, dst=dst, src1=src1, src2=src2) |
|
def p_asgn(lval, rval): return Fields(sysnum=-1, lval=lval, rval=rval) |
|
def p_reg(r): return Fields(mode=0, val=r) |
|
def p_xreg(r): return Fields(mode=1, val=r) |
|
def p_imm(v): return Fields(mode=2, val=v) |
|
|
|
bc = open('./sop_bytecode', 'rb').read() |
|
insns = [fetchInsn(pc, insn) for (pc, (insn,)) in |
|
enumerate(struct.iter_unpack('<Q', bc))] |
|
|
|
p_assign2 = List( |
|
Fields(sysnum=cst.SYS_set_tid_address, a1=Var('rval')), |
|
Fields(sysnum=cst.SYS_prctl, a1=Fields(mode=2, val=0x28), a2=Var('lval'))) |
|
p_assign4 = List( |
|
Fields(sysnum=-1, lval=Fields(mode=1, val=Var('_reg')), rval=Var('lval')), None, |
|
Fields(sysnum=-1, lval=Fields(mode=0, val=Var('_reg')), rval=Var('rval')), None) |
|
p_copy16 = List( |
|
Fields(sysnum=cst.SYS_prctl, a1=Fields(mode=2, val=0x0f), a2=Var('rval')), |
|
Fields(sysnum=cst.SYS_prctl, a1=Fields(mode=2, val=0x10), a2=Var('lval'))) |
|
p_oper32 = List( |
|
Fields(sysnum=-1, lval=Fields(mode=2, val=0x217022), |
|
rval=Var('dst')), None, |
|
Fields(sysnum=-1, lval=Fields(mode=1, val=0), rval=Var('src1')), None, |
|
Fields(sysnum=-1, lval=Fields(mode=1, val=1), rval=Var('src2')), None, |
|
Fields(sysnum=-2, op=Var('op'), a1=Fields(mode=0, val=0), |
|
a2=Fields(mode=0, val=1), a3=Fields(mode=2, val=0)), |
|
Fields(sysnum=-2, op=Var('op'), a1=Fields(mode=0, val=0), |
|
a2=Fields(mode=0, val=1), a3=Fields(mode=2, val=0x10)) |
|
) |
|
p_oper32_64 = List( |
|
Fields(sysnum=-1, lval=Fields(mode=2, val=0x217022), |
|
rval=Var('dst')), None, |
|
Fields(sysnum=-1, lval=Fields(mode=1, val=0), rval=Var('src1')), None, |
|
Fields(sysnum=-1, lval=Fields(mode=1, val=1), rval=Var('src2')), None, |
|
Fields(sysnum=-2, op=Var('op'), a1=Fields(mode=0, val=0), |
|
a2=Fields(mode=0, val=1), a3=Fields(mode=2, val=0)), |
|
Fields(sysnum=-2, op=Var('op'), a1=Fields(mode=0, val=0), |
|
a2=Fields(mode=0, val=1), a3=Fields(mode=2, val=0x10)), |
|
Fields(sysnum=-2, op=Wildcard(), a1=Fields(mode=2, val=0), |
|
a2=Fields(mode=2, val=0), a3=Wildcard()), |
|
Fields(sysnum=-2, op=Wildcard(), a1=Fields(mode=2, val=0), |
|
a2=Fields(mode=2, val=0), a3=Wildcard()) |
|
) |
|
|
|
p_shuffle = List( |
|
p_opasgn(p_xreg(11), Var('inp'), '<<', p_imm(4)), *[None] * 7, |
|
p_opasgn(p_xreg(11), p_reg(11), '+', Var('key1')), *[None] * 7, |
|
p_opasgn(p_xreg(12), Var('inp'), '>>', p_imm(5)), *[None] * 7, |
|
p_opasgn(p_xreg(12), p_reg(12), '+', Var('key2')), *[None] * 7, |
|
p_opasgn(p_xreg(11), p_reg(11), '^', p_reg(12)), *[None] * 7, |
|
p_opasgn(p_xreg(12), Var('inp'), '+', Var('round')), *[None] * 7, |
|
p_opasgn(p_xreg(11), p_reg(11), '^', p_reg(12)), *[None] * 7, |
|
) |
|
|
|
p_myst = List( |
|
p_asgn(p_xreg(2), p_imm(Var('key1'))), None, |
|
p_asgn(p_xreg(3), p_imm(Var('key2'))), None, |
|
p_asgn(p_xreg(4), p_imm(Var('key3'))), None, |
|
p_asgn(p_xreg(5), p_imm(Var('key4'))), None, |
|
Fields(sysnum=-4, lval=p_xreg(6), rval=p_imm(Var('inp1'))), None, |
|
p_asgn(p_xreg(0), p_imm(0xffffffff)), None, |
|
p_opasgn(p_xreg(6), p_reg(0), '&', p_reg(6)), *[None] * 9, |
|
Fields(sysnum=-4, lval=p_xreg(7), rval=p_imm(Var('inp2'))), None, |
|
p_asgn(p_xreg(0), p_imm(0xffffffff)), None, |
|
p_opasgn(p_xreg(7), p_reg(0), '&', p_reg(7)), *[None] * 9, |
|
p_asgn(p_xreg(8), p_imm(0)), None, |
|
p_asgn(p_xreg(9), p_imm(Var('x5'))), None, |
|
p_asgn(p_xreg(10), p_imm(0)), None, |
|
p_opasgn(p_xreg(8), p_reg(8), '+', p_reg(9)), *[None] * 7, |
|
Fields(sysnum=-5, name='r11 = shuffle'), *[None] * 55, |
|
p_opasgn(p_xreg(6), p_reg(6), '+', p_reg(11)), *[None] * 7, |
|
Fields(sysnum=-5, name='r11 = shuffle'), *[None] * 55, |
|
p_opasgn(p_xreg(7), p_reg(7), '+', p_reg(11)), *[None] * 7, |
|
p_opasgn(p_xreg(10), p_reg(10), '+', p_imm(1)), *[None] * 7, |
|
p_opasgn(p_xreg(11), p_reg(10), '>>', p_imm(5)), *[None] * 7, |
|
p_opasgn(p_xreg(11), p_imm(1), '-', p_reg(11)), *[None] * 7, |
|
Fields(sysnum=-6), *[None] * 10, |
|
p_asgn(p_imm(Var('oup1')), p_reg(6)), *[None] * 3, |
|
p_asgn(p_imm(Var('oup2')), p_reg(7))) |
|
p_bne = List( |
|
p_opasgn(p_xreg(Var('reg')), p_reg(Var('reg')), '*', p_imm(Var('off'))), |
|
*[None] * 7, |
|
p_asgn(p_imm(0x217022), p_xreg(15)), None, |
|
Fields(sysnum=-2, op='-', a1=p_imm(Var('pc')), a2=p_reg(Var('reg')), |
|
a3=p_imm(0)) |
|
) |
|
|
|
|
|
sigstruct = [0x217020, 0x0, 0x4000004, 0x0, 0x217044, 0x0, 0x0, 0x0] |
|
sighand = [0x342cb948, 0x95f5793a, 0x568b3f84, 0x11896604, 0xeb0d8d48, |
|
0x48ffffff, 0xff4801ff, 0xccccc301, 0xcccccccc, 0xfb0c031, 0xcccc050f] |
|
seccomp = [0x47, None, 0x217060, None, 0x20, 0x0, 0xd0035, 0x40000000, 0xa0015, |
|
0x1, 0x200015, 0x68, 0x150015, 0x66, 0x280015, 0xba, 0xe0015, 0x27, |
|
0x170015, 0x6c, 0x70015, 0x6f, 0x290015, 0x6e, 0x1e0015, 0x6b, 0x2c0015, |
|
0x39, 0x6, 0x7fff0000, 0x20, 0x10, 0x37000015, 0x0, 0x6, 0x0, 0x20, |
|
0x18, 0x7, 0x0, 0x20, 0x10, 0x2c, 0x0, 0x28280015, 0x0, 0x20, 0x18, 0x7, |
|
0x0, 0x20, 0x10, 0xc, 0x0, 0x23230015, 0x0, 0x20, 0x18, 0x7, 0x0, 0x20, |
|
0x10, 0x7c, 0x0, 0x1e1e0015, 0x0, 0x20, 0x18, 0x7, 0x0, 0x20, 0x10, |
|
0x1c, 0x0, 0x19190015, 0x0, 0x20, 0x18, 0x7, 0x0, 0x20, 0x10, 0x5c, 0x0, |
|
0x14140015, 0x0, 0x20, 0x18, 0x7, 0x0, 0x20, 0x10, 0xac, 0x0, 0xf0f0015, |
|
0x0, 0x20, 0x18, 0x7, 0x0, 0x20, 0x10, 0x4c, 0x0, 0xa0a0015, 0x0, 0x20, |
|
0x18, 0x7, 0x0, 0x20, 0x10, 0x6c, 0x0, 0x5050015, 0x0, 0x20, 0x18, 0x7, |
|
0x0, 0x20, 0x10, 0x3c, 0x0, 0x15, 0x0, 0x2, 0x0, 0x20, 0x20, 0x7, 0x0, |
|
0x60, 0x0, 0x7c, 0x0, 0x1, 0x30000, 0x54, 0xffff, 0x4c, 0x0, 0x16, 0x0, |
|
0x6, 0x7fff0000] |
|
|
|
def match_write(base, data): |
|
insns = [] |
|
for i, word in enumerate(data): |
|
if word == None: continue |
|
insns += [Fields(sysnum=-1, lval=Fields(mode=2, val=base + i * 4), |
|
rval=Fields(mode=2, val=word)), None, None, None] |
|
return List(*insns) |
|
|
|
|
|
rw_assign = lambda ctx: Assign(**ctx) |
|
rewrites = [ |
|
(p_assign2, rw_assign), |
|
(p_assign4, rw_assign), |
|
(p_oper32_64, lambda ctx: OperAssign(cast='(uint64_t) ', **ctx)), |
|
(p_oper32, lambda ctx: OperAssign(cast='', **ctx)), |
|
(p_copy16, lambda ctx: Copy(**ctx)), |
|
(p_shuffle, lambda ctx: Special('r11 = shuffle', **ctx)), |
|
(p_bne, lambda ctx: Bne(reg='r' + str(ctx['reg']), |
|
target=ctx['pc'] - ctx['off'] + 1)), |
|
(p_myst, lambda ctx: Special('full_shuffle', **ctx)), |
|
(match_write(0x217050, sigstruct), lambda ctx: Special('init_sigstruct')), |
|
(match_write(0x217020, sighand), lambda ctx: Special('init_sighand')), |
|
(match_write(0x217050, seccomp), lambda ctx: Special('init_seccomp')), |
|
] |
|
for num, op in sys_ops.items(): |
|
def anony(op2): |
|
rewrites.append((List(Fields(sysnum=num, a1=Var('a1'), a2=Var('a2'), |
|
a3=Var('a3'))), lambda ctx: Oper(op=op2, **ctx))) |
|
anony(op) |
|
|
|
|
|
change = True |
|
while change: |
|
change = False |
|
pc = 0 |
|
while pc < len(insns): |
|
for (pattern, rewriter) in rewrites: |
|
if pc + len(pattern) >= len(insns): continue |
|
m = match(insns[pc:pc+len(pattern)], pattern) |
|
if m: |
|
change = True |
|
for key in list(m.keys()): |
|
if key[0] == '_': del m[key] |
|
insns[pc] = rewriter(m) |
|
for i in range(1, len(pattern)): |
|
insns[pc + i] = None |
|
pc += len(pattern) |
|
break |
|
else: |
|
pc += 1 |
|
|
|
for pc, insn in enumerate(insns): |
|
if insn == None: continue |
|
print(f'{pc:04x}: {insn}') |
|
|
|
|