- 积分
- 201
- 实力分
- 点
- 金钱数
- 两
- 技术分
- 分
- 贡献分
- 分
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
Модификация прошивки в телефоне Siemens SL45(i)
Материал этой статьи основывается на информации, добытой мною из интернета, а так же обнаруженой RizaPN, Chaos, и другими авторами патчей на прошивки в форумах GSM-Forum и Клуб любителей Siemens SL45(i).
Данный документ все еще находится в процессе написания, так что статья наверняка содержит в себе ошибки, неточности, а так же язык местами кривоват.
Дата последнего изменения: 8 сентября 2003 г.
Как известно, "сердцем" телефона является микросхема Infineon E-GOLD+ PMB6850, в которую входит микропроцессор Infineon C166CBC (согласно SL45 Level 2.5e Repair Documentation). Документацию по этому процессору можно найти на сайте производителя, здесь (пара глав даже переведена на русский), тут и тут. В качестве флеш-памяти в телефоне используются две микросхемы Intel: 28F160C3 (16-Mbit Top Boot Device, код 88C2) и 28F320C3 (32 Mbit Top Boot Device, код 88C4). Документацию по ним можно списать тут. Так же небольшой ассемблер/дизассемблер ADIS16X и много всего интересного можно взять на этой странице. Небольшой недоделанный дизассемблер с исходниками желающие могут списать отсюда.
В качестве компилятора я рекомендую Tasking C166 compiler. Этот компилятор используется внутри Siemens для написания прошивок. Для декомпиляции прошивки используется IDA 4.30 Advanced или более поздняя версия. Она регулярно появляется на FTP сервере сайта www.exetools.com.
Я не буду здесь приводить описание архитектуры процессоров C166, для этого есть документация. Опишу только вкратце структуру памяти телефона. Память выглядит так:
FF0000 - FFFFFF EEPROM
C00000 - FEFFFF Второй блок Flash
BF0000 - BFFFFF Первый EEPROM, в SL45i не используется вообще
A00000 - BEFFFF Первый блок Flash
800000 - 9FFFFF Отображены адреса C00000-DFFFFF
400000 - 7FFFFF Отображены адреса C00000-FFFFFF
100000 - 3FFFFF Отображены адреса D00000-FFFFFF
0E0000 - 0FFFFF Память отсутствует (всё пространство забито нулями)
080000 - 0DFFFF RAM (Heap и т.п.)
050000 - 07FFFF Отображены адреса C50000-C7FFFF
018000 - 04FFFF RAM (Heap и т.п.)
010800 - 017FFF Word-writeable память
010000 - 0107FF Внутреннее ПЗУ процессора
00F000 - 00FFFF Память регистров (SFR/ESFR) и внутренняя память процессора
000200 - 00EFFF RAM, в том числе внутренняя память процессора
000000 - 0001FF Таблица прерываний
Участок памяти 010800-017FFF является записываемым пословно. То есть запись в него возможна только по чётным адресам, причем по 16 битов за раз. В эту область памяти копируются наиболее часто вызываемые функции прошивки. Такие как memcpy, обработка картинок перед выводом на экран, часть функций, реализующих java и т.п.
В момент включения телефона внутреннее ПЗУ процессора отображено на адреса 000000 -0007FF, и управление получает обработчик нулевого прерывания (адрес 000000). Там находится команда перехода на обработчик сигнала Reset. Обработчик первым делом инициализирует минимум аппаратуры (временно выключает Watchdog timer, устанавливает регистры стека, DPP, SYSCON и переключает внутреннее ПЗУ процессора на адреса 010000-0107FF). После этого идет проверка на подключение data-кабеля, и если кабель найден - идёт загрузка бутблока и передача на него управления. Иначе - производится переход на один из адресов: 07FFFC или 03FFFC, в зависимости от того, какой из них содержит в себе команду JMPS:
sub_42E:
extp #1Fh, #1
movb rl0, 0FFFCh ; 7FFFCh
cmpb rl0, #0FAh ; код команды JMPS
jmpr cc_Z, loc_44C
extp #0Fh, #1
movb rl0, 0FFFCh ; 3FFFCh
cmpb rl0, #0FAh
jmpr cc_Z, loc_450
ret
loc_44C:
jmps 7, 0FFFCh ; 7FFFCh
; ────────────────────────────────
loc_450:
jmps 3, 0FFFCh ; 3FFFCh
; End of function sub_42E
В случае SL45i переход производится на адрес 07FFFC. Там находятся несколько jump-ов, и в итоге настоящей точкой, которая получает управление в фуллфлеше после включения телефона является процедура с адресом C7FADA.
Для загрузки бутблока, код загрузчика "перебирает" различные скорости СОМ-порта, ожидая услышать байт 55h. Получив этот байт, он отвечает посылкой байта A0h сигнализируя о готовности получить бутблок. Затем загрузчик ожидает байт, содержащий в себе размер бутблока, и начинает загружать блок с адреса 00FA00, считая параллельно его контрольную сумму. После бутблока следует байт контрольной суммы, и если этот байт совпадает с вычисленной телефоном, то загрузчик посылает байт A5h, и передает командой JMPS управление по адресу 00FA00. Если контрольные суммы не совпали - загрузчик сообщает об этом посылкой байта 5Ah, и ожидает перепосылки бутблока. Контрольная сумма считается классически - XOR между всеми байтами бутблока.
Приведу код из ПЗУ, отвечающий за загрузку бутблока.
loc_310:
....
; Код установки скорости порта и инициализации оборудования пропущен
loc_3B0:
mov r0, #6 ; Сделать 5 попыток получить байт 55h
loc_3B2:
sub r0, #1 ; Это последняя попытка?
jb Z, loc_310 ; Если да - попробовать другую скорость СОМ-порта
callr WaitForInput ; Подождать получения байта из COM-порта
cmpb S0RBUF, #55h ; Получено 55h?
jnb Z, loc_3B2 ; Если нет - попытаться еще раз.
loc_3C2:
mov S0TBUF, #0A0h ; Сообщить о готовности принять бутблок
loc_3C6:
callr WaitForInput
movbz r4, S0RBUF ; Получить длину бутблока в байтах
mov r0, #0FA00h ; R0 - адрес по которому будет загружен бут
add r4, r0 ; R4 содержит адрес конца бутблока в памяти
movb rl5, #0 ; RL5 - содержит контрольную сумму
loc_3D4:
callr WaitForInput ; Получить следующий байт
movb [r0], S0RBUF ; Записать его в память
xorb rl5, [r0+] ; Обновить контрольную сумму
cmp r0, r4 ; Получен весь бут?
jmpr cc_NZ, loc_3D4 ; Если нет - грузить его дальше
callr WaitForInput ; Если да - получить байт контрольной суммы
cmpb rl5, S0RBUF ; Контрольные суммы совпали?
jmpr cc_Z, loc_3EE ; Если да - идём на loc_3EE
mov S0TBUF, #5Ah ; Если нет - шлём 5Ah
jmpr cc_UC, loc_3C6 ; и пытаемся получить бут еще разок
; ───────────────────────────────────────────────────────────────────────────
loc_3EE: ; Бутблок получен удачно,
mov S0TBUF, #0A5h ; сообщаем об этом посылкой A5h
bclr S0TIR
loc_3F4: ; Ждём окончания передачи байта
jnb S0TIR, loc_3F4 ; через COM-порт
jmps 0, unk_FA00 ; И передаем управление полученному коду
; ───────────────────────────────────────────────────────────────────────────
WaitForInput: ; Эта процедура ожидает появления
jnb S0RIR, WaitForInput ; очередного байта в COM-порту
bclr S0RIR
ret
; End of function WaitForInput
Приведу так же дизасемблированные буты от Unisiemens.
В первую очередь Unisiemens посылает файл GoBoot.bin, который сперва инициализирует аппаратуру, затем копирует свой код на адреса 00FC00 и занимается подгрузкой следующих бутблоков. Код загрузки - аналогичен предыдущему, только управление на загруженный код передается командой CALLS, а не JMPS.
sub_FA00: ; Код файла GoBoot.bin
diswdt ; Выключить Watchdog Timer
mov DPP3, #3
nop
mov CP, #0FB00h ; Далее идет инициализация аппаратуры
mov BUSCON0, #5AFh
mov BUSCON1, #5AFh
mov ADDRSEL1, #7
extr #1
bset IRQ74IC.15
mov SYSCON, #1406h
mov r1, #0
mov DPP0, r1
mov DPP1, #3
; assume dpp1: 3 (page 0xC000)
mov DPP3, #3
mov r0, [r1]
mov r2, r0
cpl r0
movb [r1], rl0
movb [r1+1], rh0
mov [r1+2], r2
cmp r0, [r1]
mov word_FF14, r1
extr #1
mov XADRS5, #4
extr #1
mov XADRS6, #304h
extr #1
mov XBCON5, #4AFh
extr #1
mov XBCON6, #4AFh
mov r3, #7C00h ; Скопировать 5Ch байт начиная с адреса 00FA98
mov r13, #sub_FA98 ; на адреса 00FC00 и дальше
mov r14, #7C5Ch ; Напомню что DPP2==3, а значит число 7C00 указывает
callr sub_FA92 ; на адрес FC00
mov word_FB40, r14 ; Опять непонятная работа с SFR-ами
bset P4.1
bset DP4.1
mov T3, #0C62h
mov T3CON, #0C7h
mov word_FB42, ZEROS
mov T3IC, #50h
bfldh PSW, #0F8h, #0
jmps 0, unk_FC1C ; По адресу FC1C находится код нашей процедуры
; End of function sub_FA00 ; sub_FAB4 (FC1C-FC00+FA98=FAB4)
sub_FA8C: ; Эта процедура занимается копированием памяти
mov r2, [r13+] ; с адресов R13 по R14 на адрес R3 блоками по
mov [r3], r2 ; два байта
add r3, #2
sub_FA92:
cmp r3, r14
jmpr cc_NZ, sub_FA8C
ret
; End of function sub_FA8C
sub_FA98: ; Это обработчик прерывания T3INT
scxt r1, word_FB42 ; он будет установлен в InitBoot.bin
cmpi1 r1, #14h ; Он и весь код после него скопирован
jmpr cc_C, loc_FAA4 ; на адрес FC00
bclr T3R
loc_FAA4:
movb word_FB42, rl1
bmovn P4.1, P4.1
mov T3, #0C62h
pop r1
reti
; End of function sub_FA98
sub_FAB4: ; Эта процедура оказывается скопирована на адрес FC1C
; Её код - аналогичен коду загрузчика, приведенному
callr WaitForInput ; выше
movbz r4, S0RBUF
mov r0, #0FA00h
add r4, r0
movb rl5, #0
loc_FAC2:
callr WaitForInput
movb [r0], S0RBUF
xorb rl5, [r0+]
cmp r0, r4
jmpr cc_NZ, loc_FAC2
callr WaitForInput
cmpb rl5, S0RBUF
jmpr cc_Z, loc_FADC
mov S0TBUF, #5Ah ; 'Z'
jmpr cc_UC, loc_FAB4
; ───────────────────────────────────────────────────────────────────────────
loc_FADC: ; Единственное отличие - переход на
; загруженный бутблок производится
calls 0, sub_FA00 ; командой CALLS, что дает возможность
mov S0TBUF, #0A5h ; загрузить несколько разных бутблоков
bclr S0TIR
loc_FAE6:
jnb S0TIR, loc_FAE6
jmpr cc_UC, loc_FAB4
; End of function sub_FAB4
WaitForInput:
jnb S0RIR, WaitForInput
bclr S0RIR
ret
; End of function WaitForInput
Следующим Unisiemens посылает файл InitBoot.bin. Этот файл занимается инициализацией памяти, установкой обработчика T3INT и тестом двух участков памяти - 000000-00BFFF и 030000-03FFFF. Первый участок содержит в себе таблицу прерываний и туда будет загружен AllBoot.bin, а второй - будет использоваться AllBoot.bin в качестве буфера при получении данных для записи во флеш-память.
sub_FA00: ; Код файла InitBoot.bin
mov r1, ZEROZ
mov CPUCON2, r1
mov P9, r1
mov ADDRSEL3, r1
mov BUSCON3, r1
mov ADDRSEL4, r1
mov BUSCON4, r1
mov r4, ZEROZ
mov DPP0, #0 ; Здесь начинается тестирование памяти
loc_FA24:
callr TestMemoryBlock ; Проверить 16K памяти адресуемой DPP0
mov DPP1, #0
; assume dpp1: 0 (page 0x0)
mov r2, #408Ch ; Записать по адресу 00008C
mov r3, #0FAh ; код команды JMPS 0, 0FC00h
mov [r2], r3 ; то есть установить обработчик прерывания T3INT
mov r3, #0FC00h
mov [r2+2], r3
bset IEN ; и разрешить прерывания
cmp r4, #0 ; Тест памяти вернул ошибку?
jmpr cc_NZ, loc_FA6A ; Если да - loc_FA6A
mov word_FB42, ZEROS ; Если нет - переходим к следующему блоку
add DPP0, #1
cmp DPP0, #3
jmpr cc_NZ, loc_FA54
add DPP0, #9 ; Пропускаем блок 00C000-02FFFF
loc_FA54: ; Тестируем память до адреса 03FFFF
cmp DPP0, #10h
jmpr cc_C, loc_FA24
mov r0, #unk_FBBE ; Очищаем блок памяти 00FB42-00FBBE
loc_FA5E:
mov [r0], ZEROZ
cmpd2 r0, #word_FB42
jmpr cc_NZ, loc_FA5E
rets ; Выход назад в GoBoot.bin
; ───────────────────────────────────────────────────────────────────────────
loc_FA6A: ; Обработчик ошибки оперативной памяти
add r4, #10h
mov r3, r4
mov r1, #byte_FA8F
mov r4, #SendRL2
loc_FA78: ; Шлется набор байтов FF FF FF 02 18
movb rl2, [r1+] ; для индикации ошибки в памяти
callr SendRL2
cmp r1, r4
jmpr cc_NZ, loc_FA78
movb rl2, rl3
callr SendRL2 ; Затем шлется код ошибки
xorb rl3, #0E5h
movb rl2, rl3
callr SendRL2 ; И еще раз код XOR E5H
loc_FA8C:
jmpr cc_UC, loc_FA8C
; End of function sub_FA00
byte_FA8F:db 0FFh, 0FFh, 0FFh, 2, 18h
SendRL2: ; Процедура посылает байт из RL2
bclr S0TIR ; через COM-порт
movb S0TBUF, rl2
loc_FA9A:
jnb S0TIR, loc_FA9A
ret
; End of function SendRL2
TestMemoryBlock: ; Процедура тестирует 16К байтов
mov r2, ONES ; Адресуемых через DPP0
mov r1, #4000h
loc_FAA8: ; Сперва блок памяти забивается 0FFFFh
mov [-r1], r2
cmp r1, #0
jmpr cc_NZ, loc_FAA8
loc_FAAE: ; Затем проверяется - точно ли он содержит
and r2, [r1+] ; байты 0FFFFh?
cmp r1, #4000h
jmpr cc_NZ, loc_FAAE
cmp r2, #0FFFFh
jmpr cc_NZ, loc_FAEE ; Если нет - ошибка
loc_FABC: ; Затем в каждое слово записывается его адрес
mov r2, DPP0
shl r2, #0Eh
or r2, r1
cpl r2
mov [-r1], r2
mov r3, [r1+2] ; Потом читается
cmp r2, [r1] ; И сравниваются записанное и прочитанное значения
jmpr cc_NZ, loc_FAEC ; Не совпало - ошибка
cmp r1, #0
jmpr cc_NZ, loc_FABC
mov r2, ZEROZ
mov r1, #4000h
loc_FADC: ; Затем память забивается нулями
mov [-r1], r2
or r2, [r1]
cmp r1, #0
jmpr cc_NZ, loc_FADC
cmp r2, #0
jmpr cc_NZ, loc_FAEA ; И проверяется как она забилась
ret
; ───────────────────────────────────────────────────────────────────────────
loc_FAEA:
add r4, #1 ; Если не удалось забить память нулями
loc_FAEC:
add r4, #1 ; Если прочитанное значение не равно записанному
loc_FAEE:
add r4, #1 ; Если не смогли забить память FFFFками
ret
; End of function TestMemoryBlock
Как видно из кода память тестируется очень тщательно, чтобы избежать порчи содержимого флеша в случае ошибок.
Код загрузчика LoadBoot.bin крайне примитивен:
sub_FA00: ; Код файла LoadBoot.bin
movb rl3, #0A6h ; Послать 0A6h как сигнал о готовности
callr SendRL3 ; принять большой бут
callr RecvRL3 ; Получить младший байт размера бутблока
movb rl2, rl3 ; Получить старший байт размера
callr RecvRL3
movb rh2, rl3 ; R2 содержит размер бутблока
mov r4, #200h ; Загрузить бут начиная с 000200
movb rl5, #0 ; RL5 содержит в себе контрольную сумму
mov DPP0, #0
loc_FA18:
callr RecvRL3 ; Получить следующий байт бутблока
movb [r4], rl3 ; Записать его в память
xorb rl5, rl3
add r4, #1
sub r2, #1 ; Получен весь бут?
jmpr cc_NZ, loc_FA18
callr RecvRL3 ; Если да - получить его контрольную сумму
cmpb rl3, rl5 ; и сравнить с посчитанной
jmpr cc_NZ, loc_FA2E
jmps 0, 200h ; Если суммы совпали - передать управление
; ───────────────────────────────────────────────────────────────────────────
loc_FA2E: ; Если не совпали -
movb rl3, 5Ah ; Послать 5Ah
callr SendRL3
srst ; И перезагрузиться
SendRL3: ; Посылает значение регистра RL3
movbz r3, rl3 ; через COM-порт
mov S0TBUF, r3
bclr S0TIR
loc_FA40:
jnb S0TIR, loc_FA40
ret
; End of function SendRL3
RecvRL3:
jnb S0RIR, RecvRL3
bclr S0RIR
movbz r3, S0RBUF
ret
; End of function RecvRL3
Как видно из кода - этот загрузчик сперва шлет байт A6h, чтобы просигнализировать о своей работе, затем ждёт два байта размера бутблока, далее получает сам бутблок и загружает его с адреса 000200, после чего получает байт контрольной суммы, и если она совпала с посчитанной - передает управление на полученный код командой JMPS 0, 200h.
Из-за большого размера декомпилированный вид файла AllBoot.bin я здесь не привожу. Его IDB-файл можно списать тут. Файл AllBoot.bin содержит в себе много лишнего кода, в частности он содержит в себе поддержку таких микросхем Flash, которые в SL42/SL45/SL45i никогда не использовались. Протокол по работе с ним прекрасно описан в документации к V_Klay, а информацию по программированию микросхем Flash можно найти тут.
Приведу пример бутблока, просто говорящего "Hello from mobile!" через COM-порт сразу после сввоей загрузки.
Запускаем Tasking EDE, закрываем в нём все открытые проекты, и делаем "File -> New Project Space". Придумываем имя project space и указываем ему новый каталог. В появившемся окне "Project Properties" во вкладке "Members" жмем кнопарик "Add new project to project space (ALT-N)". Придумываем имя проекту. Добавляем в него файлы с такими названиями:
cstart.asm - в нем будет находиться точка входа в проект
test.c - тело нашего проекта
Архив файлов проекта можно списать тут.
После закрытия окна, обнаруживаем что Tasking создал нам файл start.asm, содержащий сгенерированный стартап-код. Безжалостно убиваем его, так как свой код мы напишем сами.
Вот текст нашего файла cstart.asm:
$CASE ; Это набор директив для компилятора,
$GENONLY ; их описание смотрите в документации
$DEBUG
$NOLOCALS
$CHECKCPU16
$CHECKBUS18
$NOMOD166 ; disable the internal set of SAB 80C166 SFRs
@IF( ! @DEFINED(__STDNAMES ) )
$STDNAMES(reg166.def) ; use non-extended set of SFR's by default
@ELSE
$STDNAMES(@__STDNAMES) ; use processor specific register definition
@ENDI
EXTERN _main:FAR ; start label user program.
PUBLIC __CSTART
;*****************************************************************************
;* __CSTART - вот эта процедура получит управление при старте программы
;*****************************************************************************
__CSTART_MY SECTION CODE WORD PUBLIC 'CMYINIT'
__CSTART PROC FAR
mov DPP0,#0 ; Устанавливаем регистры DPP как указано в настройках проекта
mov DPP1,#1
mov DPP2,#2
mov DPP3,#3
mov r0,#0FB00h ; Устанавливаем вершину User stack
call _main ; Вызываем функцию main()
rets ; и возвращаемся туда, откуда нас вызвали.
__CSTART ENDP
__CSTART_MY ENDS
CSTART_RBANK REGDEF R0-R15
END
Как видно - тело этого файла, это просто вызов функции main(), которая описана в файле test.c. Однако тут есть одна тонкость. Процедуру __CSTART я кладу в секцию CMYINIT, а в настройках Linker/Locator я укажу, чтобы линкер помещал её в самое начало сгенерированного файла. Если этого не сделать - непонятно какая функция будет засунута компилятором в самое начало файла.
Вот текст файла test.c:
#include <reg167.h>
void SEND(int a) // процедура посылки байта через СОМ-порт
{
S0TIR=0;
S0TBUF=(unsigned char)(a);
while(S0TIR==0) ;
}
void print(char *c) // посылка строки через СОМ
{
while(*c)
SEND(*(c++));
}
/****************/
/* main program */
/****************/
void huge main (void)
{
print("Hello from mobile!\r\n");
#pragma asm
srst
#pragma endasm
}
Перед компиляцией необходимо установить настройки проекта и сообщить Linker/Locator что наш загрузчик будет находиться по адресу 00FA00 и иметь в размере максимум 256 байт. Для этого нажимаем в дереве проектов правую кнопку мыши на названии нашего проекта, выбираем Project Options, и в этом окне:
В ветке Application:
Memory Model - Paged, так как демо-версия компилятора ограничивает нас моделью Small, а мы можем захотеть адресовать всю память.
Startup - снимаем галку "Generate system startup code...", чтобы он нам не мешал.
Ветка "C Compiler":
Libraries - снимаем галку "Link standard run-time...", на этот раз мы обойдемся без сишного рантайма, чтобы уменьшить размер получаемого бут-блока.
В ветке Linker/Locator:
Output Format - ставим галку Intel HEX record (так как это самый удобный формат для дальнейшей работы с ним), все остальные снимаем.
Memory - добавляем участок памяти, в который линкер поместит сгенерированный код:
ROM: Start = 0xFA00, End = 0xFAFF
и снимаем галку "Mark internal RAM area as RAM". Наивный линкер считает, что код может находиться только в ПЗУ, поэтому приходится так извращаться. Если бы мы использовали глобальные переменные, то надо было бы добавить еще одну Memory Area под RAM.
Memory -> Reserved Dedicated Address - снимаем галку Reserve memory for default ROM monitor resources, просто на случай если вдруг нам понадобится память 000200-000FFF.
Classes - добавляем класс:
CMYINIT: Start = 0xFA00, End = 0xFA1F, Unique: No
то есть кладём нашу функцию __CSTART в самое начало блока памяти.
Interrupt Vector Table - снимаем галку "Generate vector table"
Stack and heap - говорим "32 words (0xFB00-0xFBFF)", чтобы линкер засунул свой стек куда подальше и разрешил нам использовать память 00FA00-00FAFF. Стек мы все-равно используем тот, который нам дал код в ПЗУ.
Остальные ветки не трогаем.
После компиляции проекта получаем HEX-файл, который скармливаем моей программе, переводящей HEX-файл в BIN, начинающийся с заданного адреса в памяти:
H386toBinSkip.exe имя_проекта.hex MyBoot.bin 0xFA00
Архив программы H386toBinSkip с исходником лежит тут.
Для теста этого файла воспользуемся программой SendBoot.exe, архив которой лежит здесь.
Запускаем SendBoot.exe и получаем такой ответ:
Waiting for responce...
00
A0
Sending boot....................................................................
......................................
Boot sent!
Responce:
Hello from mobile!
То есть видим, что наш бут выполнился. Код программы SendBoot.exe довольно неуклюжий, возможно он не будет работать под Win9x. Я его предоставляю только в образовательных целях.
Для того, чтобы этот бут был полноценным и мог работать дольше нескольких секунд без перезагрузки телефона необходимо в него добавить инициализацию оборудования и установить обработчики прерываний, как это делалось в InitBoot.bin.
Так же приведу декомпилированный бут-блок, использующийся внутри Bfb95eg.dll для включения различных сервисных режимов.
sub_FA00:
diswdt
mov SYSCON, #1446h
extr #2
bset ALTSELOP3.15
bclr IRQ75IC.15
mov ADDRSEL1, #9
mov BUSCON1, #4BFh
extr #2
mov XARDS1, #0EF0h
mov XBCON1, #4AFh
einit
mov DPP0, #40h ; Бессмысленная посылка данных по несуществующим адресам
; assume dpp0: 40h (page 0x100000)
movb rl0, #0 ; зачем это делается - непонятно. Все работает и без этих команд
movb 200h, rl0 ; 100200h
movb rl0, #0FFh
movb 201h, rl0 ; 100201h
mov DPP3, #3
movb rl0, #0
movb byte_F600, rl0
movb rl0, #0FFh ; Первый параметр - код функции
movb byte_F601, rl0
movb rl0, #0FFh ; Второй параметр (обычно 80h)
movb byte_F602, rl0
movb rl0, #0FFh ; Третий параметр (0 или 1), в прошивке он игнорируется
movb byte_F603, rl0
mov r0, #80h ; 'А'
or C1MCR14, r0
srst
; End of function sub_FA00
Перед посылкой этого бут-блока, он модифицируется Bfb95eg.dll в памяти, и на места отмеченные в комментариях "первый-третий параметры" (от начала бутблока они находятся на байтах 46h, 4Eh, 56h) записываются константы:
2, 80h, 1 - Включить телефон
5, 80h, 1 - режим Battery Care
7, 80h, 1 - режим Service Mode
8, 80h, 1 - режим Burn in test
Вполне возможно, что существуют и другие режимы.
Как видно - этот бут-блок инициализирует минимум железа, после чего производит запись нескольких байтов по адресам:
00F600 - записывается 0
00F601 - записывается код режима
00F602 - записывается 80h или 81h для какого-то TDMA режима (работают оба значения)
00F603 - записывается 0 или 1, но прошивка похоже игнорирует это значение
00EFE0 (в коде он назван C1MCR14) - устанавливается бит 7 для сигнализации прошивке о необходимости входа в один из сервисных режимов.
Затем происходит перезагрузка телефона, и уже код во флеше включает заданный режим.
Тестовую программу по включению одного из перечисленных сервисных режимов можно списать отсюда. Код программы аналогичен коду SendBoot.exe. Программе в качестве параметра передается номер режима, после чего она посылает в телефон загрузчик, включающий этот режим. Пример работы с программой:
C:>ServiceMode.exe 8
Waiting for responce...
00
A0
Sending boot....................................................................
......................................
Boot sent!
Responce:
После чего на телефоне появится надпись "Burn In Test", и он громко запищит.
А теперь перед тем, как начать копать саму прошивку, расскажу об ассемблерном коде, генерируемом Tasking C. Именно такой код мы увидим при декомпиляции прошивки. В первую очередь остановлюсь на указателях.
Как известно из документации, C166ой процессор умеет адресовать свои 16-мегабайт памяти двумя способами: постранично - размер страницы 16К, и посегментно - один сегмент равен 64К. Причем код программы может быть адресован только посегментно, а данные - как постранично, так и посегментно. В компиляторе для адресации данных используются far и huge указатели. FAR-указатели - это страничные указатели (то есть имея FAR-указатель мы можем за раз адресовать 16К памяти). HUGE - сегментные, с их помощью можно за раз адресовать до 64К памяти, но они работают медленнее, и в прошивке практически не используются. Есть еще NEAR-указатели, для которых сегмент явно не указывается, а выбирается из значения соответствующего регистра DPP, номер которого задается в старших двух битах указателя. Пример объявления указателей на C:
int far *FarPtr=(int far*)0x123456; // страничный указатель
int huge *HugePtr=(int huge*)0x654321; // сегментный указатель
int *NearPtr; // ближние указатели я не советую использовать не зная точного значения регистров DPP.
На ассемблере к страничным указателям производится обращение либо с помощью регистров DPPx, либо через команду EXTP. К сегментным указателям - через команду EXTS. Подробнее смотрите в документации.
Системный стек, поддерживаемый процессором имеет небольшой размер - максимум 512 байт, поэтому не используется компилятором Tasking C для передачи параметров в процедуры и для локальных переменных. Для этого используется так называемый "user stack", который адресуется через регистр R0, и имеет размер до 16К.
Параметры в функции передаются через регистры R12-R15. Если функция требует больше 4 параметров, то остальные помещаются на стек адресуемый R0 примерно таким же образом, как это делается в 80x86ых процессорах. Возвращаемое функцией значение помещается в регистр R4, если результат занимает 16 битов. Если результат - 32 бита, то старшие 16 битов помещаются в регистр R5. Например, в случае функции с прототипом на C:
long MyFunc(char far *Str, int Param);
При ее вызове регистр R12 содержит адрес (в пределах 16К-страницы) на который указывает Str , R13 - содержит адрес страницы, на которую указывает Str, R14 - содержит значение Param. Возвращать функция будет в R4 младшие 16 битов результата, в R5 - старшие 16 битов.
Для желающих начать копать прошивку не с пустого места, я привожу здесь архив с декомпиляцией памяти запущенного телефона. В данном архиве структура памяти аналогична описанной в начале статьи, за исключением повторяющихся участков памяти. Все повторяющиеся участки памяти выкинуты для уменьшения размера архива, а на повторяющиеся адреса назначены mapping-и через "Processor specific analyzis options -> add mapping". Однако, маппинги назначены не на все адреса, а только на те, которые мне были интересны, так что местами в памяти находятся "дырки". При необходимости их можно "закрыть", назначив маппинги в соответствии с таблицей, приведенной в начале статьи.
В данном варианте декомпиляции сегменты создавались мною самостоятельно, так как при автоматическом создании сегментов, IDA создает сегменты всегда по 64K, а для адресации данных в прошивке используются только 16К-сегменты. Свои сегменты я назвал так: seg000 - сегменты данных, где 000 - это 16-ричный номер страницы (реальный адрес получается путем умножения ее номера на 16384), и cseg00 - сегменты кода, 00 - это старшие 8 битов адреса. При таком названии сегментов легко преобразовывать операнды команд в адреса просто нажав в IDA ALT-R, и выбрав соответствующий сегмент из списка.
Если кто хочет сам декомпилировать прошивку - то порядок действий такой:
1. Берем свой фуллфлеш
2. Запускаем IDA, выбираем фуллфлеш и в посвившемся окне ставим:
Loading segment: 0xA0000 (именно такой, так как в IDA размер сегмента == 16 байт)
Loading offset: 0
Change processor -> Siemens C166 (IDA не знает что C166 уже давно не принадлежит Siemens)
снимаем галку "Create Segments", а то IDA нам такого понасоздаст...
3. Жмём OK.
4. Создаем сегменты вручную, или пользуемся моим IDC-файлом.
После чего переходим на адрес точки входа в фуллфлеш, и начинаем анализировать код. IDA не догадывается где в флеше находится код, а где данные, поэтому придется ей указывать это вручную, либо писать макросы самим. Но я рекомендую не заниматься лишней работой, а воспользоваться моим архивом.
Эта статья не закончена, и уже не будет закончена никогда. Использование информации приведенной в ней разрешается только с моего согласия.
Контакты:
ICQ#: 70241285
e-mail: mamaich@uymail.com |
|