
Инструментарий IDA для анализа kernelcache iOS.
ida_kernelcache — это модуль IDAPython для IDA Pro, упрощающий работу с kernelcache iOS. Модуль предоставляет функции для:
__PRELINK_INFO ядра в словарь Python.__got и функций-заглушек в секциях __stubs.Основная функция обработки предназначена для запуска до любого ручного анализа или реверс-инжиниринга. При настройках по умолчанию IDA часто упускает много полезной информации в kernelcache. Эти скрипты помогают IDA, используя известную структуру kernelcache для автоматического распространения полезной информации.
В дополнение к штатной функциональности модуля ida_kernelcache содержит несколько скриптов, упрощающих анализ kernelcache iOS. Например, с помощью скриптов можно автогенерировать структуры C, используемые функцией.
Многие методы, применяемые в ida_kernelcache, были разработаны для memctl и заимствованы непосредственно из него.
ida_kernelcache тестировался с IDA Pro 6.95 на kernelcache для iOS 10.1.1, 11.0, 11.2, 11.3.1 и 12.0 beta. В настоящее время поддерживаются только Arm64 kernelcache начиная с iOS 10.
Вам нужно, чтобы в IDA уже был загружен распакованный файл kernelcache. Ссылку для загрузки конкретного IPSW можно найти на сайте Apple, а существует множество публичных инструментов (включая memctl), способных распаковать kernelcache.
В IDA выберите "File" -> "Script file..." в строке меню, затем выберите скрипт ida_kernelcache.py
в главном каталоге. Это загрузит модуль ida_kernelcache в интерпретатор IDAPython
под именами ida_kernelcache и kc. В приглашении IDAPython введите
kc.kernelcache_process() и нажмите Enter, чтобы начать анализ kernelcache. Эта функция выполняет
все основные виды анализа, поддерживаемые ida_kernelcache. Функция будет работать несколько минут, пока
IDA распознаёт и анализирует новые функции.
ida_kernelcache старается не перезаписывать пользовательские имена для адресов. Это означает, что если
kernelcache был проанализирован вручную до инициализации с помощью kernelcache_process, результаты
могут быть не такими полными, поскольку заданные пользователем имена могут блокировать автоматическое
распространение имён. Однако нет и гарантии, что ida_kernelcache не испортит предыдущий анализ,
поэтому если вы решите запустить kernelcache_process для файла kernelcache, который вы уже
проанализировали, сначала сделайте резервную копию.
ida_kernelcache предназначен для загрузки через ida_kernelcache.py; подмодули в каталоге
ida_kernelcache не предназначены для прямой загрузки. Однако ida_kernelcache открывает
доступ к функциональности многих из этих подмодулей. Вот что делает каждый из них:
ida_utilities:
Этот модуль оборачивает некоторые функции IDA, предоставляя более удобный API. Особенно полезны
is_mapped, read_word, read_struct, force_function и ReadWords. is_mapped проверяет,
отображён ли адрес и, опционально, содержит ли он известное значение. read_word считывает
слово переменного размера по адресу. read_struct считывает тип структуры в словарь Python
или объект доступа Python, что значительно упрощает разбор структур данных. force_function использует
несколько приёмов, чтобы превратить адрес в начало функции в IDA. ReadWords — это
генератор для перебора слов данных и их адресов в диапазоне.
build_struct: Этот внутренний модуль содержит утилиты для автоматического заполнения структуры IDA на основе последовательности обращений к структуре.
class_struct:
Этот модуль предоставляет функции для генерации структур IDA, представляющих таблицы виртуальных методов C++ и
классы. initialize_vtable_structs сканирует (символизированные) таблицы виртуальных методов и создаёт структуры
IDA для хранения указателей на виртуальные методы. выполняет анализ потока данных
виртуальных методов для выявления обращений к полям каждого класса, а затем строит структуры IDA для
представления классов. Инструкции, которые, судя по всему, ссылаются на поле, также преобразуются в
ссылки со смещением в структуре. Подробнее см. docstring модуля.
Скрипт ida_kernelcache_reload.py идентичен ida_kernelcache.py, за исключением того, что он принудительно
перезагружает модуль ida_kernelcache и все подмодули. Он в основном полезен для разработки.
Каталог scripts содержит скрипты, использующие ida_kernelcache для выполнения того или иного анализа.
Эти скрипты слишком специфичны, чтобы быть частью основного модуля ida_kernelcache, но они полезны
при реверс-инжиниринге kernelcache. В их число входят:
find_virtual_method_overrides.py: Скрипт для поиска потомков класса, переопределяющих виртуальный метод, содержащий указанную строку. Подходящие переопределения выводятся на консоль.
populate_struct.py: Заполняет поля для класса C++ или структуры C, выполняя анализ потока данных, начиная с текущего адреса.
process_external_methods.py:
Преобразует массив IOExternalMethod или IOExternalMethodDispatch в стандартную форму для использования
инструментами фаззинга.
Если вы используете декомпилятор Hex-Rays, одна из наиболее интересных возможностей ida_kernelcache — это автоматическая реконструкция классов C++, которая использует информацию OSMetaClass и анализ потока данных для создания структур IDA, представляющих классы, найденные в kernelcache. Эти представления могут значительно улучшить читаемость псевдокода. Чтобы узнать больше, см. статью Reconstructing C++ classes in the iOS kernelcache using IDA Pro.
Начиная с iOS 12 Apple представила новый формат kernelcache на некоторых устройствах. Среди прочих изменений,
в этом новом kernelcache указатели ядра тегированы для связывания их в список, предположительно для того,
чтобы позволить iBoot выполнять сдвиг ядра без данных _PrelinkLinkKASLROffsets в словаре prelink.
Пытаться анализировать в IDA обычный kernelcache, использующий этот формат, сложно из-за отсутствия
перекрёстных ссылок. Подробности см. в статье Analyzing the iOS 12 kernelcache's tagged pointers.
Если вы хотите просто снять теги с указателей в kernelcache без выполнения дополнительной
обработки, запустите kc.tagged_pointers.untag_pointers().
Часть этой функциональности, вероятно, применима шире, чем только к kernelcache Apple (например,
анализ vtable и распространение символов, или большинство функций в ida_utilities.py).
Тем не менее я ограничил область импорта только модулем ida_kernelcache, потому что я не
тестировал это на других типах бинарных файлов.
ida_kernelcache выпущен под лицензией MIT.
Значительная часть функциональности ida_kernelcache заимствована из memctl, который также выпущен под лицензией MIT. Другие источники указаны в комментариях в соответствующих файлах.
Brandon Azad
initialize_class_structsclasses:
Этот модуль определяет тип ClassInfo, который хранит информацию о классах C++ в kernelcache,
и предоставляет функцию collect_class_info для сканирования kernelcache на предмет классов и
заполнения глобального словаря class_info соответствием имён классов объектам ClassInfo.
Тип ClassInfo записывает имя класса, экземпляр OSMetaClass, таблицу виртуальных методов и
имя суперкласса для каждого класса C++. Кроме того, каждый объект ClassInfo хранит ссылки на
ClassInfo суперкласса и ClassInfo всех прямых подклассов, что упрощает
изучение и обход иерархии классов. collect_class_info также сохраняет набор всех таблиц
виртуальных методов в глобальном множестве vtables.
data_flow: Этот внутренний модуль содержит операции потока данных, используемые остальной частью ida_kernelcache.
kernel:
Этот модуль предоставляет глобальные переменные base и prelink_info. base — это базовый адрес
образа ядра (начало заголовка Mach-O ядра). prelink_info — это разобранный
словарь __PRELINK_INFO.
kplist:
Этот модуль предоставляет функцию kplist_parse для разбора plist в стиле ядра.
metaclass:
Этот модуль предоставляет функцию initialize_metaclass_symbols, которая добавляет символ для
каждого известного экземпляра OSMetaClass.
offset:
Этот модуль предоставляет функции initialize_data_offsets и initialize_offset_symbols.
Первая сканирует сегменты в поисках указателей, которые можно преобразовать в смещения.
Вторая символизирует смещения в секции __got каждого kext, если цель смещения имеет
символ.
segment:
Этот модуль предоставляет функцию initialize_segments для переименования сегментов IDA в более
полезные. По умолчанию IDA, похоже, создаёт имена сегментов, комбинируя предположение об идентификаторе
пакета с секцией Mach-O, описывающей область. initialize_segments извлекает настоящий идентификатор
пакета из словаря __PRELINK_INFO и переименовывает каждый сегмент, чтобы включить идентификатор
пакета, сегмент Mach-O и секцию Mach-O. Это позволяет, например, различать
__TEXT.__const и __DATA_CONST.__const. Этот модуль также предоставляет функцию
kernelcache_kext (повторно экспортируемую на верхнем уровне) для определения kext, содержащего
указанный адрес (только для старого формата kernelcache с раздельными kext iOS 11).
stub:
Многие kext в kernelcache содержат функции-заглушки в секции __stubs, которые переходят к функциям
в самом ядре. К сожалению, эти заглушки создают барьер для распространения перекрёстных ссылок
и информации о типах. Этот модуль не решает эти проблемы, но он немного упрощает просмотр заглушек,
автоматически переименовывая функции-заглушки так, чтобы имя целевой функции было видимым.
Заглушки и их цели принудительно преобразуются в функции в IDA, что помогает
согласовать функции в IDA с функциями в исходном коде.
tagged_pointers: В новом объединённом формате kernelcache iOS 12 старшие 2 байта каждого указателя тегированы смещением, чтобы объединить указатели в список. Этот модуль содержит функции для обработки и восстановления таких тегированных указателей.
vtable:
Этот модуль предоставляет множество полезных функций для работы с таблицами виртуальных методов, включая
vtable_length, convert_vtable_to_offsets, vtable_overrides, initialize_vtable_symbols и
initialize_vtable_method_symbols. vtable_length проверяет, может ли указанный адрес быть
vtable, и возвращает длину vtable. Генератор vtable_overrides перечисляет виртуальные методы
класса, которые переопределяют виртуальные методы, используемые суперклассом. Функция
initialize_vtable_symbols добавляет символ для начала каждой опознанной vtable.
initialize_vtable_method_symbols перебирает переопределённые методы в каждой vtable и
распространяет символы от суперкласса к подклассу. Это возможно, потому что большинство базовых
классов в IOKit определены в XNU с относительно полной информацией о символах. Каждое
переопределение метода в vtable подкласса должно соответствовать тому же интерфейсу, что и метод в
суперклассе, а это значит, что мы можем сгенерировать символ для переопределения, подставив имя
подкласса вместо имени суперкласса в символе виртуального метода суперкласса. Например, если
у нас нет имени для виртуального метода с индексом 7 в классе AppleKeyStore, но мы знаем, что
виртуальный метод с индексом 7 в его суперклассе IOService называется
__ZNK9IOService12getMetaClassEv, то мы можем сделать вывод, что индекс 7 в подклассе должен
называться __ZNK13AppleKeyStore12getMetaClassEv. Этот приём можно использовать для символизации
большинства виртуальных методов в большинстве классов.