爱技术

 找回密码
 注册会员

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 2464|回复: 8
收起左侧

[转贴] SL45I

[复制链接]
发表于 2006-8-13 08:11:39 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册会员 微信登录

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
发表于 2006-8-31 08:39:16 | 显示全部楼层
好像是反汇编SL45(i)的程序吧,应该翻译一下,至少是e文啦,俄文看不了
回复 支持 反对

使用道具 举报

发表于 2006-9-9 23:09:40 | 显示全部楼层
希望俄文好的同志能翻译一下 实在望文不能生义阿...-_-
回复 支持 反对

使用道具 举报

发表于 2006-10-31 15:31:54 | 显示全部楼层
看不懂啊!!!!!!!!!!!!
回复 支持 反对

使用道具 举报

发表于 2007-1-13 00:34:19 | 显示全部楼层
太复杂了
回复 支持 反对

使用道具 举报

发表于 2007-1-16 16:08:23 | 显示全部楼层
俄文:) :)
回复 支持 反对

使用道具 举报

发表于 2007-1-23 10:40:38 | 显示全部楼层
我是学俄语的,文章内容大致看得懂,不过文章是03年的比较老,如果内容有用,我可以帮忙翻译
回复 支持 反对

使用道具 举报

发表于 2007-11-8 13:00:51 | 显示全部楼层
把大致的意思翻译出来啊!
回复 支持 反对

使用道具 举报

发表于 2009-6-1 13:19:29 | 显示全部楼层
天书。。。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员 微信登录

本版积分规则

小黑屋|Archiver|手机版|爱技术 ( 沪ICP备08115260号-3 )

GMT+8, 2024-5-20 12:15

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表