[ 0x37, 0x8e, 0xe7, 0x67, 0xf1, 0x16, 0x31, 0xba, 0xd2, 0x13, 0x80, 0xb0, 0x04, 0x49, 0xb1, 0x7a, 0xcd, 0xa4, 0x3c, 0x32, 0xbc, 0xdf, 0x1d, 0x77, 0xf8, 0x20, 0x12, 0xd4, 0x30, 0x21, 0x9f, 0x9b, 0x5d, 0x80, 0xef, 0x9d, 0x18, 0x91, 0xcc, 0x86, 0xe7, 0x1d, 0xa4, 0xaa, 0x88, 0xe1, 0x28, 0x52, 0xfa, 0xf4, 0x17, 0xd5, 0xd9, 0xb2, 0x1b, 0x99, 0x48, 0xbc, 0x92, 0x4a, 0xf1, 0x1b, 0xd7, 0x20 ]] from arybo.lib import MBA, simplify, simplify_inplace from pytanque import symbol, Vector import copy, random, sys mba8 = MBA(8) mba64 = MBA(64) data = [mba8.from_cst(random.randint(0, 255)) for i in range(32)] nbits = int(sys.argv[1]) idxes = list(range(nbits)) random.shuffle(idxes) for i in range(nbits): data[idxes[i]].vec[random.randint(0, 7)] = symbol("i%d" % i) sbox_E, X = mba8.permut2expr(sbox) sbox = sbox_E.vectorial_decomp([X]) def S(K): return [mba8.from_vec(simplify_inplace(sbox(K[i].vec))) for i in range(64)] def P(K): return [K[tau[i]] for i in range(64)]
return b def xor(a, b): return [a[i] ^ b[i] for i in range(16)] def even_mansour(block, key): block = xor(block, key[:16]) block = f(block) block = xor(block, key[16:]) return block block = [mba8.var('b%d' % i) for i in range(16)] key_zero = [mba8.from_cst(0)] * 32 cipher = even_mansour(block, key_zero) cipher = flatten_vec(cipher) CZ = mba128.from_vec(cipher).vectorial_decomp(block) Minv = CZ.matrix().inverse() def recover(plaintext, cipher_plaintext, cipher_unk): plaintext = mba128.from_bytes(plaintext) cipher_plaintext = mba128.from_bytes(cipher_plaintext) cipher_unk = mba128.from_bytes(cipher_unk) tmp = mba128.from_vec( simplify(Minv * ((cipher_plaintext ^ cipher_unk).vec))) return (plaintext ^ tmp).to_bytes()
class TritonTest(): def setUp(self): # Initialize the engine TT.setArchitecture(TT.ARCH.X86) self.mba8 = MBA(8) self.mba16 = MBA(16) self.mba8.use_esf = True self.mba16.use_esf = True # Useful variables using duringtests self.x8_t = TAst.variable(TT.newSymbolicVariable(8)) self.y8_t = TAst.variable(TT.newSymbolicVariable(8)) self.x16_t = TAst.variable(TT.newSymbolicVariable(16)) self.y16_t = TAst.variable(TT.newSymbolicVariable(16)) self.x8 = self.mba8.var(self.x8_t.getValue()) self.y8 = self.mba8.var(self.y8_t.getValue()) self.x16 = self.mba16.var(self.x16_t.getValue()) self.y16 = self.mba16.var(self.y16_t.getValue()) # Helpers def astEquals(self, ast, v): if self.use_expr: e = tritonast2arybo(ast, use_exprs=True, use_esf=False) e = eval_expr(e, use_esf=False) v = expand_esf_inplace(v) v = simplify_inplace(v) else: e = tritonast2arybo(ast, use_exprs=False, use_esf=True) self.assertEqual(e, v) # Tests def test_leaves(self): c = random.choice(range(0xff)) self.astEquals(TAst.bv(c, 8), self.mba8.from_cst(c)) self.astEquals(self.x8_t, self.x8) def test_zx_sx(self): self.astEquals(TAst.zx(8, TAst.bv(0xff, 8)), self.mba16.from_cst(0x00ff)) self.astEquals(TAst.sx(8, TAst.bv(0xff, 8)), self.mba16.from_cst(0xffff)) def test_extract_contract(self): self.astEquals(TAst.extract(7, 0, TAst.bv(0xffff, 16)), self.mba8.from_cst(0xff)) self.astEquals(TAst.concat([TAst.bv(0xff, 8), TAst.bv(0x00, 8)]), self.mba16.from_cst(0xff00)) def test_unaryops(self): self.astEquals(TAst.bvnot(self.x8_t), ~self.x8) self.astEquals(TAst.bvneg(self.x8_t), -self.x8) def test_binaryops(self): ops = ((TAst.bvadd, operator.add), (TAst.bvsub, operator.sub), (TAst.bvand, operator.and_), (TAst.bvor, operator.or_), (TAst.bvxor, operator.xor), (TAst.bvmul, operator.mul), (TAst.bvnand, lambda x, y: ~(x & y)), (TAst.bvnor, lambda x, y: ~(x | y)), (TAst.bvxnor, lambda x, y: ~(x ^ y))) for op in ops: self.astEquals(op[0](self.x8_t, self.y8_t), op[1](self.x8, self.y8)) # One udiv test because it can take a lot of time... e = TAst.bvudiv(self.x8_t, TAst.bv(15, 8)) self.astEquals(e, self.x8.udiv(15)) def test_shifts(self): # Triton interface is not consistant between sh{r,l} and ro{l,r}. # For the first kind, it can take any AstNode. For the second one, an # integer is forced. ops = ( (TAst.bvlshr, operator.rshift), (TAst.bvshl, operator.lshift), ) for op in ops: for s in range(9): self.astEquals(op[0](self.x8_t, TAst.bv(s, 8)), op[1](self.x8, s)) ops = ((TAst.bvrol, lambda x, n: x.rol(n)), (TAst.bvror, lambda x, n: x.ror(n))) for op in ops: for s in range(9): self.astEquals(op[0](s, self.x8_t), op[1](self.x8, s)) def test_mba(self): # x^y = (x+y) - ((x&y)<<1) e = TAst.bvsub( TAst.bvadd(self.x8_t, self.y8_t), TAst.bvshl(TAst.bvand(self.x8_t, self.y8_t), TAst.bv(1, 8))) ea = tritonast2arybo(e, use_exprs=False, use_esf=True) simplify_inplace(expand_esf_inplace(ea)) self.assertEqual(ea, self.x8 ^ self.y8) def test_logical(self): e = TAst.equal(self.x8_t, self.y8_t) ea = tritonast2arybo(e, use_exprs=True, use_esf=False) ea = eval_expr(ea, use_esf=False) self.assertEqual(ea.nbits, 1) def test_exprs_xor_5C(self): # Based on djo's example # This is the xor_5C example compiled with optimisations for x86-4 code = [ "\x41\xB8\xE5\xFF\xFF\xFF", "\x89\xF8", "\xBA\x26\x00\x00\x00", "\x41\x0F\xAF\xC0", "\xB9\xED\xFF\xFF\xFF", "\x89\xC7", "\x89\xD0", "\x83\xEF\x09", "\x0F\xAF\xC7", "\x89\xC2", "\x89\xF8", "\x0F\xAF\xC1", "\xB9\x4B\x00\x00\x00", "\x8D\x44\x02\x2A", "\x0F\xB6\xC0", "\x89\xC2", "\xF7\xDA", "\x8D\x94\x12\xFF\x00\x00\x00", "\x81\xE2\xFE\x00\x00\x00", "\x01\xD0", "\x8D\x14\x00", "\x8D\x54\x02\x4D", "\x0F\xB6\xF2", "\x6B\xF6\x56", "\x83\xC6\x24", "\x83\xE6\x46", "\x89\xF0", "\x0F\xAF\xC1", "\xB9\xE7\xFF\xFF\xFF", "\x89\xC6", "\x89\xD0", "\xBA\x3A\x00\x00\x00", "\x0F\xAF\xC1", "\x89\xC1", "\x89\xD0", "\x8D\x4C\x0E\x76", "\xBE\x63\x00\x00\x00", "\x0F\xAF\xC1", "\x89\xC2", "\x89\xC8", "\x0F\xAF\xC6", "\x83\xEA\x51", "\xBE\x2D\x00\x00\x00", "\x83\xE2\xF4", "\x89\xC1", "\x8D\x4C\x0A\x2E", "\x89\xC8", "\x25\x94\x00\x00\x00", "\x01\xC0", "\x29\xC8", "\xB9\x67\x00\x00\x00", "\x0F\xAF\xC1", "\x8D\x48\x0D", "\x0F\xB6\xD1", "\x69\xD2\xAE\x00\x00\x00", "\x83\xCA\x22", "\x89\xD0", "\x41\x0F\xAF\xC0", "\x89\xC2", "\x89\xC8", "\x0F\xAF\xC6", "\x8D\x44\x02\xC2", "\x0F\xB6\xC0", "\x2D\xF7\x00\x00\x00", "\x69\xC0\xED\x00\x00\x00", "\x0F\xB6\xC0", ] TT.setArchitecture(TT.ARCH.X86_64) rdi = TT.convertRegisterToSymbolicVariable(TT.REG.RDI) rdi = tritonast2arybo(TAst.variable(rdi), use_exprs=False) for opcodes in code: inst = TT.Instruction(opcodes) TT.processing(inst) rax_ast = TT.buildSymbolicRegister(TT.REG.RAX) rax_ast = TT.getFullAst(rax_ast) rax_ast = TT.simplify(rax_ast, True) # Check that this gives a xor 5C e = tritonast2arybo(rax_ast, use_exprs=self.use_expr, use_esf=False) if self.use_expr: e = eval_expr(e) self.assertEqual(e, ((rdi & 0xff) ^ 0x5C).vec)
class MBAExprsTest: def setUp(self): self.mba1 = MBA(1) self.mba4 = MBA(4) self.mba4.use_esf = False self.mba8 = MBA(8) self.mba8.use_esf = False self.x4 = self.mba4.var('x') self.y4 = self.mba4.var('y') self.z4 = self.mba4.var('z') self.x4_expr = EX.ExprBV(self.x4) self.y4_expr = EX.ExprBV(self.y4) self.z4_expr = EX.ExprBV(self.z4) self.x8 = self.mba8.var('x') self.y8 = self.mba8.var('y') self.z8 = self.mba8.var('z') self.x8_expr = EX.ExprBV(self.x8) self.y8_expr = EX.ExprBV(self.y8) self.z8_expr = EX.ExprBV(self.z8) def exprEqual(self, expr, e): expr = EX.eval_expr(expr, self.use_esf) if self.use_esf: expand_esf_inplace(expr.vec) simplify_inplace(expr.vec) simplify_inplace(e.vec) simplify_inplace(expr.vec) self.assertEqual(expr, e) def test_leaves(self): self.exprEqual(self.x4_expr, self.x4) self.exprEqual(EX.ExprCst(0xff, 4), self.mba4.from_cst(0xff)) def test_unary(self): self.exprEqual(EX.ExprNot(self.x4_expr), ~self.x4) self.exprEqual(EX.ExprBroadcast(EX.ExprCst(1, 4), 0, 4), self.mba4.from_cst(0xf)) self.exprEqual(EX.ExprBroadcast(EX.ExprCst(1, 4), 1, 4), self.mba4.from_cst(0)) def test_unaryops(self): ops = ( (EX.ExprXor, operator.xor), (EX.ExprAnd, operator.and_), (EX.ExprOr, operator.or_), ) args_expr = (self.x4_expr, self.y4_expr, self.z4_expr) args = (self.x4, self.y4, self.z4) for op in ops: self.exprEqual(op[0](*args_expr), reduce(op[1], args)) def test_binaryops(self): ops = ( (EX.ExprAdd, operator.add), (EX.ExprSub, operator.sub), (EX.ExprMul, operator.mul), ) args_expr = (self.x4_expr, self.y4_expr, self.z4_expr) args = (self.x4, self.y4, self.z4) for op in ops: self.exprEqual(reduce(op[0], args_expr), reduce(op[1], args)) E0 = EX.ExprAdd(self.x8_expr, self.x8_expr) self.exprEqual(EX.ExprAdd(E0, E0), self.x8 << 2) for i in range(1, 16): self.exprEqual(EX.ExprDiv(self.x4_expr, EX.ExprCst(i, 4)), self.x4.udiv(i)) def test_rotate_binop(self): E0 = EX.ExprRor(EX.ExprAdd(self.x4_expr, self.y4_expr), EX.ExprCst(1, 4)) self.exprEqual(E0, (self.x4 + self.y4).ror(1)) E0 = EX.ExprRor(EX.ExprSub(self.x4_expr, self.y4_expr), EX.ExprCst(1, 4)) self.exprEqual(E0, (self.x4 - self.y4).ror(1)) E0 = EX.ExprRor(EX.ExprMul(self.x4_expr, self.y4_expr), EX.ExprCst(1, 4)) self.exprEqual(E0, (self.x4 * self.y4).ror(1)) E0 = EX.ExprRol(EX.ExprAdd(self.x4_expr, self.y4_expr), EX.ExprCst(1, 4)) self.exprEqual(E0, (self.x4 + self.y4).rol(1)) E0 = EX.ExprRol(EX.ExprSub(self.x4_expr, self.y4_expr), EX.ExprCst(1, 4)) self.exprEqual(E0, (self.x4 - self.y4).rol(1)) E0 = EX.ExprRol(EX.ExprMul(self.x4_expr, self.y4_expr), EX.ExprCst(1, 4)) self.exprEqual(E0, (self.x4 * self.y4).rol(1)) def test_logical(self): ops = ((EX.ExprLShr, operator.rshift), (EX.ExprShl, operator.lshift), (EX.ExprRol, lambda x, n: x.rol(n)), (EX.ExprRor, lambda x, n: x.ror(n))) for op in ops: for s in range(5): self.exprEqual(op[0](self.x4_expr, EX.ExprCst(s, 4)), op[1](self.x4, s)) def test_zx_sx(self): self.exprEqual(EX.ExprZX(EX.ExprCst(0xf, 4), 8), self.mba8.from_cst(0x0f)) self.exprEqual(EX.ExprSX(EX.ExprCst(0x8, 4), 8), self.mba8.from_cst(0xf8)) def test_extract_contract(self): self.exprEqual(EX.ExprSlice(self.x8_expr, slice(0, 4)), self.x4) self.exprEqual(EX.ExprConcat(EX.ExprCst(0xf, 4), EX.ExprCst(0, 4)), self.mba8.from_cst(0x0f)) def test_cmp(self): e = EX.ExprCond(EX.ExprCmpEq(self.x8_expr, EX.ExprCst(10, 8)), self.y8_expr, self.z8_expr) e = EX.eval_expr(e) for i in range(256): eref = self.z8 if i != 10 else self.y8 self.assertEqual(e.eval({self.x8: i}), eref.vec)
from arybo.lib import MBA, simplify, simplify_inplace from arybo.tools import app_inverse mba = MBA(32) S, X = mba.permut2expr(S) S = S.vectorial_decomp([X]) def compute_arybo(Z): C = mba.from_cst(0xFFFFFFFF) A = 0 for N in range(0, 4): C_ = (C >> (A * 8)) & 0xFF Z_ = (Z >> (N * 8)) & 0xFF t = Z_ ^ C_ T_ = mba.from_vec(simplify(S(t.vec))) B = C >> 8 C = B ^ T_ C = (~C) return simplify_inplace(C) E = compute_arybo(X) A = E.vectorial_decomp([X]) Ainv = app_inverse(A) sol = mba.from_vec(simplify(Ainv(mba.from_cst(3298472535).vec))).to_cst() print(sol)