libTriton  version 0.4 build 1351
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 AST Representations. 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.

1 #!/usr/bin/env python2
2 ## -*- coding: utf-8 -*-
3 
4 import sys
5 import struct
6 
7 from triton import *
8 from datbase import import Manager
9 
10 unpack_size = {1: 'B', 2: 'H', 4: 'I', 8: 'Q', 16: 'QQ'}
11 
12 if __name__ == '__main__':
13 
14  # Set the arch
15  setArchitecture(ARCH.X86_64)
16 
17  # Connect to the database
18  db = Manager().connect()
19 
20  # inst_id is the instruction id into the database.
21  inst_id = 1
22 
23  while True:
24 
25  # Get opcode (from database)
26  opcode = db.get_opcode_from_inst_id(inst_id)
27  if opcode is None:
28  break
29 
30  # Get concrete register value (from database)
31  regs = db.get_registers_from_inst_id(inst_id)
32 
33  # Build the Triton instruction
34  inst = Instruction()
35 
36  # Setup opcodes
37  inst.setOpcodes(opcode)
38 
39  # Setup Address
40  inst.setAddress(regs['rip'])
41 
42  # Update concrete register state
43  setConcreteRegisterValue(Register(REG.RAX, regs['rax']))
44  setConcreteRegisterValue(Register(REG.RBX, regs['rbx']))
45  setConcreteRegisterValue(Register(REG.RCX, regs['rcx']))
46  setConcreteRegisterValue(Register(REG.RDX, regs['rdx']))
47  setConcreteRegisterValue(Register(REG.RDI, regs['rdi']))
48  setConcreteRegisterValue(Register(REG.RSI, regs['rsi']))
49  setConcreteRegisterValue(Register(REG.RBP, regs['rbp']))
50  setConcreteRegisterValue(Register(REG.RSP, regs['rsp']))
51  setConcreteRegisterValue(Register(REG.RIP, regs['rip']))
52  setConcreteRegisterValue(Register(REG.R8, regs['r8']))
53  setConcreteRegisterValue(Register(REG.R9, regs['r9']))
54  setConcreteRegisterValue(Register(REG.R10, regs['r10']))
55  setConcreteRegisterValue(Register(REG.R11, regs['r11']))
56  setConcreteRegisterValue(Register(REG.R12, regs['r12']))
57  setConcreteRegisterValue(Register(REG.R13, regs['r13']))
58  setConcreteRegisterValue(Register(REG.R14, regs['r14']))
59  setConcreteRegisterValue(Register(REG.R15, regs['r15']))
60  setConcreteRegisterValue(Register(REG.EFLAGS, regs['eflags']))
61  setConcreteRegisterValue(Register(REG.FS, regs['fs'])) # The mapped base address
62  setConcreteRegisterValue(Register(REG.GS, regs['gs'])) # The mapped base address
63 
64  # Update concrete memory access
65  accesses = db.get_memory_access_from_inst_id(inst_id)
66 
67  # Update memory access
68  for access in accesses:
69  if access['kind'] == 'R':
70  address = access['addr']
71  data = access['data']
72  value = struct.unpack(unpack_size[len(data)], data)[0]
73  setConcreteMemoryValue(MemoryAccess(address, len(data), value))
74 
75  # Process everything (build IR, spread taint, perform simplification, ...)
76  processing(inst)
77 
78  # At this point, all engines inside the Triton library were been synchronized with the concrete state.
79  # Display instruction
80  print inst
81 
82  # Display symbolic expressions
83  for expr in inst.getSymbolicExpressions():
84  print '\t', expr
85 
86  # Next instruction (from the database)
87  inst_id += 1
88 
89  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. Below, some examples.


Example - Display IR

1 #!/usr/bin/env python2
2 ## -*- coding: utf-8 -*-
3 
4 import sys
5 
6 from pintool import *
7 from triton import *
8 
9 
10 def mycb(inst):
11  print inst
12  for expr in inst.getSymbolicExpressions():
13  print expr
14  print
15  return
16 
17 
18 if __name__ == '__main__':
19  # Set arch
20  setArchitecture(ARCH.X86_64)
21 
22  # Start JIT at the entry point
24 
25  # Add callback
26  insertCall(mycb, INSERT_POINT.BEFORE)
27 
28  # Run Program
29  runProgram()

Example - Runtime Memory Tainting

1 from triton import *
2 from pintool import *
3 
4 GREEN = "\033[92m"
5 ENDC = "\033[0m"
6 
7 
8 # 0x40058b: movzx eax, byte ptr [rax]
9 #
10 # When the instruction located in 0x40058b is executed,
11 # we taint the memory that RAX holds.
12 def cbeforeSymProc(instruction):
13  if instruction.getAddress() == 0x40058b:
14  rax = getCurrentRegisterValue(REG.RAX)
15  taintMemory(rax)
16 
17 
18 def cafter(instruction):
19  print '%#x: %s' %(instruction.getAddress(), instruction.getDisassembly())
20  for se in instruction.getSymbolicExpressions():
21  if se.isTainted() == True:
22  print '\t -> %s%s%s' %(GREEN, se.getAst(), ENDC)
23  else:
24  print '\t -> %s' %(se.getAst())
25  print
26 
27 
28 if __name__ == '__main__':
29 
30  # Set architecture
31  setArchitecture(ARCH.X86_64)
32 
33  # Start the symbolic analysis from the 'check' function
35 
36  insertCall(cbeforeSymProc, INSERT_POINT.BEFORE_SYMPROC)
37  insertCall(cafter, INSERT_POINT.AFTER)
38 
39  # Run the instrumentation - Never returns
40  runProgram()

