惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
The GitHub Blog
The GitHub Blog
美团技术团队
Know Your Adversary
Know Your Adversary
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The Register - Security
The Register - Security
Stack Overflow Blog
Stack Overflow Blog
Attack and Defense Labs
Attack and Defense Labs
G
Google Developers Blog
I
InfoQ
博客园 - 司徒正美
T
Troy Hunt's Blog
Google DeepMind News
Google DeepMind News
J
Java Code Geeks
MongoDB | Blog
MongoDB | Blog
博客园 - 聂微东
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
S
Security Affairs
M
MIT News - Artificial intelligence
Simon Willison's Weblog
Simon Willison's Weblog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Tailwind CSS Blog
量子位
Vercel News
Vercel News
月光博客
月光博客
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
L
LangChain Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
L
LINUX DO - 最新话题
F
Full Disclosure
The Hacker News
The Hacker News
Hacker News: Ask HN
Hacker News: Ask HN
T
Tor Project blog
A
Arctic Wolf
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Forbes - Security
Forbes - Security
IT之家
IT之家
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Y
Y Combinator Blog
GbyAI
GbyAI
B
Blog RSS Feed
V
Visual Studio Blog
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Ghidra для реверса прошивок: практический гайд по восстановлению логики бинарника
Ксения Мосеенкова · 2026-06-25 · via Все публикации подряд на Хабре

Средний

10 мин

436

Я начал заниматься инфобезопасностью в тот же год, когда NSA осчастливило нас Ghidra. С тех пор она уверенно стала самым востребованным инструментом в моём арсенале для реверс-инжиниринга и исследования уязвимостей. Она бесплатная, расширяемая и поддерживает некоторые из более странных архитектур, с которыми нам приходится сталкиваться.

Но порог входа у неё высокий.

Эта статья — итог того, чему я научился за, возможно, слишком много часов перед слепящим и устаревшим интерфейсом Ghidra. Основной фокус здесь — анализ прошивок, так что исследователи вредоносного ПО и охотники за багами в ОС могут немного разочароваться. Но полезные приёмы должны найтись для всех. Все примеры взяты из реальных исследовательских проектов, которыми мы занимались.

Стоит сразу оговориться: реверс-инжиниринг — процесс небыстрый. Чтобы полностью понять, как работает система, могут уйти недели. Улитка всё-таки взбирается на Фудзи… рано или поздно.

Содержание

Определяем адрес загрузки
Ищем таблицу векторов прерываний
Задаём карту памяти
Аннотируем как можно больше
Ищем функции по строкам
Не пропускаем строки на стеке
Охотимся за магическими значениями
Планирование в RTOS
BSim для поиска библиотечных функций
Импорт исходного кода
Закладки
Погружаемся в ассемблерный код
Выжимаем максимум из скриптов и плагинов
Большие языковые модели

Определяем адрес загрузки

Прошивка редко загружается в память по нулевому адресу. Во многих архитектурах первые адреса зарезервированы под начальный указатель стека и вектор сброса. CPU переходит по вектору сброса, чтобы продолжить выполнение. Благодаря этому можно загружать разные прошивки: например, основной прикладной код или загрузчик восстановления.

Если не задать адрес загрузки, дизассемблер создаст некорректные ссылки на память — на функции, строки и другие объекты — и заведёт вас не туда.

Базовый адрес можно определить несколькими способами:

  • По даташиту микросхемы. Например, хорошо задокументировано, что STM32 загружает прошивку по адресу 0x0800 0000.

  • По IVT, см. следующий раздел. Если обработчики прерываний находятся по адресам 0x20061ae2, 0x2004200c и т.д., базовый адрес, скорее всего, равен 0x20000000.

  • По ссылкам на строки. Возьмите смещение строк и поищите в коде потенциальные указатели на них. Для строки по смещению 0x5eea4 ищите ссылки на 0x5eea4 с учётом порядка байтов. Повторите то же для другой строки, потом ещё для одной. Если у всех строк находятся потенциальные ссылки с одинаковыми старшими байтами, например 0x1005eea4, 0x10014a2 и так далее, вы определили базовый адрес. При ручной проверке будут ложные срабатывания, но можно использовать инструменты, которые сразу берут выборку из сотен строк и проверяют, при каком базовом адресе получается больше всего валидных ссылок.

  • По другим известным структурам прошивки. binbloom находит в автомобильных прошивках базы UDS, которые содержат указатели на функции-обработчики. Как и метод с IVT, он определяет базовый адрес по старшему байту каждого указателя на функцию. В вашей прошивке также могут быть отладочные таблицы, сопоставляющие имена функций с адресами.

