Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
TinyInst — Легковесная библиотека динамической инструментации | Kitploit
Инструменты/GitHubGitHub/googleprojectzero/tinyinst
Динамический анализ (песочница)Анализ КодаОбратная инженерияОтладчикиФаззингАнализ Бинарных Файлов
GitHubgoogleprojectzero/tinyinst

TinyInst

Легковесная библиотека динамической инструментации

Репозиторий
1.4k1405 месяцев назадПроверено 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 предполагает, что цель ведёт себя хорошо (в смысле, объяснённом ниже), что не является обязательным для более сложных фреймворков. Таким образом, вам, вероятно, не удастся успешно запустить TinyInst против вредоносного ПО, как [ранее делалось с DynamoRIO](https://www.slideshare.net/MaximShudrak/fuzzing-malware-for-fun-profit-applying-coverageguided-fuzzing-to-find-bugs-in-modern-malware). С другой стороны, если цель не работает с другими фреймворками из-за модуля, который не требует инструментации, а инструментируемый модуль ведёт себя хорошо, она может работать с 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). Это условие можно смягчить до «нет данных перед (ESP/RSP — произвольное_смещение)» с помощью флага `-stack_offset`.

TinyInst также требует, чтобы для целевого процесса был включён DEP/NX. Если это не так, можно использовать флаг `-force_dep`, чтобы принудительно включить его. Однако в маловероятном случае, когда цели действительно требуется отключение DEP для корректной работы, принудительное включение может привести к неправильному поведению.

### Какое влияние на производительность?

Согласно ранним измерениям при декодировании изображений на хорошо ведущей себя 64-битной цели с настройками TinyInst по умолчанию, накладные расходы на производительность составляли около 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

Кросс-компиляция для Android```

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-битная сборка также будет работать с 32-битными целями в операционных системах Windows и Linux.

Примечание №2: Возникают проблемы при создании 32-битной сборки на 64-битной Windows из-за неправильной настройки среды и отсутствия библиотек? Откройте сгенерированный файл .sln в Visual Studio и собирайте оттуда, вместо запуска 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 [имя модуля]` указывает, какой модуль инструментировать; можно указать несколько параметров `-instrument_module` для инструментирования нескольких модулей.

`-instrument_transitive [имя модуля]` аналогичен `-instrument_module`, за исключением того, что инструментированным будет выполняться только код, вошедший из других инструментированных модулей. В основном используется как оптимизация для вызовов вида module1->module2->module1, где не важно инструментировать весь модуль module2, но входы module2->module1 вызывают замедление.

`-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/sub) с помощью флага `-cmp_coverage`.

Особенность модуля покрытия заключается в том, что буфер покрытия в целевом процессе изначально выделяется как read-only, вызывая исключение при первом обнаружении нового покрытия. В сочетании с опцией игнорирования определенного подмножества покрытия это позволяет быстро проверить, привел ли запуск цели с заданным входным данным к новому покрытию или нет.

## Как работает TinyInst?

TinyInst построен поверх пользовательского отладчика. Отладчик следит за целевым процессом на предмет событий, таких как загрузка модулей, срабатывание точек останова, возникновение исключений и т.д. Отладчик также реализует точки останова и персистентность, если указан целевой метод.

Когда загружается модуль для инструментирования, он изначально «инструментируется» следующим образом:

- Все исполняемые области модуля помечаются как неисполняемые, при этом сохраняются другие разрешения (чтение/запись) как и было изначально. Это вызывает исключение всякий раз, когда поток управления достигает инструментированного модуля, которое перехватывается и обрабатывается отладчиком.

- В пределах 2 ГБ от исходного диапазона адресов модуля выделяется исполняемая область памяти. Здесь будет размещен инструментированный/перезаписанный код модуля. 2 ГБ важны, так как это позволяет заменить все инструкции, использующие адресацию в форме [rip+offset], на [rip+fixed_offset].

Всякий раз, когда выполняется вход в инструментированный модуль (в первый раз или в любой другой), инструментируется попавший базовый блок, а также все базовые блоки, которые можно надежно обнаружить путем рекурсивного следования по условным ветвям, а также прямым вызовам и переходам (например, jmp offset, call offset).

Этого достаточно для выполнения инструментированного кода, потому что:

- все прямые переходы/вызовы будут попадать в инструментированный код в правильное место.

- все косвенные переходы/вызовы (например, call rax) будут попадать в свои исходные местоположения кода, что вызывает исключение, которое отладчик разрешает, заменяя указатель инструкции на соответствующее местоположение в инструментированном коде.

Однако, хотя это и работает, обратите внимание, что это будет вызывать исключение при каждом косвенном вызове/переходе, цель которого находится в инструментированном модуле. Поскольку обработка исключений медленная, инструментирование целей с большим количеством косвенности (например, виртуальные методы в C++, указатели на функции) будет медленным без дополнительного инструментирования.

### Инструментирование косвенных вызовов и переходов

TinyInst может инструментировать косвенные вызовы и переходы, чтобы избежать исключений на (уже виденных) косвенных целях. Инструментированный вызов/переход вместо перехода к исходной цели переходит к голове связанного списка заглушек. Каждая заглушка содержит пару (original_target, translated_target). Она проверяет, совпадает ли цель перехода/вызова с original_target, и если да, то поток управления направляется в translated_target. В противном случае она переходит к следующей заглушке. Если достигнут конец списка, это означает, что цель перехода/вызова ранее не встречалась. Это вызовет точку останова, которая перехватывается отладчиком и разрешается созданием еще одной заглушки и вставкой ее в список.

Этот механизм может быть реализован двумя способами:

- список по месту вызова (локальный)

- глобальная хеш-таблица, используемая всеми косвенными переходами/вызовами

Глобальная хеш-таблица обеспечивает лучшую производительность. Локальный список (по месту вызова) позволяет получать правильные ребра (с правильным исходным адресом) при косвенных вызовах/переходах.

Обратите внимание, что в современных 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.
Скачать инструмент