[internal] All information about how to plug a tracer.
Description
The Triton library allows you to plug any kind of tracers. E.g: Pin, Valgrind and even a database. To use the libTriton
, your tracer must provide two kinds of information at each program point:
- The current opcode executed.
- A state context (register and memory).
Based on these two information, Triton will translate the control flow into AstNode. As an example, let assume that you have dumped a trace into a database with all registers state and memory access - these information may come from Valgrind, Pin, Qemu or whatever. The following Python code uses the Triton's API to build the semantics of each instruction stored in the database.
import sys
import struct
from triton import ARCH, Instruction, MemoryAccess
from database import Manager
unpack_size = {1: 'B', 2: 'H', 4: 'I', 8: 'Q', 16: 'QQ'}
if __name__ == '__main__':
ctxt = getTritonContext()
db = Manager().connect()
inst_id = 1
while True:
opcode = db.get_opcode_from_inst_id(inst_id)
if opcode is None:
break
regs = db.get_registers_from_inst_id(inst_id)
inst = Instruction()
inst.setOpcode(opcode)
inst.setAddress(regs['rip'])
ctxt.setConcreteRegisterValue(ctxt.registers.rax, regs['rax'])
ctxt.setConcreteRegisterValue(ctxt.registers.rbx, regs['rbx'])
ctxt.setConcreteRegisterValue(ctxt.registers.rcx, regs['rcx'])
ctxt.setConcreteRegisterValue(ctxt.registers.rdx, regs['rdx'])
ctxt.setConcreteRegisterValue(ctxt.registers.rdi, regs['rdi'])
ctxt.setConcreteRegisterValue(ctxt.registers.rsi, regs['rsi'])
ctxt.setConcreteRegisterValue(ctxt.registers.rbp, regs['rbp'])
ctxt.setConcreteRegisterValue(ctxt.registers.rsp, regs['rsp'])
ctxt.setConcreteRegisterValue(ctxt.registers.rip, regs['rip'])
ctxt.setConcreteRegisterValue(ctxt.registers.r8, regs['r8'])
ctxt.setConcreteRegisterValue(ctxt.registers.r9, regs['r9'])
ctxt.setConcreteRegisterValue(ctxt.registers.r10, regs['r10'])
ctxt.setConcreteRegisterValue(ctxt.registers.r11, regs['r11'])
ctxt.setConcreteRegisterValue(ctxt.registers.r12, regs['r12'])
ctxt.setConcreteRegisterValue(ctxt.registers.r13, regs['r13'])
ctxt.setConcreteRegisterValue(ctxt.registers.r14, regs['r14'])
ctxt.setConcreteRegisterValue(ctxt.registers.r15, regs['r15'])
ctxt.setConcreteRegisterValue(ctxt.registers.eflags, regs['eflags'])
ctxt.setConcreteRegisterValue(ctxt.registers.fs, regs['fs'])
ctxt.setConcreteRegisterValue(ctxt.registers.gs, regs['gs'])
accesses = db.get_memory_access_from_inst_id(inst_id)
for access in accesses:
if access['kind'] == 'R': address = access['addr']
data = access['data']
value = struct.unpack(unpack_size[len(data)], data)[0]
ctxt.setConcreteMemoryValue(MemoryAccess(address, len(data), value))
ctxt.processing(inst)
print inst
for expr in inst.getSymbolicExpressions():
print '\t', expr
inst_id += 1
sys.exit(0)
The database connection is a pure example to show you how to interact with the Triton API. As Triton is written in C++
, you can directly create your Triton instruction inside a DBI engine (like Pin or Valgrind). According to your tracer, you can refer to the Python or the C++ API.
The Triton's pintool
This project is shippied with a pintool as tracer. Basically, you can add callbacks, get current registers and memory values, inject values into registers and memory, start and stop analysis at specific points, select what images are jitted or not, interact with the Triton API and many more... All information about the pintool API is describe at this following page Python API - Methods and namespaces of the Pintool tracer. Below, some examples.
Example - Display IR
from pintool import *
from triton import ARCH
def mycb(inst):
print inst
for expr in inst.getSymbolicExpressions():
print expr
print
return
if __name__ == '__main__':
insertCall(mycb, INSERT_POINT.BEFORE)
runProgram()
Example - Runtime Memory Tainting
from triton import ARCH
from pintool import *
GREEN = "\033[92m"
ENDC = "\033[0m"
Triton = getTritonContext()
def cbeforeSymProc(instruction):
if instruction.getAddress() == 0x400574:
Triton.taintMemory(rax)
def cafter(instruction):
print '%#x: %s' %(instruction.getAddress(), instruction.getDisassembly())
for se in instruction.getSymbolicExpressions():
if se.isTainted() == True:
print '\t -> %s%s%s' %(GREEN, se.getAst(), ENDC)
else:
print '\t -> %s' %(se.getAst())
print
if __name__ == '__main__':
insertCall(cbeforeSymProc, INSERT_POINT.BEFORE_SYMPROC)
insertCall(cafter, INSERT_POINT.AFTER)
runProgram()
Example - Runtime Register Modification
import sys
from pintool import *
from triton import ARCH
def cb1(inst):
if inst.getAddress() == 0x4005e2:
def cb2(inst):
if inst.getAddress() == 0x4005e2:
print inst
for expr in inst.getSymbolicExpressions():
print '\t', expr
if __name__ == '__main__':
setupImageWhitelist(['crackme'])
insertCall(cb1, INSERT_POINT.BEFORE_SYMPROC)
insertCall(cb2, INSERT_POINT.BEFORE)
runProgram()
Example - Blacklist images
from triton import ARCH
from pintool import *
def mycb(instruction):
print '%#x: %s' %(instruction.getAddress(), instruction.getDisassembly())
return
if __name__ == '__main__':
setupImageBlacklist(["libc", "ld-linux"])
insertCall(mycb, INSERT_POINT.BEFORE)
runProgram()
Example - Callback on image
from triton import ARCH
from pintool import *
def image(imagePath, imageBase, imageSize):
print '----------'
print 'Image path: ', imagePath
print 'Image base: ', hex(imageBase)
print 'Image size: ', imageSize
return
if __name__ == '__main__':
insertCall(image, INSERT_POINT.IMAGE_LOAD)
runProgram()
Example - Callback on routine
from triton import ARCH
from pintool import *
Triton = getTritonContext()
def mallocEntry(threadId):
print '-> malloc(%#x)' %(sizeAllocated)
def mallocExit(threadId):
print '<- %#x' %(ptrAllocated)
if __name__ == '__main__':
insertCall(mallocEntry, INSERT_POINT.ROUTINE_ENTRY, "malloc")
insertCall(mallocExit, INSERT_POINT.ROUTINE_EXIT, "malloc")
runProgram()
Example - Callback on signals
from triton import *
from pintool import *
print 'Signal %d received on thread %d.' %(sig, threadId)
print '========================== DUMP =========================='
regs = getTritonContext().getParentRegisters()
for reg in regs:
expr = getTritonContext().getSymbolicRegister(reg)
print '%s:\t%#016x\t%s' %(reg.getName(), value, (expr.getAst() if expr is not None else 'UNSET'))
return
if __name__ == '__main__':
insertCall(signals, INSERT_POINT.SIGNALS)
runProgram()
Example - Callback on syscalls
from triton import ARCH, SYSCALL64
from pintool import *
def my_callback_syscall_entry(threadId, std):
if getSyscallNumber(std) == SYSCALL64.WRITE:
arg0 = getSyscallArgument(std, 0)
arg1 = getSyscallArgument(std, 1)
arg2 = getSyscallArgument(std, 2)
print 'sys_write(%x, %x, %x)' %(arg0, arg1, arg2)
if __name__ == '__main__':
insertCall(my_callback_syscall_entry, INSERT_POINT.SYSCALL_ENTRY)
runProgram()