libTriton  version 0.6 build 1388
Pintool tracer

Table of Contents

[internal] All information about how to plug a tracer.

Description


The new design of the Triton library (since the v0.3), 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:

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.

#!/usr/bin/env python2
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__':
# Get the current Triton context
ctxt = getTritonContext()
# Connect to the database
db = Manager().connect()
# inst_id is the instruction id into the database.
inst_id = 1
while True:
# Get opcode (from database)
opcode = db.get_opcode_from_inst_id(inst_id)
if opcode is None:
break
# Get concrete register value (from database)
regs = db.get_registers_from_inst_id(inst_id)
# Build the Triton instruction
inst = Instruction()
# Setup opcode
inst.setOpcode(opcode)
# Setup Address
inst.setAddress(regs['rip'])
# Update concrete register state
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']) # The mapped base address
ctxt.setConcreteRegisterValue(ctxt.registers.gs, regs['gs']) # The mapped base address
# Update concrete memory access
accesses = db.get_memory_access_from_inst_id(inst_id)
# Update memory access
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))
# Process everything (build IR, spread taint, perform simplification, ...)
ctxt.processing(inst)
# At this point, all engines inside the Triton library were been synchronized with the concrete state.
# Display instruction
print inst
# Display symbolic expressions
for expr in inst.getSymbolicExpressions():
print '\t', expr
# Next instruction (from the database)
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

#!/usr/bin/env python2
from pintool import *
from triton import ARCH
def mycb(inst):
print inst
for expr in inst.getSymbolicExpressions():
print expr
print
return
if __name__ == '__main__':
# Start JIT at the entry point
# Add callback
insertCall(mycb, INSERT_POINT.BEFORE)
# Run Program
runProgram()

Example - Runtime Memory Tainting

# Using: $ ./build/triton ./src/examples/pin/runtime_memory_tainting.py ./src/samples/crackmes/crackme_xor a
from triton import ARCH
from pintool import *
GREEN = "\033[92m"
ENDC = "\033[0m"
Triton = getTritonContext()
# 0x40058b: movzx eax, byte ptr [rax]
#
# When the instruction located in 0x40058b is executed,
# we taint the memory that RAX holds.
def cbeforeSymProc(instruction):
if instruction.getAddress() == 0x400574:
rax = getCurrentRegisterValue(Triton.registers.rax)
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__':
# Start the symbolic analysis from the 'check' function
insertCall(cbeforeSymProc, INSERT_POINT.BEFORE_SYMPROC)
insertCall(cafter, INSERT_POINT.AFTER)
# Run the instrumentation - Never returns
runProgram()

Example - Runtime Register Modification