Рисунок 1. Адрес загрузки основной флеш-памяти для семейства STM32L0x3 (RM0360 Reference Manual)

Рисунок 1. Адрес загрузки основной флеш-памяти для семейства STM32L0x3 (RM0360 Reference Manual)

Рисунок 2. Настройка адреса загрузки прошивки при импорте в Ghidra

Рисунок 2. Настройка адреса загрузки прошивки при импорте в Ghidra

Ищем таблицу векторов прерываний

IVT часто начинается с нулевого смещения и представляет собой таблицу адресов обработчиков прерываний.

Задайте для каждой записи тип данных «указатель» — горячая клавиша p — и сможете переходить к коду обработчика двойным щелчком. Запустите дизассемблирование горячей клавишей d.

Первая запись таблицы обычно указывает на обработчик сброса. Либо вторая, если первая запись — это начальный указатель стека. Это точка входа прошивки. Дизассемблировать обработчик сброса полезно перед запуском Auto Analysis: так анализ пройдёт по большинству функций в корректном порядке выполнения.

Назначение и расположение каждой записи IVT документируются в даташите микросхемы.

Рисунок 3. IVT микроконтроллера STM32F405 согласно справочному руководству (RM0090 Reference Manual)

Рисунок 3. IVT микроконтроллера STM32F405 согласно справочному руководству (RM0090 Reference Manual)

Рисунок 4. Дизассемблированная и аннотированная IVT в прошивке STM32F100 до запуска Auto Analysis

Рисунок 4. Дизассемблированная и аннотированная IVT в прошивке STM32F100 до запуска Auto Analysis

Задаём карту памяти

Флеш-память уже будет здесь с тем базовым адресом, который вы задали ранее. Но Ghidra не помечает её как область только для чтения, хотя обычно предполагается, что прошивка не пишет или не может писать в загруженный образ флеш-памяти.

У строк и других структур данных только для чтения из-за этого будут странные ссылки:

Рисунок 5. Почему декомпилятор не показывает строку?

Рисунок 5. Почему декомпилятор не показывает строку?

Пометьте область как доступную только для чтения, и они появятся как ожидается:

Рисунок 6. Память только для чтения позволяет Ghidra отображать строки

Рисунок 6. Память только для чтения позволяет Ghidra отображать строки

Также стоит задать область SRAM, доступную для чтения и записи, используя адреса из справочного руководства микросхемы. Иначе адреса будут подсвечиваться красным, а вы не сможете задать для них тип данных или инициализировать их данными:

Рисунок 7. Нельзя изменить тип переменных SRAM и других ссылок за пределами существующей карты памяти

Рисунок 7. Нельзя изменить тип переменных SRAM и других ссылок за пределами существующей карты памяти

Рисунок 8. Добавление области SRAM в карту памяти позволяет менять типы переменных, созданных в памяти

Рисунок 8. Добавление области SRAM в карту памяти позволяет менять типы переменных, созданных в памяти

Последняя задача — добавить расположение регистров, чтобы понимать, с какой периферией взаимодействует подпрограмма. Иначе вы будете видеть только ссылки на неизвестный адрес.

