Pull to refresh

История неожиданного «окирпичивания» и восстановления одного смартфона

Reading time 7 min
Views 16K


Началась эта история с того, что в результате неудачных экспериментов с ядром смартфона Samsung Galaxy Ace 2 (он же GT-I8160, aka codina), приводящих к ребутам девайса, оказалось так, что раздел EFS перестал читаться. Собственно, сами эксперименты отношения к данному вопросу не имеют — возможно, как-нибудь дойду и до них, но это выходит за рамки данной статьи. Хотя и раздел EFS – один из наиболее важных на этом смартфоне, убийство данного раздела само по себе не приводит к катастрофическим последствиям, поскольку его все еще можно восстановить, например, с другого телефона, после чего, при желании сменить WIFI MAC и BT MAC. На данном устройстве IMEI хранится не на разделе EFS, а CSPSA (Crash Safe Parameter Storage Area, дословно переводится как «Область хранения параметров, устойчивая к крашам»). Вот если с этим разделом пойдет что-то не так, это уже будет не столь весело, собственно об этом и пойдет речь далее. Кого заинтересовал, прошу под кат.

После выхода из строя раздела EFS, я, в первую очередь, снял его дамп и попытался поднять его при помощи e2fsck. Неудача – суперблок EXT4 поврежден, да и вообще все выглядело так, словно содержимое раздела превратилось в фарш. Самое время было поискать бэкап, но, какова беспечность (!), в самый неподходящий момент его не оказалось ни на компе, ни на флешке. Всех дальнейших мучений можно было избежать, только если бы у меня были своевременно сделанные бэкапы. Дело было поздним вечером, принявшись искать дампы этого раздела в интернете и найдя один на зарубежном ресурсе, я тут же принялся его прошивать. Наверное, одна из самых страшных вещей, что может произойти при пользовании утилитой dd — это опечатка или ошибка при наборе пути к разделу. Сейчас мне только остается удивляться собственной неосторожности (или криворукости, называйте, как хотите), но именно это и произошло… Раздел EFS на данном девайсе – это /dev/block/mmcblk0p7, но я почему-то в тот момент не имел ни тени сомнения, что это, якобы, должен быть /dev/block/mmcblk0p6. Собственно, дальнейшее не требует особых объяснений, обо всей катастрофичности произошедшего я уже понял только тогда, когда dd вывел сообщение о том, что при записи был достигнут конец раздела. Вроде далеко не первый год пользуюсь устройством и являюсь одним из тех немногих оставшихся разработчиков под этот богом забытый девайс. Как же я мог оказаться в такой, с какой стороны не посмотри, дурацкой ситуации? Не спрашивате меня, самому хотелось бы знать… Так телефон с легкой руки стал если не «кирпичом», то «инвалидом» точно.

Исследование раздела CSPSA


Итак, раздел EFS оказался убит крашем во время активной записи на диск, раздел же CSPSA, усточивый к крашам, не устоял к моей опрометчивости. Пойди я в СЦ, даже там наверняка бы развели руками. Прошивка CSPSA от другого девайса дела также не исправит, т.к. IMEI, очевидно, хранится где-то помимо данного раздела и сравнивается с тем, что находится в CSPSA. Да и статья не о смене IMEI, а об его восстановлении.

Ситуация безвыходная, как ни посмотри, думал я. Я оказался втянут в то, чем явно не планировал заниматься, ковырять внутренности раздела CSPSA. Оказалось, что среди утекших в 2014 году исходников для чипсета ST-Ericsson Novathor U8500 есть исходники на утилиты, позволяющие работать с данным разделом:

root@:/ # cd ramdisk
root@:/ramdisk # ./cspsa-cmd
[CSPSA]: open CSPSA0
[CSPSA]: <CSPSA_Open('CSPSA0').>
[CSPSA]: <Result 'T_CSPSA_RESULT_OK'>
[CSPSA]: ls
       Key       Size
         0          4
         1         96
         2         96
         3         96
      1000         38
     66048        497
     -8192         41
        -4          4
        -3          4
        -2          4
        -1          4

Number of keys in CSPSA : 11
Total size of all values: 884

