Triton 学习 - pintool 篇
字数 1341 2025-08-05 08:16:32

Triton Pintool 使用教程

一、Pin 工具简介

Pin 是 Intel 开发的一个动态二进制插桩工具,它可以在程序运行时通过回调函数机制监控程序的执行。Triton 为 Pin 提供了 Python 接口,使得我们可以用 Python 编写 Pin 工具。

主要功能:

  • 代码覆盖率分析
  • 污点分析
  • 符号执行
  • 程序行为监控

二、基本使用方法

1. 简单指令计数示例

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pintool import *
from triton import ARCH

count = 0

def mycb(inst):
    global count
    count += 1

def fini():
    print("Instruction count : ", count)

if __name__ == '__main__':
    ctx = getTritonContext()
    ctx.enableSymbolicEngine(False)
    ctx.enableTaintEngine(False)
    
    # 从程序入口开始插桩
    startAnalysisFromEntry()
    
    # 在每条指令执行前调用 mycb
    insertCall(mycb, INSERT_POINT.BEFORE)
    
    # 程序运行完毕后的回调函数
    insertCall(fini, INSERT_POINT.FINI)
    
    runProgram()

关键点:

  1. getTritonContext() 获取 Triton 上下文实例
  2. startAnalysisFromEntry() 从程序入口开始分析
  3. insertCall() 设置回调函数
  4. runProgram() 启动程序执行

2. 插桩起始点设置

Triton 支持多种插桩起始点设置方式:

startAnalysisFromAddress(addr)    # 从指定地址开始
startAnalysisFromEntry()          # 从程序入口(start函数)
startAnalysisFromOffset(offset)   # 从程序偏移处开始
startAnalysisFromSymbol('func')   # 从指定符号(函数)开始

3. 回调点类型

Triton 支持多种回调点:

回调点 描述
INSERT_POINT.BEFORE 指令执行前
INSERT_POINT.AFTER 指令执行后
INSERT_POINT.BEFORE_SYMPROC 指令符号化处理前
INSERT_POINT.FINI 程序运行结束
INSERT_POINT.ROUTINE_ENTRY 进入函数时
INSERT_POINT.ROUTINE_EXIT 退出函数时
INSERT_POINT.IMAGE_LOAD 镜像加载到内存时
INSERT_POINT.SIGNALS 出现信号时
INSERT_POINT.SYSCALL_ENTRY 系统调用执行前
INSERT_POINT.SYSCALL_EXIT 系统调用执行后

三、污点分析实战

1. 基本污点分析示例

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from triton import ARCH, MemoryAccess, OPERAND
from pintool import *

Triton = getTritonContext()

def cbeforeSymProc(instruction):
    if instruction.getAddress() == 0x400556:
        rdi = getCurrentRegisterValue(Triton.registers.rdi)
        # 标记内存为污点源
        Triton.taintMemory(MemoryAccess(rdi, 8))

def cafter(inst):
    if inst.isTainted():
        if inst.isMemoryRead():
            for op in inst.getOperands():
                if op.getType() == OPERAND.MEM:
                    print("read:0x{:08x}, size:{}".format(
                        op.getAddress(), op.getSize()))
        if inst.isMemoryWrite():
            for op in inst.getOperands():
                if op.getType() == OPERAND.MEM:
                    print("write:0x{:08x}, size:{}".format(
                        op.getAddress(), op.getSize()))

if __name__ == '__main__':
    startAnalysisFromSymbol('check')
    insertCall(cbeforeSymProc, INSERT_POINT.BEFORE_SYMPROC)
    insertCall(cafter, INSERT_POINT.AFTER)
    runProgram()

关键点:

  1. taintMemory() 标记内存区域为污点源
  2. isTainted() 检查指令是否涉及污点数据
  3. isMemoryRead()/isMemoryWrite() 检查内存访问类型

四、符号执行实战

1. 快照功能的使用

Triton 支持快照功能,可以在执行前保存状态,之后恢复:

# -*- coding: utf-8 -*-
from triton import ARCH
from pintool import *

