#!/usr/bin/env python2 ## -*- coding: utf-8 -*- ## ## Small example about how it's easy to plug Triton on top of QBDI. ## ## ## Output: ## ## $ LD_PRELOAD=./build/tools/pyqbdi/libpyqbdi.so PYQBDI_TOOL=./triton_qbdi.py ./crackme_xor a ## [triton] Register (rax: 4005b3 != 0) synchronization ## [triton] Register (rdx: 7ffd724eacc0 != 0) synchronization ## [triton] Register (rdi: 2 != 0) synchronization ## [triton] Register (rsi: 7ffd724eaca8 != 0) synchronization ## [triton] Register (rbp: 400610 != 0) synchronization ## [triton] Register (rsp: 7ffd724eabc8 != 0) synchronization ## [triton] Register (rip: 4005b3 != 0) synchronization ## [triton] Register (r9: 7ff57b138540 != 0) synchronization ## [triton] Register (eflags: 246 != 0) synchronization ## [triton] Register (r10: 8 != 0) synchronization ## [triton] Register (r11: 246 != 0) synchronization ## [triton] Register (r12: 400460 != 0) synchronization ## [triton] Register (r13: 7ffd724eaca0 != 0) synchronization ## [triton] 0x4005b3: push rbp ## ref_0 = ((0x7FFD724EABC8 - 0x8) & 0xFFFFFFFFFFFFFFFF) # Stack alignment ## ref_1 = ((0x400610 >> 56) & 0xFF) # Byte reference - PUSH operation ## ref_2 = ((0x400610 >> 48) & 0xFF) # Byte reference - PUSH operation ## ref_3 = ((0x400610 >> 40) & 0xFF) # Byte reference - PUSH operation ## ref_4 = ((0x400610 >> 32) & 0xFF) # Byte reference - PUSH operation ## ref_5 = ((0x400610 >> 24) & 0xFF) # Byte reference - PUSH operation ## ref_6 = ((0x400610 >> 16) & 0xFF) # Byte reference - PUSH operation ## ref_7 = ((0x400610 >> 8) & 0xFF) # Byte reference - PUSH operation ## ref_8 = (0x400610 & 0xFF) # Byte reference - PUSH operation ## ref_9 = ((((((((((0x400610 >> 56) & 0xFF)) << 8 | ((0x400610 >> 48) & 0xFF)) << 8 | ((0x400610 >> 40) & 0xFF)) << 8 | ((0x400610 >> 32) & 0xFF)) << 8 | ((0x400610 >> 24) & 0xFF)) << 8 | ((0x400610 >> 16) & 0xFF)) << 8 | ((0x400610 >> 8) & 0xFF)) << 8 | (0x400610 & 0xFF)) # Temporary concatenation reference - PUSH operation ## ref_10 = 0x4005B4 # Program Counter ## ## [triton] 0x4005b4: mov rbp, rsp ## ref_11 = ref_0 # MOV operation ## ref_12 = 0x4005B7 # Program Counter ## ## [triton] 0x4005b7: sub rsp, 0x20 ## ref_13 = ((ref_0 - 0x20) & 0xFFFFFFFFFFFFFFFF) # SUB operation ## ref_14 = (0x1 if (0x10 == (0x10 & (ref_13 ^ (ref_0 ^ 0x20)))) else 0x0) # Adjust flag ## ref_15 = ((((ref_0 ^ (0x20 ^ ref_13)) ^ ((ref_0 ^ ref_13) & (ref_0 ^ 0x20))) >> 63) & 0x1) # Carry flag ## ref_16 = ((((ref_0 ^ 0x20) & (ref_0 ^ ref_13)) >> 63) & 0x1) # Overflow flag ## ref_17 = ((((((((0x1 ^ (((ref_13 & 0xFF) >> 0x0) & 0x1)) ^ (((ref_13 & 0xFF) >> 0x1) & 0x1)) ^ (((ref_13 & 0xFF) >> 0x2) & 0x1)) ^ (((ref_13 & 0xFF) >> 0x3) & 0x1)) ^ (((ref_13 & 0xFF) >> 0x4) & 0x1)) ^ (((ref_13 & 0xFF) >> 0x5) & 0x1)) ^ (((ref_13 & 0xFF) >> 0x6) & 0x1)) ^ (((ref_13 & 0xFF) >> 0x7) & 0x1)) # Parity flag ## ref_18 = ((ref_13 >> 63) & 0x1) # Sign flag ## ref_19 = (0x1 if (ref_13 == 0x0) else 0x0) # Zero flag ## ref_20 = 0x4005BB # Program Counter ## ## [...] ## ## [triton] Memory cells (601010:8) synchronization ## [triton] 0x400426: jmp qword ptr [rip + 0x200be4] ## ref_232 = ((((((((0x0) << 8 | 0x0) << 8 | 0x7F) << 8 | 0xF5) << 8 | 0x7B) << 8 | 0x13) << 8 | 0xE5) << 8 | 0x20) # Program Counter ## ## fail ## [triton] Register (rax: 5 != 1) synchronization ## [triton] Register (rcx: 0 != 35) synchronization ## [triton] Register (rdx: 7ff57b0be8a0 != 400694) synchronization ## [triton] Register (rdi: 0 != 40069e) synchronization ## [triton] Register (rsi: 0 != 7ffd724eaca8) synchronization ## [triton] Register (rsp: 7ffd724eaba0 != 7ffd724eab88) synchronization ## [triton] Register (rip: 400601 != 7ff57b13e520) synchronization ## [triton] Register (r8: 7ff57a0e2740 != 0) synchronization ## [triton] Register (eflags: 206 != 202) synchronization ## [triton] Register (r10: fffffffffffff34f != 8) synchronization ## [triton] 0x400601: mov eax, 0 ## ref_233 = 0x0 # MOV operation ## ref_234 = 0x400606 # Program Counter ## ## [triton] 0x400606: leave ## ref_235 = ref_175 # Stack Pointer ## ref_236 = ((((((((ref_1) << 8 | ref_2) << 8 | ref_3) << 8 | ref_4) << 8 | ref_5) << 8 | ref_6) << 8 | ref_7) << 8 | ref_8) # Stack Top Pointer ## ref_237 = ((ref_235 + 0x8) & 0xFFFFFFFFFFFFFFFF) # Stack alignment ## ref_238 = 0x400607 # Program Counter ## ## [...] import pyqbdi import triton def get_seg(enum): import ctypes libc = ctypes.CDLL(None) syscall = libc.syscall sys_arch_prctl = 158 syscall.restype = ctypes.c_int syscall.argtypes = ctypes.c_long, ctypes.c_int, ctypes.POINTER(ctypes.c_ulong) v = ctypes.c_ulong() syscall(sys_arch_prctl, enum, ctypes.pointer(v)) return v.value # QBDI callback def cb(vm, gpr, fpr, tt): # Create a Triton instruction tt_inst = triton.Instruction(pyqbdi.readMemory(gpr.rip, 16)) tt_inst.setAddress(gpr.rip) # Process the Triton instruction synch_regs(tt, gpr) tt.processing(tt_inst) # Print its semantics addr = tt_inst.getAddress() disas = tt_inst.getDisassembly() print(F"[triton] 0x{addr:x} {disas}") for se in tt_inst.getSymbolicExpressions(): print('\t', se) print return pyqbdi.CONTINUE # Triton callback. This callback is called when triton need to know # the concrete value of memory cells. Synchronize memory cells between # Triton and QBDI def mem_read(tt, mem): addr = mem.getAddress() size = mem.getSize() qbdi_value = pyqbdi.readMemory(addr, size) triton_value = tt.getConcreteMemoryAreaValue(addr, size) # If qbdi and triton mem cells are not equal, synch Triton with # the context of qbdi if qbdi_value != triton_value: print(F"[triton] Memory cells ({addr:x}:{size:d}) synchronization") tt.concretizeMemory(mem) tt.setConcreteMemoryAreaValue(addr, qbdi_value) return # Synchronize registers between Triton and QBDI def synch_regs(tt, qbdi_gpr): values = { tt.registers.rax: qbdi_gpr.rax, tt.registers.rbx: qbdi_gpr.rbx, tt.registers.rcx: qbdi_gpr.rcx, tt.registers.rdx: qbdi_gpr.rdx, tt.registers.rdi: qbdi_gpr.rdi, tt.registers.rsi: qbdi_gpr.rsi, tt.registers.r8: qbdi_gpr.r8, tt.registers.r9: qbdi_gpr.r9, tt.registers.r10: qbdi_gpr.r10, tt.registers.r11: qbdi_gpr.r11, tt.registers.r12: qbdi_gpr.r12, tt.registers.r13: qbdi_gpr.r13, tt.registers.r14: qbdi_gpr.r14, tt.registers.r15: qbdi_gpr.r15, tt.registers.rbp: qbdi_gpr.rbp, tt.registers.rsp: qbdi_gpr.rsp, tt.registers.rip: qbdi_gpr.rip, tt.registers.eflags: qbdi_gpr.eflags, tt.registers.fs: get_seg(0x1003), tt.registers.gs: get_seg(0x1004), } for k, v in values.items(): qbdi_value = v % (1 << k.getBitSize()) # ex. v % (1 << 64) to handle negative values with Python triton_value = tt.getConcreteRegisterValue(k) # If qbdi and triton register is not equal, synch Triton with # the context of qbdi if qbdi_value != triton_value: name = k.getName() print(F"[triton] Register ({name}: {qbdi_value:x} != {triton_value:x}) synchronization") tt.concretizeRegister(k) tt.setConcreteRegisterValue(k, qbdi_value) return def pyqbdipreload_on_run(vm, start, stop): # Create a Triton context tt = triton.TritonContext() tt.setArchitecture(triton.ARCH.X86_64) tt.setAstRepresentationMode(triton.AST_REPRESENTATION.PYTHON) tt.addCallback(triton.CALLBACK.GET_CONCRETE_MEMORY_VALUE, mem_read) # QBDI callback and run vm.addCodeCB(pyqbdi.PREINST, cb, tt) vm.run(start, stop)