[CSPSA]: read_to_file 3e8 /sdcard/1000.bin
[CSPSA]: <Read (000003e8) to file '/sdcard/1000.bin'>
[CSPSA]: CSPSA_GetSizeOfValue(000003e8): T_CSPSA_RESULT_OK
[CSPSA]: <CSPSA_Read(000003e8).>
[CSPSA]: <CSPSA_ReadValue(000003e8, 38): T_CSPSA_RESULT_OK>
[CSPSA]: <38 bytes written to file '/sdcard/1000.bin'.>
[CSPSA]: write_from_file 3e8 /sdcard/1000.bin
[CSPSA]: <Write (000003e8) from file '/sdcard/1000.bin'>
[CSPSA]: <38 bytes read from file '/sdcard/1000.bin'.>
[CSPSA]: <CSPSA_WriteValue(000003e8, 38).>
[CSPSA]: <CSPSA_WriteValue(000003e8, 38): T_CSPSA_RESULT_OK>

Команда «open CSPSA0» открывает сокет CSPSA0, таким образом подключаясь к процессу cspsa-server. Как можно видеть, команда ls отображает хранимые в CSPSA параметры и их размер.

Далее, командой read_to_file можно записать параметр (здесь это номер 1000, указаный в HEX, — 3e8) в файл, и точно также записать параметр из файла в раздел командой write_from_file.

Это конечно здорово, что удалось найти такую утилиту, но не давало мне никаких подсказок по поводу того, что должно было находиться в данных параметрах, чтобы IMEI снова читался нормально. По факту, утилита могла «скрывать» часть правды, выдавая не все параметры, и скрывая тот, в котором хранится IMEI. Для того, чтобы понять, что могло находиться в этих параметрах, нужно было иметь несколько таких различных разделов CSPSA, но в самом деле, не могу же я просить кого-то слить раздел со столь приватной информацией. В интернете нашлись два различных раздела CSPSA, но сравнение считанных через cspsa-cmd параметров выдало слишком большую разницу, около 512-768 байт суммарно при сравнении их друг с другом. Даже при наличии всех исходников, могло пройти немало времени до того, пока я бы разобрался (если разобрался вообще). Идею о восстановлении CSPSA «в лоб» пришлось оставить, обратив взгляд на другие части слитых исходников, которые бы могли помочь восстановить телефон.

Я наткнулся на еще одну утилиту, которая выглядела многообещающе.

По ссылке приведен список комманд поддерживающихся данной утилитой.

(...)
static const struct {
    const char *str;
    cops_return_code_t (*func)(cops_context_id_t *ctx,
                               int *argc, char **argv[]);
} api_funcs[] = {
    {"read_imei", cmd_read_imei},
    {"bind_properties", cmd_bind_properties},
    {"read_data", cmd_read_data},
    {"get_nbr_of_otp_rows", cmd_get_nbr_of_otp_rows},
    {"read_otp", cmd_read_otp},
    {"write_otp", cmd_write_otp},
    {"authenticate", cmd_authenticate},
    {"deauthenticate", cmd_deauthenticate},
    {"get_challenge", cmd_get_challenge},
    {"modem_sipc_mx", cmd_modem_sipc_mx},
    {"unlock", cmd_simlock_unlock},
    {"lock", cmd_simlock_lock},
    {"ota_ul", cmd_ota_simlock_unlock},
    {"get_status", cmd_simlock_get_status},
    {"key_ver", cmd_verify_simlock_control_keys},
    {"get_device_state", cmd_get_device_state},
    {"verify_imsi", cmd_verify_imsi},
    {"bind_data", cmd_bind_data},
    {"verify_data_binding", cmd_verify_data_binding},
    {"verify_signed_header", cmd_verify_signed_header},
    {"calcdigest", cmd_calcdigest},
    {"lock_bootpartition", cmd_lock_bootpartition},
    {"init_arb_table", cmd_init_arb_table},
    {"write_secprofile", cmd_write_secprofile},
    {"change_simkey", cmd_change_simkey},
    {"write_rpmb_key", cmd_write_rpmb_key},
    {"get_product_debug_settings", cmd_get_product_debug_settings}

};
(...)

Как и в предыдущем случае с cspsa-cmd, cops_cmd подключается к процессу-серверу, copsdaemon (COPS расшифровывается как COre Platform Security).

Этот самый бинарник copsdaemon на девайсе оказался отличным от того, что в исходниках (или у меня не получилось правильно сконфигурировать Android.mk), так или иначе, собранный из исходников отказывался работать. Однако, похоже утилита cops_cmd была совместима с остальным проприетарным ПО и запускалась нормально.