#!/usr/bin/env python2
import sys
from pintool import *
from triton import ARCH
def cb1(inst):
if inst.getAddress() == 0x4005e2:
setCurrentRegisterValue(getTritonContext().registers.rax, 0)
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 *
# Output
#
# $ ./build/triton ./src/examples/pin/blacklist.py ./src/samples/crackmes/crackme_xor a
# 0x4005ca: push rbp
# 0x4005cb: mov rbp, rsp
# 0x4005ce: sub rsp, 0x20
# 0x4005d2: mov dword ptr [rbp-0x14], edi
# 0x4005d5: mov qword ptr [rbp-0x20], rsi
# 0x4005d9: cmp dword ptr [rbp-0x14], 0x2
# 0x4005dd: jz 0x4005e6
# 0x4005e6: mov rax, qword ptr [rbp-0x20]
# 0x4005ea: add rax, 0x8
# 0x4005ee: mov rax, qword ptr [rax]
# 0x4005f1: mov rdi, rax
# 0x4005f4: call 0x40056d
# 0x40056d: push rbp
# 0x40056e: mov rbp, rsp
# 0x400571: mov qword ptr [rbp-0x18], rdi
# 0x400575: mov dword ptr [rbp-0x4], 0x0
# 0x40057c: jmp 0x4005bd
# 0x4005bd: cmp dword ptr [rbp-0x4], 0x4
# 0x4005c1: jle 0x40057e
# 0x40057e: mov eax, dword ptr [rbp-0x4]
# 0x400581: movsxd rdx, eax
# 0x400584: mov rax, qword ptr [rbp-0x18]
# 0x400588: add rax, rdx
# 0x40058b: movzx eax, byte ptr [rax]
# 0x40058e: movsx eax, al
# 0x400591: sub eax, 0x1
# 0x400594: xor eax, 0x55
# 0x400597: mov ecx, eax
# 0x400599: mov rdx, qword ptr [rip+0x200aa0]
# 0x4005a0: mov eax, dword ptr [rbp-0x4]
# 0x4005a3: cdqe
# 0x4005a5: add rax, rdx
# 0x4005a8: movzx eax, byte ptr [rax]
# 0x4005ab: movsx eax, al
# 0x4005ae: cmp ecx, eax
# 0x4005b0: jz 0x4005b9
# 0x4005b2: mov eax, 0x1
# 0x4005b7: jmp 0x4005c8
# 0x4005c8: pop rbp
# 0x4005c9: ret
# 0x4005f9: mov dword ptr [rbp-0x4], eax
# 0x4005fc: cmp dword ptr [rbp-0x4], 0x0
# 0x400600: jnz 0x40060e
# 0x40060e: mov edi, 0x4006ce
# 0x400613: call 0x400450
# 0x400450: jmp qword ptr [rip+0x200bc2]
# 0x400456: push 0x0
# 0x40045b: jmp 0x400440
# loose
# 0x400618: mov eax, dword ptr [rbp-0x4]
# 0x40061b: leave
# $
def mycb(instruction):
print '%#x: %s' %(instruction.getAddress(), instruction.getDisassembly())
return
if __name__ == '__main__':
# libc and ld-linux will be unjited
setupImageBlacklist(["libc", "ld-linux"])
insertCall(mycb, INSERT_POINT.BEFORE)
runProgram()

Example - Callback on image

from triton import ARCH
from pintool import *
# Output
#
# $ ./build/triton ./src/examples/pin/callback_image.py ./src/samples/ir_test_suite/ir
# ----------
# Image path: /dir/Triton/samples/ir_test_suite/ir
# Image base: 0x400000L
# Image size: 2101312
# ----------
# Image path: /lib64/ld-linux-x86-64.so.2
# Image base: 0x7fb9a14d9000L
# Image size: 2236744
# ----------
# Image path: /lib64/libc.so.6
# Image base: 0x7fb98b739000L
# Image size: 3766680
def image(imagePath, imageBase, imageSize):
print '----------'
print 'Image path: ', imagePath
print 'Image base: ', hex(imageBase)
print 'Image size: ', imageSize
return
if __name__ == '__main__':
# Start the symbolic analysis from the Entry point
# Add a callback.
insertCall(image, INSERT_POINT.IMAGE_LOAD)
# Run the instrumentation - Never returns
runProgram()

Example - Callback on routine

from triton import ARCH
from pintool import *
# Output
#
# $ ./build/triton ./src/examples/pin/callback_routine.py ./src/samples/vulns/testSuite
# -> malloc(0x20)
# <- 0x8fc010
# -> malloc(0x20)
# <- 0x8fc040
# -> malloc(0x20)
# <- 0x8fc010
Triton = getTritonContext()
def mallocEntry(threadId):
sizeAllocated = getCurrentRegisterValue(Triton.registers.rdi)
print '-> malloc(%#x)' %(sizeAllocated)
def mallocExit(threadId):
ptrAllocated = getCurrentRegisterValue(Triton.registers.rax)
print '<- %#x' %(ptrAllocated)
if __name__ == '__main__':
# Start the symbolic analysis from the Entry point
# Add a callback.
insertCall(mallocEntry, INSERT_POINT.ROUTINE_ENTRY, "malloc")
insertCall(mallocExit, INSERT_POINT.ROUTINE_EXIT, "malloc")
# Run the instrumentation - Never returns
runProgram()