Example - Runtime Register Modification

1 #!/usr/bin/env python2
2 ## -*- coding: utf-8 -*-
3 
16 
17 import sys
18 from pintool import *
19 from triton import *
20 
21 
22 def cb1(inst):
23  if inst.getAddress() == 0x4005f9:
24  setCurrentRegisterValue(REG.RAX, 0)
25 
26 def cb2(inst):
27  if inst.getAddress() == 0x4005f9:
28  print inst
29  for expr in inst.getSymbolicExpressions():
30  print '\t', expr
31 
32 
33 if __name__ == '__main__':
34  setArchitecture(ARCH.X86_64)
35  setupImageWhitelist(['crackme'])
37  insertCall(cb1, INSERT_POINT.BEFORE_SYMPROC)
38  insertCall(cb2, INSERT_POINT.BEFORE)
39  runProgram()

Example - Blacklist images

1 from triton import *
2 from pintool import *
3 
4 
5 def mycb(instruction):
6  print '%#x: %s' %(instruction.getAddress(), instruction.getDisassembly())
7  return
8 
9 
10 if __name__ == '__main__':
11 
12  setArchitecture(ARCH.X86_64)
13 
14  # libc and ld-linux will be unjited
15  setupImageBlacklist(["libc", "ld-linux"])
16 
18  insertCall(mycb, INSERT_POINT.BEFORE)
19  runProgram()

Example - Callback on image

1 from triton import *
2 from pintool import *
3 
4 # Output
5 #
6 # $ ./triton ./src/examples/pin/callback_image.py ./src/samples/ir_test_suite/ir
7 # ----------
8 # Image path: /dir/Triton/samples/ir_test_suite/ir
9 # Image base: 0x400000L
10 # Image size: 2101312
11 # ----------
12 # Image path: /lib64/ld-linux-x86-64.so.2
13 # Image base: 0x7fb9a14d9000L
14 # Image size: 2236744
15 # ----------
16 # Image path: /lib64/libc.so.6
17 # Image base: 0x7fb98b739000L
18 # Image size: 3766680
19 
20 
21 def image(imagePath, imageBase, imageSize):
22  print '----------'
23  print 'Image path: ', imagePath
24  print 'Image base: ', hex(imageBase)
25  print 'Image size: ', imageSize
26  return
27 
28 
29 if __name__ == '__main__':
30  # Set the architecture
31  setArchitecture(ARCH.X86_64)
32 
33  # Start the symbolic analysis from the Entry point
35 
36  # Add a callback.
37  insertCall(image, INSERT_POINT.IMAGE_LOAD)
38 
39  # Run the instrumentation - Never returns
40  runProgram()

Example - Callback on routine

1 from triton import *
2 from pintool import *
3 
4 # Output
5 #
6 # $ ./triton ./src/examples/pin/callback_routine.py ./src/samples/vulns/testSuite
7 # -> malloc(0x20)
8 # <- 0x8fc010
9 # -> malloc(0x20)
10 # <- 0x8fc040
11 # -> malloc(0x20)
12 # <- 0x8fc010
13 
14 
15 def mallocEntry(threadId):
16  sizeAllocated = getCurrentRegisterValue(REG.RDI)
17  print '-> malloc(%#x)' %(sizeAllocated)
18 
19 
20 def mallocExit(threadId):
21  ptrAllocated = getCurrentRegisterValue(REG.RAX)
22  print '<- %#x' %(ptrAllocated)
23 
24 
25 if __name__ == '__main__':
26 
27  # Set the architecture
28  setArchitecture(ARCH.X86_64)
29 
30  # Start the symbolic analysis from the Entry point
32 
33  # Add a callback.
34  insertCall(mallocEntry, INSERT_POINT.ROUTINE_ENTRY, "malloc")
35  insertCall(mallocExit, INSERT_POINT.ROUTINE_EXIT, "malloc")
36 
37  # Run the instrumentation - Never returns
38  runProgram()

Example - Callback on signals

