Volvo SCT / part 1 — как проникнуть в чужое ядро

от автора

Предисловие

Я являюсь обладателем одной интересной железки — SCT unit touch. Это медиа система с Android, которую ставили в виде дополнительного аксессуара в автомобили Volvo. Железка продавалась дистребьютерами Volvo, но разработана французским производителем электроники Parrot. По меркам Android, железка слабая, но по меркам встраиваемых систем вполне себе ок. Проблема в том, что ее оставили без поддержки как Volvo так и Parrot, cсобственно Гугл тоже давно убрал поддержку для столь старого Android. Из плюсов — железка не плохо интегрирована в машину и можно легко получить root. Из минусов — ни Parrot, ни Volvo не предоставляют ни исходники ядра ни SDK. В DevZone все давно потерто, поэтому придется допиливать напильником.

Спека

  • OS: Android 2.3.7 SDK:10 (API level — 10)

  • Processor: TI OMAP 3630 ARM Cortex-A8 800MHz (ARMv7 rev 2 v71)

  • Video: PowerVR SGX530 Resolution: 800×480 — 7″

  • Memory: 512MiB

  • Internal storage: 360MiB

  • System storage: 516MiB

  • System cache: 7,61MiB

  • 2xUSB (max.64GiB FAT32)

  • 1xSD-card (max.64GiB FAT32)

  • 1xRCA input

  • Bluetooth

  • WI-FI 802.11 n

Внешний вид

Видео #1
Видео #2

Знакомство

Посмотрим, что за ядро нам досталось и с какими параметрами оно запускается. Начнем с uname и /proc/cmdline. Первое дает нам информацию о ядре, второе показывает с какими параметрами оно было запущенно. В параметрах есть первая улика — sgx_reduce_pixels_left интересный аргумент, специфичный для TI OMAP. Немного поисков в интернете выдают ссылку на чужой github репозиторий — asteroid_smart_kernel. Упомяну, что Asteroid это название серии продуктов Parrot, на котором базируется SCT, при этом используется чип от TI.

Первая удача — версия ядра в репозитории соответствует тому, что мы имеем на устройстве. К счастью, ядро на системе скомпилированно с опцией CONFIG_IKCONFIG_PROC — это дает возможность узнать у ядра конфигурацию с которой его компилировали. Поэтому копируем конфиг с которым компилировалось ядро из /proc/config.gz. Файл конфигурации задает диспозицию, т.е. он включает или выключает определенные функции ядра и решает, какой функционал пойдет в само ядро, а какой будет скомпилирован в виде модулей.

# uanme -r 2.6.35.13-02963-g0ac0fc3  # cat /proc/cmdline androidboot.serialno=c5burjtir81tofqu5061 mtdparts=omap2-nand.0:512K(Pbootloader),16M(Pmain_boot),4M(Pfactory),1003M(Psystem),512K(Pmtdoops) console=null,115200 loglevel=0 ubi.mtd=Psystem,2048 ubi.mtd=Pfactory,2048 omap_vout.vid1_static_vrfb_alloc=y videoout=omap24xxvout omap_vout.video1_numbuffers=6 omapfb.vram=0:2M omapfb.mode=lcd:800x480MR-16@60 omaplfb.sgx_reduce_pixels_left=64 omaplfb.sgx_reduce_pixels_right=0 omaplfb.sgx_reduce_pixels_down=0  loglevel=0 androidboot.bootloader=ecos-bootloader-omap3630-start-58-gf2afe20  # adb pull /proc/config.gz

Дальше смотрим, по какому адресу ядро раскладывает свои внутренние символы. Для этого ищем в логах ядра начало инициализации. В данном случае — 0xc0108000 — 0xc0137000.

dmesg | grep init <7>[    0.000000] free_area_init_node: node 0, pgdat c06a60fc, node_mem_map c0792000<5>[    0.000000]        .init : 0xc0108000 - 0xc0137000   ( 188 kB)

Вторая улика, которая нам поможет — ядро скомпилированно с CONFIG_KALLSYMS_ALL. Поэтому достаем внутренние символы ядра:

adb pull /proc/kallsyms

Судя по адресам, в kallsyms адреса соответствуют инициализации.

cat kallsyms c0108000 T stext c0108000 T _sinittext c0108000 T _stext c0108000 T __init_begin c0108034 t __enable_mmu c0108060 t __turn_mmu_on c0108078 t __create_page_tables c01080f0 t __switch_data c0108118 t __mmap_switched c0108160 t __error c0108160 t __error_a c0108160 t __error_p c0108168 t __lookup_processor_type

Что это все значит и как с этим жить? Идем в исходники и берем уже знакомый нам по cmdline sgx_reduce_pixels_left. Это u32, т.е. 32 битный беззнаковый int. Описание говорит, что он урезает размер frame buffer с левой стороны.

В коде драйвера это выглядит подобным образом:

