Около года назад я начал работать над эмуляцией iPod Touch 1G с помощью программы эмуляции QEMU. После нескольких месяцев реинжиниринга, выяснения спецификаций различных аппаратных компонентов и бесчисленных отладок с помощью GDB у меня теперь есть функциональная эмуляция iPod Touch, которая включает в себя визуализацию дисплея и поддержку мультитач. На эмулируемом устройстве установлена первая прошивка, когда-либо выпущенная Apple для iPod Touch: iPhoneOS 1.0, сборка 3A101a. Эмулятор запускает iBoot (загрузчик), ядро XNU, а затем запускает Springboard. Springboard отображает домашний экран и отвечает за запуск других приложений, таких как Safari и календарь. Я не вносил никаких изменений в загрузчик, ядро или другие загружаемые двоичные файлы. Весь исходный код можно найти в моей ветке QEMU. Примечание: для эмулятора требуется собственный образ NOR и NAND (подробнее об этом позже в этом посте). Я планирую вскоре опубликовать еще один пост в блоге с подробными инструкциями о том, как создавать эти пользовательские имиджи.
На видео ниже показан эмулятор в действии при загрузке устройства и при навигации по различным приложениям:
Чтобы достичь вышеизложенного, я опирался на некоторые из предыдущих работ по эмуляции устройств iOS/Apple, выполненных другими:
https://worthdoingbadly.com/xnuqemu/ - Этот первоначальный пост в блоге @zhuowei изначально вдохновил меня начать этот проект.
https://alephsecurity.com/2019/06/17/xnu-qemu-arm64-1/ - gоследующая работа Йохатана Афека, основанная на работе @zhuowei.
https://github.com/danzatt/QEMU-s5l89xx-port - Ранняя работа над эмуляцией SoC S5L8900.
https://github.com/TrungNguyen1909/qemu-t8030 - Эта эмуляция iPhone 11 с QEMU — обеспечивает полную функциональность эмуляции ядра.
https://github.com/iDroid-Project/openiBoot - Проект openiboot стал бесценным ресурсом для понимания аппаратных компонентов iPod Touch (я очень надеюсь, что когда-нибудь мы сможем запускать Android на устройствах iOS).
https://ghidra-sre.org/ Инструмент обратного проектирования Ghidra, который я использовал для дизассемблирования образов загрузчика/ядра и других двоичных файлов.
https://gist.github.com/dizimka/2922441 - Этот дамп дерева устройств iPod Touch, сделанный @dizima, содержит обзор и технические характеристики аппаратных компонентов, включенных в iPod Touch 1G.
Самой сложной частью этого проекта была эмуляция многих аппаратных компонентов, включенных в iPod Touch. Спецификации большинства этих компонентов, которые я должен был ввести в эксплуатацию, являются проприетарными и недокументированными, что иногда затрудняет их правильную эмуляцию. Однако я думаю, что это первый эмулированный продукт Apple, который не только имеет открытый исходный код, но и имеет полную поддержку дисплея и мультитач (хотя Correllium также предлагает виртуализированные iPhone, Correllium является коммерческим и закрытым исходным кодом). В этом сообщении блога я расскажу о некоторых проблемах, с которыми я столкнулся, опишу шаги, предпринятые в процессе загрузки, и перечислю некоторые будущие задачи, которые могут сделать эмуляцию еще лучше. Мне понравилось работать с этим эмулятором, и я узнал много нового о внутреннем устройстве мобильных устройств.
Я специально решил сосредоточиться на эмуляции iPod Touch 1G с первой выпущенной версией iOS. Я сделал это по двум причинам: во-первых, старые устройства имеют меньше аппаратных компонентов, чем новые устройства, что упрощает создание эмулятора устройства. Современные устройства Apple содержат множество дополнительных аппаратных компонентов, таких как нейронные механизмы, защищенные анклавы и различные датчики, которые сделают эмуляцию таких устройств намного более сложной и трудоемкой. Вторая причина заключается в том, что в более старых версиях iPhoneOS/iOS практически не реализованы меры безопасности, такие как кеш доверия. Сосредоточившись на самой примитивной версии iPhoneOS, мне не пришлось обходить какой-либо механизм безопасности.
Текущий статус проекта
Все аппаратные компоненты, необходимые для запуска iBoot, ядра XNU, Springboard и предустановленных приложений iPhoneOS, работают. Эти аппаратные компоненты:
- Криптографический движок AES
- Механизм хеширования SHA1
- Модуль идентификации чипа
- Аппаратные часы и таймер
- GPIO-контроллер
- ЖК-дисплей и фреймбуферы
- Контроллер NAND и модуль кода исправления ошибок (ECC)
- Контроллер флэш-памяти (FMC), используемый для связи с памятью NAND.
- Мультисенсорное устройство
- Блок управления питанием и встроенные часы реального времени
- SDIO-контроллер
- SPI-контроллер
- Контроллер I2C
- Контроллер векторных прерываний (VIC) и контроллер прерываний GPIO
- Контроллер прямого доступа к памяти (DMA)
- UART-контроллер
Следующие аппаратные компоненты еще не работают, но они также не обязательны для полной загрузки iPod Touch:
- Устройства USB OTG/Synopsys
- Аудиоустройства
- Контроллер Wi-Fi 802.11
- Графический процессор PowerVR MBX
- Движок видеокодера/декодера
- Акселератор и датчик света
- Процедура загрузки iPod Touch
На приведенной ниже диаграмме показаны все пять этапов загрузки пользовательских приложений с iPod Touch:
Bootrom и низкоуровневый загрузчик
iPod Touch 1G использует набор инструкций ArmV6 (Little Endian). Первый шаг проверки этого проекта включал настройку машины QEMU с процессором, чтобы мы могли выполнять некоторый код. К счастью, QEMU поддерживает ARM1176ЦП и необходимый набор инструкций. После инициализации машины QEMU и инициализации некоторой памяти мы готовы загрузить наши двоичные файлы в память и выполнить некоторый код!
Первым кодом, который выполняется при включении iPod Touch, является код начальной загрузки, предположительно разработанный Samsung, когда был представлен iPod Touch 1G. Bootrom встроен в устройство, доступен только для чтения и не может быть изменен с помощью программного обеспечения. Поэтому уязвимости в bootrom очень востребованы, так как такие уязвимости не могут быть исправлены с помощью программного обеспечения ( Checkm8 был последней уязвимостью такого рода). Дамп кода bootrom можно скачать с этого сайта - https://securerom.fun/. Сначала я попытался загрузить и выполнить загрузочный код на своей машине QEMU. Однако я быстро обнаружил, что загрузчик переходит к какому-то коду, который, вероятно, также зашит в устройство и отсутствует в дампе загрузчика, который я использовал (отсутствующий код, кажется, находится по смещению 0x22000000в памяти). Поскольку в начале этого проекта у меня не было физического iPod Touch 1G, я не мог получить недостающий код. Низкоуровневый загрузчик (LLB, шаг 2 на приведенном выше рисунке) также переходит к этому загадочному коду, поэтому вместо этого я переключил свое внимание на выполнение iBoot (шаг 3 на приведенном выше рисунке).
Веселье с загрузчиком iBoot
Основной функцией загрузчика iBoot является инициализация периферийных устройств устройства, а также загрузка и выполнение образа ядра. iBoot также может войти в режим восстановления, который позволяет переустановить iPhoneOS с помощью iTunes. К счастью, проект openiBoot проделал большую работу по повторной реализации большей части функций, предоставляемых iBoot. Этот исходный код помог мне понять основную логику и процедуры iBoot. Поскольку iBoot инициализирует и взаимодействует с различными аппаратными компонентами, мне также пришлось сосредоточиться на том, чтобы эти компоненты были запущены и работали для запуска iBoot.
Первым аппаратным компонентом, над которым я работал, был векторный контроллер прерываний (VIC). Этот компонент регистрирует запросы на прерывание от других аппаратных компонентов и информирует ЦП, когда произошло прерывание. iPod Touch 1G, похоже, оснащен PL192, который хорошо задокументирован. После того, как VIC был запущен и запущен, я работал над перенаправлением операторов печати, сгенерированных ядром, в консоль QEMU, что помогло в процессе отладки. Ниже вы можете увидеть консольный вывод iBoot вплоть до того момента, когда iBoot загружает и расшифровывает ядро XNU:
iis_init()
spi_init()
power supply type batt
battery voltage Reading PMU register 87
error
SysCfg: version 0x00010001 with 4 entries using 200 of 8192 bytes
BDEV: protecting 0x2000-0x8000
image 0x1802bd20: bdev 0x1802b6a8 type dtre offset 0x10800 len 0x7d28
image 0x1802c170: bdev 0x1802b6a8 type batC offset 0x18d40 len 0x101e1
image 0x1802c5c0: bdev 0x1802b6a8 type logo offset 0x29a80 len 0x1c3a
image 0x1802ca10: bdev 0x1802b6a8 type nsrv offset 0x2bfc0 len 0x4695
image 0x1802ce60: bdev 0x1802b6a8 type batl offset 0x30d00 len 0xc829
image 0x1802d2b0: bdev 0x1802b6a8 type batL offset 0x3e240 len 0xe9d2
image 0x1802e888: bdev 0x1802b6a8 type recm offset 0x4d780 len 0xb594
display_init: displayEnabled: 0
otf clock divisor 5
fps set to: 59.977
SFN: 0x600, Addr: 0xfe00000, Size: 0x14001e0, hspan: 0x500, QLEN: 0x140
merlot_init() -- Universal code version 08-29-07
Merlot Panel ID (0x71c200):
Build: PVT1
Type: TMD
Project/Driver: M68/NSC-Merlot
ClcdInstallGammaTable: No Gamma table found for display_id: 0x0071c200
power supply type batt
battery voltage error
power supply type batt
battery voltage error
usb_menu_init()
vrom_late_init: unknown image crc: 0x66a3fbbf
=======================================
::
:: iBoot, Copyright 2007, Apple Inc.
::
:: BUILD_TAG: iBoot-204
::
:: BUILD_STYLE: RELEASE
::
=======================================
[FTL:MSG] Apple NAND Driver (AND) 0x43303032
[NAND] Device ID 0xa514d3ad
[NAND] BANKS_TOTAL 8
[NAND] BLOCKS_PER_BANK 4096
[NAND] SUBLKS_TOTAL 4096
[NAND] USER_SUBLKS_TOTAL 3872
[NAND] PAGES_PER_SUBLK 1024
[NAND] PAGES_PER_BANK 524288
[NAND] SECTORS_PER_PAGE 4
[NAND] BYTES_PER_SPARE 64
[FTL:MSG] FIL_Init [OK]
[FTL:MSG] BUF_Init [OK]
[FTL:MSG] VFL_Init [OK]
[FTL:MSG] FTL_Init [OK]
[FTL:MSG] VFL_Open [OK]
[FTL:MSG] FTL_Open [OK]
Boot Failure Count: 0 Panic Fail Count: 0
Delaying boot for 0 seconds. Hit enter to break into the command prompt...
HFSInitPartition: 0x1802b8f0
Reading 8900 header with length 2048 at address 0x0b000000
Will decrypt 8900 image at address 0x0b000000 (len: 3319392 bytes)
Loading kernel cache at 0xb000000...
data starts at 0xb000180
Как видно из приведенного выше журнала, iBoot сначала инициализирует различные аппаратные компоненты; затем он считывает несколько изображений из флэш-памяти NOR, инициализирует ЖК-экран, инициализирует блок управления питанием (PMU) для считывания состояния батареи, а затем считывает образ ядра из флэш-памяти NAND. Наконец, он передает выполнение ядру. Если по какой-либо причине загрузка не удалась, iBoot переходит в режим восстановления, который позволяет выполнять несколько команд отладки через интерфейс UART.
iPod Touch 1G содержит два типа постоянной памяти: NOR и NAND. Память NOR представляет собой относительно небольшое блочное устройство. Основная файловая система сохраняется в памяти NAND и имеет размер от 8 до 32 ГБ для iPod Touch 1G, в зависимости от модели. Чтобы эмулятор работал правильно, нам нужно эмулировать эти блочные устройства и убедиться, что загрузчик/ядро может правильно их читать.
Построение образа NOR
Во время загрузки загрузчик iBoot считывает несколько файлов, хранящихся во флэш-памяти NOR. К таким файлам относятся, например, логотип Apple, отображаемый при загрузке устройства, экран режима восстановления, экран низкого заряда батареи и дерево устройств. Память NOR также содержит разделы NVRAM и SysCfg, в которых хранятся различные свойства устройства, такие как серийный номер, MAC-адрес, параметры загрузки для ядра и журналы крешев. Я написал пользовательский инструмент для создания действительного образа памяти NOR из файлов, включенных в файл IPSW, и предоставил этот пользовательский образ памяти при запуске QEMU. Исходный код для создания этого образа NOR можно найти в этом репозитории GitHub - https://github.com/devos50/generate-ipod-touch-1g-nor.
Создание образа NAND
Одной из обязанностей iBoot является загрузка ядра XNU в память и передача ему выполнения. iBoot может загружать образ ядра двумя способами: он либо считывает образ из файловой системы в памяти NAND, либо загружает образ, расположенный по определенному смещению памяти. Поскольку я хочу, чтобы эмуляция была как можно ближе к реальной процедуре загрузки, я сосредоточился на том, чтобы запустить и запустить ввод-вывод NAND. На первый взгляд это звучит просто, поскольку память NAND разделена на разные страницы, и каждая страница пронумерована. Таким образом, наш эмулятор может просто вернуть соответствующие данные на странице, когда их запрашивает iBoot или ядро. Однако под капотом устройство NAND намного сложнее, в основном потому, что память NAND требует алгоритмов для выравнивания износа. Это необходимо, потому что каждый физический блок в NAND может быть надежно стерт и записан столько раз, прежде чем производительность снизится. Драйверы NAND также содержат другие алгоритмы, например, для кода исправления ошибок, управления поврежденными блоками и сборки мусора. В результате физическое расположение страниц в памяти NAND сильно отличается от логической организации этих страниц.
К счастью, Openiboot содержит реализацию драйвера NAND из iPod Touch 1G. Это помогло мне не только понять физическое расположение памяти NAND, но и понять взаимодействие ввода-вывода с памятью NAND. Я также просмотрел просочившуюся версию исходного кода iBoot, которая содержит исходный код драйверов NAND. Подобно образу NOR, я написал различные сценарии, которые создают образ NAND, который может быть прочитан драйвером NAND. Исходный код можно найти в этом репозитории GitHub - https://github.com/devos50/generate-ipod-touch-1g-nand. Образ NAND создается из корневой файловой системы, включенной в файл прошивки IPSW.
Расшифровка и загрузка образа ядра
В этот момент iBoot корректно загружает образ ядра из хранилища NAND (находится в файловой системе по адресу /System/Library/Caches/com.apple.kernelcaches/kernelcache.s5l8900xrb). Однако этот образ ядра зашифрован с использованием проприетарной схемы шифрования 8900, и iBoot переходит к процедуре расшифровки в памяти, инструкций которой у меня нет. Чтобы по-прежнему иметь возможность расшифровать образ, я реализовал обратный вызов в начале функции шифрования, к которой выполняется переход, и вместо этого расшифровал образ ядра в логике QEMU. Затем я оставляю расшифрованный образ ядра в памяти, после чего iBoot переходит к методу входа образа ядра.
Были некоторые другие аппаратные компоненты, которые я должен был настроить и запустить, прежде чем iBoot дойдет до загрузки ядра. Эти компоненты включают блок управления питанием (PMU), контроллер прямого доступа к памяти, аппаратные таймеры и часы, а также ЖК-дисплей.
Эмуляция ядра XNU
Большая часть моих усилий по реверсу ушла на понимание ядра XNU и эмуляцию аппаратных компонентов, используемых ядром. Несмотря на то, что ядро XNU в основном имеет открытый исходный код, Apple, похоже, поддерживает приватный форк для ядра, включенного в устройства Apple, такие как iPod Touch и iPhone. Сравнивая ядро, поставляемое в iOS, с кодом ядра с открытым исходным кодом, кажется, что Apple внесла различные изменения в ядро iOS, чтобы гарантировать, что оно может работать на процессорах ARM. Кроме того, в реализации ядра с открытым исходным кодом отсутствует исходный код драйверов аппаратных компонентов для конкретных устройств.
Ядро XNU сначала инициализирует несколько подсистем BSD, включая логику управления памятью, планировщик и поддержку потоков. Затем ядро считывает дерево устройств, включенное в образ NOR. Дерево устройств — это структура данных, описывающая все аппаратные компоненты, входящие в состав конкретного устройства. Ядро использует дерево устройств для загрузки соответствующих драйверов для всех этих компонентов и для инициализации этих компонентов с правильными настройками. Дамп дерева устройств, используемого iPod Touch 1G, можно найти здесь - https://gist.github.com/dizimka/2922441и, как видите, содержит довольно много информации! Дерево устройств также может отображать информацию о зависимостях между различными компонентами. Например, это указывает на то, что связь с мультитач-экраном осуществляется через интерфейс SPI, управляемый контроллером SPI.
Пожалуй, самым важным полем в узлах дерева устройств является адрес памяти компонентов. Большинство аппаратных компонентов используют технику, называемую вводом-выводом с отображением памяти или MMIO. В MMIO одно и то же адресное пространство используется для адресации как основной памяти, так и устройств ввода/вывода. В результате ядро может просто читать и записывать в основную память для связи с аппаратными компонентами. Реализация поддержки ввода-вывода с отображением памяти в QEMU оказалась относительно простой. Однако некоторые аппаратные компоненты не используют MMIO, и доступ к ним должен осуществляться с использованием других аппаратных протоколов связи, таких как SPI, I2C или SDIO.
После инициализации подсистем BSD ядро запускает фреймворк IOKit и начинает загружать драйверы для аппаратных компонентов, включенных в дерево устройств. Так как ядро загружает довольно много драйверов (примерно 30), проверка того, что все эти драйверы правильно запущены, заняло у меня несколько месяцев. Процесс загрузки иногда зависал, потому что он ждал аппаратного компонента, который я еще не эмулировал должным образом, чтобы дать определенный ответ. Ниже вы можете увидеть скриншот некоторых декомпилированных драйверов:
И некоторые файлы в моем репозитории QEMU :
В какой-то момент во время выполнения ядро начинает читать двоичные файлы из файловой системы в NAND. Несмотря на то, что у меня уже была полная поддержка NAND, чтобы порадовать iBoot, ядро читает из хранилища NAND через контроллер флэш-памяти или FMC. Это оказалось одним из самых сложных аппаратных компонентов, которые мне приходилось эмулировать. FMC также был первым аппаратным компонентом, который мне пришлось эмулировать без какой-либо документации или исходного кода. На расшифровку различных операций ввода-вывода, выполняемых FMC, и обеспечение считывания правильных страниц NAND у меня ушло несколько недель проб и ошибок. На данный момент операции чтения NAND с помощью FMC должны работать правильно, но я еще не добавил поддержку операций записи NAND.
После инициализации всех драйверов настало время ядру выполнить launchd приложение. launchd - это первая программа, запускаемая ядром и, как следует из названия, она отвечает за запуск других приложений и сценариев запуска (она также работает с PID 1). Загрузка ядра считается завершенной при запуске launchd. С этого момента приложения исполняеиые launchd запускаемые в пространстве пользователя, а не в пространстве ядра. Когда launchd работает правильно, следующим шагом был запуск стандартного приложения, управляющего домашним экраном iPod Touch: Springboard.
Запуск трамплина
Приложение launchd ищет сценарии запуска в /System/Library/LaunchDaemons каталоге в файловой системе и выполняет эти сценарии. Эти сценарии запуска включают, например, демоны для управления звуком, адресную книгу и поддержку Bluetooth. Один из этих сценариев запуска com.apple.SpringBoard.plist содержит инструкции по запуску Springboard.app приложения. К сожалению, Springboard застрял вскоре после его запуска, потому что я еще не реализовал рендеринг дисплея.
Да прибудет дисплей
Springboard.App содержит логику для отображения главного экрана, включая значки приложений, диалоговые окна и строку состояния. Рендеринг дисплея на iPod Touch (или любом мобильном устройстве в этом отношении) обычно ускоряется аппаратным графическим процессором. Из реверс-инжиниринга я уже мог видеть, что этот аппаратный компонент весьма задействован и что протокол связи между ядром и графическим процессором сложен. В качестве альтернативы я начал искать способ отключить на данный момент графический процессор. К счастью, сценарий запуска Springboard.App позволил мне добавить переменную средыLK_ENABLE_MBX2D=0 который успешно отключает графический процессор. С этой опцией весь рендеринг дисплея выполняется ядром, что также значительно медленнее, чем при рендеринге на выделенном оборудовании. Несмотря на то, что рендеринг с аппаратным ускорением не работает, анимация на эмулируемом устройстве довольно плавная, что также показано на видео в начале сообщения в блоге.
Эмулируемое устройство в этот момент успешно загружает Springboard и отображает домашний экран


