Предисловие
Я являюсь обладателем одной интересной железки — 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
Внешний вид



Знакомство
Посмотрим, что за ядро нам досталось и с какими параметрами оно запускается. Начнем с 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/
Добавить комментарий