Основы безопасности операционной системы Android. Native user space, ч.1

от автора

Вступление

В этой статье я попробую рассмотреть безопасность чуть-чуть повыше ядра, а именно: как работает безопасность в Native user space. Мы коснемся темы процесса загрузки операционной системы и рассмотрим структуру файловой системы Android. Как я уже говорил, я не очень силен в Linux, поэтому если заметите неточности, то исправляйте — меня научите и статью улучшите. Так как эта тема довольно обширная, я решил разбить её на две части. В первой части мы рассмотрим процесс загрузки операционной системы и особенности файловой системы. Всем кому интересно, добро пожаловать!

Список статей

Здесь собраны ссылки на предыдущие или следующие мои статьи из этой темы:

  1. Основы безопасности операционной системы Android. Уровень ядра
  2. Основы безопасности операционной системы Android. Native user space, ч.1

Что подразумевается под Native user space

Под Native user space подразумеваются все компоненты пространства пользователя, которые выполняются вне Dalvik Virtual Machine, и которые не являются частью Linux kernel.

Файловая система Android

Для начала давайте рассмотрим структуру файловой системы Android. Хотя Android и базируется на Linux kernel, привычную нашему глазу структуру файловой системы мы здесь не увидим. Давайте запустим эмулятор и посмотрим, что у нас есть. Для этого выполним комманду:

adb shell ls -al 

В моем терминале для эмулятора на Android 4.2 я вижу следующий результат:

drwxr-xr-x root     root              2013-04-10 08:13 acct drwxrwx--- system   cache             2013-04-10 08:13 cache dr-x------ root     root              2013-04-10 08:13 config lrwxrwxrwx root     root              2013-04-10 08:13 d -> /sys/kernel/debug drwxrwx--x system   system            2013-04-10 08:14 data -rw-r--r-- root     root          116 1970-01-01 00:00 default.prop drwxr-xr-x root     root              2013-04-10 08:13 dev lrwxrwxrwx root     root              2013-04-10 08:13 etc -> /system/etc -rwxr-x--- root     root       244536 1970-01-01 00:00 init -rwxr-x--- root     root         2487 1970-01-01 00:00 init.goldfish.rc -rwxr-x--- root     root        18247 1970-01-01 00:00 init.rc -rwxr-x--- root     root         1795 1970-01-01 00:00 init.trace.rc -rwxr-x--- root     root         3915 1970-01-01 00:00 init.usb.rc drwxrwxr-x root     system            2013-04-10 08:13 mnt dr-xr-xr-x root     root              2013-04-10 08:13 proc drwx------ root     root              2012-11-15 05:31 root drwxr-x--- root     root              1970-01-01 00:00 sbin lrwxrwxrwx root     root              2013-04-10 08:13 sdcard -> /mnt/sdcard d---r-x--- root     sdcard_r          2013-04-10 08:13 storage drwxr-xr-x root     root              2013-04-10 08:13 sys drwxr-xr-x root     root              2012-12-31 03:20 system -rw-r--r-- root     root          272 1970-01-01 00:00 ueventd.goldfish.rc -rw-r--r-- root     root         4024 1970-01-01 00:00 ueventd.rc lrwxrwxrwx root     root              2013-04-10 08:13 vendor -> /system/vendor 

Я отмечу здесь только главные директории и те, которые нам пригодятся в будущем. В Интернете можно найти описание и предназаначение других директорий. Можно заметить, что некоторые директории такие же, как и в Linux, например, /dev, /proc, /sys, /mnt, /etc И их предназначение в основном такое же, как и в Linux. Кстати, отметьте, что мы не видим /bin и /lib директорий. Где они скрылись, я расскажу чуть позже.

C другой стороны можно заметить директории, которых в Linux вообще нет. Среди них нас интересуют /data, /system, /cache, /init, /init.rc Давайте рассмотрим их назначение поподробнее.
/system Это главная директория, где хранятся неизменяемые компоненты Android системы. Если проводить аналогию, то эта папка похожа на папку C:\windows\, доступную только для чтения. Т.е. изменять данные в этой директории мы не можем. Как раз здесь можно найти директории /bin и /lib, где хранятся различные исполняемые файлы и shared libraries. Кроме того, здесь же лежат системные приложения, которые встроены в операционку и которые, по умолчанию, нельзя удалить. Содержимое этой директории формируется во время компиляции операционной системы.
/data Т.к. /system у нас доступна только для чтения, то должна быть директория где хранятся изменяемые данные. /data как раз ею и является. Например, в эту директорию в /data/app сохраняются apk файлы устанавливаемых приложений, а в /data/data хранятся их данные (эту директорию мы подробно рассматривали в прошлой статье).
/cache Это просто временное хранилище. Также в эту директорию сохраняются, а потом из неё запускаются системные обновления.