Файлы CMSIS System View Description (SVD) перечисляют эти расположения для ARM-микроконтроллеров. Их можно загрузить в Ghidra с помощью скрипта SVD-Loader (https://github.com/leveldown-security/SVD-Loader-Ghidra). Скрипт также задаёт для регистров периферии типы данных в виде хорошо описанных структур:

Рисунок 9. Memory Map и Data Type Manager после запуска SVD-Loader

Рисунок 9. Memory Map и Data Type Manager после запуска SVD-Loader

У процессоров M68000 расположение периферии зависит от регистров базового адреса, настраиваемых во время выполнения: VBR, RAMBAR и MBAR. Посмотрите в последовательности инициализации прошивки, как они задаются:

Рисунок 10. Некоторые архитектуры используют регистры с переменным смещением

Рисунок 10. Некоторые архитектуры используют регистры с переменным смещением

Аннотируем как можно больше

Даже если функция или адрес данных кажутся неважными, но у вас есть хотя бы догадка об их назначении, присвойте им метку горячей клавишей l. Это поможет позже, когда вы увидите ссылки на них в других местах. Для бинарников с символами добавляйте к своим меткам префикс, чтобы отличать их от автоматически сгенерированных.

Добавляйте комментарии — горячая клавиша ;.

Имена переменных и исправление их типов данных (Ctrl-l) помогают разобраться в декомпиляции, особенно когда возвращаешься к проекту после длинных выходных. В какой-то момент вы дойдёте до того, что будете восстанавливать подпрограммы в компилируемые фрагменты C.

Ищем функции по строкам

Это очевидный приём. Поскольку из прошивок часто удалены символы, строки остаются единственной формой документации, которую поставщик вам всё-таки оставил.

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

Рисунок 11. Проверка assert, по которой можно определить функцию strncmp и понять, что param_1 и param_2 — это структура, используемая виртуальной файловой системой ESP32

Рисунок 11. Проверка assert, по которой можно определить функцию strncmp и понять, что param_1 и param_2 — это структура, используемая виртуальной файловой системой ESP32

Таблицы адресов иногда имеют заголовки или включают имена функций рядом с указателями на них. Ищите их автоматически, через другие перекрёстные ссылки, закладки или просто вручную прокручивая дизассемблированный код:

Рисунок 12. Таблица адресов CLI с записями для имени команды, указателя на функцию и описания

Рисунок 12. Таблица адресов CLI с записями для имени команды, указателя на функцию и описания

Не пропускаем строки на стеке

Строки могут собираться на стеке, а не храниться во время компиляции в статических секциях данных. Зачем так делают? Ради оптимизации — например, четырёхбайтовые строки можно обрабатывать в 32-битных регистрах, — обфускации или осознанных проектных решений, связанных с выделением памяти, временем жизни объектов и ограничениями реального времени.

Рисунок 13. Ghidra не отображает присваивания строк переменным на стеке так, как ожидается: 16-байтный ключ декомпилируется как четыре четырёхбайтовых целых числа

Рисунок 13. Ghidra не отображает присваивания строк переменным на стеке так, как ожидается: 16-байтный ключ декомпилируется как четыре четырёхбайтовых целых числа

Рисунок 14. Аннотирование с корректными типами данных помогает отображать строки на стеке

Рисунок 14. Аннотирование с корректными типами данных помогает отображать строки на стеке

Охотимся за магическими значениями

Это менее очевидный способ идентификации функций. Используйте поиск по памяти и поиск скалярных значений для известных констант: например, magic cookie DHCP (0x63825363), ARP EtherType (0x0806) или AES S-Box.

Рисунок 15. ARP EtherType помог определить функцию tfEtherRecv в стеке TCP/IP Treck

Рисунок 15. ARP EtherType помог определить функцию tfEtherRecv в стеке TCP/IP Treck

Рисунок 16. Поиск первых нескольких записей таблицы поиска CRC-16-ANSI привёл к функциям, которые обрабатывали пакеты Modbus и проверяли целостность прошивки.

Рисунок 16. Поиск первых нескольких записей таблицы поиска CRC-16-ANSI привёл к функциям, которые обрабатывали пакеты Modbus и проверяли целостность прошивки.

Планирование в RTOS

Операционные системы реального времени усложняют реверс-инжиниринг ещё на один уровень. Глобальные ресурсы делятся между подпрограммами, чаще встречаются выделение памяти в куче и объекты, переданные по ссылке, а отделить код ядра от прикладного кода бывает непросто.

Полезно понять, как запускаются задачи. Функция создания задачи принимает указатель на функцию, которая реализует эту задачу. Если поискать вызовы этой функции, вы получите список всех указателей на функции прикладного кода.

При создании задач имена задач также часто передаются аргументами для отладки. Поэтому вместе с полезными указателями на функции вы получаете возможность присвоить им осмысленные имена. FreeRTOS определяет xTaskCreate так:

BaseType_t xTaskCreate(
   TaskFunction_t pvTaskCode,    // Указатель на входную функцию задачи
   const char * const pcName,    // Имя задачи
   const configSTACK_DEPTH_TYPE uxStackDepth, // Количество слов для стека
   void *pvParameters,           // Аргументы задачи
   UBaseType_t uxPriority,       // Приоритет планирования
   TaskHandle_t *pxCreatedTask   // Дескриптор созданной задачи
);

Часто под каждый сетевой сервис выделена отдельная задача, поэтому такое понимание критически важно, если вы, например, хотите сосредоточиться на реверс-инжиниринге собственного протокола.

Рисунок 17. Использование создания задач для идентификации тринадцати задач на устройстве под управлением FreeRTOS

Рисунок 17. Использование создания задач для идентификации тринадцати задач на устройстве под управлением FreeRTOS

BSim для поиска библиотечных функций

Ghidra BSim — это плагин для нечёткого сопоставления кода с базой известных функций.

Если вы знаете, какие библиотеки использует прошивка, например RTOS или стек TCP/IP, можно самостоятельно скомпилировать эти библиотеки с отладочными строками или взять публичные предкомпилированные релизы. Затем вы загружаете их в Ghidra, и она создаёт базу с сигнатурами всех библиотечных функций.

После этого вы сравниваете функции из своей прошивки с базой BSim. В отличие от Function ID, BSim выполняет нечёткое сопоставление. Ему удаётся сопоставить значительную часть функций даже тогда, когда библиотека другой версии или собрана другим компилятором под другую архитектуру. За два клика можно скопировать имя функции и любые пользовательские типы данных.

Рисунок 18. Идентификация функций из Wi-Fi-стека ESP32 с помощью нечёткого сопоставления BSim

Рисунок 18. Идентификация функций из Wi-Fi-стека ESP32 с помощью нечёткого сопоставления BSim

Импорт исходного кода

Попробуйте импортировать заголовочные файлы библиотек, которые, как вы знаете, использует прошивка. Ghidra заполнит Data Type Manager всеми typedef, структурами и прототипами функций. Константы препроцессора можно использовать для аннотирования декомпиляции через Set Equate… — горячая клавиша e.

Заставить это работать бывает непросто. Парсер исходного кода в Ghidra не слишком умён, и порядок импортов имеет значение. Также нужно задать правильные константы препроцессора, чтобы пройти условные блоки #ifdef, и добавить заголовочные файлы стандартной библиотеки C в путь поиска include-файлов, если библиотека на них опирается. Полноценное окружение C не требуется: существуют минимальные наборы стандартных заголовочных файлов.

Рисунок 19. После импорта заголовочных файлов известной библиотеки мы можем использовать её определения типов данных при аннотировании переменных

Рисунок 19. После импорта заголовочных файлов известной библиотеки мы можем использовать её определения типов данных при аннотировании переменных

Закладки

Ghidra заполняет таблицу закладок во время автоанализа.

Здесь можно найти таблицы адресов, полезные для идентификации функций, «неопределённые» функции — найденные по известным последовательностям операндов, а не по ссылкам, — встроенные медиафайлы и места, где дизассемблирование не удалось.

Закладки также можно ставить вручную или через плагины.

Рисунок 20. Закладки, показывающие таблицу функций — вероятно, обработчиков для взаимодействия с памятью Winbond, — сбои дизассемблирования, которые нужно исправить вручную

Рисунок 20. Закладки, показывающие таблицу функций — вероятно, обработчиков для взаимодействия с памятью Winbond, — сбои дизассемблирования, которые нужно исправить вручную

Погружаемся в ассемблерный код

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

Так проще отслеживать ход логики и аннотировать код типами данных и осмысленными именами.

Но перевод в псевдо-C не всегда получается идеальным. Ghidra часто неверно определяет количество параметров функции или размер переменных. Иногда она вообще не может декомпилировать код.

Тревожными признаками могут быть переменные с именами in_, in_stack_, unaff_ и extraout_ в окне декомпилятора.

В таких случаях критически важно читать ассемблерный код. Разберитесь, какое соглашение о вызовах используется в вашей архитектуре: передаются ли параметры через стек или через регистры, в каком порядке они читаются и какие регистры функция может перезаписать.

Без отладочных символов Ghidra не умеет надёжно определять вариадические функции и функции с соглашением thiscall, например printf(char *fmt, …) и object::method(). Задавайте их вручную в окне Edit Function Signature.

Рисунок 21. Это странно: почему однобайтовая переменная char размещена на 15 байтов глубже в стеке и на целых четыре байта дальше второго параметра?

Рисунок 21. Это странно: почему однобайтовая переменная char размещена на 15 байтов глубже в стеке и на целых четыре байта дальше второго параметра?

Рисунок 22. В соглашении о вызовах Motorola 68000 параметры передаются через стек и помещаются туда справа налево. Используя это и смещение 0x8 относительно SP в дизассемблированном коде, мы задаём для функции custom storage: char находится по смещению 0xc, а не 0xf

Рисунок 22. В соглашении о вызовах Motorola 68000 параметры передаются через стек и помещаются туда справа налево. Используя это и смещение 0x8 относительно SP в дизассемблированном коде, мы задаём для функции custom storage: char находится по смещению 0xc, а не 0xf

Выжимаем максимум из скриптов и плагинов

Я уже упоминал про SVD-Loader, но есть много других скриптов, которые автоматизируют процессы или аннотируют интересную функциональность.

Например, я успешно использовал VxWorks Symbol Table Finder, Leaf Blower для поиска форматных строк и функций из string.h, FindCrypt и Stack String Explorer.

Большие языковые модели

Я часто загружаю дизассемблированный код и результаты декомпиляции из Ghidra в LLM. Это хорошо помогает быстро распознавать распространённые алгоритмы: выделение памяти, контрольные суммы и криптографические процедуры. В самом простом варианте достаточно без затей скопировать вывод Ghidra в чат-бот вроде ChatGPT и дать простой промпт — уже можно получить полезные наблюдения.

Мы также успешно использовали специализированные ИИ-расширения.

GhidrAssist (https://github.com/jtang613/GhidrAssist/) поддерживает разные API LLM и предоставляет окно чата с неплохим шаблоном промпта по умолчанию. Дополнительный контекст можно давать через текстовые документы, написанные вручную, или справочные руководства. Кроме того, GhidrAssist можно поручить автономно выполнять циклы исследования для анализа более сложной логики с саморефлексией.

Он также поддерживает MCP-серверы Model Context Protocol, чтобы улучшать результат за счёт автоматической декомпиляции и аннотирования символов. Тот же автор сделал для этой цели GhidrAssistMCP, но мы также успешно использовали GhidraMCP от LaurieWired (https://github.com/LaurieWired/GhidraMCP).

Как обычно советуют при работе с ИИ: он галлюцинирует, поэтому проверяйте результат. А для кода с коммерчески чувствительной информацией используйте локальный экземпляр или сервис с корпоративной защитой данных.

Рисунок 23. По нашему опыту, общедоступные LLM неплохо анализируют фрагменты как дизассемблированного, так и декомпилированного кода

Рисунок 23. По нашему опыту, общедоступные LLM неплохо анализируют фрагменты как дизассемблированного, так и декомпилированного кода

Рисунок 24. LLM полезны для примерной идентификации функций

Рисунок 24. LLM полезны для примерной идентификации функций

Реверс-инжиниринг редко сводится к одному инструменту: приходится разбираться в адресах загрузки, таблицах прерываний, соглашениях о вызовах, поведении рантайма и странностях конкретной системы. Часть этих тем можно отдельно разобрать на бесплатных уроках OTUS — ни них можно задать вопросы, проверить формат обучения и закрыть пробелы в понимании низкоуровневого анализа.

  • 1 июля в 20:00. «Классические методы перехвата управления в Linux». Записаться

  • 23 июля в 20:00. «Фаззинг и реверс: как понять, что делает программа, найти в ней ошибки». Записаться

Полный список бесплатных уроков июня смотрите в дайджесте.