Example - Callback on signals

from triton import ARCH, SYMEXPR
from pintool import *
# Output
#
# $ ./build/triton ./src/examples/pin/callback_signals.py ./src/samples/others/signals
# Signal 11 received on thread 0.
# ========================== DUMP ==========================
# rax: 0x00000000000000 ((_ zero_extend 32) (_ bv234 32))
# rbx: 0x00000000000000 UNSET
# rcx: 0x00000000001ba4 ((_ zero_extend 32) ((_ extract 31 0) #81))
# rdx: 0x0000000000000b ((_ sign_extend 32) ((_ extract 31 0) #34))
# rdi: 0x00000000001ba4 ((_ sign_extend 32) ((_ extract 31 0) #83))
# rsi: 0x00000000001ba4 ((_ sign_extend 32) ((_ extract 31 0) #90))
# rbp: 0x007fff097e3540 ((_ extract 63 0) #0)
# rsp: 0x007fff097e3528 (bvsub ((_ extract 63 0) #47) (_ bv8 64))
# rip: 0x007f3fa0735ea7 (_ bv139911251582629 64)
# r8: 0x007f3fa0a94c80 UNSET
# r9: 0x007f3fb671b120 UNSET
# r10: 0x00000000000000 UNSET
# r11: 0x007f3fa0735e70 UNSET
# r12: 0x00000000400460 UNSET
# r13: 0x007fff097e3620 UNSET
# r14: 0x00000000000000 UNSET
# r15: 0x00000000000000 UNSET
# xmm0: 0x000000ff000000 UNSET
# xmm1: 0x2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f UNSET
# xmm2: 0x00000000000000 UNSET
# xmm3: 0x00ff000000ff00 UNSET
# xmm4: 0x000000000000ff UNSET
# xmm5: 0x00000000000000 UNSET
# xmm6: 0x00000000000000 UNSET
# xmm7: 0x00000000000000 UNSET
# xmm8: 0x00000000000000 UNSET
# xmm9: 0x00000000000000 UNSET
# xmm10: 0x00000000000000 UNSET
# xmm11: 0x00000000000000 UNSET
# xmm12: 0x00000000000000 UNSET
# xmm13: 0x00000000000000 UNSET
# xmm14: 0x00000000000000 UNSET
# xmm15: 0x00000000000000 UNSET
# af: 0x00000000000000 (ite (= (_ bv16 64) (bvand (_ bv16 64) (bvxor #12 (bvxor ((_ extract 63 0) #0) (_ bv16 64))))) (_ bv1 1) (_ bv0 1))
# cf: 0x00000000000000 (_ bv0 1)
# df: 0x00000000000000 UNSET
# if: 0x00000000000001 UNSET
# of: 0x00000000000000 (_ bv0 1)
# pd: 0x00000000000001 (ite (= (parity_flag ((_ extract 7 0) #73)) (_ bv0 1)) (_ bv1 1) (_ bv0 1))
# sf: 0x00000000000000 (ite (= ((_ extract 31 31) #73) (_ bv1 1)) (_ bv1 1) (_ bv0 1))
# tf: 0x00000000000000 UNSET
# zf: 0x00000000000001 (ite (= #73 (_ bv0 32)) (_ bv1 1) (_ bv0 1))
def signals(threadId, sig):
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__':
# Start the symbolic analysis from the Entry point
# Add a callback.
insertCall(signals, INSERT_POINT.SIGNALS)
# Run the instrumentation - Never returns
runProgram()

Example - Callback on syscalls

from triton import ARCH, SYSCALL64
from pintool import *
# Output
#
# $ ./build/triton ./src/examples/pin/callback_syscall.py ./src/samples/crackmes/crackme_xor a
# sys_write(1, 7fb7f06e1000, 6)
# loose
#
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__':
# Start the symbolic analysis from the Entry point
insertCall(my_callback_syscall_entry, INSERT_POINT.SYSCALL_ENTRY)
# Run the instrumentation - Never returns
runProgram()