В данном посте мы опишем, как получить образ NAND и использовать FTL-метаданные для восстановления удаленных файлов на устройствах, использующих процессор A4.
Механизмы шифрования и защиты данных файловой системы iOS теперь хорошо документированы и поддерживаются многими утилитами ПТЭ (программно-технической экспертизы или forensics). В качестве основной области хранения данных iOS-устройства используют NAND flash-память, но создание физических образов обычно имеет отношение к "dd image" логических разделов. Уровень трансляции Flash в iOS для текущих устройств имеет программную основу (реализован в iBoot и ядре), что означает, что CPU имеет прямой доступ к сырой NAND-памяти. В данном посте мы опишем, как получить образ NAND и использовать FTL-метаданные для восстановления удаленных файлов на устройствах, использующих процессор A4. Информация, представленная здесь, основана на серьезной работе по реверс-инжинирингу, выполненной командой iDroid/openiBoot.
iOS-устройства используют один или несколько идентичных чипов, адресуемых номером CE ("chip enable"). Фактические параметры геометрии (число CE, число блоков на CE, число страниц на блок, размер страниц и резервной области) зависят от модели устройства и общей емкости хранилища. Физический адрес составляется из CE-номера чипа и физического номера страницы (PPN) на этом чипе.
Из-за ограничений NAND в операционных системах распространены механизмы трансляции (FTL), которые позволяют использовать NAND-память как обычное блочное устройство, оптимизируя при этом ее производительность и срок службы. Главная цель FTL заключается в уменьшении операций стирания и распределения их по всем блокам. С точки зрения ПТЭ, интересным побочным эффектом здесь является то, что при перезаписи логического блока на уровне блочного устройства старые данные на физическом уровне обычно не стираются сразу. Поэтому при поиске удаленных данных работа с сырым образом NAND может быть очень полезна.
Можно прочитать сырые данные NAND с помощью openiBoot, но передача данных по USB на данный момент довольно медленна, что делает этот способ непрактичным для выгрузки содержимого всей Flash-памяти.
Начиная с iOS 3 на ram-дисках Apple iOS можно найти программу ioflashstoragetool. Данная утилита может производить множество низкоуровневых операций, касающихся flash-хранилища и может читать сырые NAND-страницы и резервные области (не производя никакого рода дешифрования). Данная функциональность предоставлялась сервисом ядра IOFlashControllerUserClient.
В iOS 5 большая часть функций, предоставлявшихся данным интерфейсом IOKit, была убрана. Чтобы создать дамп посредством этого интерфейса, мы можем загрузить ram-диск с помощью более старой, четвертой версии ядра iOS. Это отлично работает, однако в таком случае мы потеряем способность использовать имеющийся код ядра для атаки перебором более новых связок ключей iOS 5. Чтобы выгрузить NAND под пятой версией ядра iOS, мы заново реализовали часть функции IOFlashControllerUserClient::externalMethod, ответственной за функциональность чтения. Когда наш инструмент выгрузки запускается под пятой версией ядра iOS, он замещает данную функцию той, что обрабатывает селектор kIOFlashControllerReadPage.
Кроме того, мы можем установить флаг загрузки nand-disable-driver, чтобы предотвратить высокоуровневый доступ к NAND и гарантировать, что ее содержимое не изменится в ходе получения.
Virtual Flash Layer (VFL) ответственен за переназначение bad-блоков и представление NAND, не содержащее ошибок, для FTL-слоя. VFL-слой знает физическую геометрию и транслирует виртуальные номера страниц, используемые FTL, в физические адреса (номер CE + номер физической страницы).
Слой FTL оперирует над VFL и предоставляет операционной системе интерфейс блочного устройства. Он транслирует номера логических страниц блочного устройства (LPN) в виртуальные номера страниц, занимается wear leveling и сборкой мусора в для блоков, содержащих устаревшие данные. На устройствах, поддерживающих аппаратное шифрование, все страницы, содержащие структуры данных, связанные с VFL и FTL, зашифрованы статическим ключом метаданных.
В различных версиях iOS использовались следующие варианты подсистем FTL:
PPN-устройства имеют свой собственный контроллер с прошивкой, которая может быть изменена через интерфейс IOFlashControllerUserClient, но большая часть работы FTL похоже все еще выполняется программно – с помощью YaFTL поверх нового PPNVFL.
На основании кода openiBoot мы написали минимальную read-only реализацию YaFTL/VSVFL на Питоне, чтобы получить возможность просматривать образы NAND как блочные устройства. В сочетании с реализацией HFS+ на Питоне она позволяет извлечение логических разделов для получения эквивалента dd-image. Далее нам нужно понять механизмы YaFTL, чтобы воспользоваться дополнительными данными, доступными в образе NAND.
Следующий рисунок обобщает процесс преобразования YaFTL:
В ходе нормальной работы в каждый момент лишь один суперблок каждого типа является "открытым": страницы записываются последовательно в манере log-block. Когда текущий суперблок заполнен, YaFTL находит пустой суперблок для продолжения процесса. старевшие пользовательские данные стираются только в процессе работы сборщика мусора.
Последние страницы индексного и пользовательского суперблоков используются для хранения BTOC (таблица содержимого блока). Для пользовательских блоков BTOC перечисляет логические номера страниц, хранимые в этих блоках. В индексном блоке BTOC хранит первые логические номера страниц, на которые указывает каждая из индексных страниц.
Восстановление FTL производится при загрузке, если FTL не был должным образом отмонтирован (после kernel panic или жесткой перезагрузки, например) и контекстная информация не была зафиксирована на flash-носителе. Функции восстановления FTL приходится исследовать все блоки (используя BTOC для ускорения процесса), чтобы восстановить корректный контекст.
// Page types (as defined in the spare data "type" bitfield)
#define PAGETYPE_INDEX (0x4)
// Index block indicator #define PAGETYPE_LBN (0x10)
// User data #define PAGETYPE_FTL_CLEAN (0x20)
// FTL context (unmounted, clean) #define PAGETYPE_VFL (0x80)
// VFL context ... typedef struct { uint32_t lpn;
// Logical page number uint32_t usn;
// Update sequence number uint8_t field_8; uint8_t type;
// Page type uint16_t field_A; } __attribute__((packed)) SpareData;
Поле lpn позволяет FTL-коду проверять корректность преобразования при чтении страницы. Оно также используется в ходе процесса восстановления FTL, чтобы обнаруживать страницы в "открытых" суперблоках, которые не имеют BTOC.
В поле usn записывается глобальный порядковый номер обновления во время записи страницы. Этот номер увеличивается каждый раз при фиксации новой версии контекста FTL или когда суперблок заполнен и происходит открытие нового суперблока. Данное поле позволяет легко сортировать суперблоки по возрасту.
static uint32_t h2fmi_hash_table[256]; ... void h2fmi_init() { ...
// This is a very simple PRNG with
// a preset seed. What are you // up to Apple? -- Ricky26
// Same as in 3GS NAND -- Bluerise uint32_t val = 0x50F4546A; for(i = 0; i < 256; i++)
{ val = (0x19660D * val) + 0x3C6EF35F; int j; for(j = 1; j < 763; j++) { val = (0x19660D * val) + 0x3C6EF35F; }
h2fmi_hash_table[i] = val; } ... } error_t h2fmi_read_single_page(...) { ... if
(h2fmi_data_whitening_enabled) { uint32_t i; for(i = 0; i < 3; i++) ((uint32_t*)_meta_ptr)[i] ^=
h2fmi_hash_table[(i + _page) % ARRAY_SIZE(h2fmi_hash_table)]; } ... }
Признак того, что отбеливание метаданных включено, находится в поле флагов специальной страницы NANDDRIVERSIGN.
Когда таблица поиска построена, мы можем легко обратиться ко всем доступным версиям заданной логической страницы. Чтобы восстановить удаленные данные на разделе мы реализовали простой алгоритм, похожий на метод вырезания данных из журнала HFS:
Данный наивный алгоритм дает хорошие результаты на "статичных" файлах вроде изображений, где весь файл записывается один раз и более не обновляется. Для файлов вроде баз данных SQLite понадобится больше логики, чтобы восстановить состоятельные снимки последовательных версий. Для этого можно, например, обнаруживать акты записи в заголовок файла или отслеживать модификации в записях файла каталога (дата модификации файла).
Один из файлов, который было бы интересно восстановить – системное хранилище ключей. Если атакующий смог получить доступ к первой версии системного хранилища ключей (без установленного пароля, сразу после восстановления прошивки), он мог бы затем получить доступ ко всем ключам классов без необходимости в атаке пароля пользователя. Однако, эксплуатировать более старые версии хранилища ключей невозможно из-за второго слоя шифрования: полезная нагрузка systembag.kb шифруется ключом BAG1, который хранится в уничтожаемой области и принимает случайное значение каждый раз, когда на диск записывается новая версия файла (когда пользователь меняет свой пароль). Данный механизм, очевидно, был разработан для предотвращения подобных атак, как объяснено в выступлении "Securing application data" с Apple WWDC 2010 (Сессия 209).
Инструменты получения содержимого NAND и вырезания данных теперь доступны в репозитории iphone-dataprotection. Дополнительные подробности также доступны на вики. Наконец, большое спасибо Патрику Вилдту и команде openiBoot за их работу над iOS FTL, которая позволила нам создать эти инструменты.