Имеется на обслуживании более 2-х сотен SIP телефонов Akuvox SP-R50. Отличаются они крайней нестабильностью имеющейся русской прошивки версии 50.175.6.120.
В сети была найдена более новая прошивка версии 50.0.6.179, которая должна исправить ошибки предыдущей (я на это надеюсь). У этой прошивки пока выявлен один, но очень большой недостаток (с точки зрения пользователей) — она не содержит русского языка интерфейса.
Естественно возникает желание скреститьежа с ужомстарую и новую прошивки, то есть извлечь файлы перевода из старой прошивки и добавить их в новую.
И так приступаем.
Извлечение файлов перевода — особых проблем не доставляет. При помощи binwalk быстро выясняем, что файл прошивки содержит 3 сжатых раздела squashfs, сжатый образ ядра Linux и ещё что-то:
В первом же образе squashfs, находятся необходимые нам файлы русификации:
Кроме того, необходимо ещё поправить 2 файла, добавив в них русский язык по имеющимуся образцу:
А вот теперь самое интересное: собрать обратно прошивку, так чтобы телефон её принял. Самый очевидный метод, просто заменить старый образ squashfs на новый, не работает. Нужно разбираться с форматом прошивки.
Вооружимся любым HEX-редактором и посмотрим на начало файла прошивки вооружённым взглядом:
Явно просматриваются 3 различных сигнатуры:
С последним всё ясно: это первый, из найденных ранее, разделов squashfs. Его размер 0x424000 (0x4241c0-0x1c0).
Оставим пока в покое сигнатуру RROM.
Сигнатурой RPAT помечено 7 записей размером 44 байта (0xb8-0x8c=0xe4-0xb8=..=0x194-0x168). Я предположил, что они составляют таблицу разделов файла прошивки. Получается, что прошивка содержит 7 разделов.
Присмотримся к содержимому первой записи подробнее:
как видим, предположение оказалось верным:
в 10 двойном слове располагается смещение от начала файла первого раздела squashfs,
в 8 — размер данных раздела,
2 и 9 похожи на контрольные суммы.
Надо сказать, что я был прав в своих предположениях, но дальше гадать не стал. Нашёл, что данные сигнатуры содержатся в файле /bin/upgrader из второго образа squashfs. Взяв в руки IDA, я просто отреверсил все интересующие меня места.
Назначение полей структуры, рассмотренной выше следующее:
partition_checksum — считается как сложение по модулю 2 (xor) всех двойных слов начиная с partition_offset до partition_offset+partition_size.
partition_header_checksum — xor значений текущей структуры от partition_type до partition_offset.
А теперь самое интересное, двумя сигнатурой RROM помечена структура заголовка всего файла прошивки:
rom_size — размер данных от смещения (0x8c) первой сигнатуры RPAT до конца файла.
rom_checksum — xor всех значений от смещения первой сигнатуры RPAT до конца файла.
Но не всё так просто, заголовок файла слегка зашифрован: байты заменены по таблице и сдвинуты на некоторое значение:
Процедура шифровки заголовка, надеюсь, осилите сами.
Таким образом процесс модификации прошивки выглядит следующим образом:
В сети была найдена более новая прошивка версии 50.0.6.179, которая должна исправить ошибки предыдущей (я на это надеюсь). У этой прошивки пока выявлен один, но очень большой недостаток (с точки зрения пользователей) — она не содержит русского языка интерфейса.
Естественно возникает желание скрестить
И так приступаем.
Извлечение файлов перевода — особых проблем не доставляет. При помощи binwalk быстро выясняем, что файл прошивки содержит 3 сжатых раздела squashfs, сжатый образ ядра Linux и ещё что-то:
$ binwalk 50.175.6.120.rom
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
448 0x1C0 Squashfs filesystem, little endian, version 4.0,
compression:gzip, size: 4341451 bytes, 494 inodes,
blocksize: 131072 bytes, created: 2017-10-17 13:23:12
4342208 0x4241C0 Squashfs filesystem, little endian, version 4.0,
compression:gzip, size: 91850 bytes, 32 inodes,
blocksize: 131072 bytes, created: 2017-10-17 13:23:12
4436416 0x43B1C0 Squashfs filesystem, little endian, version 4.0,
compression:gzip, size: 1551743 bytes, 265 inodes,
blocksize: 131072 bytes, created: 2017-10-17 13:11:59
6125036 0x5D75EC CRC32 polynomial table, little endian
6158912 0x5DFA40 uImage header, header size: 64 bytes,
header CRC: 0x66623955, created: 2017-10-17 13:09:48,
image size: 1410328 bytes, Data Address: 0x20008000,
Entry Point: 0x20008000, data CRC: 0xCC266E28,
OS: Linux, CPU: ARM, image type: OS Kernel Image,
compression type: none, image name: "Linux-2.6.32.28-svn34715"
6172280 0x5E2E78 gzip compressed data, maximum compression, from Unix,
last modified: 2017-10-17 13:09:46
7569304 0x737F98 ELF, 32-bit LSB executable, ARM, version 1 (SYSV)
7631987 0x747473 Unix path: /config/Phone/Key/ProgramableKey.conf
7698370 0x7577C2 Unix path: /svn/software/project/r52-v6.0_stable:local-37614:remote-r37614
В первом же образе squashfs, находятся необходимые нам файлы русификации:
- resources/Language/Russian.txt b/resources/Language/Russian.txt
- resources/www/htdocs/lang/RUSSIAN.js
- resources/www/htdocs/note/Note_RUSSIAN.js
- resources/Font/Arial_12_0410_TO_045F.bin
- resources/Font/Arial_18_0410_TO_045F.bin
- factory/Static/System/InputMethod.conf
Кроме того, необходимо ещё поправить 2 файла, добавив в них русский язык по имеющимуся образцу:
- factory/Static/System/General.conf
- resources/www/htdocs/lang/LANGUAGELIST.js
А вот теперь самое интересное: собрать обратно прошивку, так чтобы телефон её принял. Самый очевидный метод, просто заменить старый образ squashfs на новый, не работает. Нужно разбираться с форматом прошивки.
Вооружимся любым HEX-редактором и посмотрим на начало файла прошивки вооружённым взглядом:
00000000 4D 4F 52 52 80 00 00 00 EB 1C 23 CB FA FA FA FA MORR......#.....
00000010 FA FA FA FA 07 00 00 00 FA FA FA FA FA FA FA FA ................
00000020 FA FA FA FA 1C 77 75 00 0E 92 46 CB FA FA FA FA .....wu...F.....
00000030 FA FA 5B FA FA FA 3F FA FA FA 77 FA FA FA C5 B5 ..[...?...w...ŵ
00000040 77 3F EE 88 92 FA FA FA FA FA FA 7C 7C 7C 7C 7C w? ......|||||
00000050 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C FA FA FA FA FA FA ||||||||||......
00000060 FA FA FA FA FA FA FA FA 4D 4F 52 52 FA FA FA FA ........MORR....
00000070 FA FA FA FA FA FA FA FA 00 00 00 00 1E 73 6B 1F .............sk.
00000080 FF DE 21 D8 36 7B 35 87 7B 5F 83 D5 54 41 50 52 ..!.6{5.{_..TAPR
00000090 20 00 00 00 B7 96 C0 C4 00 00 00 00 01 00 00 00 ...............
000000A0 00 00 00 00 06 00 00 00 78 06 AF 32 00 40 42 00 ........x..2.@B.
000000B0 08 D1 2D F6 C0 01 00 00 54 41 50 52 20 00 00 00 ..-.....TAPR ...
000000C0 9A 5A A6 2D 00 00 00 00 01 00 00 00 00 00 00 00 .Z.-............
000000D0 05 00 00 00 78 06 AF 32 00 70 01 00 26 6D 4A 1F ....x..2.p..&mJ.
000000E0 C0 41 42 00 54 41 50 52 20 00 00 00 3A 67 BB EC .AB.TAPR ...:g..
000000F0 00 00 00 00 01 00 00 00 00 00 00 00 03 00 00 00 ................
00000100 07 00 00 01 00 B0 17 00 FF 66 EF ED C0 B1 43 00 .........f....C.
00000110 54 41 50 52 20 00 00 00 B1 7E 84 9F 00 00 00 00 TAPR ....~......
00000120 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 01 ................
00000130 80 98 02 00 E1 87 DD 9E C0 61 5B 00 54 41 50 52 ......ݞ .a[.TAPR
00000140 20 00 00 00 5F 91 7F DD 00 00 00 00 01 00 00 00 ..._...........
00000150 00 00 00 00 02 00 00 00 0B 00 00 01 58 85 15 00 ............X...
00000160 4F EE 37 DC 40 FA 5D 00 54 41 50 52 20 00 00 00 O.7.@.].TAPR ...
00000170 45 27 F7 29 01 00 00 00 00 00 00 00 00 00 00 00 E'.)............
00000180 FF 00 00 00 4B 00 00 01 00 F8 01 00 68 A0 85 28 ....K.......h..(
00000190 98 7F 73 00 54 41 50 52 20 00 00 00 18 B7 84 F6 ..s.TAPR .......
000001A0 03 00 00 00 00 00 00 00 00 00 00 00 FF 00 00 00 ................
000001B0 01 00 00 01 10 00 00 00 6D C0 F1 F7 98 77 75 00 ........m....wu.
000001C0 68 73 71 73 EE 01 00 00 40 04 E6 59 00 00 02 00 hsqs....@..Y....
Явно просматриваются 3 различных сигнатуры:
- MORR (RROM, так как байты записываются в обратном порядке) — по смещениям 0x0 и 0x68
- TAPR (RPAT) — по смещениям 0x8c, 0xb8, 0xe4, 0x110, 0x13c, 0x168 и 0x194
- hsqs (sqhs) — по смещению 0x1c0
С последним всё ясно: это первый, из найденных ранее, разделов squashfs. Его размер 0x424000 (0x4241c0-0x1c0).
Оставим пока в покое сигнатуру RROM.
Сигнатурой RPAT помечено 7 записей размером 44 байта (0xb8-0x8c=0xe4-0xb8=..=0x194-0x168). Я предположил, что они составляют таблицу разделов файла прошивки. Получается, что прошивка содержит 7 разделов.
Присмотримся к содержимому первой записи подробнее:
0. 0x52504154
1. 0x00000020
2. 0xC4C096B7
3. 0x00000000
4. 0x00000001
5. 0x00000000
6. 0x00000006
7. 0x32AF0678
8. 0x00424000
9. 0xF62DD108
10. 0x000001C0
как видим, предположение оказалось верным:
в 10 двойном слове располагается смещение от начала файла первого раздела squashfs,
в 8 — размер данных раздела,
2 и 9 похожи на контрольные суммы.
Надо сказать, что я был прав в своих предположениях, но дальше гадать не стал. Нашёл, что данные сигнатуры содержатся в файле /bin/upgrader из второго образа squashfs. Взяв в руки IDA, я просто отреверсил все интересующие меня места.
Назначение полей структуры, рассмотренной выше следующее:
0. magic_num
1. partition_header_size
2. partition_header_checksum
3. partition_type
4. process_type
5. partition_data_type
6. partition_id
7. partition_version
8. partition_size
9. partition_checksum
10. partition_offset
partition_checksum — считается как сложение по модулю 2 (xor) всех двойных слов начиная с partition_offset до partition_offset+partition_size.
partition_header_checksum — xor значений текущей структуры от partition_type до partition_offset.
А теперь самое интересное, двумя сигнатурой RROM помечена структура заголовка всего файла прошивки:
0, 26. magic_num
1. rom_header_size
2. rom_header_checksum
3. rom_type
4. process_type
5. partition_num
6. device_id
7. oem_id
8. rom_version
15. mid_version
16. sw_protect
9. rom_size
10. rom_checksum
rom_size — размер данных от смещения (0x8c) первой сигнатуры RPAT до конца файла.
rom_checksum — xor всех значений от смещения первой сигнатуры RPAT до конца файла.
Но не всё так просто, заголовок файла слегка зашифрован: байты заменены по таблице и сдвинуты на некоторое значение:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
uint8_t DeCryptTab[256] =
{
0xdc, 0xdf, 0xa5, 0x5a, 0xe1, 0xe3, 0xe6, 0xee, 0x6b, 0x89, 0xa8, 0x10, 0x1d, 0x35, 0xc9, 0x64,
0x40, 0x12, 0x9f, 0xaa, 0xc0, 0x4f, 0x2b, 0x5b, 0xcd, 0xa4, 0xa9, 0x08, 0xb9, 0x6e, 0x84, 0xba,
0xd2, 0xd4, 0xe0, 0xe5, 0x09, 0x7c, 0xe9, 0x3f, 0x55, 0xe8, 0xd3, 0x37, 0x57, 0x59, 0xc3, 0x9e,
0xd1, 0x97, 0x63, 0x73, 0x51, 0x7e, 0x4b, 0x49, 0xde, 0xe2, 0xea, 0x05, 0x29, 0xeb, 0xec, 0x32,
0x5e, 0x60, 0x54, 0x4d, 0x86, 0x95, 0x45, 0xb3, 0xce, 0x2d, 0x11, 0xef, 0x8b, 0xbd, 0xe7, 0xf0,
0x65, 0xf1, 0xf2, 0x85, 0xb7, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0x07, 0x69, 0xed, 0x76, 0xf9,
0xfa, 0x79, 0x23, 0xc2, 0xdd, 0x7f, 0xe4, 0xfb, 0x53, 0xb1, 0xfc, 0xfd, 0xa6, 0x9c, 0x1b, 0xda,
0xfe, 0x34, 0x39, 0x25, 0x7b, 0x94, 0x93, 0xaf, 0xb6, 0x0d, 0xb8, 0xd0, 0xff, 0x3c, 0x0a, 0x0c,
0x13, 0x2a, 0x41, 0x36, 0x48, 0x3d, 0x4c, 0x68, 0x77, 0x42, 0x21, 0x47, 0xae, 0x0f, 0x90, 0x33,
0x67, 0xca, 0x75, 0xd5, 0x30, 0x56, 0x6f, 0x28, 0x82, 0x3a, 0x58, 0x62, 0x7a, 0x99, 0x02, 0x01,
0x0b, 0x4a, 0x6c, 0x2e, 0x52, 0x80, 0x87, 0x98, 0x9a, 0xa0, 0x20, 0xbf, 0xd6, 0xd7, 0x88, 0x8a,
0xb4, 0x83, 0xd8, 0x5c, 0x2c, 0x06, 0x31, 0x46, 0x04, 0x8f, 0x18, 0x66, 0x15, 0x26, 0x22, 0x2f,
0x3e, 0x6d, 0x71, 0x5d, 0x5f, 0x78, 0x24, 0x6a, 0x61, 0x0e, 0x8c, 0x9b, 0x8d, 0xa1, 0x16, 0x81,
0xa2, 0xa3, 0xa7, 0xab, 0xb0, 0x03, 0x43, 0x3b, 0xc1, 0x7d, 0x9d, 0xc4, 0x19, 0xc5, 0xc7, 0x91,
0xcb, 0x27, 0x8e, 0xcf, 0xb5, 0xbb, 0xac, 0x92, 0x1f, 0x38, 0x74, 0xb2, 0x1e, 0xc8, 0x1c, 0x1a,
0x70, 0x44, 0xd9, 0xbc, 0x17, 0x4e, 0xad, 0x50, 0xbe, 0x96, 0x00, 0x14, 0xc6, 0xcc, 0x72, 0xdb
};
int deCrypt(uint8_t ARG1, uint8_t *ARG2, int ARG3)
{
int I;
int v8;
int v9;
char v10;
int v11;
char v13;
signed int v12;
// замена по таблице
if ( ARG3 > 0 )
{
I = 0;
do
{
ARG2[I] = DeCryptTab[ARG2[I]];
++I;
}
while ( ARG3 != I );
}
// циклический сдвиг влево
if ( ARG1 % ARG3 > 0 )
{
v8 = ARG3 - 1;
v9 = 0;
do
{
v10 = *ARG2;
if ( v8 > 0 )
{
v11 = 0;
v12 = 1;
do
{
v13 = ARG2[v12++];
ARG2[v11++] = v13;
}
while ( v12 != ARG3 );
}
ARG2[ARG3 - 1] = v10;
++v9;
}
while ( ARG1 % ARG3 > v9 );
}
return 0;
}
void main(int argc, char *argv[])
{
FILE *f;
uint32_t buff[35];
uint32_t tmpROMChecksum;
uint32_t tmpPartitionNum;
uint32_t tmpROMSize;
uint32_t vRomHeaderSize;
int i;
uint32_t checksum;
f = fopen(argv[1],"rb");
if(f)
{
fread(buff,140,1,f);
fclose(f);
if ((buff[0] != 0x52524f4d) && (buff[26] != 0x52524f4d))
{
printf("Signature not found! Exit");
exit(1);
}
checksum = 0;
for ( i = 3; i < 35; i++ )
{
checksum = checksum ^ buff[i];
}
if ( checksum != buff[2])
{
printf("\n Bad rom_header_checksum: %x (%x)\n", checksum, buff[2]);
}
vRomHeaderSize = buff[1];
if ( vRomHeaderSize > 0x7F )
{
tmpROMChecksum = buff[10];
tmpPartitionNum = buff[5];
tmpROMSize = buff[9];
buff[10] = buff[28];
buff[5] = buff[27];
buff[9] = buff[29];
deCrypt((uint8_t)(buff[31] & 0xff), (uint8_t *)(&buff[3]), vRomHeaderSize - 36);
buff[28] = buff[10];
buff[27] = buff[5];
buff[29] = buff[9];
buff[10] = tmpROMChecksum;
buff[5] = tmpPartitionNum;
buff[9] = tmpROMSize;
}
f = fopen(argv[1],"r+");
if(f)
{
fseek(f, 0, 0);
fwrite(buff,140,1,f);
fclose(f);
}
}
else
printf("Error\n");
}
Процедура шифровки заголовка, надеюсь, осилите сами.
Таким образом процесс модификации прошивки выглядит следующим образом:
- Расшифровываем заголовок
- Разбиваем прошивку на части
- Модифицируем
- Собираем все части обратно
- Поправляем в заголовке смещения, размеры, контрольные суммы
- Шифруем заголовок
- Заливаем прошивку в телефон (осторожно, можно получить кирпич!)