password = dict()
cur_char_ptr = None
Triton = getTritonContext()

def csym(instruction):
    global cur_char_ptr
    
    # 目标函数入口,第一次执行拍快照
    if instruction.getAddress() == 0x400556 and isSnapshotEnabled() == False:
        takeSnapshot()
        return
    
    if instruction.getAddress() == 0x400574:
        rax = getCurrentRegisterValue(Triton.registers.rax)
        cur_char_ptr = rax
        
        # 如果已求解,设置解
        if rax in password:
            setCurrentMemoryValue(rax, password[rax])
        return
    
    # check函数结尾,检查返回值
    if instruction.getAddress() == 0x4005b2:
        rax = getCurrentRegisterValue(Triton.registers.rax)
        
        # 返回值不为0,恢复快照继续求解
        if rax != 0:
            restoreSnapshot()
        else:
            disableSnapshot()
            # 打印解
            addrs = password.keys()
            addrs.sort()
            answer = ""
            for addr in addrs:
                c = chr(password[addr])
                answer += c
                print("0x{:08x}: {}".format(addr, c))
            print("answer: {}".format(answer))
        return

def cafter(instruction):
    global password
    
    # 将取出的字符设置为符号量
    if instruction.getAddress() == 0x400574:
        var = Triton.convertRegisterToSymbolicVariable(Triton.registers.rax)
        return
    
    # 开始求解约束
    if instruction.getAddress() == 0x400597:
        astCtxt = Triton.getAstContext()
        zf = Triton.getRegisterAst(Triton.registers.zf)
        # 正确时需要zf == 1
        cstr = astCtxt.land([
            Triton.getPathConstraintsAst(),
            zf == 1
        ])
        models = Triton.getModel(cstr)
        for k, v in list(models.items()):
            password.update({cur_char_ptr: v.getValue()})
        return

def fini():
    print('[+] Analysis done!')
    return

if __name__ == '__main__':
    setupImageWhitelist(['crackme_xor'])
    startAnalysisFromAddress(0x0400556)
    insertCall(cafter, INSERT_POINT.AFTER)
    insertCall(csym, INSERT_POINT.BEFORE_SYMPROC)
    insertCall(fini, INSERT_POINT.FINI)
    runProgram()

关键点:

  1. takeSnapshot() 保存当前状态
  2. restoreSnapshot() 恢复保存的状态
  3. convertRegisterToSymbolicVariable() 将寄存器值设为符号量
  4. getModel() 求解约束条件

2. 基于分支约束的求解

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from triton import *
from pintool import *

TAINTING_SIZE = 10
Triton = getTritonContext()

def tainting(threadId):
    rdi = getCurrentRegisterValue(Triton.registers.rdi)  # argc
    rsi = getCurrentRegisterValue(Triton.registers.rsi)  # argv
    
    # 将argv最后一个参数设置为符号量
    while rdi > 1:
        argv = getCurrentMemoryValue(rsi + ((rdi-1) * CPUSIZE.QWORD), CPUSIZE.QWORD)
        offset = 0
        while offset != TAINTING_SIZE:
            Triton.taintMemory(argv + offset)
            concreteValue = getCurrentMemoryValue(argv + offset)
            Triton.setConcreteMemoryValue(argv + offset, concreteValue)
            Triton.convertMemoryToSymbolicVariable(MemoryAccess(argv + offset, CPUSIZE.BYTE))
            offset += 1
        print('[+] %02d bytes tainted from the argv[%d] (%#x) pointer' %(offset, rdi-1, argv))
        rdi -= 1
    return

def fini():
    pco = Triton.getPathConstraints()
    astCtxt = Triton.getAstContext()
    
    for pc in pco:
        if pc.isMultipleBranches():
            b1 = pc.getBranchConstraints()[0]['constraint']
            b2 = pc.getBranchConstraints()[1]['constraint']
            seed = list()
            
            # 分支1的模型
            models = Triton.getModel(b1)
            for k, v in list(models.items()):
                seed.append(v)
            
            # 分支2的模型
            models = Triton.getModel(b2)
            for k, v in list(models.items()):
                seed.append(v)
            
            if seed:
                print('进入分支B1的要求: %s (%c) | 进入分支B2的要求: %s (%c)' %(
                    seed[0], chr(seed[0].getValue()), 
                    seed[1], chr(seed[1].getValue())))
    return

if __name__ == '__main__':
    # 从main函数开始分析
    startAnalysisFromSymbol('main')
    
    # 内存对齐
    Triton.enableMode(MODE.ALIGNED_MEMORY, True)
    
    # 只分析目标二进制
    setupImageWhitelist(['crackme_xor'])
    
    # 添加回调
    insertCall(tainting, INSERT_POINT.ROUTINE_ENTRY, 'main')
    insertCall(fini, INSERT_POINT.FINI)
    
    # 运行
    runProgram()

关键点:

  1. getPathConstraints() 获取路径约束
  2. getBranchConstraints() 获取分支约束
  3. isMultipleBranches() 检查是否多分支
  4. getModel() 求解约束模型

五、性能优化技巧

  1. 使用 setupImageWhitelist() 限制分析范围
  2. 不需要时关闭符号执行和污点分析引擎
  3. 合理设置插桩范围,避免全程序分析
  4. 使用快照功能避免重复分析

六、参考资料

  1. Triton Python 示例
  2. Triton Pin 示例
  3. Triton 官方文档
Triton Pintool 使用教程 一、Pin 工具简介 Pin 是 Intel 开发的一个动态二进制插桩工具,它可以在程序运行时通过回调函数机制监控程序的执行。Triton 为 Pin 提供了 Python 接口,使得我们可以用 Python 编写 Pin 工具。 主要功能: 代码覆盖率分析 污点分析 符号执行 程序行为监控 二、基本使用方法 1. 简单指令计数示例 关键点: getTritonContext() 获取 Triton 上下文实例 startAnalysisFromEntry() 从程序入口开始分析 insertCall() 设置回调函数 runProgram() 启动程序执行 2. 插桩起始点设置 Triton 支持多种插桩起始点设置方式: 3. 回调点类型 Triton 支持多种回调点: | 回调点 | 描述 | |--------|------| | INSERT_ POINT.BEFORE | 指令执行前 | | INSERT_ POINT.AFTER | 指令执行后 | | INSERT_ POINT.BEFORE_ SYMPROC | 指令符号化处理前 | | INSERT_ POINT.FINI | 程序运行结束 | | INSERT_ POINT.ROUTINE_ ENTRY | 进入函数时 | | INSERT_ POINT.ROUTINE_ EXIT | 退出函数时 | | INSERT_ POINT.IMAGE_ LOAD | 镜像加载到内存时 | | INSERT_ POINT.SIGNALS | 出现信号时 | | INSERT_ POINT.SYSCALL_ ENTRY | 系统调用执行前 | | INSERT_ POINT.SYSCALL_ EXIT | 系统调用执行后 | 三、污点分析实战 1. 基本污点分析示例 关键点: taintMemory() 标记内存区域为污点源 isTainted() 检查指令是否涉及污点数据 isMemoryRead() / isMemoryWrite() 检查内存访问类型 四、符号执行实战 1. 快照功能的使用 Triton 支持快照功能,可以在执行前保存状态,之后恢复: 关键点: takeSnapshot() 保存当前状态 restoreSnapshot() 恢复保存的状态 convertRegisterToSymbolicVariable() 将寄存器值设为符号量 getModel() 求解约束条件 2. 基于分支约束的求解 关键点: getPathConstraints() 获取路径约束 getBranchConstraints() 获取分支约束 isMultipleBranches() 检查是否多分支 getModel() 求解约束模型 五、性能优化技巧 使用 setupImageWhitelist() 限制分析范围 不需要时关闭符号执行和污点分析引擎 合理设置插桩范围,避免全程序分析 使用快照功能避免重复分析 六、参考资料 Triton Python 示例 Triton Pin 示例 Triton 官方文档