u32 sgx_reduce_pixels_left = CONFIG_SGX_XRES_REDUCE_PIXELS; module_param(sgx_reduce_pixels_left, uint, S_IRUGO); MODULE_PARM_DESC(sgx_reduce_pixels_left,"Reduce Android's size on framebuffer. Left"); EXPORT_SYMBOL_GPL(sgx_reduce_pixels_left);

В kallsyms это будет выглядеть таким образом:

cat kallsyms | grep sgx_reduce_pixels_left  c063b5b0 r __param_sgx_reduce_pixels_left c0521b10 t __param_str_sgx_reduce_pixels_left c061f810 r __ksymtab_sgx_reduce_pixels_left c0625180 r __kcrctab_sgx_reduce_pixels_left c0630bfe r __kstrtab_sgx_reduce_pixels_left

Формат: aдрес / тип / имя (префикс имеет значение)

  • T — глобальная функция

  • t — локальная функция модуля компиляции (static)

  • D — глобальные данные

  • d — локальные данные

  • R — глобальные read-only данные

  • r — локальные read-only данные

  • kstrtab — строковое имя символа

  • kcrctab — адрес CRC суммы каждого символа (будет разобрано дальше)

  • ksymtab — структура, которая описывает сам символ

Компиляция

Для начала определимся с компилятором, который использовал оригинальный автор.

# cat /proc/version Linux version 2.6.35.13-02963-g0ac0fc3 (g-beguin@FR-B-800-0057) (gcc version 4.4.3 (GCC) ) #1 PREEMPT Mon Feb 10 11:02:21 CET 2014

GCC 4.4.3 можно взять тут. Интересно, что beguin — это имя пользователя. Нашел на Linkedin только одного embedded разработчика с такой фамилией, но он не признался, что это его рук дело =) Вторая теория более грустная и говорит, что beguin можно перевести с французского, как «увлечение».

Выкачиваем один рандомный модуль ядра с устройства, чтобы посмотреть на его внутренности:

adb pull /system/lib/modules/2.6.35.13-02963-g0ac0fc3/kernel/drivers/media/video/tvp5150.ko /system/lib/modules/2.6.35.13-02963-g0ac0fc3/kernel/drivers/media/video/tvp5150.ko: 1 file pulled. 0.2 MB/s (27496 bytes in 0.162s)

что говорит modinfo:

modinfo ./tvp5150.ko  filename:       ./tvp5150.ko license:        GPL author:         Mauro Carvalho Chehab description:    Texas Instruments TVP5150A video decoder driver srcversion:     A9FDE6A9EB55B0DBACD3892 alias:          i2c:tvp5150 depends:         vermagic:       2.6.35.13-02963-g0ac0fc3 preempt mod_unload modversions ARMv7  parm:           debug:Debug level (0-2) (int)

Самое интересное тут — vermagic. По сути это — виза для модуля, по которой ядро решает пускать его или нет. Если vermagic не сойдется, то ядро нас вышвырнет. К сожалению, хотя исходники у нас соответствует по версии, но у нас нет нужного коммита, т.к. кровавые корпорации не хотят делиться модифицированным GPL кодом. По скольку у нас есть конфиг — это частично решает проблему, но не до конца. Выкачиваем исходники ядра и . скармливаем ему наш конфиг.

git clone https://github.com/CunningLogic/asteroid_smart_kernel.git dtrx config.gz

Теперь нам нужно подделать vermagic. Для этого выставим два параметра: включим логику билд системы для автоматического определения версии и выставим локальную версию на нужный нам коммит.

CONFIG_LOCALVERSION_AUTO=y CONFIG_LOCALVERSION="-02963-g0ac0fc3"

Первый полёт

Создаем наш тестовый модуль (vkb.c).

#include <linux/module.h> #include <linux/kallsyms.h>  static int __init prsyms_init(void) {     printk(KERN_INFO "we are in!\n");     return 0; }  static void __exit prsyms_exit(void) {      }  module_init(prsyms_init); module_exit(prsyms_exit);  MODULE_AUTHOR("incogn1to"); MODULE_DESCRIPTION("Get into unknown kernel"); MODULE_LICENSE("GPL"); 

Простенький мейк

obj-m += vkb.o  all:  make -C .. M=$(PWD) modules clean:  make -C .. M=$(PWD) clean 

При компиляции — выставляем архитектуру, версию и путь к компилятору. Почем так сложно? Это станет понятно немного дальше.

#!/bin/sh export ARCH=arm export CONFIG_LOCALVERSION="-02963-g0ac0fc3" export CROSS_COMPILE=~/tools/arm-linux-gcc-4.4.3/bin/arm-none-linux-gnueabi- make

Смотрим, что получилось:

modinfo vkb.ko filename:       /home/incogn1to/src/asteroid_smart_kernel/vkb/vkb.ko license:        GPL description:    Control over virtual buttons author:         incogn1to srcversion:     B73A9A163E53563A49DD2B5 depends:        path vermagic:       2.6.35.13-02963-g0ac0fc3+ preempt mod_unload modversions ARMv7 

Vermagic почти такой, какой нам нужен, но есть лишний плюсик. Это работа скрипта, который называется setlocalversion. Этот скрипт распознал изменения в репозитории, которые не были закомичены в репозиторий и решил таким образом отметить этот факт. Придется отучить его это делать. Сделаем так, чтобы scm_version просто возвращала CONFIG_LOCALVERSION.

scm_version() {     return ${CONFIG_LOCALVERSION}; }

Еще раз запускаем компиляцию и смотрим результат.

modinfo vkb.ko filename:       /home/incogn1to/src/asteroid_smart_kernel/vkb/vkb.ko license:        GPL description:    Control over virtual buttons author:         incogn1to srcversion:     B73A9A163E53563A49DD2B5 depends:        path vermagic:       2.6.35.13-02963-g0ac0fc3 preempt mod_unload modversions ARMv7 

Теперь veramagic идентичен модулю взятому с системы и можно попробовать внедрить модуль в kernel space. Пробуем…

# insmod ./vkb.ko insmod: init_module './vkb.ko' failed (Exec format error)

К сожалению что-то пошло не так. Но что…

dmesg | grep vkb <4>[  444.145111] vkb: no symbol version for module_layout

Хьюстон, у нас проблема…

Основная проблема прячется за включенным modversions. Это значит, что при компиляции, для каждого символа ядра вычисляется CRC и мы можем вызвать только те символы, CRC которых мы знаем. Если CRC не сходится — ядро отказывается от вызова. Что мы можем сделать? Взять CRC символов из модулей, которые есть на системе. Расковырять их нам поможет вот этот скрипт. В итоге получаем Module.symvers с известными нам CRC. Их смело можем использовать. Вызовы к другим символам будут отвергнуты ядром.

Натравливаем скрипт на доступные модули ядра и в итоге получаем новый Module.symvers.

0x56941536tty_kref_putdummy/path 0xa4755a9atty_port_tty_getdummy/path 0xdb3877d___dma_single_dev_to_cpudummy/path 0xa69430b5mmc_card_sleepdummy/path 0x94af632ecomplete_alldummy/path 0xefef88d5posix_lock_file_waitdummy/path 0xe55e144aproc_dointvec_minmaxdummy/path 0xdd80f922usb_wwan_dtr_rtsdummy/path 0xaf5bf6efnfs_debugdummy/path 0xf8ca57a9nf_ct_l3proto_putdummy/path 0xc4ddd373usbnet_get_ethernet_addrdummy/path 0xe0878bfe__kreallocdummy/path 0x8463fddaconsume_skbdummy/path 0x98447920sunrpc_cache_unregister_pipefsdummy/path 0x8e9e1a6csock_sendmsgdummy/path 0xd03c7700secure_ipv4_port_ephemeraldummy/path 0x20000329simple_strtouldummy/path 0x3d7690a1crypto_spawn_tfmdummy/path 0x767002b6snd_ctl_adddummy/path 0xb54533f7usecs_to_jiffiesdummy/path 0xa9a951a0tty_hangupdummy/path 0x4336eda0dev_driver_stringdummy/path 0xfbe27a1crb_firstdummy/path 0x2882da10rfkill_allocdummy/path 0x2a14ddb8ethtool_op_set_tx_hw_csumdummy/path 0x567e5b8sdio_readsbdummy/path  <much more>

Копируем наш новенький Module.symvers в корень ядра и запускаем компиляцию модуля. Теперь становится понятно, почему нам нужен такой странный способ компиляции. Задача — скомпилировать модуль относительно существующих symvers и config, так чтобы при этом система сборки ядра их не трогала и не пыталась пересчитать. Если все сделано правильно, то ядро примет наш модуль.

NB! Если symvers для символа не известен — модуль все равно скомпилируется, т.к. make просто выставит там 0x00, что будет отвергнуто ядром.

Заливаем его на устройство и скармливаем ядру c помощью insmod.

adb push vkb.ko /mnt/sdcard vkb.ko: 1 file pushed. 0.2 MB/s (23834 bytes in 0.129s) adb shell cd /mnt/sdcard insmod ./vkb.ko dmesg <3>[  869.831817] init: untracked pid 4872 exited <6>[ 1130.217773] we are in!

Репозиторий с моими изменениями доступен тут.

PS

Была part-0 с получением root, но я решил, что это уже сто раз было описано. Если кому-то будет интересно — могу описать.

Если у кого-то по абсолютной случайности сохранились исходники от этой железки — буду очень рад.

Ссылки по теме:


ссылка на оригинал статьи https://habr.com/ru/post/706840/


Комментарии

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

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