Первое, что я попробовал сделать — запустить команду cops_cmd read_imei — точно сейчас не вспомню, выдало, что-то вроде «error 13, device is tampered». Ага, конечно, чего еще можно было ожидать, с запоротым-то разделом CSPSA. Недолгое чтение исходников привело к команде «bind_properties»:

static cops_return_code_t cmd_bind_properties(cops_context_id_t *ctx,
        int *argc, char **argv[])
{
    cops_return_code_t ret_code;
    cops_imei_t imei;
(...)
usage:
(...)
    fprintf(stderr,
            "Usage: bind_properties imei <imei> (15 digits)\n"
            "Usage: bind_properties keys <k1 k2 k3 k4 k5> (keys are space delimited)\n"
            "Usage: bind_properties auth_data <auth_number> <auth data file name>\n"
            "Usage: bind_properties data <data file name> <optional don't merge flag 0, otherwise merge will occur>\n");
    return COPS_RC_ARGUMENT_ERROR;
}

Как можно видеть из исходников, функция предназначена для записи IMEI в устройство. Но вот незадача, только непосредственно перед записью необходимо произвести аутентификацию при помощи закрытого ключа, которого у меня, очевидно, нет. Оставалось только продолжать ковырять copsdaemon, с тем чтобы попытаться избежать необходимость аутентификации, к счастью, все обошлось и без этого.

В поиске идей


Прошло несколько дней в поисках и размышлениях. После общения с одним своим знакомым с xda-developers я узнал, что для разновидности GT-I8160 с чипом NFC, GT-I8160P, есть прошивка с неким дефолтным, «полу-пустым» разделом CSPSA, и что прошивка этого РОМа на данном девайсе приводит к тому, что IMEI «обнуляется», то есть все 15 цифр IMEI становятся нулями (не помню точно, происходит ли это в случае запоротого раздела CSPSA или вообще независимо от того). Первым же делом скачал данную прошивку и прошил раздел CSPSA — безрезультатно. Коллега предлагал частичную прошивку (т.е. прошивку отдельных разделов, не включая такие «опасные», как загрузчик и другие) данного РОМа. Довольно бесперспективное занятие, это могло окончательно «окирпичить» девайс. Наконец по прошествию еще пары дней, в то время, как я был занят исходниками выше, коллега с XDA сделал поистине невероятную и бесценную находку:

#TA Loader to write default IMEI
service ta_load /system/bin/ta_loader recovery
    user root
    group radio
    oneshot

Это вырезка содержимого файла init.samsungcodina.rc рамдиска из стоковой прошивки Android 4.1.2, где прямо указано в комментарии, что это сервис для восстановления IMEI по умолчанию.
Недолго думая, я запустил из терминала:

/system/bin/ta_loader recovery

Девайс перезагрузился в режим рекавери, затем еще одна перезагрузка вручную в систему, и вуаля! IMEI уже отображается не как «null», а обнуленный, регистрация в сети доступна, прогресс, однако. Тайна «обнуленного» IMEI была раскрыта.

Но это, разумеется, совсем не здорово ходить вот с таким IMEI «по умолчанию». Совсем недолгих поисков хватило, на то, чтобы глянуть на бинарник ta_loader в HEX-редакторе (исходников на эту тулзу уже не нашлось) и подменить нулевой IMEI на свой командой типа:

sed -i "s,<15_zeroes>,<IMEI>," /ramdisk/ta_loader
sed -i "s,<IMEI>0,<16_zeroes>," /ramdisk/ta_loader

Почему команда sed вызывается два раза? В бинарнике есть последовательность из более, чем 15 нулей, не относящаяся к IMEI, поэтому, чтобы вернуть нежеланное изменение, необходимо вызвать команду второй раз. Спешу заверить, пытаться записать «левый» IMEI таким образом бесполезно, утилита работает так, что записать можно только IMEI с коробки (либо дефолтный). Еще одна перезагрузка в рекавери, потом в систему, и, о чудо — IMEI на месте! Более подробно процесс восстановления я описал на форуме XDA Developers. Такие вот дела, повезло, что производитель оставил лазейку для восстановления исходного IMEI. Не случись злоключений выше, наверно, мне и в голову бы не пришло ковыряться во всем этом, но с другой стороны, и не было бы всей этой истории.
Tags:
Hubs:
+11
Comments 20
Comments Comments 20

Articles