1 from triton import *
2 from pintool import *
3 
4 # Output
5 #
6 # $ ./triton ./src/examples/pin/callback_signals.py ./src/samples/others/signals
7 # Signal 11 received on thread 0.
8 # ========================== DUMP ==========================
9 # rax: 0x00000000000000 ((_ zero_extend 32) (_ bv234 32))
10 # rbx: 0x00000000000000 UNSET
11 # rcx: 0x00000000001ba4 ((_ zero_extend 32) ((_ extract 31 0) ref!81))
12 # rdx: 0x0000000000000b ((_ sign_extend 32) ((_ extract 31 0) ref!34))
13 # rdi: 0x00000000001ba4 ((_ sign_extend 32) ((_ extract 31 0) ref!83))
14 # rsi: 0x00000000001ba4 ((_ sign_extend 32) ((_ extract 31 0) ref!90))
15 # rbp: 0x007fff097e3540 ((_ extract 63 0) ref!0)
16 # rsp: 0x007fff097e3528 (bvsub ((_ extract 63 0) ref!47) (_ bv8 64))
17 # rip: 0x007f3fa0735ea7 (_ bv139911251582629 64)
18 # r8: 0x007f3fa0a94c80 UNSET
19 # r9: 0x007f3fb671b120 UNSET
20 # r10: 0x00000000000000 UNSET
21 # r11: 0x007f3fa0735e70 UNSET
22 # r12: 0x00000000400460 UNSET
23 # r13: 0x007fff097e3620 UNSET
24 # r14: 0x00000000000000 UNSET
25 # r15: 0x00000000000000 UNSET
26 # xmm0: 0x000000ff000000 UNSET
27 # xmm1: 0x2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f UNSET
28 # xmm2: 0x00000000000000 UNSET
29 # xmm3: 0x00ff000000ff00 UNSET
30 # xmm4: 0x000000000000ff UNSET
31 # xmm5: 0x00000000000000 UNSET
32 # xmm6: 0x00000000000000 UNSET
33 # xmm7: 0x00000000000000 UNSET
34 # xmm8: 0x00000000000000 UNSET
35 # xmm9: 0x00000000000000 UNSET
36 # xmm10: 0x00000000000000 UNSET
37 # xmm11: 0x00000000000000 UNSET
38 # xmm12: 0x00000000000000 UNSET
39 # xmm13: 0x00000000000000 UNSET
40 # xmm14: 0x00000000000000 UNSET
41 # xmm15: 0x00000000000000 UNSET
42 # af: 0x00000000000000 (ite (= (_ bv16 64) (bvand (_ bv16 64) (bvxor ref!12 (bvxor ((_ extract 63 0) ref!0) (_ bv16 64))))) (_ bv1 1) (_ bv0 1))
43 # cf: 0x00000000000000 (_ bv0 1)
44 # df: 0x00000000000000 UNSET
45 # if: 0x00000000000001 UNSET
46 # of: 0x00000000000000 (_ bv0 1)
47 # pd: 0x00000000000001 (ite (= (parity_flag ((_ extract 7 0) ref!73)) (_ bv0 1)) (_ bv1 1) (_ bv0 1))
48 # sf: 0x00000000000000 (ite (= ((_ extract 31 31) ref!73) (_ bv1 1)) (_ bv1 1) (_ bv0 1))
49 # tf: 0x00000000000000 UNSET
50 # zf: 0x00000000000001 (ite (= ref!73 (_ bv0 32)) (_ bv1 1) (_ bv0 1))
51 
52 
53 
54 def signals(threadId, sig):
55  print 'Signal %d received on thread %d.' %(sig, threadId)
56  print '========================== DUMP =========================='
57  regs = getParentRegisters()
58  for reg in regs:
59  value = getCurrentRegisterValue(reg)
60  exprId = getSymbolicRegisterId(reg)
61  print '%s:\t%#016x\t%s' %(reg.getName(), value, (getSymbolicExpressionFromId(exprId).getAst() if exprId != SYMEXPR.UNSET else 'UNSET'))
62  return
63 
64 
65 if __name__ == '__main__':
66 
67  # Set architecture
68  setArchitecture(ARCH.X86_64)
69 
70  # Start the symbolic analysis from the Entry point
72 
73  # Add a callback.
74  insertCall(signals, INSERT_POINT.SIGNALS)
75 
76  # Run the instrumentation - Never returns
77  runProgram()

Example - Callback on syscalls

1 from triton import *
2 from pintool import *
3 
4 # Output
5 #
6 # $ ./triton examples/callback_syscall.py ./samples/crackmes/crackme_xor a
7 # sys_write(1, 7fb7f06e1000, 6)
8 # loose
9 #
10 
11 def my_callback_syscall_entry(threadId, std):
12  if getSyscallNumber(std) == SYSCALL.WRITE:
13  arg0 = getSyscallArgument(std, 0)
14  arg1 = getSyscallArgument(std, 1)
15  arg2 = getSyscallArgument(std, 2)
16  print 'sys_write(%x, %x, %x)' %(arg0, arg1, arg2)
17 
18 
19 if __name__ == '__main__':
20 
21  # Set the architecture
22  setArchitecture(ARCH.X86_64)
23 
24  # Start the symbolic analysis from the Entry point
26 
27  insertCall(my_callback_syscall_entry, INSERT_POINT.SYSCALL_ENTRY)
28 
29  # Run the instrumentation - Never returns
30  runProgram()