Чтобы понять, что такое /init файл и для чего нужны непонятные файлы с расширением *.rc, рассмотрим процесс загрузки системы.

Процесс загрузки Android


Давайте рассмотрим несколько шагов процесса загрузки операционной системы Android. Эта картинка взята из книги «Embedded Android», там же можно найти и более детальное описание. Хотя в целом я и понимаю процесс, но для меня это больше магия 🙂

CPU. Когда вы нажимаете на кнопку включения, на процессор вашего устройства начинает подаваться напряжение. Так как до этого момента процессор был выключен, и так как он не способен сохранять свое состояние без подачи напряжения, то сразу после старта он находится в некотором неинициализированном состоянии. В данном случае процессор считывает из своего специального регистра некоторый жестко зашитый адрес и начинает выполнять инструкции начиная с него. Чаще всего, этот адрес указывает на чип, в который зашит bootloader (загрузчик).
Bootloader. Bootloader инициализирует RAM и загружает в неё Linux kernel. Кроме того Bootloader создает RAMdisk.
Linux kernel. Ядро инициализирует различные подсистемы, встроенные драйвера и монтирует root filesystem (корневую файловую систему). После этого ядро может запускать первую программу.
На этом магия заканчивается и дальше всё становится более-менее понятно.

Init

Первой программой в случае Android является init. Исполняемый файл находится в корневой директории (/init). Именно эту программу стартует ядро после своей загрузки. Её исходники находятся в папке system/core/init/ Давайте в них слегка покопаемся. Нас интересует system/core/init/init.c:

... int main(int argc, char **argv) {     ...     /* clear the umask */     umask(0);          /* Get the basic filesystem setup we need put          * together in the initramdisk on / and then we will          * let the rc file figure out the rest.          */     mkdir("/dev", 0755);     mkdir("/proc", 0755);     mkdir("/sys", 0755);      mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");     mkdir("/dev/pts", 0755);     mkdir("/dev/socket", 0755);     mount("devpts", "/dev/pts", "devpts", 0, NULL);     mount("proc", "/proc", "proc", 0, NULL);     mount("sysfs", "/sys", "sysfs", 0, NULL);     ...     init_parse_config_file("/init.rc");     ... } 

Вначале мы создаем и монтируем некоторые необходимые для работы директории, а потом парсим файл /init.rc и выполняем то, что распарсили. Формат /init.rc файла очень хорошо описан в readme, там же можно найти и пример. Если кратко, то этот файл представляет собой набор actions (секций — именнованная последовательность комманд). Каждая последовательность команд срабатывает по определенному trigger (триггеру). Например, следующая последовательно — это action, в которой trigger — это fs, а последовательность команд — это набор mount команд:

on fs     # mount mtd partitions     # Mount /system rw first to give the filesystem a chance to save a checkpoint     mount yaffs2 mtd@system /system     mount yaffs2 mtd@system /system ro remount     mount yaffs2 mtd@userdata /data nosuid nodev     mount yaffs2 mtd@cache /cache nosuid nodev 

Исходный файл /init.rc находится в system/core/rootdir/init.rc Давайте рассмотрим некоторые основные его части, хотя я вам очень советую просмотреть его полность. После этого многие вещи вам должны стать понятны. Итак, начинается наш файл следующими строками:

import /init.usb.rc import /init.${ro.hardware}.rc import /init.trace.rc 

Они означают, что кроме init.rc файла нужно также импортировать настройки из файлов init.usb.rc, init.trace.rc и из файла с непонятным именем init.${ro.hardware}.rc Впрочем, ${ro.hardware} — это просто переменная, значение которая определяет тип железа. В случае эмулятора, её значение, например, — goldfish. Далее определяются переменные окружения:

... on init     ...     # setup the global environment     export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin     export LD_LIBRARY_PATH /vendor/lib:/system/lib     export ANDROID_BOOTLOGO 1     export ANDROID_ROOT /system     export ANDROID_ASSETS /system/app     export ANDROID_DATA /data     export ANDROID_STORAGE /storage     export ASEC_MOUNTPOINT /mnt/asec     export LOOP_MOUNTPOINT /mnt/obb     export BOOTCLASSPATH /system/framework/core.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar     ... 

После этого происходит инициализация переменных, необходимых для работы устройства. Если вас заинтересует эта тема, то вы легко найдете информацию о той или иной комманде. Давайте подробно рассмотрим следующий блок (который я уже приводил в этой статье):

on fs     # mount mtd partitions     # Mount /system rw first to give the filesystem a chance to save a checkpoint     mount yaffs2 mtd@system /system     mount yaffs2 mtd@system /system ro remount     mount yaffs2 mtd@userdata /data nosuid nodev     mount yaffs2 mtd@cache /cache nosuid nodev 

MTD — Memory Technology Devices. Если в общих чертах, то MTD — это специальный чип с энергонезависимой (т.е. данные на этом чипе сохраняются после перезагрузки или выключения) flash-памятью (типа NOR или NAND), на который сохраняются образы дисков. В этой статье более подробно рассказывается об этом типе устройств, а также об ограничениях. Специально для этих разновидностей flash-памяти были разработаны специальные файловые системы, например, YAFFS. Одно из самых важных ограничений этих типов памяти заключается в том, что для того чтобы записать данные в сектор, куда уже записаны какие-то данные, вам надо полностью переформатировать всё устройство. Поэтому производители стали переходить на новый тип блочной flash-памяти (eMMC), на которые можно поставить обычную ext4 файловую систему и избавиться от указанного ограничения. Т.к. я показываю пример init.rc файла для эмулятора, где вся работа эмулируется, то в нем по умолчанию используется файловая система YAFFS2 (думаю, что это пережитки прошлого, т.к. YAFFS2 использовалась для всех устройств до Android 2.2). В реальном устройстве (это как раз один из примеров, когда необходимо использовать init.rc файл для определенного железа) эти комманды будут перезаписаны. Например, в случае устройства herring (Google Nexus S), в файле init.herring.rc эта секция выглядит следующим образом:

on fs     mkdir /efs 0775 radio radio     mount yaffs2 mtd@efs /efs noatime nosuid nodev     chmod 770 /efs/bluetooth     chmod 770 /efs/imei     mount_all /fstab.herring     ... 

Где fstab.herring — это файл, содержимое которого выглядит следующим образом:

... /dev/block/platform/s3c-sdhci.0/by-name/system          /system             ext4      ro                                                    wait /dev/block/platform/s3c-sdhci.0/by-name/userdata        /data               ext4      noatime,nosuid,nodev,nomblk_io_submit,errors=panic    wait,encryptable=/efs/userdata_footer 

Как вы могли заметить, /system, /data, /cache — это просто mounting points (точки монтирования файловой системы), которые указывают либо на MTD устройства (в случае эмулятора), либо на блочные устройства (в случае настоящего устройства), куда записаны соответствующие дисковые образы (system.img, userdata.img и cache.img). Я не уверен, но думаю, что внутри смартфона находится один единственный чип с flash-памятью, разделенный на partitions (тома), в каждый из которых записан соответствующий образ. Этот чип с flash-памятью — то, что мы знаем под именем Internal storage (внутренняя память), объем которой — один из основных параметров смартфона.

Следует заметить, что /system смонтирован read-only (только для чтения). Это означает, что содержимое данного раздела не изменяется в процессе работы устройства, а только когда вы, например, обновляете систему на вашем устройстве (используя системные обновления).

Продолжим рассматривать наш init.rc. По триггеру post-fs-data формируется базовая структура файловой системы /data раздела. Там, в общем всё понятно — набор mkdir, chown, chmod команд.

Далее init.rc запускает несколько демонов. Если вернуться к рисунку в начале статьи, то они перечислены в блоке Native daemons. На этом мы пока остановимся. Как вы могли заметить из рисунка, я не полностью рассмотрел процесс загрузки операционной системы. Некоторые непокрытые этапы я рассмотрю в следующих статья.

Заключение

В следующей части я расскажу, откуда берутся образы system.img, userdata.img и cache.img и рассмотрю безопасность на уровне Native user space. Как всегда приветствуются исправления, дополнения, а так же предложения, о чем написать. И хотя у меня уже есть некоторый план, о чем писать в следующих статья, я готов его подкорректировать.

Ссылки

  1. Working with MTD Devices
  2. «Embedded Android» by Karim Yaghmour
  3. «Android Security Underpinnings» by Marko Gargenta

ссылка на оригинал статьи http://habrahabr.ru/post/176131/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *