Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
TinyInst — 一个轻量级的动态插桩库 | Kitploit
工具/GitHubGitHub/googleprojectzero/tinyinst
动态分析 (沙盒)代码分析逆向工程调试器模糊测试二进制分析
GitHubgoogleprojectzero/tinyinst

TinyInst

一个轻量级的动态插桩库

查看仓库
1.4k140158天前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

TinyInst```

Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

root@kitploit:~
https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

root@kitploit:~
## TinyInst是什么?

TinyInst 是一个轻量级动态插桩库,可用于仅对进程中选定的模块进行插桩,而让进程的其他部分以原生方式运行。它易于理解、易于修改、易于利用。它并非设计为兼容所有目标(稍后会详细说明)。

### 它如何与 [DynamoRIO](https://dynamorio.org/) 和 [PIN](https://software.intel.com/en-us/articles/pintool) 比较?

TinyInst 并非旨在替代 DynamoRIO 和 PIN 等复杂的插桩框架,而是在需要更轻量级解决方案的场景下提供一种替代方案。TinyInst 假设目标行为良好(在下面解释的意义上),而更复杂的框架则不然。因此,你可能无法像[之前使用 DynamoRIO 所做的那样](https://www.slideshare.net/MaximShudrak/fuzzing-malware-for-fun-profit-applying-coverageguided-fuzzing-to-find-bugs-in-modern-malware)成功地对恶意软件运行 TinyInst。另一方面,如果目标因不需要插桩的模块而与其它框架不兼容,但需要插桩的模块行为良好,那么它可能与 TinyInst 配合使用。因为使用 TinyInst,大部分进程将以原生方式运行,进程启动时间更短,并且在目标进程在不需要插桩的模块中花费大量时间的情况下,性能可能优于其他解决方案。

### 它如何与 [Mesos](https://github.com/gamozolabs/mesos) 和 [TrapFuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz) 比较?

TinyInst 是一个完整的二进制重写解决方案,因此可以改变目标模块中的任意行为。例如,这使得它能够提取边覆盖率而不仅仅是基本块。此外,TinyInst 不依赖于其他软件(如 IDA Pro)来识别基本块。

### TinyInst 支持哪些操作系统?

TinyInst 可在 Windows(x86 和 x64)、macOS(x64 和 ARM64)、Linux(x64 和 ARM64)和 Android(ARM64)上运行。请参阅每个操作系统对应目录中的 README 以获取更多说明和限制。

### 哪些目标与 TinyInst 兼容?

TinyInst 假设所有被插桩的模块行为良好,具体而言:

- 不存在自修改代码
- 堆栈上的返回地址不会被程序直接访问
或者/并且(取决于设置)
- 没有数据存储在堆栈顶部之前(低于 ESP/RSP 指向的地址)。可以使用 `-stack_offset` 标志将此条件放宽为“没有数据在 (ESP/RSP - arbitrary_offset) 之前”。

TinyInst 还要求目标进程启用 DEP/NX。如果尚未启用,你可以使用 `-force_dep` 标志强制启用。然而,在极少数情况下,目标确实需要关闭 DEP 才能正常运行,强制启用可能会导致其行为异常。

### 性能开销是多少?

根据早期的图像解码测量,在默认 TinyInst 设置下,行为良好的 64 位目标的性能开销约为 15%,而使用示例覆盖收集客户端时约为 20%。请注意,这不包括初始插桩模块所引入的超时。有关更多详细信息,请参阅下面的性能提示。

## 构建 TinyInst

1. 打开终端并设置构建环境(例如,在 Windows 上,运行 vcvars64.bat / vcvars32.bat)

2. 导航到包含源代码的目录

3. 运行以下命令(根据要构建的 IDE 版本和平台更改生成器):

#### Windows```
mkdir build
cd build
cmake -G "Visual Studio 16 2019" -A x64 ..
cmake --build . --config Release

macOS```

mkdir build cd build cmake -G Xcode .. cmake --build . --config Release

root@kitploit:~
#### Linux```
mkdir build
cd build
cmake ..
cmake --build . --config Release

