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()
关键点:
getTritonContext()获取 Triton 上下文实例startAnalysisFromEntry()从程序入口开始分析insertCall()设置回调函数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()
关键点:
taintMemory()标记内存区域为污点源isTainted()检查指令是否涉及污点数据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()
关键点:
takeSnapshot()保存当前状态restoreSnapshot()恢复保存的状态convertRegisterToSymbolicVariable()将寄存器值设为符号量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()
关键点:
getPathConstraints()获取路径约束getBranchConstraints()获取分支约束isMultipleBranches()检查是否多分支getModel()求解约束模型
五、性能优化技巧
- 使用
setupImageWhitelist()限制分析范围 - 不需要时关闭符号执行和污点分析引擎
- 合理设置插桩范围,避免全程序分析
- 使用快照功能避免重复分析