Реализация поддержки мультитач
Следующим шагом для меня было добавление поддержки навигации по пользовательскому интерфейсу касанием экрана. Моя идея состояла в том, чтобы использовать тот же подход, что и iPhone Simulator, включенный в Xcode, где щелчки мыши преобразуются в прикосновения к экрану. То, что кажется относительно простой задачей — определить, где пользователь нажал на экран, преобразовать это прикосновение в пару координат (x, y) и передать ее ядру — на самом деле является очень сложной задачей. Этот патент, выданный Apple в 2007 году, описывает некоторые необходимые шаги для точной регистрации прикосновений и жестов пользователя. Таким образом, мультитач-устройство генерирует кадры которые читаются мультитач-драйвером в ядре. Каждый кадр, содержащий событие касания, содержит подробную информацию о касании в виде многоточия
В какой-то момент ядро начинает инициализировать HID-устройства, в том числе мультитач-устройство. Процедура инициализации мультитач-устройства примерно выглядит следующим образом:
1. Загрузка данных калибровки: ядро загружает данные калибровки на мультитач-устройство и калибрует устройство. Эти калибровочные данные включены в файловую систему, а также встроены в дерево устройств.
2. Загрузка данных прошивки: ядро загружает некоторые данные прошивки Zephyr2 на мультитач-устройство. Эти данные прошивки включены в файловую систему, а также встроены в дерево устройств.
3. Чтение информации об устройстве : ядро получает различные отчеты о состоянии с мультитач-устройства. Эти отчеты включают информацию о нескольких аспектах мультисенсорного устройства, например информацию о версиях и количестве точек касания в горизонтальном/вертикальном направлении сенсорной поверхности.
Ядро взаимодействует с мультисенсорным устройством через интерфейс SPI. Чтобы кадры, сгенерированные мультитач-устройством, успешно передавались в ядро, мне пришлось настроить и запустить SPI-контроллер. Мультитач-устройство генерирует прерывание GPIO, чтобы сообщить ядру о доступности кадров, например, есть ли касание или какое-то другое событие, которое необходимо обработать. Чтобы получить больше информации о структуре фреймов, включающих сенсорные события, я модифицировал openiboot для инициализации мультитач-устройства, скомпилировал его и зарегистрировал все поля во фрейме, как видно на скриншоте ниже:
Тщательно проанализировав кадры, генерируемые различными касаниями и свайпами, я понял, как преобразовать щелчки мыши в окне QEMU в касания и кадры мультитач-устройства. Каждый кадр, связанный с событием касания, также содержит информацию о скорости смахивания. Эта скорость используется, например, при прокрутке вертикального списка или при настройке горизонтального ползунка. Чтобы убедиться, что эти действия прокрутки работают правильно, мне также пришлось обеспечить горизонтальную и вертикальную скорость в каждом кадре, генерируемом касанием. Я вычисляю эти скорости, сравнивая координаты x/y предыдущего события мыши с координатами текущего события мыши.
Наконец, я добавил поддержку кнопки «Домой» (активируется нажатием клавиши «H») и кнопки питания (активируется нажатием клавиши «P»). Этот шаг был довольно простым. На данный момент у меня есть полностью функциональный iPod Touch, который загружается на домашний экран и по которому можно перемещаться с помощью щелчков мыши и клавиатуры.
Я также обнаружил, что некоторые приложения аварийно завершают работу из-за отсутствия важных файлов ресурсов. Причина отсутствия этих файлов в том, что я создаю хранилище NAND из корневой файловой системы, предоставленной в IPSW. Однако эта чистая файловая система заполняется различными файлами при восстановлении или установке iPhoneOS. В моей эмуляции я не выполняю сценарии восстановления. Мне также пришлось скопировать записи активации с реального устройства, чтобы обойти активацию устройства.
Некоторые другие скриншоты при просмотре предустановленных приложений iPhoneOS:
Известные проблемы и дальнейшие действия
Хотя теперь у меня есть функциональный эмулятор iPod Touch, осталось немало проблем:
- Устройство аварийно завершает работу при попытке отобразить клавиатуру. Похоже, это из-за того, что libicucore.dylib(библиотека, отвечающая за поддержку юникода) неправильно загружается в память, но я так и не разобрался, почему именно так происходит.
- Есть несколько нечастых сбоев, связанных с драйвером USB и контроллером флэш-памяти. Я подозреваю, что это условия гонки, потому что аппаратная связь в QEMU намного быстрее, чем на реальном устройстве, что может нарушить некоторые базовые предположения в логике ядра.
- Расширенные жесты не поддерживаются, например, сведение и увеличение масштаба.
- Регулятор яркости тоже пока не работает.
- Память NAND не сохраняется.
- Бывают разные глюки при выключении устройства или переходе в режим автоблокировки.
Иногда было сложно отлаживать и выяснять, что происходит на устройстве. Большая часть отладки выполнялась путем подключения отладчика GDB к гостевой системе QEMU. Было бы полезно запустить интерактивную оболочку. Я пытался скомпилировать и запустить bashна эмулируемом устройстве, но у меня не получилось.
Также было бы неплохо работать над единой инфраструктурой для эмуляции других поколений iPhone, iPod Touch, Apple TV и, возможно, даже Apple Watch. Однако все эти устройства имеют различия в технических характеристиках аппаратного и программного обеспечения, и их эмуляция может занять очень много времени. В качестве следующего шага я хотел бы попытаться получить функциональный iPod Touch 2G.
Я надеюсь, что этот пост в блоге дал некоторое представление о процессе эмуляции iPod Touch 1G. Есть много деталей, о которых я не писал, но я мог бы написать о них в других сообщениях блога. В моем следующем сообщении в блоге я предоставлю инструкции по компиляции QEMU, созданию пользовательских образов NOR/NAND и запуску эмуляции QEMU. А пока, пожалуйста, дайте мне знать, если у вас есть какие-либо идеи, предложения или вопросы по этому проекту!
Переведено специально для xss.pro
Автор перевода: yashechka
Источник:
На видео ниже показан эмулятор в действии при загрузке устройства и при навигации по различным приложениям:
Чтобы достичь вышеизложенного, я опирался на некоторые из предыдущих работ по эмуляции устройств iOS/Apple, выполненных другими:
https://worthdoingbadly.com/xnuqemu/ - Этот первоначальный пост в блоге @zhuowei изначально вдохновил меня начать этот проект.
https://alephsecurity.com/2019/06/17/xnu-qemu-arm64-1/ - gоследующая работа Йохатана Афека, основанная на работе @zhuowei.
https://github.com/danzatt/QEMU-s5l89xx-port - Ранняя работа над эмуляцией SoC S5L8900.
https://github.com/TrungNguyen1909/qemu-t8030 - Эта эмуляция iPhone 11 с QEMU — обеспечивает полную функциональность эмуляции ядра.
https://github.com/iDroid-Project/openiBoot - Проект openiboot стал бесценным ресурсом для понимания аппаратных компонентов iPod Touch (я очень надеюсь, что когда-нибудь мы сможем запускать Android на устройствах iOS).
https://ghidra-sre.org/ Инструмент обратного проектирования Ghidra, который я использовал для дизассемблирования образов загрузчика/ядра и других двоичных файлов.
https://gist.github.com/dizimka/2922441 - Этот дамп дерева устройств iPod Touch, сделанный @dizima, содержит обзор и технические характеристики аппаратных компонентов, включенных в iPod Touch 1G.
Самой сложной частью этого проекта была эмуляция многих аппаратных компонентов, включенных в iPod Touch. Спецификации большинства этих компонентов, которые я должен был ввести в эксплуатацию, являются проприетарными и недокументированными, что иногда затрудняет их правильную эмуляцию. Однако я думаю, что это первый эмулированный продукт Apple, который не только имеет открытый исходный код, но и имеет полную поддержку дисплея и мультитач (хотя Correllium также предлагает виртуализированные iPhone, Correllium является коммерческим и закрытым исходным кодом). В этом сообщении блога я расскажу о некоторых проблемах, с которыми я столкнулся, опишу шаги, предпринятые в процессе загрузки, и перечислю некоторые будущие задачи, которые могут сделать эмуляцию еще лучше. Мне понравилось работать с этим эмулятором, и я узнал много нового о внутреннем устройстве мобильных устройств.
Я специально решил сосредоточиться на эмуляции iPod Touch 1G с первой выпущенной версией iOS. Я сделал это по двум причинам: во-первых, старые устройства имеют меньше аппаратных компонентов, чем новые устройства, что упрощает создание эмулятора устройства. Современные устройства Apple содержат множество дополнительных аппаратных компонентов, таких как нейронные механизмы, защищенные анклавы и различные датчики, которые сделают эмуляцию таких устройств намного более сложной и трудоемкой. Вторая причина заключается в том, что в более старых версиях iPhoneOS/iOS практически не реализованы меры безопасности, такие как кеш доверия. Сосредоточившись на самой примитивной версии iPhoneOS, мне не пришлось обходить какой-либо механизм безопасности.
Текущий статус проекта
Все аппаратные компоненты, необходимые для запуска iBoot, ядра XNU, Springboard и предустановленных приложений iPhoneOS, работают. Эти аппаратные компоненты:
- Криптографический движок AES
- Механизм хеширования SHA1
- Модуль идентификации чипа
- Аппаратные часы и таймер
- GPIO-контроллер
- ЖК-дисплей и фреймбуферы
- Контроллер NAND и модуль кода исправления ошибок (ECC)
- Контроллер флэш-памяти (FMC), используемый для связи с памятью NAND.
- Мультисенсорное устройство
- Блок управления питанием и встроенные часы реального времени
- SDIO-контроллер
- SPI-контроллер
- Контроллер I2C
- Контроллер векторных прерываний (VIC) и контроллер прерываний GPIO
- Контроллер прямого доступа к памяти (DMA)
- UART-контроллер
Следующие аппаратные компоненты еще не работают, но они также не обязательны для полной загрузки iPod Touch:
- Устройства USB OTG/Synopsys
- Аудиоустройства
- Контроллер Wi-Fi 802.11
- Графический процессор PowerVR MBX
- Движок видеокодера/декодера
- Акселератор и датчик света
- Процедура загрузки iPod Touch
На приведенной ниже диаграмме показаны все пять этапов загрузки пользовательских приложений с iPod Touch:
Bootrom и низкоуровневый загрузчик
iPod Touch 1G использует набор инструкций ArmV6 (Little Endian). Первый шаг проверки этого проекта включал настройку машины QEMU с процессором, чтобы мы могли выполнять некоторый код. К счастью, QEMU поддерживает ARM1176ЦП и необходимый набор инструкций. После инициализации машины QEMU и инициализации некоторой памяти мы готовы загрузить наши двоичные файлы в память и выполнить некоторый код!
Первым кодом, который выполняется при включении iPod Touch, является код начальной загрузки, предположительно разработанный Samsung, когда был представлен iPod Touch 1G. Bootrom встроен в устройство, доступен только для чтения и не может быть изменен с помощью программного обеспечения. Поэтому уязвимости в bootrom очень востребованы, так как такие уязвимости не могут быть исправлены с помощью программного обеспечения ( Checkm8 был последней уязвимостью такого рода). Дамп кода bootrom можно скачать с этого сайта - https://securerom.fun/. Сначала я попытался загрузить и выполнить загрузочный код на своей машине QEMU. Однако я быстро обнаружил, что загрузчик переходит к какому-то коду, который, вероятно, также зашит в устройство и отсутствует в дампе загрузчика, который я использовал (отсутствующий код, кажется, находится по смещению 0x22000000в памяти). Поскольку в начале этого проекта у меня не было физического iPod Touch 1G, я не мог получить недостающий код. Низкоуровневый загрузчик (LLB, шаг 2 на приведенном выше рисунке) также переходит к этому загадочному коду, поэтому вместо этого я переключил свое внимание на выполнение iBoot (шаг 3 на приведенном выше рисунке).
Веселье с загрузчиком iBoot
Основной функцией загрузчика iBoot является инициализация периферийных устройств устройства, а также загрузка и выполнение образа ядра. iBoot также может войти в режим восстановления, который позволяет переустановить iPhoneOS с помощью iTunes. К счастью, проект openiBoot проделал большую работу по повторной реализации большей части функций, предоставляемых iBoot. Этот исходный код помог мне понять основную логику и процедуры iBoot. Поскольку iBoot инициализирует и взаимодействует с различными аппаратными компонентами, мне также пришлось сосредоточиться на том, чтобы эти компоненты были запущены и работали для запуска iBoot.
Первым аппаратным компонентом, над которым я работал, был векторный контроллер прерываний (VIC). Этот компонент регистрирует запросы на прерывание от других аппаратных компонентов и информирует ЦП, когда произошло прерывание. iPod Touch 1G, похоже, оснащен PL192, который хорошо задокументирован. После того, как VIC был запущен и запущен, я работал над перенаправлением операторов печати, сгенерированных ядром, в консоль QEMU, что помогло в процессе отладки. Ниже вы можете увидеть консольный вывод iBoot вплоть до того момента, когда iBoot загружает и расшифровывает ядро XNU:
iis_init()
spi_init()
power supply type batt
battery voltage Reading PMU register 87
error
SysCfg: version 0x00010001 with 4 entries using 200 of 8192 bytes
BDEV: protecting 0x2000-0x8000
image 0x1802bd20: bdev 0x1802b6a8 type dtre offset 0x10800 len 0x7d28
image 0x1802c170: bdev 0x1802b6a8 type batC offset 0x18d40 len 0x101e1
image 0x1802c5c0: bdev 0x1802b6a8 type logo offset 0x29a80 len 0x1c3a
image 0x1802ca10: bdev 0x1802b6a8 type nsrv offset 0x2bfc0 len 0x4695
image 0x1802ce60: bdev 0x1802b6a8 type batl offset 0x30d00 len 0xc829
image 0x1802d2b0: bdev 0x1802b6a8 type batL offset 0x3e240 len 0xe9d2
image 0x1802e888: bdev 0x1802b6a8 type recm offset 0x4d780 len 0xb594
display_init: displayEnabled: 0
otf clock divisor 5
fps set to: 59.977
SFN: 0x600, Addr: 0xfe00000, Size: 0x14001e0, hspan: 0x500, QLEN: 0x140
merlot_init() -- Universal code version 08-29-07
Merlot Panel ID (0x71c200):
Build: PVT1
Type: TMD
Project/Driver: M68/NSC-Merlot
ClcdInstallGammaTable: No Gamma table found for display_id: 0x0071c200
power supply type batt
battery voltage error
power supply type batt
battery voltage error
usb_menu_init()
vrom_late_init: unknown image crc: 0x66a3fbbf
=======================================
::
:: iBoot, Copyright 2007, Apple Inc.
::
:: BUILD_TAG: iBoot-204
::
:: BUILD_STYLE: RELEASE
::
=======================================
[FTL:MSG] Apple NAND Driver (AND) 0x43303032
[NAND] Device ID 0xa514d3ad
[NAND] BANKS_TOTAL 8
[NAND] BLOCKS_PER_BANK 4096
[NAND] SUBLKS_TOTAL 4096
[NAND] USER_SUBLKS_TOTAL 3872
[NAND] PAGES_PER_SUBLK 1024
[NAND] PAGES_PER_BANK 524288
[NAND] SECTORS_PER_PAGE 4
[NAND] BYTES_PER_SPARE 64
[FTL:MSG] FIL_Init [OK]
[FTL:MSG] BUF_Init [OK]
[FTL:MSG] VFL_Init [OK]
[FTL:MSG] FTL_Init [OK]
[FTL:MSG] VFL_Open [OK]
[FTL:MSG] FTL_Open [OK]
Boot Failure Count: 0 Panic Fail Count: 0
Delaying boot for 0 seconds. Hit enter to break into the command prompt...
HFSInitPartition: 0x1802b8f0
Reading 8900 header with length 2048 at address 0x0b000000
Will decrypt 8900 image at address 0x0b000000 (len: 3319392 bytes)
Loading kernel cache at 0xb000000...
data starts at 0xb000180
Как видно из приведенного выше журнала, iBoot сначала инициализирует различные аппаратные компоненты; затем он считывает несколько изображений из флэш-памяти NOR, инициализирует ЖК-экран, инициализирует блок управления питанием (PMU) для считывания состояния батареи, а затем считывает образ ядра из флэш-памяти NAND. Наконец, он передает выполнение ядру. Если по какой-либо причине загрузка не удалась, iBoot переходит в режим восстановления, который позволяет выполнять несколько команд отладки через интерфейс UART.
iPod Touch 1G содержит два типа постоянной памяти: NOR и NAND. Память NOR представляет собой относительно небольшое блочное устройство. Основная файловая система сохраняется в памяти NAND и имеет размер от 8 до 32 ГБ для iPod Touch 1G, в зависимости от модели. Чтобы эмулятор работал правильно, нам нужно эмулировать эти блочные устройства и убедиться, что загрузчик/ядро может правильно их читать.
Построение образа NOR
Во время загрузки загрузчик iBoot считывает несколько файлов, хранящихся во флэш-памяти NOR. К таким файлам относятся, например, логотип Apple, отображаемый при загрузке устройства, экран режима восстановления, экран низкого заряда батареи и дерево устройств. Память NOR также содержит разделы NVRAM и SysCfg, в которых хранятся различные свойства устройства, такие как серийный номер, MAC-адрес, параметры загрузки для ядра и журналы крешев. Я написал пользовательский инструмент для создания действительного образа памяти NOR из файлов, включенных в файл IPSW, и предоставил этот пользовательский образ памяти при запуске QEMU. Исходный код для создания этого образа NOR можно найти в этом репозитории GitHub - https://github.com/devos50/generate-ipod-touch-1g-nor.
Создание образа NAND
Одной из обязанностей iBoot является загрузка ядра XNU в память и передача ему выполнения. iBoot может загружать образ ядра двумя способами: он либо считывает образ из файловой системы в памяти NAND, либо загружает образ, расположенный по определенному смещению памяти. Поскольку я хочу, чтобы эмуляция была как можно ближе к реальной процедуре загрузки, я сосредоточился на том, чтобы запустить и запустить ввод-вывод NAND. На первый взгляд это звучит просто, поскольку память NAND разделена на разные страницы, и каждая страница пронумерована. Таким образом, наш эмулятор может просто вернуть соответствующие данные на странице, когда их запрашивает iBoot или ядро. Однако под капотом устройство NAND намного сложнее, в основном потому, что память NAND требует алгоритмов для выравнивания износа. Это необходимо, потому что каждый физический блок в NAND может быть надежно стерт и записан столько раз, прежде чем производительность снизится. Драйверы NAND также содержат другие алгоритмы, например, для кода исправления ошибок, управления поврежденными блоками и сборки мусора. В результате физическое расположение страниц в памяти NAND сильно отличается от логической организации этих страниц.
К счастью, Openiboot содержит реализацию драйвера NAND из iPod Touch 1G. Это помогло мне не только понять физическое расположение памяти NAND, но и понять взаимодействие ввода-вывода с памятью NAND. Я также просмотрел просочившуюся версию исходного кода iBoot, которая содержит исходный код драйверов NAND. Подобно образу NOR, я написал различные сценарии, которые создают образ NAND, который может быть прочитан драйвером NAND. Исходный код можно найти в этом репозитории GitHub - https://github.com/devos50/generate-ipod-touch-1g-nand. Образ NAND создается из корневой файловой системы, включенной в файл прошивки IPSW.
Расшифровка и загрузка образа ядра
В этот момент iBoot корректно загружает образ ядра из хранилища NAND (находится в файловой системе по адресу /System/Library/Caches/com.apple.kernelcaches/kernelcache.s5l8900xrb). Однако этот образ ядра зашифрован с использованием проприетарной схемы шифрования 8900, и iBoot переходит к процедуре расшифровки в памяти, инструкций которой у меня нет. Чтобы по-прежнему иметь возможность расшифровать образ, я реализовал обратный вызов в начале функции шифрования, к которой выполняется переход, и вместо этого расшифровал образ ядра в логике QEMU. Затем я оставляю расшифрованный образ ядра в памяти, после чего iBoot переходит к методу входа образа ядра.
Были некоторые другие аппаратные компоненты, которые я должен был настроить и запустить, прежде чем iBoot дойдет до загрузки ядра. Эти компоненты включают блок управления питанием (PMU), контроллер прямого доступа к памяти, аппаратные таймеры и часы, а также ЖК-дисплей.
Эмуляция ядра XNU
Большая часть моих усилий по реверсу ушла на понимание ядра XNU и эмуляцию аппаратных компонентов, используемых ядром. Несмотря на то, что ядро XNU в основном имеет открытый исходный код, Apple, похоже, поддерживает приватный форк для ядра, включенного в устройства Apple, такие как iPod Touch и iPhone. Сравнивая ядро, поставляемое в iOS, с кодом ядра с открытым исходным кодом, кажется, что Apple внесла различные изменения в ядро iOS, чтобы гарантировать, что оно может работать на процессорах ARM. Кроме того, в реализации ядра с открытым исходным кодом отсутствует исходный код драйверов аппаратных компонентов для конкретных устройств.
Ядро XNU сначала инициализирует несколько подсистем BSD, включая логику управления памятью, планировщик и поддержку потоков. Затем ядро считывает дерево устройств, включенное в образ NOR. Дерево устройств — это структура данных, описывающая все аппаратные компоненты, входящие в состав конкретного устройства. Ядро использует дерево устройств для загрузки соответствующих драйверов для всех этих компонентов и для инициализации этих компонентов с правильными настройками. Дамп дерева устройств, используемого iPod Touch 1G, можно найти здесь - https://gist.github.com/dizimka/2922441и, как видите, содержит довольно много информации! Дерево устройств также может отображать информацию о зависимостях между различными компонентами. Например, это указывает на то, что связь с мультитач-экраном осуществляется через интерфейс SPI, управляемый контроллером SPI.
Пожалуй, самым важным полем в узлах дерева устройств является адрес памяти компонентов. Большинство аппаратных компонентов используют технику, называемую вводом-выводом с отображением памяти или MMIO. В MMIO одно и то же адресное пространство используется для адресации как основной памяти, так и устройств ввода/вывода. В результате ядро может просто читать и записывать в основную память для связи с аппаратными компонентами. Реализация поддержки ввода-вывода с отображением памяти в QEMU оказалась относительно простой. Однако некоторые аппаратные компоненты не используют MMIO, и доступ к ним должен осуществляться с использованием других аппаратных протоколов связи, таких как SPI, I2C или SDIO.
После инициализации подсистем BSD ядро запускает фреймворк IOKit и начинает загружать драйверы для аппаратных компонентов, включенных в дерево устройств. Так как ядро загружает довольно много драйверов (примерно 30), проверка того, что все эти драйверы правильно запущены, заняло у меня несколько месяцев. Процесс загрузки иногда зависал, потому что он ждал аппаратного компонента, который я еще не эмулировал должным образом, чтобы дать определенный ответ. Ниже вы можете увидеть скриншот некоторых декомпилированных драйверов:
И некоторые файлы в моем репозитории QEMU :
В какой-то момент во время выполнения ядро начинает читать двоичные файлы из файловой системы в NAND. Несмотря на то, что у меня уже была полная поддержка NAND, чтобы порадовать iBoot, ядро читает из хранилища NAND через контроллер флэш-памяти или FMC. Это оказалось одним из самых сложных аппаратных компонентов, которые мне приходилось эмулировать. FMC также был первым аппаратным компонентом, который мне пришлось эмулировать без какой-либо документации или исходного кода. На расшифровку различных операций ввода-вывода, выполняемых FMC, и обеспечение считывания правильных страниц NAND у меня ушло несколько недель проб и ошибок. На данный момент операции чтения NAND с помощью FMC должны работать правильно, но я еще не добавил поддержку операций записи NAND.
После инициализации всех драйверов настало время ядру выполнить launchd приложение. launchd - это первая программа, запускаемая ядром и, как следует из названия, она отвечает за запуск других приложений и сценариев запуска (она также работает с PID 1). Загрузка ядра считается завершенной при запуске launchd. С этого момента приложения исполняеиые launchd запускаемые в пространстве пользователя, а не в пространстве ядра. Когда launchd работает правильно, следующим шагом был запуск стандартного приложения, управляющего домашним экраном iPod Touch: Springboard.
Запуск трамплина
Приложение launchd ищет сценарии запуска в /System/Library/LaunchDaemons каталоге в файловой системе и выполняет эти сценарии. Эти сценарии запуска включают, например, демоны для управления звуком, адресную книгу и поддержку Bluetooth. Один из этих сценариев запуска com.apple.SpringBoard.plist содержит инструкции по запуску Springboard.app приложения. К сожалению, Springboard застрял вскоре после его запуска, потому что я еще не реализовал рендеринг дисплея.
Да прибудет дисплей
Springboard.App содержит логику для отображения главного экрана, включая значки приложений, диалоговые окна и строку состояния. Рендеринг дисплея на iPod Touch (или любом мобильном устройстве в этом отношении) обычно ускоряется аппаратным графическим процессором. Из реверс-инжиниринга я уже мог видеть, что этот аппаратный компонент весьма задействован и что протокол связи между ядром и графическим процессором сложен. В качестве альтернативы я начал искать способ отключить на данный момент графический процессор. К счастью, сценарий запуска Springboard.App позволил мне добавить переменную средыLK_ENABLE_MBX2D=0 который успешно отключает графический процессор. С этой опцией весь рендеринг дисплея выполняется ядром, что также значительно медленнее, чем при рендеринге на выделенном оборудовании. Несмотря на то, что рендеринг с аппаратным ускорением не работает, анимация на эмулируемом устройстве довольно плавная, что также показано на видео в начале сообщения в блоге.
Эмулируемое устройство в этот момент успешно загружает Springboard и отображает домашний экран



Реализация поддержки мультитач
Следующим шагом для меня было добавление поддержки навигации по пользовательскому интерфейсу касанием экрана. Моя идея состояла в том, чтобы использовать тот же подход, что и iPhone Simulator, включенный в Xcode, где щелчки мыши преобразуются в прикосновения к экрану. То, что кажется относительно простой задачей — определить, где пользователь нажал на экран, преобразовать это прикосновение в пару координат (x, y) и передать ее ядру — на самом деле является очень сложной задачей. Этот патент, выданный Apple в 2007 году, описывает некоторые необходимые шаги для точной регистрации прикосновений и жестов пользователя. Таким образом, мультитач-устройство генерирует кадры которые читаются мультитач-драйвером в ядре. Каждый кадр, содержащий событие касания, содержит подробную информацию о касании в виде многоточия
В какой-то момент ядро начинает инициализировать HID-устройства, в том числе мультитач-устройство. Процедура инициализации мультитач-устройства примерно выглядит следующим образом:
1. Загрузка данных калибровки: ядро загружает данные калибровки на мультитач-устройство и калибрует устройство. Эти калибровочные данные включены в файловую систему, а также встроены в дерево устройств.
2. Загрузка данных прошивки: ядро загружает некоторые данные прошивки Zephyr2 на мультитач-устройство. Эти данные прошивки включены в файловую систему, а также встроены в дерево устройств.
3. Чтение информации об устройстве : ядро получает различные отчеты о состоянии с мультитач-устройства. Эти отчеты включают информацию о нескольких аспектах мультисенсорного устройства, например информацию о версиях и количестве точек касания в горизонтальном/вертикальном направлении сенсорной поверхности.
Ядро взаимодействует с мультисенсорным устройством через интерфейс SPI. Чтобы кадры, сгенерированные мультитач-устройством, успешно передавались в ядро, мне пришлось настроить и запустить SPI-контроллер. Мультитач-устройство генерирует прерывание GPIO, чтобы сообщить ядру о доступности кадров, например, есть ли касание или какое-то другое событие, которое необходимо обработать. Чтобы получить больше информации о структуре фреймов, включающих сенсорные события, я модифицировал openiboot для инициализации мультитач-устройства, скомпилировал его и зарегистрировал все поля во фрейме, как видно на скриншоте ниже:
Тщательно проанализировав кадры, генерируемые различными касаниями и свайпами, я понял, как преобразовать щелчки мыши в окне QEMU в касания и кадры мультитач-устройства. Каждый кадр, связанный с событием касания, также содержит информацию о скорости смахивания. Эта скорость используется, например, при прокрутке вертикального списка или при настройке горизонтального ползунка. Чтобы убедиться, что эти действия прокрутки работают правильно, мне также пришлось обеспечить горизонтальную и вертикальную скорость в каждом кадре, генерируемом касанием. Я вычисляю эти скорости, сравнивая координаты x/y предыдущего события мыши с координатами текущего события мыши.
Наконец, я добавил поддержку кнопки «Домой» (активируется нажатием клавиши «H») и кнопки питания (активируется нажатием клавиши «P»). Этот шаг был довольно простым. На данный момент у меня есть полностью функциональный iPod Touch, который загружается на домашний экран и по которому можно перемещаться с помощью щелчков мыши и клавиатуры.
Я также обнаружил, что некоторые приложения аварийно завершают работу из-за отсутствия важных файлов ресурсов. Причина отсутствия этих файлов в том, что я создаю хранилище NAND из корневой файловой системы, предоставленной в IPSW. Однако эта чистая файловая система заполняется различными файлами при восстановлении или установке iPhoneOS. В моей эмуляции я не выполняю сценарии восстановления. Мне также пришлось скопировать записи активации с реального устройства, чтобы обойти активацию устройства.
Некоторые другие скриншоты при просмотре предустановленных приложений iPhoneOS:
Известные проблемы и дальнейшие действия
Хотя теперь у меня есть функциональный эмулятор iPod Touch, осталось немало проблем:
- Устройство аварийно завершает работу при попытке отобразить клавиатуру. Похоже, это из-за того, что libicucore.dylib(библиотека, отвечающая за поддержку юникода) неправильно загружается в память, но я так и не разобрался, почему именно так происходит.
- Есть несколько нечастых сбоев, связанных с драйвером USB и контроллером флэш-памяти. Я подозреваю, что это условия гонки, потому что аппаратная связь в QEMU намного быстрее, чем на реальном устройстве, что может нарушить некоторые базовые предположения в логике ядра.
- Расширенные жесты не поддерживаются, например, сведение и увеличение масштаба.
- Регулятор яркости тоже пока не работает.
- Память NAND не сохраняется.
- Бывают разные глюки при выключении устройства или переходе в режим автоблокировки.
Иногда было сложно отлаживать и выяснять, что происходит на устройстве. Большая часть отладки выполнялась путем подключения отладчика GDB к гостевой системе QEMU. Было бы полезно запустить интерактивную оболочку. Я пытался скомпилировать и запустить bashна эмулируемом устройстве, но у меня не получилось.
Также было бы неплохо работать над единой инфраструктурой для эмуляции других поколений iPhone, iPod Touch, Apple TV и, возможно, даже Apple Watch. Однако все эти устройства имеют различия в технических характеристиках аппаратного и программного обеспечения, и их эмуляция может занять очень много времени. В качестве следующего шага я хотел бы попытаться получить функциональный iPod Touch 2G.
Я надеюсь, что этот пост в блоге дал некоторое представление о процессе эмуляции iPod Touch 1G. Есть много деталей, о которых я не писал, но я мог бы написать о них в других сообщениях блога. В моем следующем сообщении в блоге я предоставлю инструкции по компиляции QEMU, созданию пользовательских образов NOR/NAND и запуску эмуляции QEMU. А пока, пожалуйста, дайте мне знать, если у вас есть какие-либо идеи, предложения или вопросы по этому проекту!
Переведено специально для xss.pro
Автор перевода: yashechka
Источник:
Emulating an iPod Touch 1G and iPhoneOS 1.0 using QEMU (Part I) | Martijn de Vos
My personal website.
devos50.github.io