面向安卓的交叉编译```

mkdir build cd build cmake -DCMAKE_TOOLCHAIN_FILE=</path/to/android/ndk>build/cmake/android.toolchain.cmake -DANDROID_NDK=</path/to/android/ndk> -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM= .. cmake --build . --config Release

root@kitploit:~
注意 #1:64位构建也可以在Windows和Linux操作系统上针对32位目标运行

注意 #2:在64位Windows上创建32位构建时遇到问题,因为环境未正确设置且缺少库?请改为在Visual Studio中打开生成的.sln文件并从中构建,而不是运行cmake --build。另请注意,64位构建可以针对32位目标工作,因此可能不需要创建32位构建。

## 使用TinyInst

TinyInst主要作为库在其他程序中使用。

TinyInst客户端被编写为TinyInst类的子类。然后客户端可以覆盖它需要的API方法。API方法定义如下。

创建客户端后,必须通过调用以下命令使用命令行选项初始化它:

`void init(int argc, char **argv);`

命令行选项定义如下,客户端也可以定义自己的选项。之后,要运行和控制一个被插桩的程序,可以使用以下函数:

`DebuggerStatus Run(int argc, char **argv, uint32_t timeout);`
`DebuggerStatus Attach(unsigned int pid, uint32_t timeout);`

这些函数要么运行一个程序(使用指定的命令行),要么附加到一个正在运行的程序。如果未指定目标方法,目标将继续运行,直到程序退出、程序崩溃或超时(以毫秒为单位)到期。如果定义了目标方法,TinyInst将在进入目标方法时以及目标方法返回时返回,允许调用者执行其他任务。

当`Run`和`Attach`返回时,如果目标进程仍然存活,可以使用以下函数来终止进程或继续执行:

`DebuggerStatus Kill();`

`DebuggerStatus Continue(uint32_t timeout);`

TinyInst附带一个示例覆盖率二进制文件,可以使用以下命令调用:

`<options> -- <target command line>`

Windows上的示例:

`litecov.exe -instrument_module notepad.exe -coverage_file coverage.txt -- notepad.exe`

## 插桩API

### 调试器事件回调

这些回调仅用于信息目的,客户端不应在此期间发出任何插桩代码。客户端在处理这些事件之前必须调用超类中定义的同一处理程序。

`OnProcessCreated`
当目标进程被创建或附加时调用。

`OnProcessExit`
当目标进程退出时调用。

`OnProcessEntrypoint`
当进程(主二进制文件)入口点被到达时调用。

`OnTargetMethodReached`
如果定义了目标方法,则在首次到达目标方法时调用。

`OnModuleLoaded`
当模块被加载时调用。为每个模块调用,不仅仅是插桩的模块。

`OnModuleUnloaded`
当模块被卸载时调用。为每个模块调用,不仅仅是插桩的模块。

`OnException`
当遇到异常时调用。客户端必须返回true(如果异常已被处理)或父类上相同方法的结果。

### 插桩回调

在这些回调期间,客户端可以通过调用`WriteCode()`向目标添加代码。请注意,客户端负责保存和恢复任何上下文(例如在插入的代码中破坏的寄存器和标志)。

`InstrumentBasicBlock`
可用于插入将在特定基本块上运行的代码。

`InstrumentEdge`
可用于插入将在特定边上运行的代码。注意:出于性能考虑,此回调仅在非确定性边(即条件跳转)和间接跳转/调用(例如`call rax`)上触发。对于给定前一个基本块始终知道下一个基本块的边(例如`jmp offset`,`call offset`),不会触发回调。

`InstrumentInstruction`
可用于修改指令或在指令之前插入代码。根据返回码,原始指令将在回调之后发出或不发出。

### 其他回调

`OnModuleEntered`
当控制流从另一个模块传输到已插桩的模块时调用。

`OnModuleInstrumented`
当模块被插桩时调用。这通常发生在进程入口点到达时(如果未定义目标方法)或目标方法到达时(如果已定义)。客户端可以在此处初始化其与插桩相关的数据。

`OnModuleUninstrumented`
当插桩数据不再有效且需要清除时调用。请注意,这与模块卸载不同,因为默认情况下插桩在模块卸载/重新加载之间保持有效。此回调可用于清除客户端中与插桩相关的任何数据。

### 钩子API

除了上面记录的通用API之外,TinyInst还实现了一个更适合检查和修改单个函数行为的钩子API。此API在[单独页面](https://github.com/googleprojectzero/TinyInst/blob/master/hook.md)上有文档说明。

## 命令行选项

### 插桩相关

`-instrument_module [module name]` 指定要插桩的模块,可以指定多个`-instrument_module`选项来插桩多个模块。

`-instrument_transitive [module name]` 类似于`-instrument_module`,不同之处在于只有从其他已插桩模块进入的代码才会被插桩运行。主要用作模块1->模块2->模块1等调用的优化,其中插桩整个模块2并不重要,但模块2->模块1的入口会导致速度减慢。

`-indirect_instrumentation [none|local|global|auto]` 用于间接跳转/调用的插桩类型。

`-patch_return_addresses` - 将返回地址替换为原始值,导致返回使用指定的`-indirect_instrumentation`方法进行插桩。

`-generate_unwind` - 为插桩代码生成栈展开数据(用于更快的C++异常处理)。请注意,在某些较旧的Windows版本上可能无法正常工作。

`-persist_instrumentation_data` (默认=true) 在模块卸载/重新加载时不重新插桩模块。仅当模块加载到与之前相同的地址时才有效。

`-instrument_cross_module_calls` (默认=true) 如果指定了多个`-instrument_module`模块,并且一个模块调用另一个模块,则跳转到另一个模块的插桩代码而不引发异常(这会导致速度减慢)。

`-stack_offset` (默认=0) 在栈上保存上下文时,在栈顶(栈指针之前)保留这么多字节不变。

`-patch_module_entries [off|data|code|all]` 尝试通过搜索指向先前检测到的入口点的指针并将它们替换为插桩后的对应部分来解决由于过多模块入口导致的速度减慢。该标志的值控制搜索这些指针的位置。警告:启用此选项可能引入目标的不稳定性。

### 调试相关

`-trace_debug_events` - 打印调试器事件(模块加载、异常等)

`-trace_basic_blocks` - 打印执行的基本块

`-trace_module_entries` - 打印所有进入插桩代码的入口

`-trace_syscalls` - [仅Linux/Android] 允许客户端通过`OnSyscall()` / `OnSyscallEnd()`回调接收系统调用开始/结束事件。

`-full_address_map` - 维护插桩代码中地址到原始代码中地址的指令级映射。内存占用大,但有助于调试。

### 目标方法和持久性

TinyInst允许用户定义目标方法。如果定义了目标方法,则在第一次到达目标方法之前,不会插桩任何代码(所有代码将以原生方式运行)。此外,TinyInst将在目标方法入口和出口处中断执行。

`-target_module` - 包含目标方法的模块

`-target_method` - 目标方法的名称。这仅在目标方法已导出或具有目标模块的符号时才有效。

`-target_offset` - 当无法通过名称指定目标方法时使用。目标方法相对于模块基址的相对地址。

`-loop` - 如果指定此标志,TinyInst将在无限循环中运行目标方法(或直到调用Kill()或进程因其他原因终止)。函数参数将在迭代之间保存和恢复。这主要用于强制持久性以进行模糊测试。

`-nargs` - 在迭代之间保存的目标方法参数数量。与`-loop`一起使用。

`-callcon [ms64|stdcall|fastcall|thiscall]` - 目标方法使用的调用约定。与`-loop`一起使用。

### 其他

`-target_env key=value` - [目前仅macOS和Linux/Android] 指定要传递给目标进程的附加环境变量。可以指定多个`-target_env`选项以传递多个环境变量。

`-force_dep` - [仅Windows] 强制启用目标进程的DEP。

## 覆盖率模块

TinyInst附带一个(示例)覆盖率模块`LiteCov`。该模块可以收集基本块或边覆盖率(由`-covtype`标志控制)。此外,通过指定`-cmp_coverage`标志,该模块可以提取“比较”覆盖率(计算cmp/sub指令中匹配的字节数)。

覆盖率模块的一个特殊功能是,目标进程中的覆盖率缓冲区最初被分配为只读,导致首次遇到新覆盖率时引发异常。结合忽略特定子集覆盖率的选项,这可以快速查询使用给定输入运行目标是否产生了新覆盖率。

## TinyInst如何工作?

TinyInst构建在自定义调试器之上。调试器监视目标进程的事件,例如模块加载、断点命中、异常触发等。如果指定了目标方法,调试器还实现断点和持久性。

当要插桩的模块被加载时,最初以以下方式“插桩”:

- 模块中所有可执行区域都被标记为不可执行,同时保留其他权限(读/写)不变。这会在控制流到达插桩模块时引发异常,由调试器捕获和处理。

- 在原始模块地址范围的2GB内分配一个可执行内存区域。模块的插桩/重写代码将放置在此处。2GB很重要,因为它允许使用[rip+offset]形式寻址的所有指令被替换为[rip+fixed_offset]。

每当进入一个插桩模块时(无论是第一次还是其他任何时候),被命中的基本块将被插桩,连同所有可以通过递归跟随条件分支以及直接调用和跳转(例如jmp offset,call offset)可靠发现的基本块。

这足以运行插桩代码,因为:

- 所有直接跳转/调用将落在插桩代码中的正确位置

- 所有间接跳转/调用(例如call rax)将落在其原始代码位置,这会导致异常,调试器通过将指令指针替换为插桩代码中的相应位置来解决。

然而,虽然这可行,但请注意它会在每次间接调用/跳转的目标位于插桩模块中时引发异常。由于异常处理很慢,插桩具有大量间接性(例如C++中的虚方法、函数指针)的目标在没有额外插桩的情况下会很慢。

### 插桩间接调用和跳转

TinyInst可以插桩间接调用和跳转,以避免在(已经见过的)间接目标上引发异常。插桩后的调用/跳转不是跳转到原始目标,而是跳转到存根链表的头部。每个存根包含一对(original_target,translated_target)。它测试跳转/调用目标是否匹配original_target,如果匹配,控制流被导向translated_target。否则,跳转到下一个存根。如果到达链表末尾,意味着尚未见过该跳转/调用目标。这将导致一个被调试器捕获的断点,通过创建另一个存根并将其插入链表来解决。

此机制可以通过2种方式实现:
- 每个调用点(本地)链表
- 所有间接跳转/调用使用的全局哈希表

全局哈希表具有更好的性能。本地(每个调用点链表)允许在间接调用/跳转上获得正确的边(带有正确的源地址)。

请注意,在现代Windows上,由于CFG,所有间接跳转/调用都来自同一位置,因此对于使用CFG编译的二进制文件,无论如何(没有某种特殊处理)都无法获得精确的边。这加上性能优势,是TinyInst中处理间接调用/跳转的默认方法使用全局哈希表的原因。

### 返回地址修补

默认情况下,当在插桩代码中发生调用时,写入的返回地址将是*插桩代码*中的下一条指令。这在大多数情况下可以正常工作,但如果目标进程出于返回以外的目的访问返回地址,则会导致问题。一个显著的例子是64位操作系统上异常处理期间的栈展开。因此,需要捕获异常的目标默认情况下无法与TinyInst正常工作。

在大多数情况下,可以通过添加`-generate_unwind`标志来解决,这会导致TinyInst为目标进程生成并注册栈展开/异常处理元数据。请注意,`-generate_unwind`可能在某些较旧的Windows版本上无法正常工作,因为它需要UNWIND_INFO版本2。

TinyInst还有一个选项(通过`-patch_return_addresses`标志公开),可以在发生调用时将返回地址重写为未插桩代码中的相应值。然而请注意,此选项引入了相当多的开销,因为它会在每次从未插桩模块返回到插桩模块时导致上下文切换(反向边)。

## 性能提示

TinyInst最大的开销来自每当从未插桩模块进入插桩模块时引发的异常。您可以使用`-trace_module_entries`标志看到这些异常被触发。应尽可能使用间接跳转/调用插桩,并尽可能避免使用返回插桩。TinyInst在相当自包含的模块(或模块组)上性能最佳。例如,如果您有两个模块A和B,其中A经常调用B,但只有B被插桩,这会导致大量速度减慢。通过同时插桩A和B可以获得更好的性能。

## 调试提示

使用`-trace_basic_blocks`查看正在执行的基本块。您将看到插桩代码中的地址和未插桩代码中的相应地址。

使用OnException()回调检查崩溃发生时的程序状态。

## 免责声明

这不是官方Google产品。
下载工具