Содержание статьи
- Основные концепции
- Базовые блоки
- Управление симуляцией
- Автоматический выбор путей
- Символическое исполнение
- Символические функции
- Восстановление серийного номера
- Выводы
Angr — эмулятор на стероидах. Он кросс‑платформенный и поддерживает большинство популярных архитектур: с ним на Linux можно искать уязвимости в PE32, а на Windows — ковырять прошивки роутеров. В этой статье я на примере работы в Linux покажу, как его использовать.
Например, возьмем код из моей статьи про Intel Pin:
#include <stdio.h>#include <string.h>int main(int argc, char* argv[]){ if (argc == 2) { if (strcmp(argv[1], "secret") == 0) { printf("You did it!n"); } else { printf("Better luck next timen"); } }}
import sysimport angrimport claripyproject = angr.Project('get_pass.bin')arg = claripy.BVS('arg', 8*10)initial_state = project.factory.entry_state(args=['./a.out', arg])initial_state.options.add('SYMBOL_FILL_UNCONSTRAINED_MEMORY')def is_successful(state): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b'You did it' in stdout_outputsimulation = project.factory.simgr(initial_state)simulation.explore(find=is_successful)if simulation.found: solution_state = simulation.found[0] solution = solution_state.solver.eval(arg, cast_to=bytes).decode() print('Password:', solution)
Запустив скрипт, за секунду получаем искомый пароль.
$ python angr_get_pass.pyPassword: secret
Не пересказывая всю документацию, объясню, что необходимо для старта.
Первым делом ставим Angr (потребуется Python 3.8 или новее):
Angr спроектирован для работы из консоли. Документация зачастую не раскрывает всех возможностей, так что советую изучать инструмент «живьем». По каждому объекту в интерактивном режиме можно получить справку из docstring командой help(project) (или project?, если у тебя iPython).
Пользоваться справкой можно так же, как и man: управление — на стрелках, выход через q.
Ну а смотреть поля и методы классов удобно через автодополнение: допиши к названию класса точку и пару раз нажми Tab.
Основные концепции
Все начинается с класса Project, его создание — это начало взаимодействия с Angr. Проект отвечает за загрузку и первичный анализ.
Передаем путь до исследуемого файла и, чтобы начать симуляцию со старта программы, создаем новое состояние через factory.entry_state. Фабрика создает экземпляры основных классов. Состояние — это объект SimState, фактически — снимок виртуальной машины. Он содержит блок кода, память, регистры, стек вызовов и другие вещи, реализуемые как плагины к SimState.
initial_state.regs.rip <BV64 0x401080>
Используемые в симуляции данные хранятся в битовом массиве bitvector. Он строго ограничен по длине и может быть переполнен, то есть ведет себя как процессорный регистр. Его размер всегда указывается в битах.
Существует два основных типа: BVV (bit-vector value) и BVS (bit-vector symbol). Первый представляет конкретные значения чисел. Второй содержит только имя и размер. Это основа символического исполнения, конкретного значения здесь нет.
Базовые блоки
Каждое состояние связано с конкретным блоком кода. Местный базовый блок — это набор инструкций, который заканчивается командой передачи управления. Каждый шаг эмуляции перемещает нас к следующему блоку, создавая новое состояние.