Copy Link
Add to Bookmark
Report
SET 030 0x0e
-[ 0x0E ]--------------------------------------------------------------------
-[ El apasionante mundo de los móviles ]-------------------------------------
-[ by FCA00000 ]-----------------------------------------------------SET-30--
Bienvenido a un nuevo capitulo de "El apasionante mundo de los móviles".
PRESENTACION
*************
En episodios anteriores hemos visto cosas que están alrededor del móvil:
comandos AT, SMS, SIM Toolkit, ficheros en el SIM, simulacion de teclas, ...
Ahora voy a ver algo que me permite cambiar el funcionamiento interno del móvil.
Como casi todos los cacharros modernos con un mínimo de funcionalidad, el
corazón del móvil es un chip con acceso a una memoria y a unos dispositivos.
Los periféricos con los que se comunica el chip son:
-radio
-teclado
-pantalla
-iluminación
-altavoz
-micrófono
-infrarrojos
-puerto serie
-cámara
-batería
-memoria extraíble
Esto lo cuento para que veas que es un dispositivo realmente potente, y todo
está integrado en un espacio realmente pequeño.
Piensa simplemente en lo que ocuparía (y lo que costaría) instalar todos
estos dispositivos en un ordenador. Claro que en este caso son más grandes
y tienen mayores capacidades.
Lo que quiero decir es que si intentas hacer un emulador de un móvil para un
ordenador de sobremesa, posiblemente llevaría mucho trabajo.
En realidad no es un dispositivo tan pequeño. En el caso de mi
Siemens C45i, contiene un microprocesador C166 de 16 bits, con memoria interna
de 4Kb, y memoria flash externa de 4 Mg, además de otra memoria de 1 Mg.
Ahora recuerda los tiempos del Spectrum, que tenian 8 bits, 16 Kb de ROM, y
48 Kb de RAM. Y era (es) una máquina realmente magnífica, con juegos
extraordinarios.
El micro funciona a una velocidad de 25MHz, tiene 76 lineas de entrada/salida,
16 canales de captura de datos, convertidor A/D, unidades de multiplicación,
bus externo, 58 modos de interrupcion basado en niveles y permisos, 5 timers,
puerto serie incorporado, con arquitectura parcialmente RISC y CISC.
Todo ello, con un consumo mínimo de batería y en un espacio reducido.
Los fabricantes de móviles entienden que los programas que ellos hacen
también pueden tener fallos, así que dejan abierta la posibilidad de
cargar nuevas versiones del software.
ACTUALIZACION
*************
La página web de Siemens contiene actualizaciones para los diversos modelos.
Para el modelo S45, la última version oficial es v21 , aunque yo creo que
solo existen v5, v16, v18, y v21.
Pero lo interesante es que existe un método para modificar el programa incluido.
En cierto modo, es una actualización del Sistema Operativo.
Para ello hay que conectar el móvil con un ordenador mediante un cable
serie, que viene incluido con el teléfono.
Mirando con un sniffer de puerto los comandos que se intercambian, veo que
usa AT+SQWE=1 , lo que significa que pasa a un modo especial llamado BFB.
Este mismo protocolo se usa para meter archivos en la memoria FLEXMEM, por
ejemplo juegos en WML (no java) o notas de voz.
Simplemente que para en este caso los comandos son mas potentes.
Para acceder a la actualizacion del SO, se pasa a un modo llamado bootstrap.
Básicamente, se semi-apaga el móvil. Luego se envía un programa pequeño
desde el ordenador hacia el móvil, y ese programa se ejecuta.
Cuando digo semi-apagar me refiero a que los dispositivos electrónicos nunca
se apagan del todo. Simplemente entran en un modo de ahorro de batería.
Cuando reciben un señal, se vuelven a poner en marcha. Esta señal viene
generada por hardware. Puede ser la pulsación de la tecla de encendido, o
un impulso mandado a traves de algun cable.
Esto explica porqué cuando conecto el cargador de batería, el móvil es
capaz de mostrar un dibujo con una pila.
Para que el bootstrap se pueda enviar, el móvil debe enviar una señal
indicando que está listo para recibir. Esta señal va por el pin 4 del cable.
Pero el cable de datos original tiene cortado ese pin, así que hace falta un
cable especial. Cualquier persona que desee liberar un móvil debe tener ese
cable, y un programa que altera la EPROM, eliminando una rutina de chequeo.
Existen múltiples programas de estos, y hacer el cable no es complicado de
construir si se tienen unos mínimos conocimientos de soldadura.
Tambien es posible comprarlo, claro.
El cable no es el mismo para todos los móviles. Por ejemplo, en los
modelos S65, la señal va por el pin 2.
Por una casualidad del destino, probé mi cable de datos del S45 con el
modelo C35, y funciona. No sé si es porque el cable sirve, o porque el
teléfono ya estaba parcheado.
Para los atrevidos, el cable original solo necesita ser modificado para que
dé la señal de alimentacion por el pin 4. El cargador de batería incluye
esta seníl por el pin 3, así que si conectas el pin 1 (masa) del cargador
con el pin 1 del cable de datos, y el pin 3 del cargador con el pin 4 del
cable de datos, ya te has hecho tu propio cable.
Al menos a mí me funciona con el modelo S45. Y no, no me hago responsable
de lo que puede pasar si haces la prueba y lo conectas mal.
Existe una tecnica para usar el cable por primera vez, parchear el móvil, y
luego no hace falta mas el cable. Pero esto lo explicaré mas tarde.
Así que las herramientas hardware necesarios son un ordenador con puerto
serie, un cable de S45, un teléfono C35 o similar.
Las herramientas software son Windows (algunos de los programas sólo
funcionan en este SO), y los programas:
Siemens Flashing Tools (zSiemenz), por RizaPN, para volcar y grabar la EPROM
v_klay, hecho por ValeraVi, para meter un parche
Smelter, hecho por avkiev, para investigar lo que hay en un archivo EPROM
SPC2, hecho por ACiD [mrp], para modificar los dibujos
sfe, por RizaPN, para desensamblar y tracear un archivo EPROM, y hacer parches
Siemens EEPROM tool , por Skylord, para ver lo que hay en un archivo EPROM
AT Debugger, por BoBa! , para ver lo que pasa por la memoria.
Siemens Debugger, por ACiD y SiNgle, para monitorear programas en tiempo real
Keil uVision C166, por la compa#ia Keil Software, debuggea programas y compila
Documentacion del C166, de la compa#ia Infineon
No es mi criterio habitual mencionar autores, programas, o direcciones web, pero
en ese caso hago una excepción porque realmente esta gente se lo ha
trabajado, y despues de tratar con ellos me doy cuenta de que valen mucho.
Ademas me caen bien.
La mayoria de estos programas se pueden encontrar en www.gsm-dev.com
o en www.gsm-multifund.de/board
Dado que Siemens es una empresa alemana, no es de extrañar que la mayoría
de la documentacion, los foros, y los programas, estén escritos en esta
lengua. Aber du sprichst deutsch, oder?
Tampoco es raro encontrar documentación en ruso, sobre todo en las webs
de ValeraVi y BoBa! aunque lo básico está ya traducido al inglés.
También hay otros muchos programas que incluyen código fuente y te
pueden servir para meterte en este mundillo:
Siemens Service Mode (flash_2.1.tar.gz), por Wilder.
sources.rar , por RizaPN
x35_patch , por BoBa!
Todos los fuentes de los parches, disponibles en www.gsm-dev.com
Tambien puedes pedirles a los autores que te pasen el código fuente de
sus programas. A mí nunca me pusieron impedimentos.
Recientemente he descubierto que hay una página web en español en
cs.comunidad-siemens.com
A mí me parece que el nivel es bueno, y hay alguna gente adaptando
y desarrollando parches, sobre todo para los nuevos modelos.
A ver si después de leer este artículo te pica el gusanillo y te
unes también a la comunidad.
PRIMEROS PASOS
**************
Bueno, pues el primer paso es ver que la cosa funciona.
Conecto el cable al ordenador y al móvil, inicio 'Siemens Flashing Tools', y
cuando me indica que apague el teléfono, y pulse el boton de encendido
brevemente, lo hago, y el programa indica que todo va bien.
Selecciono le menu de 'Read Flash', y en menos de 10 minutos obtengo un
fichero con la memoria de mi móvil.
Hay muchas páginas dedicadas a explicar la diferencia entre el cable original
y el cable especial para desbloquear, asi que solo repetire lo que todas
dicen: el cable original no sirve.
(Claro que todavia no me explico porque el cable del S45 sirve para el C35)
Si tienes interes -y asumo que lees este articulo porque lo tienes- no te será
difícil contactar con algunas de las muchas empresas que fabrican el
cable especial. Y en eBay puedes encontrar Siemens muy baratos.
El archivo con la memoria, también conocido como Flash, EEPROM, o FuBu, es
conveniente guardarlo en un lugar seguro. Así puedo volver a meterlo en
el móvil si algo falla.
Cuando se enciende el móvil, hay una rutina que verifica que la memoria no
ha sido corrompida. Se toman todos los bytes, y se calcula un checksum.
Por eso uno de los primeros pasos es eliminar esta verificación, para
poder meter mis programas y modificaciones tranquilamente.
Los teléfonos x35 contienen un único chequeo CRC
Los teléfonos x45, x50 contienen dos chequeos CRC
Los modelos x55 y x60 no contienen chequeo.
Para hacerte tu propio parche, puedes usar el programa CRC Patcher
http://www.gsmdev.de/page/index.php?c=viewprojektinfo&id=20
Además, en mi caso el teléfono C35 tiene la versión v5, que es bastante
antigua y contiene fallos. Decido meter la v18, también porque la mayoría
de los parches están desarrollados para esta versión.
Esto es un paso importante: los parches oficiales de Siemens ocupan posiciones
de memoria totalmente diferentes segun la versión; si la rutina de enviar
mensajes en la version v5 empieza en la dirección D01234, puede que
empiece en la C43210 en la versión v18.
Puede que algunos parches incluyen las modificaciones para hacerlo
funcionar en otras versiones, pero no es lo normal.
Yo he decidido no usar la version v24 o v25 porque parece que contienen
nuevos fallos, y los parches no están desarrollados para estas versiones.
Aunque no todos los modelos de Siemens contienen la misma funcionalidad, hay
parches que se han desarrollado para un modelo, y luego alguien los adapta a
otros modelos, siempre que sea posible. Por ejemplo, BoBa!, ZZToP y bEGEM0t se
han encargado de pasar al C35 muchos de los parches que RizaPN o ACiD[mrp] han
hecho para el SL45i. Gran trabajo, por otra parte.
Como iba diciendo, ya que tengo una copia de la Flash, lo siguiente que hay
que hacer es una copia de la EEPROM.
Esta otra zona de la memoria contiene datos que se pueden modificar sin
problemas, siempre que se tenga cuidado.
Me explico: la Flash contiene el programa, teóricamente inalterable, mientras
que la EEPROM contiene datos que deben ser almacenados de manera
permanente, aunque se pueden cambiar, pero no se deben perder.
Notar que la EEPROM no contiene los datos que estan en el SIM.
Por ejemplo, la EEPROM almacena la puntuación que has conseguido en el
juego del buscaminas. También guarda el nivel de volúmen para el
altavoz, el libro de direcciones del teléfono, el bloc de notas , o la
clave para el navegador de internet.
La EEPROM se compone de unos cuantos ficheros, dependiendo del modelo de
móvil hay mas o menos ficheros, y todos los modelos coinciden en usar los
mismos indicadores.
Por ejemplo, el fichero 5012 del C35i contiene el nivel de batería, mientras
que 5076 contiene el mensaje de saludo que aparece al encender el móvil.
Este fichero 5012 es especial y también hay que guardarlo, ya que contiene
información tal como la carga de la bateriá, y la configuración de los
parámetros de fábrica por defecto.
Para leer la EEPROM, no es necesario el cable especial; vale con el cable
original. Así que puedo leer mi SiemensS45 o el C35.
Uso el S45 porque contiene más datos.
Si tienes archivos Flash de otros modelos también se pueden investifar.
Arranco el Siemens Debugger, y en el botón de 'Setup' uso el puerto COM1.
Pulso el botón 'Start Service Mode' , y al cabo de un momento me dice que
el BFB no se ha abierto. Esto quiere decir que no tengo acceso a toda la
funcionalidad del programa, pero puedo hacer bastantes cosas.
EEPROM
*************
En la ventana de EEPROM, pulso 'existing only' y tras un momento la
lista 'Block name' contiene varios valores. Elijo 'Notes Function 10' , que es
el bloque 5179, y veo que aparecen un montón de caracteres, correspondiente
al menú del móvil 4-3-5: Notes.
A partir de la posición 0C , y en las posiciones pares (0C, 0E, 10, 12, ...)
aparece el texto "Hola", que es justamente lo que yo tengo almacenado en la
primera entrada del bloc de notas. En la parte hexadecimal veo que el
carácter es 48, tal como corresponde a la letra 'H'.
Es curioso que la primera nota esté en el bloque 10. Posteriores pruebas
revelan que la nota segunda está en el bloque 9, y asi sucesivamente.
Armado de valor, hago doble-click sobre la celda 00000C , y escribo '4C'.
Entonces aparece el boton 'Save block', indicando que he hecho una
modificación y puedo guardarla. Tras pulsar este boton me aparece un
mensaje diciendo que se han escrito 112 bytes en el bloque EEPROM 5179.
Miro el móvil, y la nota aparece ahora como "Lola", ya que 4C corresponde
a la letra 'L'.
Antes de que se me echen al cuello todos aquellos que comprenden la
codificación ASCII, notar que este es un gran descubrimiento: el teléfono
usa la tabla ASCII. Tambien podría usar la EBCBIC, o cualquiera otra
que Siemens se haya inventado. No creas que todos los microprocesadores
trabajan en ASCII; esto es sólo una convención.
Desde el teléfono, es posible marcar esa nota como confidencial. En este
caso se pide el código del móvil para protegerla.
Cuando apago el teléfono y lo vuelvo a encender, al intentar leer la nota
me pide este código.
Notar que el móvil lo tengo puesto para que no pida el código cuando
lo enciendo: por eso me lo pide ahora al intentar leer la nota.
Desde el Siemens Debugger, miro de nuevo el bloque 5179, y veo que la nota
original sigue allí! Sólo han cambiado algunos bytes antes de la dirección C0.
O sea, que no lo ha cifrado. Esto quiere decir que cualquiera puede coger
el móvil de otro, y leer las notas -teóricamente confidenciales- del usuario
original usando Siemens Debugger. No muy confidencial, diría yo.
La nota original 'sin cifrar' tiene los bytes:
00 00 10 0A 36 FF D4 07 08 1D 04 00 y luego el texto de la nota.
La nota confidencial tiene los bytes:
01 00 10 13 14 FF D4 07 08 1D 04 00 y luego la nota.
Así que si cambio los bytes desde el 0 hasta el 5 para que sean como el
caso inicial, la nota pasa a ser no-confidencial.
Es más: simplemente cambiando el primer byte de 01 a 00, la nota es
no-confidencial. No sé el significado de los otros bytes, pero no
parecen servir para nada en este caso.
Probando con otros valores veo que 00 significa no-confidencial, y
cualquier otro dato significa confidencial.
Estarás contento, no? Ya he hecho el primer crack.
Borro la nota desde el télefono, y al mirar el bloque 5179, veo que
todavía está allí! Los primeros bytes han cambiado, pero los datos
siguen allí. Así que tampoco hay mucha seguridad en este apartado.
Cualquiera puede leer una nota antigua, por más que se haya borrado.
El bloque este ocupa 112 bytes. No es mucho, pero suficiente para guardar notas
de 50 letras, pues cada letra ocupa 2 bytes. La cabecera ocupa otros 12 bytes.
Pero también es un sitio ideal para guardar un programa pequeño. A veces
la memoria Flash no es el mejor sitio para escribir un mini-parche, pero
en 100 bytes se pueden escribir grandes cosas en ensamblador.
Luego veré esto con más detalle.
En artículos anteriores me quejaba de que, aunque puedo leer muchos de los
parámetros del móvil mediante comandos AT, es imposible leer las notas.
Bien, ya es posible hacerlo gracias a ACiD[mrp] y Siemens Debugger.
También os había enseñado a escribirlas usando el comando de simulación
de teclas AT+CKPD , pero he encontrado otro método mejor.
Ya veré si soy capaz de automatizar esta tarea.
MAS EEPROM
*************
Otro bloque fácil de interpretar es 5166: Alarm Clock
El fichero ocupa 6 bytes, y en mi caso vale
08 20 00 1F F1 FF
Mirando el móvil, yo tengo puesto que suene a las 08:32 de lunes a viernes.
La hora 08:32 se escribe en hexadecimal como 08 20.
El dato 1F F1 se escribe en binario 00011111 11110001
Haciendo que no suene el martes, tengo
08 20 00 1D F1 FF, donde el dato 1D F1 se escribe en binario 00011101 11110001
Haciendo que no suene el miércoles, tengo
08 20 00 1B F1 FF, donde el dato 1B F1 se escribe en binario 00011011 11110001
O sea, que los días de la semana se marcan en bits desde atras hacia adelante.
Todos los días es 7F F1, mientras que 'ningún diá' se escribe 00 F0.
El último bit de byte 04 indica si la alarma está activa o no.
Así, ya es fácil hacer un programa que sincronice la alarma del ordenador
con la del móvil.
Hay muchos ficheros interesantes, y entre ellos ha captado mi atención el 5086:
'* WAP Profile 1b (CSD Dialup) *'
Esta es la explicación preliminar: en un teléfono con WAP, primero se
inicia una llamada a un servidor telefónico; algo así como un modem que
siempre está escuchando. Cuando uno se da de alta en este servicio, el
operador de red manda un SMS para configurar el teléfono, y decirle algunos
paramétros, por ejemplo el número de teléfono que tiene que marcar, el
nombre de usuario, y la clave.
Esto en el caso de una llamada a traves de la red 2G, mediante un CSD.
Cuando el teléfono es GPRS estos datos se amplían a un servidor web llamado APN.
Para la conexión WAP, otros de los parámetros son el
servidor WAP primario, y el puerto.
Todos estos datos se guardan en la EEPROM, y, si bien se pueden ver desde
el propio teléfono, el dato de la clave está oculto.
Pero seguro que está en alguna parte de la memoria.
Así que con ayuda del 'Siemens Flashing Tools' hago una copia de la EEPROM.
Luego desde el móvil voy y cambio el perfil CSD, y sustituyo la
clave, poniendo '2222' en vez de 'xxxxxxxx'.
Hago otra copia de la EEPROM, y las comparo.
Es posible sacar cada uno de los ficheros independientemente; así resulta
más fácil ver lo que ha cambiado.
Los bytes que han cambiado pertenecen al fichero 5086, como ya intuia.
La nueva clave '2222' aparece en la posición 0x36 (esto es, 54 en
decimal), sin cifrar ni nada.
Para saber la clave inicial, no tengo más que usar un editor hexdecimal, abrir
el archivo original 5086, y mirar a partir del byte 54 . En el caso de mi
operador, la clave resulta ser 'wap'.
Para el caso de Telefónica, el nombre de usuario es MOVISTAR, y también la
clave es MOVISTAR. Esto no es ningún secreto.
Y esto es el segundo hack del dia.
Otro caso: empiezo una partida al juego 'Balloon Shooter', y hago 130 puntos.
Guardo toda la EEPROM.
Borro el record, y guardo la EEPROM de nuevo.
Comparo los archivos, y difieren en
000087D9: FF 7D
O sea: ahora marca FF, pero con 130 puntos marca 7D.
El valor 7D es 125, y 255-125=130.
Asi que el dato se guarda en negativo.
La posicion global 87D9 tiene unos bytes antes, con el texto 'Championship', que
coincide con el archivo 'Games 1', en el bloque 5180. El bloque mide 250 bytes.
Así que supongo que el record se guarda en el siguiente bloque 5181: 'Games 2'.
En efecto, el primer byte es 7D. Nada más fácil que cambiar los primeros
bytes a '9F' y '15' para tener 60000 puntos.
Lo explico otra vez, porque es otro dato que no hay que olvidar:
No sólo se guarda en complemento a 2, sino que el byte menos significativo
va primero, como corresponde a una arquitectura little-indian
de 16 bits: 65535-60000=5535, que se escribe '0x159F' en hexadecimal.
Alterando el orden de los bytes, queda '9F 15', como he dicho antes.
Esto es el tercer hack.
Cuando se modifica uno de los bloques, no se sobre-escriben los datos, sino que
se crea un nuevo bloque, y el anterior se invalida. Por eso la memoria EEPROM
necesita ser más grande que toda la suma de los tamaños de los archivos.
IMEI EN LA EEPROM
*****************
Hay unos ficheros en la EEPROM que contienen información sobre el IMEI:
5009=IMEI Block 00
0076=IMEI Block 01
5008=IMEI Block 02
5077=IMEI Block 03
Todos los móviles tienen un identificador único, creado por el fabricante.
Esto sirve, por ejemplo, cuando te roban el móvil. Sólo hay que denunciarlo, y
el operador de telefonía lo mete en una base de datos, que comparte con otras
operadoras. Cuando se intenta hacer una llamada desde ese móvil, no se permite.
También sirve para saber el modelo. Esto hace que cuando haces o recibes una
llamada, la red sabe cual teléfono tienes, y puede proporcionar funcionalidad
diferente. Un caso es que los tonos de la red (congestión, no hay
respuesta, ocupado, ...) pueden ser polifónicos, si tu modelo lo soporta.
Otra utilidad es identificar y cambiar los modelos que se sabe tienen
algun defecto.
Este identificador llamado IMEI se almacena en un archivo de la EEPROM.
En teoría se puede modificar para hacerlo aparecer como otro teléfono.
Notar que esto es ilegal en la mayoría de los países.
Además Siemens ha decidido que esto no sea tan fácil; no se guarda en un
fichero en texto claro, sino que está cifrado.
Como medida extra, se guarda en varios ficheros, con distintos checksum.
Aun asi, resulta sencillo tomarlos de otro teléfono, y copiarlo al mio.
Cuando se actualiza la EEPROM o la flash, lo que debe hacerse es una copia
de mis datos, meter la flash, y luego restaurar los datos de estos 4 archivos.
Si la actualización se hace con el software original proporcionado por
Siemens, el funcionamiento es ligeramente distinto: el software obtiene
el IMEI con el comando AT+CGSN , despues actualiza la EEPROM, calcula
el contenido de los ficheros, y los mete de nuevo. Esto deja la puerta
abierta a modificar el IMEI en memoria justo antes de escribirlo.
Este comportamiente se puede confirmar viendo el protocolo que usa el
programa de actualización original UpdateTool, disponible en la web de Siemens.
No me he metido en esto, pero suena interesante, no?
Otro fichero que hay que guardar es
0067 = Measurement values for temperature and voltaje
Esto guarda el nivel de batería actual y máximo, además de otros datos.
Si no guardas este fichero, creerá que el nivel de batería es el de la
persona que te ha pasado la EEPROM, seguramente se confundirá a la hora
de cargarlo, y el teléfono no te durará encendido ni 5 minutos.
Como antes, hay que guardar el tuyo, meter la flash, y restaurar tu fichero.
Esto es lo que hacen automáticamente muchos de los programas que sirven
para desbloquear el teléfono para usarlo en otras redes.
Un programa para esto es AllSiemens , aunque hace falta el cable original.
NO TODO ES ORO
**************
Otro de los bloques es
5058: Calculator & Currency converter
Este móvil tiene una utilidad que permite traducir una moneda en otra.
Se puede usar para convertir pesetas en euros, o pulgadas en centímetros.
El calculo es una simple regla de tres mediante un factor que se guarda
en las posiciones 1D-23 del fichero, mientras que el nombre de las
monedas se guarda en las posiciones 64-77 .
Pero al cambiar cualquiera de los datos desde el Siemens Debugger, no
parece guardarse en el teléfono. De hecho, cuando apago el móvil y lo
vuelvo a encender, los datos no estan allí. Es como si fuera de solo lectura.
Desde el propio móvil sí se pueden modificar, pero Siemens Debugger no los ve.
Parece que se guardan temporalmente en algún otro fichero, aunque sin
modificarse en tiempo real.
La explicación es el móvil usa un sistema de versiones de ficheros.
Cuando modifico un fichero, en realidad se hace una copia, se modifica el
nuevo fichero, se marca como activo, y el anterior se marca como inactivo.
Usando la aplicacion 'Smelter', voy al menu EEPROM, pulso con el botón
derecho del ratón, y marco la opción 'Show deleted blocks'.
Esto es util para saber cuales archivos han sido modificados, y se puede
ver el original junto con las versiones que se han ido creando.
Así veo que hay 4 versiones del fichero 5058, con todos los cambios que
he ido haciendo. El móvil no responde al refresco de estos datos, ya que
sólo lee el fichero cuando se enciende. Es por esto que no tiene sentido
cambiar este fichero desde el ordenador.
Lo digo para que no te sorprendas si a veces tus cambios no los ves
reflejados inmediatamente.
Intentaría entender el formato de los datos usados en la conversión de
la moneda, pero no es cuestión de encender y apagar el móvil 200 veces.
Hay otros muchos ficheros que se comportan igual.
En total, hay definidos unos 500 ficheros, aunque estoy seguro de que nuevos
modelos necesitan implementar nuevos ficheros, sobre todo para las redes 3G.
Si te decides a jugar con los ficheros, considera hacer una copia.
A mí todavía no me ha pasado nada grave, pero es más que probable
que algún día me encuentre con que el móvil no funcione y sea
debido a algo que he tocado en los ficheros.
Por eso es bueno experimentar con otro móvil que no sea el que usas a diario.
Ya sabes: los experimentos se hacen con gaseosa.
DONDE ESTA LA EEPROM
********************
La EEPROM del C35i se guarda a partir de la direccion 0x3F0000, ocupa
en total 0x10000 bytes y la lista de ficheros esta a partir de 3F7CE0.
En el S45 la memoria está en dos trozos: en 0x1F0000 ocupando 0x10000 , y
en 0x5F0000 ocupando 0x10000.
Esto se puede ver con el programa zSiemens. Elige la opción de guardar
la EEPROM, y dirá la zona de memoria que debes guardar.
Para ver cómo está organizado, empiezo por el modelo mas sencillo.
Con un volcado de la Flash del C35i, en la dirección 0x3F7CE0+0x196=0x3F7E76
está el dato 0x20, o sea, 32 en decimal.
En 0x3F7CE0+0x196+0x02=0x3F7E78 esta el dato 'D6 6D', que pasado a little-endian
(primero el byte menos significativo) quiere decir 6DD6.
Sumando la dirección base de la EEPROM (0x3F0000) obtengo 0x3F6DD6.
En la direccion 0x3F7CE0+0x196+0x06=0x3F7E7C está el dato 'D4 13' que se
lee como 0x13D4 y en decimal es 5076.
Todo junto: el bloque 5076 mide 32 bytes y esta a partir de 0x3F6DD6.
Este bloque resulta ser 'Personalisation - Greeting Text' así que cuando lo
cambio, consigo cambiar el texto que aparece cuando enciendo el teléfono.
Por defecto es 'Siemens C35i' pero lo cambio sin más que alterar la EEPROM.
Existe una manera infinitamente mas sencilla de ver estos datos: vuelca toda
la flash (4Mg) y cárgala en el Smelter.
Ve al menu EEPROM, y ordena por el campo llamado Offset. Entonces puedes
ver la lista de todos los ficheros.
Lo que pasa es que el metodo anterior me va a servir para leer los ficheros
desde el propio móvil. Pero no quiero adelantar acontecimientos.
Hay dos zonas de EEPROM: una llamada EEFULL y otra EELITE.
Unos ficheros estan en una zona, y otros en otra. Creo que para usar
la EELITE se necesitan mayores permisos, pero no estoy seguro.
HAY VIDA MAS ALLA DE LOS FICHEROS
*********************************
Y ya que estoy con el Smelter, explico otras opciones.
Para verlas hace falta un archivo con la Flash.
Lo normal es usar la que has extraido de tu móvil, pero también es posible
encontrar otras versiones de tu modelo, u otras memorias de otros modelos.
Esto se hace para comparar parches y adaptarlos.
Uno de los modelos que más parches recibe es el SL45v56.
Busco su Flash y la cargo en Smelter.
El menú de EEPROM ya lo conozco. Pero ahora puedo ver los archivos existentes
en otros modelos, y ver los que me faltan en el C35. También puedo sacar
configuraciones de otros usuarios, aunque es más que probable que, si
pertenecen a otro modelo, no tengan sentido en el mio.
TONOS
*****
Otro de los menús usados es el de Ringtones. Cada uno de los
sonidos (excepto la voz) generados por el móvil se guarda en la memoria.
Las melodías modificables por el usuario se guardan en la EEPROM, en
los bloques 73='Ringer melodies' y 5071='User defined melodies'.
Hay bastantes programas que permiten cambiar estas melodías con
un interface mucho más sencillo, así que no me meto en el tema.
Pero hay otros tonos que no se pueden cambiar. Entre estos están las
melodías predefinidas, y los tonos de operatoria normal del móvil.
Por ejemplo, cuando la red esta congestionada, se oye un pitido tut-tut-tut, o
cuando se enciende el móvil emite otro pitido. También cuando se sobrepasa
la longitud de un SMS, los sonidos de los juegos, o el nivel bajo de batería.
Todos estos tonos tienen definido un número, y se encuentran en una
posición de la memoria. Smelter nos dice esto, permitiendo además la
posibilidad de escucharlo, e incluso guardarlos a un archivo.
De esta manera veo que cada nota se compone de dos partes: tono y duración.
No hay un dato para el volúmen de cada nota.
Cuanto mayor es el tono, suena más agudo.
Los datos ocupan cada uno 2 bytes, así que un tono de valor 860 (0x035C) y
duración 210 (0x00D2) se guarda como 5C 03 D2 00
Dado que también me dice la posición de memoria donde se guarda, hacer un
parche para cambiar un tono es cuestión de segundos.
Voy a verlo con un ejemplo.
Cuando la batería se va a acabar, el teléfono emite un pitido corto.
Dado que caerzco de sentido musical, no he podido identificar cual es ese tono.
Pero buscando los sonidos de sólo 1 nota he encontrado 5. Modificando todos
ellos hasta encontrarlo, lo he convertido de 860:210 a 860:1, y ahora no se oye.
La melodía sigue estando allí y suena, pero la duración es tan corta
que es imperceptible.
De modo similar a los ficheros de la EEPROM, en el C35 los sonidos están
indexados a partir de la direcion 0xC8E786. Cada melodía es un puntero
que ocupa 8 posiciones, de las cuales la cuarta indica la direccion de
memoria donde se almacenan las notas.
Esto me va a servir para identificar cuando se invoca a una melodía particular.
DIBUJOS
*******
Otra de las posibilidades es modificar los dibujos.
Si modificar los sonidos es fácil, los dibujos es todavía más.
Uso el programa 'Siemens Picture Change', y cargo la Flash de mi móvil C35i.
Me aparece una lista con los identificadores de los dibujos. Elijo el 210, que
es el icono de infrarrojos, y me aparece en el cuadro de la izquierda.
Lo gracioso de este caso es que este modelo no tiene puerto de infrarrojos!
Lo mejor será elegir un dibujo que puede ver: 468 es el icono que indica
que el timbre esta apagado, y las llamadas no harán que mi móvil pite.
Pulsando el botón derecho lo guardo en un archivo BMP, que luego edito.
Después lo vuelvo a cargar haciendo doble-click sobre la lista de la derecha.
Pulsando 'save Patch as...' genero un parche para luego meterlo en el móvil.
El formato del fichero es RTF, aunque la extension es .vkp (v_klay).
La razón para usar RTF es que Word permite incluir iconos y gráficos dentro
de sus documentos. Así es posible abrir el fichero con word, y se verá
cómo es el dibujo incluido en el parche.
No solo eso, sino que además v_klay también mostrará el dibujo al
abrir el código del parche.
Arrancar el v_klay con el parche, y pulso 'Apply Patch' para transferirlo.
Pulso brevemente la tecla de encendido para entrar en el modo bootstrap, y
cuando el parche está cargado, apago el teléfono, lo vuelvo a encender, y
tengo mi nuevo icono.
Cuarto hack del día.
Gracias a 'Siemens Picture Change' veo que la dirección es 0x352A29, y esto
sirve para saber a qué posición de la memoria va a ir a parar el parche.
La dirección, e incluso el identificador de cada icono, es distinto
entre versiones y modelos. Por eso los parches para modificar dibujos
también tienen que ser adaptados de un modelo a otro.
Por ejemplo, en el modelo SL45i com la EEPROM v56 , este icono tiene
número 307 y está en la posición 0x4B1F98.
MENUS
*****
En los teléfonos Siemens los menús están organizados como una lista simple.
Cada menú está localizado en una posición de memoria, en la que se dice
quien es su padre, el texto que muestra, la rutina a la que salta, y el
número de elementos en el caso de contener sub-menús.
Con el Smelter no hay más que abrir la Flash, e ir al menú 'Menu'.
Al principio aparece la lista pero no el texto del menú, pero si
voy a 'Langpack', pulso con el botón derecho, activo 'Show tags' y
'Show texts from firmware', voy de nuevo a 'Menu' y ya me aparecen todos.
La importancia de los menús es que son los puntos de entrada para las rutinas.
Si quiero modificar alguna funcionalidad, lo normal es recorrer el camino
desde el menú de entrada hasta esa funcionalidad. Por ejemplo, si quiero
quitar el mensaje 'Minesweeper. With compliments from Microsoft' que sale
antes de jugar al buscaminas, empiezo desde el menu Games->Minesweeper hasta
llegar al punto donde se referencia este mensaje.
El parche más sencillo consiste en cambiar el texto del menú. Pero como
en general están traducidos, esto no es necesario, a no ser que lo quieras
adaptar a un lenguaje que no está soportado.
También es posible cambiar los menús para que estén en otros sitios.
En mi móvil es posible definir cada una de las teclas como un acceso directo.
Si pulso durante 1 segundo esa tecla puedo ir directamente a un sub-sub-menu.
Lo malo es que no todos los sub-sub-menús están disponibles.
Puedo elegir:
-Un cierto número de teléfono
-Conectar a Internet
-Sitio web
-Listín de teléfonos
-Listín de direcciones
-Calendario
-Alarma
-Notas
-Calculadora
-Conversor de moneda
-Nuevo SMS
-Iluminación
-Ocultar ID
-GPRS
-IrDA
-Juegos
-Llamadas perdidas
-Llamadas recibidas
-Mensajes recibidos
-Mensajes enviados
-Favoritos
-Tarjeta de negocios
Pero yo uso frecuentemente el menú de 'Datos de la última llamada' para
ver cuanto tiempo he hablado. Y lamentablemente ese menú no está entre
los seleccionables.
El menú es Registros->Duración->Ultima llamada
En la Flash del ME45v28 (que es similar al S45) veo que ése es el menú 011C
dependiente de 86, y el punto de entrada es F3F5C0. Su padre es 854CA6.
Pero también aparece dependiente del menú 87, con los mismos datos.
Así que lo mas fácil (por ahora; luego mostraré otro método mejor) es
intercambiar los puntos de entrada entre el menú 'Tarjeta de negocios' y 'Datos
de la última llamada'.
El menú 'Tarjeta de negocios' tiene numero 0223, depende de 60, y tiene
punto de entrada F39310. Su padre es 85351A.
Desde el sfe busco la cadena '10,93,F3', ya que los datos de puntos de
entrada se guardan en formato inverso. Lo encuentro en 05352A. Notar que es
el quinto menu dentro del 60, y
0x05352A - (5-1)*4 = 0x5351A , que es 0x85351A - 0x800000
El otro punto de entrada F3F5C0='C0,F5,F3' se encuentra en 054CA6 y 054CB6 .
Esto concuerda con el hecho de que aparece en el menu 86 y 87.
Igualmente notar que es el primer menu, y
0x054CA6 - (1-1)*4 = 0x054CA6 , que es 0x854CA6 - 0x800000
O sea, que Smelter nos indica, en la columna 'Entry' del menú, donde está
definido este menú.
Así que el parche intercambiará esos datos , pero sólo para el menú 86:
0x05352A: 1093F3 C0F5F3
0x054CA6: C0F5F3 1093F3
Lo cargo en el v_klay , lo meto en el móvil, y a partir de ahora el menú
'Tarjeta de negocios' me lleva a 'Datos de la última llamada', y viceversa.
Para que la técnica sea perfecta, solo tengo que cambiar los textos de los
menús correspondientes.
Por curiosidad, en la flash S45i v4 este menú es también el 011C pero
depende del 102 y salta a F6A870.
MAS DIFICIL TODAVIA
*******************
Este es el primer parche complicado. A ver si lo sigues sin perderte.
En el móvil la tecla '0' también tiene el símbolo'+' que sirve para
escribir números internacionales, por ejemplo +34666.. y si se mantiene
pulsada durante un tiempo aparece el menú '+List' para elegir el prefijo de
cualquier país. Tanto los móviles como los números de teléfono de la red
fija tienen el mismo prefijo. Si recibes una llamada que empieza por +376 sabes
que viene desde Andorra.
Esto resulta informativo cuando alguien desde China marca mal y acaba llamando
a tu móvil. Suena raro, pero yo ya he recibido un par de llamadas desde Rusia
y otra desde Indonesia. Pero en general esta lista resulta una tontería.
Mi propósito es cambiarlo para que me diga la red española.
En España hay hasta el momento 5 operadores de telefonía
móvil: Airtel , Movistar, Amena, Xfera, y Moviline.
Cada uno de ellos tiene asignado unos dígitos de prefijo nacional.
Toda la telefonía móvil empieza por '6', aunque para ser más correcto
debería decir que comienza por '+34 6'.
A partir de esta base, cada uno tiene un grupo de números.
Movistar tiene '+34 606', '+34 609' y '+34 615' y muchos más.
Xfera tiene '+34 622'.
Airtel tiene '+34 607' , '+34 610' y '+34 617'....
Esta es la lista completa:
AIRTEL 600 607 610 617 627 637 647 661 662 666 667 670 677 678 687 697
AMENA 605 615 625 635 650 651 652 653 654 655 656 657 658
MOVILINE 608 689
MOVISTAR 606 609 616 618 619 620 626 628 629 630 636 639 646 649 659
660 669 676 679 680 686 690 696 699
XFERA 622
Cuando quiero llamar a alguien, me interesa saber en cual red está, para
elegir el mejor horario que se ajuste a mi tarifa, o llamar desde el teléfono
de la oficina. Por eso voy a desarrollar un parche que sustituya la lista
de prefijos internacionales por otra de prefijos nacionales para móviles.
La lista original tiene 90 países, y la nueva lista tendrá 40, ya que
sólo hay otorgados 40 prefijos '+34 6xx'.
Todos los cambios los voy a hacer en el C35i, aunque siguiendo el mismo
proceso es fácil adaptarlo a otros modelos.
Para empezar, busco en la Flash el nombre del primer país de la lista: Algeria.
Sí, ya se que en español se dice 'Argelia', pero mi móvil está en inglés.
Lo encuentro en la posición 0x3CD06A , seguido por los bytes 0x00 0x95 , y
el siguiente país : Andorra (en 0x3CD073) y de nuevo 0x00 0x95 .
Luego está Argentina en 0x3CD07C , con su currespondiente 0x00 0x95 .
Y después Australia en 0x3CD087 .
Asi que para empezar sustituyo 'Algeria' por 'Almeria':
3CD06C: 67 6D
Meto el parche, y aparece tal como esperaba.
El número de Algeria es +213 asi que busco en el fichero de la flash esa
cadena, como si fuera una palabra de 3 letras.
No la encuentro. Mi siguiente idea es buscar el byte 213, pero seguro que
aparece miles de veces.
El siguiente país de la lista es Andorra, con prefijo +376, así que lo
traduzco a hexadecimal: 0x01 0x78, y aparece varias veces, pero nunca
cerca de 0xD5, que es el valor hexadecimal de +213. Tras un instante de
perplejidad, recuerdo que el C166 trabaja en formato little-endian, así
que lo que tengo que buscar es 0x78 0x01. Pronto aparece en 0x387C88, y
antes de él también está el código de Algeria: 0xD5, 0x00 .
0x387C84: D5 00 20 07
0x387C88: 78 01 21 07
0x387C8C: 36 00 22 07 , correspondiente a +54, el codigo de Argentina
Mi corto ni perezoso cambio el byte D5 por D6, y se muestra el +214 en el móvil.
Para hacer aparecer el prefijo +34 606 , uso la calculadora de Windows que
me lo convierte en hexadecimal : 0x872E = 0x2E 0x87 en little-endian.
Ahora el primer elemento de la lista me aparece 'Almeria' con prefijo +34 606
UN PASO MAS
***********
El siguiente problema al me enfrento es que no puedo poner 'Movistar-606' en
lugar de 'Algeria', porque ocupa más caracteres y no cabe.
Sospecho que tiene que haber alguna relación entre la lista de prefijos y
la lista de países.
Dado que el caracter 0x07 aparece todas las veces, supongo que es un separador.
Pero el numero precedente parece un índice. Lo cambio y voy a ver que pasa.
Pongo 0x25 en vez de 0x20. Lo que sucede es que el primer país de la
lista es ahora Austria, el cual estaba antes en la posicion 5.
O sea, que es un índice a la lista de países.
Los nombres de los países a su vez están separados por 0x00 0x95.
Retraso esto 2 posiciones para incluir 'xx', y ahora la lista
es Algeriaxx, dorra, Argentina, ...
Es decir, que hay una lista que apunta a los nombres de los paises, los
cuales se imprimen hasta encontrar el carácter 0x00.
Intento averiguar donde se referencia a esta posición de memoria, pero
no encuentro nada.
Despues de darle algunas vueltas, se me ocurre una solución:
0x387C84 tendrá +34 606 y usará el tercer byte=20 para apuntar a 0x3CD06A ,
donde guardaré 'Movistar-606', sustituyendo a 'Algeria'
Nadie apuntará a 'Andorra'
0x387C88 tendrá +34 609 y usara el tercer byte=22 para apuntar a 0x3CD07C ,
donde guardaré 'Movistar-609', sustituyendo a 'Argentina'.
Nadie apuntara a 'Australia'
Esta es una manera de hacer hueco: uso sólo la mitad de los países.
Tambien podría poner 'Mstar606', 'Xfera654' y similares, aunque voy a ver
si hay otra manera de hacerlo más bonito y completo.
Como estas dos ideas son un poco chapuceras, intento de nuevo encontrar
referencias a la lista de países.
La manera fácil de sacarlo habría sido usar Smelter para ver las
palabras escondidas en la EPROM. Lamentablemente este método falla porque
no es capaz de interpretar correctamente el Langpack del C35i, aunque
funciona bien con otros modelos. Así que experimentando con el ME45 veo que
en esta Flash la palabra 'Algeria' se localiza en el offset 0x061201, y está
referenciada desde 0x061CA9. Intento buscar algo similar en mi Flash del C35.
Tras muchas pruebas, llego a la conclusión de que 0x3CDA20 es un apuntador
al nombre del primer país.
En 0x3CDA20 hay los bytes 0x69 0x10.
En 0x3CDA22 hay los bytes 0x72 0x10.
Decremento el dato que esta en 0x3CDA20 ; esto es, cambio en 0x3CDA20 para que
ponga 0x6A 0x10 (recordar: little-indian) y la lista de paises sale:
lgeria , Andorra, Argentina, ... El cambio es que la 'A' inicial
de Algeria me la he comido.
Cambiando en 0x3CDA20 para que ponga 0x72 0x10 hago que la lista de paises sea:
Andorra , Andorra, Argentina, ...
En resumen, la palabra 'Algeria' aparece en 0x3CD06A y está referenciada
desde 0x3CDA20 , con valor 0x1069
Asi que sin mucho cuidado empiezo a modificar los paises en 0x3CD06A :
Airtel600<00><95>Airtel607<00><95>Airtel610<00><95>Airtel617<00><95>...
y sus referencias en 0x3CDA20 y siguientes:
0x3CDA20: 1069
0x3CDA22: 1069+strlen("Airtel600")+2=1074
0x3CDA24: 1074+strlen("Airtel607")+2=107F
0x3CDA26: 107F+strlen("Airtel610")+2=108A
También tengo que cambiar los códigos de los prefjos:
0x387C84: 2887 , porque +34 600 = 0x8728
0x387C88: 2F87 , porque +34 607 = 0x872F
0x387C8C: 3287 , porque +34 610 = 0x8732
0x387C90: 3987 , porque +34 617 = 0x8739
Para hacer un parche a partir de estos datos, se puede usar un programa
llamado PATSeach, aunque a mí me resulta más sencillo usar el
comando fc del MSDOS y comparar la Flash inicial con mi Flash alterada.
Por ejemplo:
fc /b C35_original.bin C35_cambia_prefijos.bin
que, para el trozo que cambia los numeros (+213 pasa a ser +34 600 , +376 pasa
a ser +34 667, +54 pasa a ser +34 610 , +61 pasa a ser +34 617 ) resulta:
387C84: D5 28
387C85: 00 87
387C88: 78 2F
387C89: 01 87
387C8C: 36 32
387C8D: 00 87
387C90: 3D 39
387C91: 00 87
Otro hack satisfactorio.
PARCHES AJENOS
**************
Un parche fácil:
He encontrado por ahí un parche para eliminar el
mensaje 'Minesweeper. With compliments from Microsoft' que aparece
antes de empezar a jugar con el buscaminas.
El parche simplemente dice:
;Firmware: C35 v18 lg4
;Author : [ZZToP]
;File : Minesweeper.vkp
;Remove message "With compliments of Microsoft" on start Minesweeper
2589BA: DAC4 0D01
O sea, que cambiara soló 2 bytes.
Para intentar comprender lo que hace, voy a desensamblar el código original.
En vez de empezar por la posicion 2589BA, empiezo 16 bytes antes, en 2589AA.
Eso me indicará lo que hay antes del codigo original:
sfe d C35_18_From_00.bin,2589AA,20 C00000
E589AA: 88 D0 : mov [-r0], r13
E589AC: 88 C0 : mov [-r0], r12
E589AE: E6 FC 1C 10 : mov r12, #101Ch
E589B2: E6 FD E1 03 : mov r13, #3E1h
E589B6: E0 0E : mov r14, #0
E589B8: E0 0F : mov r15, #0
E589BA: DA C4 AA 85 : calls 0C4h, loc_C485AA ;<<< esto será cambiado
E589BE: 08 04 : add r0, #4
E589C0: DB 00 : rets
;-----------------------------------------------
E589C2: F0 CC : mov r12, r12
E589C4: E0 2D : mov r13, #2
E589C6: FA E9 D2 4E : jmps 0E9h, loc_E94ED2
Parece que pone algunas variables en la pila, les asigna valores, y llama a
la rutina C485AA. Luego mueve la pila, y retorna en E589C0 .
Desenamblando el parche con:
sfe d Minesweeper.vkp,2589BA,20 E589BA
E589BA: 0D 01 : jmpr cc_UC, loc_E589BE
Veo que sustituye el 'calls ... ' por 'jmpr ...' con lo que saltará
hasta la instruccion siguiente, en E589BE.
Habría sido un poco mas sencillo cambiar el 'calls ... ' por un 'nop', pero
así también funciona.
O sea, que elimina la llamada a C485AA.
Eso quiere decir que C485AA es una rutina que
imprime 'Minesweeper. With compliments from Microsoft'.
Para verlo con más detalle desensamblo a partir de C485AA-02=C4859A8 :
sfe d C35_18_From_00.bin,0485A8,60
0485A8: DB 00 : rets
;---------------------------------------------
0485AA: 26 F0 66 00 : sub r0, #66h
0485AE: 88 F0 : mov [-r0], r15
0485B0: 88 E0 : mov [-r0], r14
0485B2: 88 C0 : mov [-r0], r12
0485B4: 88 D0 : mov [-r0], r13
0485B6: D4 10 6E 00 : mov r1, [r0+#6Eh]
Indica que 0485AA es el comienzo de una subrutina (esto es buena señal: no
aterrizo en mitad de la nada) y almacena los registros r12, r13, r14 y r15, que
son los que justamente ha asignado en E589AE-E589B8.
?Cómo ha sabido el autor del parche que había que modificar esa
dirección, y no otra?
Mediante el menú. Antes de E589BA, se han puesto las variables para
que r13 y r12 apunten al dibujo con el mensaje.
En realidad, el parche fué desarrollado para el modelo S35. El autor
del parche para el C35 sólo ha tenido que entender lo que hace el
original, buscar la rutina correspondiente en el C35, la ha encontrado
en 0485AA, y cambia la zona a sobreescribir.
Yo voy a hacer lo mismo.
APRENDIENDO DE OTROS
********************
He encontrado otro parche fácil :
;----- without_sim.vkp -------
;Firmware : SL45v56
;Author : DeadMans
024C2A: E004 E014
Más simple, imposible. Sólo cambiará 2 bytes en la posición de
memoria 0x024C2A para hacer que se pueda trabajar con algunos menús del
móvil, aun sin tener insertada la tarjeta SIM.
Pero este parche sólo vale para el móvil SL45, version 56.
Voy a ver si lo puedo adaptar para mi C35v18.
Para trabajar en la adaptación de parches, es necesario entender el
modelo original, y el destino.
Así que tras un poco de buscar por ahí encuentro la Flash del SL45iv56 en
un fichero llamado SL45iv56.bin . Dado que la mayoría de los parches los han
hecho los chicos de www.gsm-dev.com y ellos tienen SL45iv56 , no es difícil
encontrar una flash.
Desensamblando esa zona de memoria en la Flash del SL45v56 con el programa sfe:
sfe.exe d SL45iv56.bin,024C2A,20
024C2A: E0 04 : mov r4, #0 ;<<< esto será cambiado
024C2C: DB 00 : rets
;------------------------------------------------
024C2E: E0 0C : mov r12, #0
024C30: F0 DC : mov r13, r12
024C32: 5C 1D : shl r13, #1
024C34: D7 40 0C 00 : extp #0Ch, #1
024C38: D4 ED 52 23 : mov r14, [r13+#2352h]
024C3C: 48 E3 : cmp r14, #3
024C3E: 3D 02 : jmpr cc_NZ, loc_024C44
024C40: F0 4C : mov r4, r12
024C42: DB 00 : rets
Y desensamblando el parche:
sfe d without_sim.vkp,180AC,2
0180AC: E0 14 : mov r4, #1
O sea, que el parche hace que, en vez de asignar r4=0 , hace r4=1.
Para ver como adapto este parche al C35, uso el sfe para buscar
una cadena de bytes parecida
sfe f C35_18_From_00.bin E0,04,DB,00,E0,0C,F0,DC,5C,1D
1. 0x0180AC (0306:00AC): E0 04 DB 00 E0 0C F0 DC 5C 1D
2. 0x192AF4 (0364:2AF4): E0 04 DB 00 E0 0C F0 DC 5C 1D
3. 0x192B5A (0364:2B5A): E0 04 DB 00 E0 0C F0 DC 5C 1D
Desensamblando estas 3 zonas de memoria, la que más se parece es la primera:
sfe d C35_18_From_00.bin,180AC,50
0180AC: E0 04 : mov r4, #0
0180AE: DB 00 : rets
;------------------------------------------------
0180B0: E0 0C : mov r12, #0
0180B2: F0 DC : mov r13, r12
0180B4: 5C 1D : shl r13, #1
0180B6: D7 40 40 00 : extp #40h, #1
0180BA: D4 ED D0 24 : mov r14, [r13+#24D0h]
0180BE: 48 E3 : cmp r14, #3
0180C0: 3D 02 : jmpr cc_NZ, loc_0180C6
0180C2: F0 4C : mov r4, r12
0180C4: DB 00 : rets
Asi que el parche para el C35 no hay mas que:
0180AC: E004 E014
Lo cargo, lo pruebo, y funciona. Ahora ya puedo acceder a algunos
de los menús sin tener la tarjeta SIM.
Pero no siempre es tan sencillo, claro.
ADAPTANDO DE OTROS
******************
Otro parche que he encontrado se llama
111_Do_Not_Allow_To_Enter_112_In_Keylocked_Mode.vkp
;Firmware: SL45 v56
0311BC: EA207012 CC00CC00
que hace que NO se pueda marcar el numero de teléfono de emergencia 112.
El parche sólo vale para SL45 v56. Voy a ver si lo puedo traducir para mi C35i.
Desensamblo la flash del SL45 v56:
0311B8: 08 02 : add r0, #2
0311BA: 48 40 : cmp r4, #0
0311BC: EA 20 70 12 : jmpa cc_Z, loc_031270 ; <<<<esto será cambiado
0311C0: loc_0311C0:
: E6 FC 4A 00 : mov r12, #4Ah
La nueva instruccion CC00CC00 significa NOP , NOP , o sea, que no hace
nada, y por lo tanto no salta a loc_031270
Para adaptarlo al C35i tengo que buscar una cadena que se parezca a esta.
Naturalmente cambiarán las direcciones de las rutinas, por lo que no
puedo confiar en encontrar 'jmpa cc_Z, loc_031270'
Buscando la cadena 08 02 la encuentro mas de 1000 veces.
La cadena 48 40 la encuentro tambien mas de 1000 veces.
La cadena E6 FC 4A 00 la encuentro tambien 16 veces.
Como no me apetece desensamblar estas 16 ocurrencias, hago un programa
llamado near que busque una secuencia de bytes lo suficientemente cercanos.
#include <stdio.h>
#include <string.h>
main(int argc, char *argv[])
{
FILE *ap;
unsigned char c1=0, c0, c;
int i, j;
int buscados[10], total=0, exitos=0;
long posi[10], posi0=-1, posi10;
char cad[200];
ap=fopen(argv[1],"rb");
if(ap==NULL)
{
printf("No encuentro archivo flash %s \n", argv[1]);
exit(1);
}
/* busca los bytes a partir del argumento 2 */
/* el formato es en hexa: C0 DF 66 ... */
for(i=2;i<argc;i++)
{
strcpy(cad, argv[i] );
c0=cad[1]-'0'; if(c0>9) c0=c0+'0'-'A'+10;
c1=cad[0]-'0'; if(c1>9) c1=c1+'0'-'A'+10;
buscados[total]=c0+c1*16;
printf("Buscando %0.1X \n", buscados[total]);
posi[total]=-1000;
total++;
}
while(!feof(ap))
{
c0=getc(ap);
posi0++;
for(i=0;i<total;i++)
if(c0==buscados[i])
{
posi[i]=posi0;
exitos=0;
for(j=0;j<total;j++) /* los datos buscados tienen que estar */
if(posi0-posi[j]<4+10*total/8) /* cerca, no necesariamente pegados */
exitos++; /* el orden tampoco importa */
posi10=posi0/2-10; posi10*=2; /* muestra direccion par, 20 bytes antes */
if(exitos>8*total/10 && exitos!=total ) /* exito parcial */
printf("%i en %0.6X -> %0.6X\n", exitos, posi0, posi10 );
if(exitos==total )
printf("*** %i en %0.6X -> %0.6X\n", exitos, posi0, posi10);
}
}
return 1;
}
Lo ejecuto con
di_near C35_18_From_00.bin 08 02 48 40 E6 FC 4A 00
y me salen 5 posibilidades. De ellas la que más se parece es
0219DE: 08 02 : add r0, #2
0219E0: 48 40 : cmp r4, #0
0219E2: 3D 03 : jmpr cc_NZ, loc_0219EA
0219E4: E0 14 : mov r4, #1
0219E6: EA 00 BA 1A : jmpa cc_UC, loc_021ABA
0219EA: loc_0219EA:
E6 FC 4A 00 : mov r12, #4Ah
que, aunque tiene otras lineas en medio, el resto es idéntica a la
rutina del SL45.
Ahora tengo 2 saltos: a loc_0219EA y a loc_021ABA.
Y para cada uno, hay posibilidades: puedo eliminar el salto, o forzarlo.
Empiezo por cambiar
jmpr cc_Z, loc_0219EA
o sea:
0219E2: 3D03 2D03
y compruebo que al bloquear teclado (Keylocked_Mode), ahora puedo
escribir cualquier numero, excepto el '1' y el '2'.
Bueno, es una manera de deshabilitar la llamada al número de emergencia.
Lo que me interesa no es deshabilitarlo, sino entender cómo funciona.
Lo que hace esta rutina es chequear la tecla pulsada cuando el móvil está
con el teclado bloqueado.
Originalmente, si es '1' o '2', entonces permite esa tecla, aunque hace otro
chequeo posterior para ver que los números 1-1-2 están en el orden correcto.
Mi parche lo que hace es permitir la tecla si NO es '1' ni '2'.
Además, evita el segundo chequeo.
Aunque esto parezca una simple conversión, en realidad es otro hack exitoso.
OTRA ADAPTACION
***************
Hay otro parche llamado 'Call Minutes Beep'.
Este es más difícil de entender, asi que si no estás despejado, mejor
dejas la lectura para otro momento.
Abro el fichero cmb_vibra.vkp con un editor de textos.
En las primeras lineas veo que dice:
; Firmware: SL45i v56
; Author: rc-flitzer
O sea, que este parche tampoco vale para mi S45 ni para mi C35. Pero al
parecer, el propósito de este parche es cambiar el tiempo y periodo
del pitido de cada minuto.
El móvil tiene una configuración que hace que cuando estoy hablando, puede
emitir un pitido cada minuto. Esto sirve para saber el tiempo que llevo de
charla, y el parche me permitirá cortar la llamada justo antes que transcurra
un minuto completo, apurando al máximo el coste de la llamada.
Además, el modo de vibración tambien se puede activar.
Este es el contenido del parche
----------------------------------------------------
0x001F20: F2F40CFE DAC710D1 ; Olvídate de esta linea.
0x27D6EA: FFFFFFFFFFFFFFFF F2F40CFE88C046F4
; This value is time, when vibra should start,
; here 50 = 50 seconds. Because of period is 60 seconds the vibra
; sounds ten seconds before every minute.
; Change value if you like, but has to be smaller than the time period.
0x27D6F2: FFFF 3200 ; = 0x32 = 50 decimal
0x27D6F4: FFFFFFFFFFFFFFFFFFFFFFFFFFFF 3D05E6FC2F00DAC314000D0646F4
; This value is value above plus one for one second vibra.
; You can make two or more seconds, but it's not recommended
0x27D702: FFFF 3300 ; = 0x33 = 0x32 + 1
0x27D704: FFFFFFFFFFFFFFFFFFFFFFFF 9D03E00CDAC3140098C0DB00
----------------------------------------------------
El primer dato de cada linea (0x27D6EA) indica la posición de memoria
donde se tiene que meter.
El segundo (FFFFFFFFFFFFFFFF) indican los datos originales.
El tercero (F2F40CFE88C046F4) indica los datos que hay que meter.
Luego hay algunas lineas con comentarios. En este caso particular, es posible
modificar algunos de los parámetros, tal como la duración y el momento en
el que debe vibrar.
Ahora explico cada uno de ellos.
Para la posicion de memoria, hay que entender que viene dada en
hexadecimal, ocupando 24 bits. El procesador C166 usa bancos de 64 Kb (16 bits),
paginados mediante un indicador de 8 bits, lo que da acceso a 16 Mb (24 bits).
La Flash para el C35 ocupa 4 Mb y empieza en 0xC00000 , mientras que para
el modelo SL45 ocupa 6 Mb y empieza en 0xA00000.
Pero para los parches, la dirección se considera empezando en 0x000000, tal
como se encuentra en el fichero con la Flash.
En otras palabras, direccion_memoria=0xA00000+direcion_fichero
Por eso un parche que dice empezar en 0x27D6EA realmente va a 0xC7D6EA.
El segundo dato me dice que en esa posicion debería haber FFFFFFFFFFFFFFFF , lo
cual son 8 bytes con el valor 0xFF. Esto sirve para no aplicar 2 veces el
parche, y para verificar que efectivamente estoy tratando con la
versión adecuada de la Flash.
El tercer dato contiene los datos a grabar. El formato permite usar lineas
de tantos caracteres como queramos, aunque 16 bytes es un buen número.
En esta primera linea veo que los datos son F2F40CFE88C046F4, ocupando 8 bytes.
La segunda linea dice que los datos deben ir a 0x27D6F2 .
Esto es obvio, pues 0x27D6EA+0x8=0x27D6F2
El autor ha creado la linea
0x27D6F2: FFFF 3200 ; = 0x32 = 50 decimal
para explicar claramente que este dato se puede cambiar si deseo que
vibre en el segundo 50-esimo, o en otro que se me antoje.
Así que arranco el programa v_klay y cargo este fichero. Me pide apagar
el móvil y pulsar brevemente el boton de encender. Dado que no tengo
un SL45i , el parche ni siquiera se intenta meter en el móvil.
Hay una primera linea que no he explicado:
0x001F20: F2F40CFE DAC710D1
Este linea va en la posición de memoria 0xA01F20, y espera encontrar
los bytes F2F40CFE para sustituirlos por DAC710D1.
Tras cargar la Flash del SL45iv56 en un editor hexadecimal, compruebo
que en 0x001F20 yo tengo los bytes F2F40CFE .
A DESENSAMBLAR
**************
Ahora con el programa sfe (Siemens Flash Explorer) escribo:
sfe d SL45iv56.bin,1f00,200
desensambla el fichero SL45iv56.bin desde la posición 1F00 hasta 200 bytes más.
No elijo la direccion 01F20 porque me interesa ver un poco de lo que está antes:
001F00: 40 29 : cmp r2, r9
001F02: F0 C4 : mov r12, r4
001F04: F0 D5 : mov r13, r5
001F06: DA A0 0C 2F : calls 0A0h, loc_A02F0C
001F0A: DA DE 6E 15 : calls 0DEh, loc_DE156E
001F0E: 48 41 : cmp r4, #1
001F10: 3D 12 : jmpr cc_NZ, loc_001F36
001F12: DA A0 78 2F : calls 0A0h, loc_A02F78
001F16: E6 FC 3C 00 : mov r12, #3Ch
001F1A: F6 F4 0E FE : mov mem_FE0E, r4
001F1E: 5B CC : divu r12
001F20: F2 F4 0C FE : mov r4, mem_FE0C ; <<<<esto sera cambiado
001F24: 3D 08 : jmpr cc_NZ, loc_001F36
001F26: E6 FC E8 35 : mov r12, #35E8h
001F2A: E6 FD 0E 00 : mov r13, #0Eh
001F2E: E6 FE 52 00 : mov r14, #52h
001F32: DA C1 64 B1 : calls 0C1h, loc_C1B164
001F36: DA CC 28 C5 : loc_001F36:
001F36: DA CC 28 C5 : calls 0CCh, loc_CCC528
001F3A: 48 40 : cmp r4, #0
001F3C: 3D 04 : jmpr cc_NZ, loc_001F46
y el comando
sfe d cmb_vibra.vkp,1F20,4
dice
001F20: DA C7 10 D1 : calls 0C7h, loc_C7D110
O sea, que el comando
mov r4, mem_FE0C
sera sustituido por
calls 0C7h, loc_C7D110
En principio esto no tiene mucho sentido, pero sigo desensamblando el parche:
sfe d cmb_vibra.vkp,27D6EA,300
27D6EA: F2 F4 0C FE : mov r4, mem_FE0C
27D6EE: 88 C0 : mov [-r0], r12
27D6F0: 46 F4 32 00 : cmp r4, #32h
27D6F4: 3D 05 : jmpr cc_NZ, loc_27D700
27D6F6: E6 FC 2F 00 : mov r12, #2Fh
27D6FA: DA C3 14 00 : calls 0C3h, loc_C30014
27D6FE: 0D 06 : jmpr cc_UC, loc_27D70C
;------------------------------------------------
27D700: 46 F4 33 00 : loc_27D700:
27D700: 46 F4 33 00 : cmp r4, #33h
27D704: 9D 03 : jmpr cc_NC, loc_27D70C
27D706: E0 0C : mov r12, #0
27D708: DA C3 14 00 : calls 0C3h, loc_C30014
27D70C: 98 C0 : loc_27D70C:
27D70C: 98 C0 : mov r12, [r0+]
27D70E: DB 00 : rets
O sea, que de algun modo
calls 0C7h, loc_C7D110
llamara a 27D6EA que hara
27D6EA: F2 F4 0C FE : mov r4, mem_FE0C
al igual que la rutina original, y además hara algo más.
Ahora es cuando hay que echar mano de los manuales del C166, que dicen que
mov r4, mem_FE0C
sirve para que el registro r4 tenga el mismo valor que la posición
de la memoria mem_FE0C.
El documento que yo uso se llama sj2003551.pdf y contiene la descripción
de todos los comandos, así como los datos de algo que se llaman registros SFR.
Básicamente, en un C166 los registros siempre se encuentran en una posición
de memoria, y lo mismo da leerlos de la memoria que usar su propio resultado.
El registro FE0C se llama 'Multiply/Divide High Word' , y se usa en las
operaciones de multiplicación y división.
Por ejemplo, cuando hago
mul r0, r4
lo que pasa es que se multiplica r0*r4 y el resultado va a parar a mem_FE0C .
Analogamente se usa para la division.
Es por eso que no extraña encontrar
001F1E: 5B CC : divu r12
justo antes de consultar mem_FE0C .
Pero me estoy desviando del tema.
La nueva rutina a partir de 27D6EE mete r12 en [-r0]
Esto es una manera de almacenar un dato en la pila.
El comando mov [-r0] , r12 quiere decir meter r12 en la dirección a la que
apunta r0 (que suele ser la zona de stack) y luego decrementarlo, haciendo
hueco para un nuevo valor. Esto se hace porque la subrutina modificará r12, y
no deseo perderlo.
cmp r4, #32h compara r4 con 0x32 . El valor de r4 se ha asignado
anteriormente, probablemente en loc_DE156E, y creo deducir que almacena
el segundo (hh:mm:SS) en el que está el reloj.
Por eso lo compara con 0x32, para ver si está en el segundo 50-esimo.
Si no es 0, salta a loc_27D700 .
En caso contrario mete #2Fh en r12, y llama a loc_C30014
Desensamblando 0xC30014 (recordar que está en la flash en 0x230014) con
sfe d SL45iv56.bin,230014,200
veo que hace muchas cosas, y r12 parece ser algun tipo de flag de activación.
Tras esto, salta a loc_27D70C
En loc_27D700 (viniendo desde 27D6F4) mira si está en el segundo 51-esimo.
Si no es asi, salta a loc_27D70C, desde donde se recupera r12, y sale.
Pero si esta en el segundo 51-esimo, pone el flag r12 a 0 , llama
de nuevo a loc_C30014 y luego sale.
Asi que está más claro ahora: loc_C30014 se encarga de activar/desactivar
el vibrador en función de lo que diga r12.
Para adaptar este parche a otro modelo, debo saber cual es la rutina para
activar el vibrador, y tambien parchear 001F20, o su equivalente.
Entonces hay que cargar en el editor el archivo con mi flash del S45 y ver
si por casualidad las mismas rutinas están en los mismos sitios.
En el archivo ME45FubuV28.fls, los datos
001F1A: F6 F4 0E FE : mov mem_FE0E, r4
aparecen en 8 posiciones, pero filtrando en función de la linea precedente
001F16: E6 FC 3C 00 : mov r12, #3Ch
me quedo con
0x202390 y 0x20b0e0.
Para decidir por una u otra posición lo mejor es desensamblar los dos
trozos y ver cual se parece más al original.
Lamentablemente me da un error, pero dice:
File ME45FubuV28.fls (pos=0x0,sz=0x600000,rd=0x600000) buffered
FirmwareID: ME45v28-lg1-ken8.z2 (ME45 memory mapping)
O sea, que al menos ha reconocido que el fichero pertenece a un ME45, lo
cual no me vale de consuelo.
Encuentro otra Flash de un S45i, y tampoco es capaz de desensamblarlo.
Es cierto que un fichero Flash no es lo mismo que un fichero bin , pero ya
que reconoce lo que hay dentro, esperaba que fuera capaz de desensamblarlo.
No todo está perdido: del fichero ME45FubuV28.fls, corto un trozo
entre 0x202390-20 y
0x202390+20 , lo guardo en un mini-fichero, y esta
vez sí que lo puedo desensamblar:
000078: F0 C4 : mov r12, r4
00007A: F0 D5 : mov r13, r5
00007C: DA C0 16 36 : calls 0C0h, loc_C03616
000080: DA F5 8A 78 : calls 0F5h, loc_F5788A
000084: 48 41 : cmp r4, #1
000086: 3D 12 : jmpr cc_NZ, loc_0000AC
000088: DA C0 EA 36 : calls 0C0h, loc_C036EA
00008C: E6 FC 3C 00 : mov r12, #3Ch
000090: F6 F4 0E FE : mov mem_FE0E, r4; <<<< la linea buscada
000094: 5B CC : divu r12
000096: F2 F4 0C FE : mov r4, mem_FE0C <<<< seguramente lo cambiaré
00009A: 3D 08 : jmpr cc_NZ, loc_0000AC
00009C: E6 FC 24 14 : mov r12, #1424h
0000A0: E6 FD 43 00 : mov r13, #43h
0000A4: E6 FE 55 00 : mov r14, #55h
0000A8: DA CA 16 AF : calls 0CAh, loc_CAAF16
Como se puede observar, este código del ME45v28 es muy parecido al
del SL45iv56 en 001F00, aunque cambian las direcciones de las rutinas.
Así, puedo cotejar entre las flash del SL45iv56 y del ME45v28 .
Similarmente corto y analizo el trozo en 0x20b0e0 :
00003A: 46 FC FF 00 : cmp r12, #0FFh
00003E: FD 09 : jmpr cc_ULE, loc_000052
000040: F0 4C : mov r4, r12
000042: E6 FC 3C 00 : mov r12, #3Ch
000046: F6 F4 0E FE : mov mem_FE0E, r4
00004A: 5B CC : divu r12
00004C: F2 F4 0E FE : mov r4, mem_FE0E
000050: DB 00 : rets
que no se parece en nada al original.
Así que parece que el parche para el ME45FubuV28.fls tiene que
cambiar 0x202390 para apuntar a mi rutina.
Inciso: si tuviera un editor hexadecimal potente, podría posicionarme en
cualquier byte, y desde allí hacer que extrajera un trozo de bytes
para invocar al sfe .
Lamentablemente no conozco ningún editor que permita hacer eso. Quizás Emacs?
En resumen, por ahora ya sé dónde tengo que parchear. Ahora me queda
saber porqué el parche original cambia esta posición y lo redirige a
calls 0C7h, loc_C7D110
y en cambio el código nuevo se localiza en 27D6EA (en realidad, 0xC7D6EA, debido
a que la memoria esta desplazada 0xA00000 bytes respecto al fichero)
La zona 27D6EA es un bloque de FF FF (o sea, vacio) desde 27D07A hasta 27ECFF.
Una vez aclarado esto, hay que convertir la rutina 27D6EA-27D70E.
Solo hay saltos relativos, y dos llamadas a C30014, así que va a
tocar desensamblar esto.
Como antes, empiezo antes de 230014 para ver si lo anterior tiene sentido
sfe d SL45iv56.bin,230000,180
230000: DA C2 B8 EB : calls 0C2h, loc_C2EBB8
230004: E0 0C : mov r12, #0
230006: DA C3 D4 00 : calls 0C3h, loc_C300D4
23000A: DA E6 90 44 : calls 0E6h, loc_E64490
23000E: 5E 0B : bclr STKUN.5
230010: 6E 0B : bclr STKUN.6
230012: DB 00 : rets
;----------------------------------------------------
;<<<<<aqui entraré
230014: 88 80 : mov [-r0], r8 ; guarda r8 en la pila
230016: F0 8C : mov r8, r12 ; usa r12. Bien, porque lo asigné antes
230018: 46 F8 32 00 : cmp r8, #32h ; en mi caso, puede ser #2Fh o #0h
23001C: 2D 19 : jmpr cc_Z, loc_230050
23001E: 46 F8 31 00 : cmp r8, #31h
230022: 2D 16 : jmpr cc_Z, loc_230050
230024: 46 F8 34 00 : cmp r8, #34h
230028: 2D 25 : jmpr cc_Z, loc_230074
...... mas comparaciones de r8
230046: 2D 24 : jmpr cc_Z, loc_230090
230048: 46 F8 0A 00 : cmp r8, #0Ah
23004C: 2D 21 : jmpr cc_Z, loc_230090
23004E: 0D 29 : jmpr cc_UC, loc_2300A2 ;<<si r8 es >0, salta.
;----------------------------------------------------
230050: E0 2C :loc_230050;<<llego aquí en la primera llamada, desde 27D6FA
230050: E0 2C : mov r12, #2
230052: 88 C0 : mov [-r0], r12
230054: E0 0D : mov r13, #0
230056: 88 D0 : mov [-r0], r13
230058: E6 FC C0 34 : mov r12, #34C0h
23005C: E6 FD 0E 00 : mov r13, #0Eh
230060: E0 1E : mov r14, #1
230062: F0 F8 : mov r15, r8
230064: DA C1 30 AA : calls 0C1h, loc_C1AA30
230068: 08 04 : add r0, #4
23006A: 0D 23 : jmpr cc_UC, loc_2300B2
..............
2300A2: E6 FC C0 34 : mov r12, #34C0h ;<<aterrizo aquí en la
segunda llamada, desde 27D708
2300A6: E6 FD 0E 00 : mov r13, #0Eh
2300AA: E0 0E : mov r14, #0
2300AC: F0 F8 : mov r15, r8
2300AE: DA C1 30 AA : calls 0C1h, loc_C1AA30
2300B2: 98 80 : loc_2300B2:
2300B2: 98 80 : mov r8, [r0+]
2300B4: DB 00 : rets
En ambos casos, llama a C1AA30. La diferencia es que en el primer caso ha hecho
mov r14, #1
y en el segundo ha hecho
mov r14, #0
Parece que en el primer caso hace algo, y en el segundo caso deja de hacerlo.
Ahora tengo que buscar la rutina similar para mi modelo.
No es bueno confiar en los nombres de las variables usadas, ya que cambian
entre versiones; es mejor basarse en las intrucciones.
Buscando los bytes de
23001E: 46 F8 31 00 : cmp r8, #31h
en el fichero ME45FubuV28.fls, se encuentran en varias posiciones, pero en
2C0DB0 (menos algunos bytes) al desensamblar queda:
2C0D88: DA CB 24 F7 : calls 0CBh, loc_CBF724
2C0D8C: E0 0C : mov r12, #0
2C0D8E: DA CC 56 0F : calls 0CCh, loc_CC0F56
2C0D92: DA F7 B8 8B : calls 0F7h, loc_F78BB8
2C0D96: 7E 0B : bclr STKUN.7
2C0D98: 8E 0B : bclr STKUN.8
2C0D9A: DB 00 : rets
;------------------------------------------------
2C0D9C: 88 80 : mov [-r0], r8
2C0D9E: F0 8C : mov r8, r12
2C0DA0: DA CA 6A 95 : calls 0CAh, loc_CA956A
2C0DA4: 46 F8 32 00 : cmp r8, #32h
2C0DA8: EA 20 7A 0E : jmpa cc_Z, loc_2C0E7A
2C0DAC: 46 F8 31 00 : cmp r8, #31h
2C0DB0: EA 20 82 0E : jmpa cc_Z, loc_2C0E82
2C0DB4: 46 F8 34 00 : cmp r8, #34h
2C0DB8: EA 20 A6 0E : jmpa cc_Z, loc_2C0EA6
Que de nuevo se parece un montón a la rutina original de
SL45iv56.bin que estaba en 230000
Así que el parche para el ME45FubuV28.fls tiene que llamar a 0x2C0D9C para
activar el vibrador.
Otro hack para la lista.
PRIMER PROGRAMA
***************
Como es obvio, el móvil tiene un puerto serie que permite la comunicación
con el ordenador. Este puerto está manejado directamente por el micro C166 ; no
hace falta ningun artificio raro.
La manera de escribir en el puerto es a través de DMA: se pone el dato
en una posición de memoria, se activa un flag, y ya está.
El registro de memoria es S0TBUF y está en la dirección de memoria 0xFEB0 .
Su significado es 'Serial Channel 0 Transmit Buffer Register'
Para meter un dato, se puede usar una instrucción del tipo
mov S0TBUF, r3
que se codifica como
F6 F3 B0 FE (observar que los últimos 2 bytes son 0xFEB0, en little-indian).
Para decir que el dato ya puede ser transmitido, se usa la instrucción
bclr S0TIC.7
que pone a 0 el bit 7 de la dirección de memoria S0TIC , la cual está
en 0xFF6C y significa 'ASC0 Transmit Interrupt Control Register'
se codifica como
7E B6
Ahora hay que esperar a que el byte esté transmitido.
Esto se sabe porque el micro pone a 1 el mismo bit.
Así que espero hasta que valga 1 antes de seguir:
jnb S0TIC.7, aqui_mismo
y se codifica como
9A B6 FE 70
Normalmente se tiene el dato a transmitir en alguna zona de la memoria, aunque
para este ejemplo voy a transmitir la letra 'V', cuyo valor es 0x56, y que
almaceno temporalmente en rl3.
Suponiendo que estoy en la posición 0x0485C6, la rutina queda:
0485C6: E7 F6 55 00 : movb rl3, #56h
0485CA: C0 63 : movbz r3, rl3
0485CC: F6 F3 B0 FE : mov S0TBUF, r3
0485D0: 7E B6 : bclr S0TIC.7
0485D2: 9A B6 FE 70 : loc_0485D2:
0485D2: 9A B6 FE 70 : jnb S0TIC.7, loc_0485D2
El hecho de que las dos ultimas líneas aparezcan duplicadas es la manera
que tiene el des-ensamblador sfe de definir una etiqueta.
Aprovechando que ya conozco algo del ejemplo del buscaminas, sé que llama
a 0x485AA, así que en vez de aplicar el parche para eliminiar la pantalla
de espera, decido meter allí mi rutina.
La rutina original empieza en 485AA y acaba en 485FE por lo que dispongo
de mucho espacio libre.
Pongo todos los bytes a NOP (no=operacion), cuya codificación es
CC 00 : nop
y luego los bytes de mi rutina, desde 0485C6 hasta 0485D2
Mas claramente:
nop
nop
...
nop
nop
movb rl3, #55h
movbz r3, rl3
mov S0TBUF, r3
bclr S0TIC.7
loc_0485D2:
jnb S0TIC.7, loc_0485D2
nop
nop
...
nop
rets
Ensamblado quedará algo asi como:
CC00 CC00 ..... CC00 E7F65500 C063 F6F3B0FE 7EB6 9AB6FE70 CC00 ..... CC00
Ya con todo preparado meto el parche en el teléfono, conecto el
terminal de Windows, y cuando empiezo a jugar al buscaminas, no me
sale la pantalla de 'Cumpliments to Microsoft' sino que escribe la
letra 'V' en el hyperterminal.
Es importante conectarlo al ordenador. Si no, no podrá transmitir
el byte 'V' y entrara en un bucle infinito.
Un momento! Ahora el buscaminas desde E589BA vuelve a llamar
a 485AA, aunque 485AA no hace lo que debería, sino que imprime la 'V'.
Lo malo es que hay otros muchos menús que dejan de funcionar, y en su
lugar imprimen 'V'.
Se ve que 485AA es un punto común de entrada para varias rutinas.
Esto me puede servir para saber quien llama a esta rutina, aunque seguramente
debería restaurar el comportamiento original para que todo funcione como antes.
De todos modos ya tengo algo así como un mini-debugger. Si quiero saber cuando
una rutina es llamada, sólo tengo que llamar a mi rutina de enviar datos.
Claro que primero tengo que ver cómo guardo los valores de rl3 y r3, porque
yo los modifico en mi rutina.
Pero eso corresponde a otro hack.
TRACEANDO
*********
Ahora voy a mejorar el programa anterior para que sea mas útil.
En corto, haré una rutina que se puede llamar desde cualquier sitio, y me
dirá desde dónde me han llamado.
El nombre apropiado sería print_donde_estoy
O sea, que si mi rutina es llamada desde 89BA, imprimirá precisamente 89BA.
Esto me permitirá modificar masivamente (con cuidado) la Flash
y me irá diciendo los sitios por los que va pasando.
#include C166.inc
org 0485BCh
nop
nop
nop
mov [-r0], r3
pop r3
push r3
mov S0TBUF, r3
bclr S0TIR
aqui_mismo1:
jnb S0TIR, aqui_mismo1
movbz r3, rh3
mov S0TBUF, r3
bclr S0TIR
aqui_mismo2:
jnb S0TIR, aqui_mismo2
mov r3, [r0+]
rets
nop
nop
end
Para compilarlo:
sfe a puerto.asm d
Primero incluyo el fichero C166.inc que contiene constantes comunmente usadas.
Este fichero es fácil de encontrar, aunque quizás con otros nombres.
Se puede encontrar en D:\Keil\C166\asm\REG166.INC si instalas el compilador Kiel
Tambien lo encontraras al instalar el 'COSMIC ST10 Evaluation Kit'
Esto me permite, entre otras cosas, usar los nombres S0TBUF y S0TIR sin
necesidad de especificar la dirección de memoria en la que están.
A continuación digo dónde hay que poner la rutina. Sólo es necesario
si hago llamadas absolutas a otras funciones en este mismo fichero.
Como yo uso direcciones relativas, no lo necesito.
Siempre es buena idea incluirlo porque cuando se compila dirá
automáticamente las direcciones en las que se colocará.
Luego uso varios nop simplemente para localizar mi rutina en memoria.
Cuando quiero localizar mi rutina en la memoria, me resulta más fácil
buscar nop nop nop en vez de ir a la dirección 0485BC.
Además así sé el hueco de que dispongo si quiero ampliar
el código con nuevas instrucciones.
El siguiente paso es guardar en la pila local los datos que voy a usar.
En todos los micros que yo conozco, hay una pila para guardar datos.
Se usan las instrucciones push y pop para meter y sacar datos.
Además, cuando se llama a una rutina, la rutina llamante almacena en la pila
la dirección de la siguiente instruccion, para saber dónde hay que retornar.
En algunas arquitecturas la pila se usa para guardar los argumentos que
se pasan entre una rutina y otra, además de los parametros de retorno.
Pero el C166 tiene un mecanismo extra. Dado que está pensado para soportar
múltiples tareas, existen unas variables llamadas 'descriptores de contexto'.
Cada tarea tiene una memoria de 4 Kb en la que guardar sus variables. Cuando
se produce un cambio de contexto (una reanudación de una tarea) simplemente
se conmuta la memoria privada. Todas las variables son restauradas a sus
valores, que fueron guardados al efectuarse el cambio de contexto precedente.
Una de dichas variables es r0 , que se suele usar como una mini-pila local, sin
interferir con la pila comunitaria.
La instrucción
mov [-r0], r3
mete r3 en la pila local, y decrementa r0 para que haya
sitio para otros nuevos valores.
La siguiente intrucción
pop r3
saca de la pila el valor que haya, que resulta ser la dirección de la
rutina que nos ha llamado.
Técnicamente en la pila está el valor del IP: Instruction Pointer
Luego lo vuelvo a meter , ya que se ha quedado en r3
Entonces meto la parte baja de r3 en el buffer de transmisión S0TBUF.
Limpio el flag S0TIR para que empiece a transmitir y espero hasta que se envía.
Necesitaré un programa tipo hyperterminal o algo mejor para recibir
este dato, ya que por ahora va en binario.
A continuación meto el otro byte de r3 para transmitir el byte alto de IP.
Recupero r3 de la pila local, y retorno con
rets
Para generar el parche:
sfe a puerto.asm p
Antes de que los mas quisquillosos se me echen al cuello, advierto que ya
me he dado cuenta de que esta rutina tiene varios fallos:
-no tiene cuidado con los flags. Debería guardarlos, pero no lo hago.
-no considera las interrupciones. Esta rutina no se puede llamar desde otra
que haya deshabilitado las interrupciones o este sirviendo una trampa (trap).
-es pesada de llamar. Necesito 4 bytes, y siempre deberá ser considerada
como una rutina en otro segmento, ya que su retorno es con rets.
Si quieres una rutina mucho mejor, existe un parche llamado
ATCGSN
que permite leer cualquier zona de memoria, escribirla (sólo la RAM) , llamar
a una rutina, o buscar datos en la memoria.
No sólo eso, sino que hay un programa ATDebugger que es un
buenísimo interface para este parche.
Una vez metido este parche en el teléfono, se puede usar con el
cable normal, y fuera del 'Service mode'.
Se pueden leer y escribir las variables SFR y los registros r0-r15, aunque
par su propia operatoria, también los modifica.
En mi caso no me vale para mis propósitos porque lo que yo quiero es saber los
puntos de entrada de los menús, y eso sólo lo puedo hacer yendo uno por uno.
Ya he averiguado que algunos (al menos 10, sin contar el buscaminas) de ellos
van a parar a 0x485AA, y esto me proporciona una base para sustituirlos
por mis propias rutinas.
Por ejemplo, el menu Juegos->Reversi->Opciones->Ayuda
a partir de ahora no dirá ayuda, sino que hará lo que yo quiera.
Y lo que quiero es que imprima en la pantalla del móvil la dirección desde
la que lo llamo. El mismo dato que se manda por el puerto, pero por pantalla.
IMPRIMIR
********
Para averiguar cómo se hace para imprimir, desensamblo un parche
llamado Thermometer.vkp
Este parche imprime en la pantalla la temperatura de la batería.
Sí, el móvil tiene un pequeñio termómetro para que se apague
automáticamente cuando hace mucho calor o mucho frio.
No tiene mucha precisión, y dado que está cerca de la batería, no
reacciona rápidamente a los cambios de la temperatura ambiente.
El parche saca el dato de 0x0C2C08 , hace algunos cálculos, asigna
r12=x , r13=y, r14=0x13A+un_indice y llama a 0x0E5ABA4
Tiene en total 60 lineas en ensamblador, pero no voy a aburrir con los detalles.
La asignación de r12 y r13 está clara. La pantalla mide 101x54 y se
empieza a contar desde la esquina superior izquierda.
Estas variables conendrán las coordenadas donde quiero imprimir.
La rutina en 0x0E5ABA4 no imprime una letra, sino un dibujo. Siguiendo las
indicaciones de antes para usar el 'Siemens Picture Change' , veo que en
la posición 0x13A esta el '0', por eso tiene que hacer r14=#13Ah.
Para imprimir el '1', el índice es 0x13A+1=0x13B, y así hasta el '9'.
Como mi dato es una palabra de 16 bytes, lo escribiré como un código
hexadecimal de 4 letras 0-9A-F
Por ejemplo, si la dirección es 0x89BA, imprimo los caracteres 8, 9, B, A.
Pero lamentablemente el índice 0x13A+0x0A no apunta a la letra 'A', sino a
un rectángulo de 6x9 totalmente vacío.
Es mas, el índice 0x13A+0x0B = 0x145 apunta a un dibujo de 21x7 de una llave.
No todo está perdido. Usando 'Siemens Picture Change' puedo cambiar
los dibujos, incluso su tamanio.
En un momento edito desde 0x144 hasta 0x14A para que contengan los dibujos
de las letras 'A' hasta 'F', y los meto de nuevo en el télefono.
Basicamente, si r3 contiene el semi-byte (0-F) a imprimir:
mov r12, #32h ; x
mov r13, #22h ; y
mov r14, r3
and r14, #000Fh
add r14, #13Ah ; apuntador base para dibujos
calls 0E5ABA4h
El segundo problema al que me enfrento es que no voy a tener espacio
suficiente en 0C485AAh . Pero la memoria tiene muchas areas vacías.
Escojo 0C7FAE0, donde puedo usar 256 bytes.
También podría usar 0x2D0000, donde tengo 64kb libres.
En el C35i hay 8 bloques de 64Kb que no se usan. Esto da para escribir muchos
programas en estas zonas libres.
En particular, desde 0x2C6ED2 hasta 0x334000 hay 436Kb libres.
Para imprimir toda la dirección IP tengo que llamar a esta
subrutina 4 veces con distintas posiciones x.
Cada dibujo mide 6x9 así que tengo que variar 'x' en 6+1 pixels.
-------------------------
base 0C00000h ; autoconvierte las direcciones. La memoria empieza en 0x0C00000
#include C166.inc
#define imprime 0E5ABA4h ; Rutina de la EPROM que imprime un icono
org 0C485AAh ; dirección original de la llamada
push PSW ; guardo los flags. Sólo se pueden guardar en la pila
mov [-r0], r3 ; guardo los registros que modificaré
pop r3 ; saco los flags de la pila
mov [-r0], r3 ; y los meto en la pila local
pop r3 ; extraigo InstructionPointer de la pila
push r3 ; y lo meto de nuevo. Ahora lo tengo en r3
movb S0TBUF, rh3 ; Primero saco el byte mas significativo
bclr S0TIR ; lo mando al puerto
aqui_mismo1:
jnb S0TIR, aqui_mismo1
movb S0TBUF, rl3 ; luego la parte baja: el menos significativo
bclr S0TIR
aqui_mismo2:
jnb S0TIR, aqui_mismo2
calls imprime_ip ; una vez 'puerteado', lo muestro en pantalla
mov r3, [r0+] ; recupero los flags
push r3 ; meto los flags en la pila
mov r3, [r0+] ; recupero el registro r3
pop PSW ; saco los flags de la pila
rets
org 0C7FAE0h ; no hay espacio suficiente en 0C485AA. Pero aquí hay un montón
imprime_ip: ; imprime r14 de derecha a izquierda . Si r14=ABCD, imprime
; primero D , luego C, despues B, y al final A
; pero como la posicionX se va decrementanto, aparece ABCD
mov [-r0], r12 ; guardo los registros que uso
mov [-r0], r13
mov [-r0], r14
mov r14, r3 ; contiene la palabra (16 bits) a imprimir
mov r13, #2Ah ; y
mov r12, #39h ; x
callr mi_imprime ; imprime solo el nibble (4-bits) menos significativo
mov r14, r3
shr r14, #04h ; ahora imprimo el segundo nible menos significativo
mov r12, #32h ; a la _izquierda_ del anterior dato
callr mi_imprime
mov r14, r3
shr r14, #08h
mov r12, #2Bh ; x
callr mi_imprime
mov r14, r3
shr r14, #0Ch
mov r12, #24h ; x
callr mi_imprime
mov r14, [r0+] ; recupero los registros
mov r13, [r0+]
mov r12, [r0+]
rets
mi_imprime:
and r14, #000Fh ; me quedo con la parte baja
add r14, #13Ah ; base para pictures
mov [-r0], r13
mov [-r0], r3
calls imprime
mov r3, [r0+]
mov r13, [r0+]
ret
end
--------------------------
Notar que he cambiado el orden de los bytes para que los imprima en big-endian.
Como antes, para compilar:
sfe a puerto_pantalla.asm d
Y para generar el parche:
sfe a puerto_pantalla.asm p
Lo meto en el móvil, navego por los menús, y veo la dirección tanto en el
hyperterminal como en la pantalla del móvil en la zona que hay abajo, en medio.
La rutina anterior tampoco es perfecta. Ahora guardo bien los flags, pero
todavía hay otros detalles que no tengo en cuenta, ej. las interrupciones.
Al menos, ahora tengo algo que se puede llamar desde cualquier sitio.
No afectará al programa que llame a mi rutina, pero a mí me será util.
Ciertamente me pueden llamar desde otras rutinas en otros segmentos, pero
todavía no imprime correctamente la direccion completa.
Para ello haría falta extraer también el CSP (Code Segment Pointer) aunque
esto es más delicado, pues hace falta saber si me han llamado con una
instrucción CALLS (Call Inter-Segment Subroutine, que si guarda en CSP) o
desde una instrucción CALLA (Call Subroutine Absolute),
CALLI (Call Subroutine Indirect),
o CALLR(Call Subroutine Relative), las cuales no usan CSP.
MEMORIA
*******
Espero que haya quedado suficientemente claro que la zona en la que están
almacenados los programas en el C35i empieza en la Flash a partir de 0xC00000 .
El teléfono tiene otras zonas de memoria:
0x000000 = inicio. Mide 0x000200
0x000200 = DRAM. Mide 0x00EE00
0x00F000 = SFR/ESFR. Mide 0x001000 y almacena los registros.
0x010000 = ROM del chip. Mide 0x008000
0x010800 = Word-writeable. Mide 0x007800
0x100000 = RAM. Mide 0x040000
0xC00000 = Fullflash. Mide 0x400000
La última dirección es 0xFFFFFF
Estos 16Mg de memoria ocupan un espacio de direcciones
de 2^24 bits, es decir, 3 bytes.
Hay 2 maneras de agruparlos:
-en 256 bloques de 64Kb, llamados segmentos. Se usa la notación 0x123456
-en 1024 páginas de 16Kb. Se usa notación 048:3456
La formula para pasar entre uno y otro es dividir entre 0x4000
Asi, 0xFCA123=3F2:2123 , porque 0x3F2*0x4000+0x2123=0xFCA123
Algunos programas tales como el 'AT Debugger' usan notación de
páginas, mientras que el resto usan segmentos.
Es conveniente usar la calculadora incluida en 'AT Debugger' para hacer
las transformaciones.
Para acceder a la memoria se puede direccionar de 3 maneras distintas:
-Código, usando el Puntero de Segmento de Código. También llamado Modo Corto
Es el modo que todos imaginamos; usa la dirección completa: jmpr loc_FCA123
-Datos, usando Puntero a Página de Datos. También llamado Modo Largo.
Hay 4 registros DPP0, DPP1, DPP2, DPP3 que apuntan a una página.
Se combina con otro valor (offset) para obtener la dirección completa:
mov DPP0, #03F2h
mov r12, #2123h
otro ejemplo : jmps #03F2h, #2123h
-Direccionamiento de Datos via Modo Extendido.
-Extensión mediante segmento:
Primero se usa una instrucción para usar un segmento alternativo, y
luego la instrucción para tomar el dato:
extp #0FCh, #1h
mov r12, #0A123h
-Extensión mediante página:
Primero se usa una instruccíon para usar una página alternativa, y
luego la instruccion para tomar el dato:
extp #03F2h, #1h
mov r12, #2123h
Hay instrucciones que funcionan con segmentos, y otras con páginas:
JMPS seg, caddr ; salta a una dirección 'caddr' en un segmento 'seg'
JMPA cc, caddr ; salta a una dirección en el mismo segmento
CALLS seg, caddr ; llama a la subrutina en 'caddr' del segmento 'seg'
CALLA cc, caddr ; llama a la subrutina en la dirección absoluta 'caddr' .
RET ; retorna desde una subrutina en el mismo segmento
RETS ; retorna desde una subrutina entre segmentos
El segmento 0, que va desde 0x000000 hasta 0x010000 ocupa 64 Kb (como todos los
demás) y cuando se cambia algun dato en el segmento 1 (direccion 0x100000),
también resulta modificado en el segmento 0.
O sea:
mov r3, #1234h
extp #40h, #1h
mov 00h, r3
Escribe 1234 en 40:0000 , es decir, 0x100000
Pues bien: el dato en 00:0000 también contiene 1234.
De hecho, para escribir en el segmento 0, debo hacerlo en el segmento 1, y
se copiará automaticamente.
Es por esto que hablaré indistintamente de 0x100000 o de 0x000000.
Para leer de la memoria, se puede usar el 'Siemens Debugger' o el 'ATDebugger'.
O tambien puedo modificar mi programa para que lo haga.
Navegando por los menus aprendo que:
Juegos->Wayout->Highscore llama a 3E26
Juegos->Buscaminas->Highscore llama a 1F3A
Lo primero que debo es discernir cual menú me ha llamado.
Por simplicidad, solo chequeo el byte menor (será #26h o #3Ah)
La dirección de memoria que voy a usar es 1000D4=40:00D4
Así que renombro la rutina imprime_ip para llamarla imprime_r3, y queda así:
cmpb rl3, #26h ; vengo de Juegos-Wayout-Highscore
jmpr cc_Z, go_wayout
cmpb rl3, #3Ah ; vengo de Juegos-Buscaminas-Highscore
jmpr cc_Z, go_mines
go_wayout:
extp #40h, #1h
mov r3, 0D4h ; lee la memoria
calls imprime_r3
jmpr cc_NZ, go_fin
go_mines:
mov r3, #1234h
extp #40h, #1h
mov 0D4h, r3 ; escribe '1234' en la memoria
calls imprime_r3
jmpr cc_NZ, go_fin
go_fin:
mov r14, [r0+] ; recupero los registros
mov r13, [r0+]
mov r12, [r0+]
mov r5, [r0+]
rets
Hala, ya puedo escribir donde me apetezca, y leerlo despues.
Claro que esto no es muy versátil.
Tendría que permitir elegir la zona de la memoria donde quiero leer/escribir, y
pedir también el dato en caso de que quiera escribirlo.
Pero para eso ya estan los otros programas que he mencionado antes.
Una cosa curiosa es que el 'Siemens Debugger' parece tener problemas a la hora
de leer algunas direcciones, en particular me resetea el móvil cuando intento
acceder a direcciones entre 0x00E000 y 0x00E400.
Funciona si uso 0x10E000 , y los datos parecen ser correctos.
Por contra, el 'ATDebugger' tiene otro fallo y es que sólo me deja
escribir 5 ó 6 comandos. Al cabo del tiempo, la paridad del puerto cambia y el
programa se vuelve loco. La solución es apagar en móvil, lo cual es un fastidio
cuando he tardado media hora en preparar el escenario correcto.
Por eso me he acostumbrado a usar ambos programas y verifico los datos 2 veces.
El 'ATDebugger' necesita que el móvil esté encendido, mientras que
el 'Siemens Debugger' necesita que esté apagado, para meter el BootStrap.
Pero en el C35i es posible usar un rodeo:
-encender el móvil
-pulsar el boton de 'Iniciar modo de servicio'
-esperar a que se queje de que el modo BFB no se ha iniciado
-pulsar el boton de 'Salir del modo de servicio'
-pulsar de nuevo el boton de 'Iniciar modo de servicio'
-ahora ya se tiene aceso a toda la funcionalidad del 'Siemens Debugger'.
Bueno, ahora ya puedo manejarme sin problemas con la memoria.
DESARMANDO TRAMPAS
******************
La manera normal de llamar a una subrutina es con JMPS o CALL.
Pero existe una manera mas cómoda de llamar a rutinas que se
usan habitualmente: los traps.
Desde la dirección 0x000000 hasta 0x000200 hay unas rutinas muy
breves que gestionan los errores que pueden suceder en tiempo de ejecución.
Recordar que el segmento 0x100000 es una copia de 0x000000 .
Según el manual, en la posición 0 hay una rutina que se llamará cuando
se produce un RESET por hardware, por software, o Watchdog.
Brevemente, un Watchdog es una rutina invocada por un dispositivo
externo -típicamente el reloj- que tiene que ser respondida por el programa.
Si no es respondida, el dispositivo entiende que el programa se ha colgado, y
hace un reset del procesador.
Sólo hay una rutina que gestiona estos 3 eventos, pero se puede saber cual
de los eventos ha sucedido mirando el valor del registro SFR
llamado 'WDT Control Register' en la dirección FFAE.
Si el bit 1 (llamado WDTR=Watchdog Timer Reset Indication Flag) vale 1, entonces
es Watchdog quien llama.
Si el bit 2 (llamado SWR=Software Reset Indication Flag) vale 1, entonces
es un RESET por Software
Si SWR vale 0, entonces es un RESET por Hardware.
La rutina llamada desde 0x000000 hace simplemente:
jmps 0D4h, 04704h
Desensamblando a partir de 0xD44704h veo que hace
mov CC7, #10h
trap #30h
rets
Esto es: pone a #10h el registro SFR llamado CC1, que está en la posición FE8E
Luego llama al trap número #30h
Un trap funciona igual que una interrupción RESET de las anteriores; se
mira cual es el número de trap en la tabla que empieza en 0x000000 , y
se llama a la rutina.
Para calcularlo, se toma el numero de trap y se multiplica por 4.
El trap #30h llama a la rutina en 0x0000C0, pues 30h*4h=C0h
En 0x0000C0 hay:
jmps 0D4h, loc_D44A28
y en 0xD44A28 hay:
bfldh PSW, #0F0h, #0F0h
extp #3, #4
mov mem_37AA, r0
mov mem_37B6, mem_FE10
mov mem_37B8, DPP0
mov mem_37BA, DPP1
.......
La primera instrucción limpia todos los bits del registro SFR llamado
PSW (posición FF10), que contiene los flags.
El bit 7 de la parte alta de PSW (bit 7+8=15) se pone a 1.
Esto hace que el procesador ejecute este trozo de código en la máxima
prioridad, sin permitir que otras tareas lo interrumpan. Esto es normal
cuando se procesa una interrupción.
luego hace que las siguientes 4 instrucciones usen el segmento #3.
Dichas 4 instrucciones hacen:
guardar r0 en 3:37AA = 0x00F7AA
meter Context Pointer en 3:37B6 = 0x00F7B6
meter el Data Page Pointer DPP0 en 3:37B8 = 0x00F7B8
meter el Data Page Pointer DPP1 en 3:37BA = 0x00F7BA
Y un montón de cosas más que involucran muchos registros y no entiendo lo
que hacen. No me apetece ponerme a desensamblar 400 páginas de ensamblador.
Pero el concepto está claro, no?
En total hay 128 traps, y muchas de ellas saltan a 04704h, por lo que
entiendo que es una rutina de propósito general capaz de interpretar
casi todas las situaciones que se producen.
Sin embargo, hay unas pocas traps que saltan a otras rutinas:
El trap 20h salta a 0xC3FFFA
en 0xC3FFFA hay:
rets
Esta es la manera más simple de retornar de un trap : no hacer nada.
El trap 35h salta a 0xCCFFEE
en CCFFEE hay:
bset mem_FE0C.13
bset STKOV.6
calls 0D3h, loc_D337B0
reti
Y en D337B0 hay:
bclr CC5IC.6
bclr CC14IC.6
bclr CC10IC.6
extr #1
bclr CRIC.6
rets
O sea, que limpia el bit 6 de las variables del periferico CAPCOM1.
Esto no tiene mucho sentido para mí, porque no todas las variables se limpian.
Ni tampoco se preservan los registros usados.
Es posible que este código en realidad no se llame nunca.
Voy a investigar sobre esto.
Los traps es una manera eficiente de llamar a una rutina.
Para invocarlos, se usa la instruccion
trap #35h
que se codifica en los bytes
9B 6A
Buscando en la memoria estos datos, aparecen 2 veces:
Primera vez, en 0xDA48F6. Al desensamblar:
trap #35h
xorb 7F78h, rl0
movb PWMCON1, #4Dh
add r0, r0
add r0, r0
add r0, r0
que no tiene mucho sentido, pues "add r0, r0" no sirve de
mucho, y menos hacerlo 3 veces seguidas.
Segunda vez, en 0xDA6712. Desensamblando 8 bytes antes, en 0xDA670A :
DA6704: 79 53 : orb rh2, #3
DA6706: 96 F1 D2 7C : cmpi2 r1, #7CD2h
DA670A: 41 43 : cmpb rl2, rh1
DA670C: 10 81 : addc r8, r1
DA670E: 38 86 : subc r8, #6
DA6710: 6A 59 9B 6A : band mem_FF36.10, S0RBUF.6
DA6714: 54 60 0F E2 : xor 0E20Fh, PECC0
Veo que en realidad forman parte de la
instrucción "band mem_FF36.10, S0RBUF.6", que tampoco tiene mucho sentido.
Por ello deduzco que la aparición de estos bytes es simple casualidad.
Quizás sea parte de un dibujo, o de datos. No es código válido.
Entonces, parece que nadie llama al trap 35h... hasta ahora
HACIENDO TRAMPAS
****************
Como el C166 es un micro de 16 bits, todas las instrucciones
ocupan 2 o 4 bytes. Incluso la instrucción más simple
NOP
se codifica como CC 00
También es necesario que empiecen en una posición par. En el ejemplo
anterior, si los bytes 9B 6A estuvieran en la dirección 0xDA6711, yo
sabría de inmmediato que no es una intruccion.
El mini-tracer de antes tiene un inconveniente: para llamarlo necesito hacer
CALLS 0xC485AAh
que se codifica como
DA C4 AA 85
y ocupa 4 bytes.
Sería mejor si ocupara solo 2 bytes. Así podría sustituir
cualquier instrucción por una llamada a mi rutina.
Gracias a los traps puedo hacerlo:
-Establezco que el trap #35h salte a 0xC7FBC0
Para ello solo tengo que variar la memoria 35h*4 para que haga
jmps 0xC7FBC0h
Es decir, poner bytes FA C7 C0 FB (little-indian) a partir de 0x0000D4
-En mi víctima, llamar a mi trap con
trap #35h
Es decir, poner bytes 9B 6A
-Imprimir la dirección que me ha llamado, y hacer lo que hubiera
en la instrucción original
Así, una rutina que sea:
98 90 : mov r9, [r0+]
F0 C9 : mov r12, r9
DB 00 : rets
la sustituiré por:
98 90 : mov r9, [r0+]
F0 C9 : mov r12, r9
9B 6A : trap #35h
DB 00 : rets
La rutina de respuesta al trap es:
org 0C7FBC0h
mi_trap:
mov [-r0], r3 ; guardo r3 porque lo voy a sobrescribir
pop r3 ; en la pila está IP , o sea, dónde ha saltado la trampa
push r3 ; lo vuelvo a meter.
calls imprime_r3 ; y lo imprimo
mov r3, [r0+]
reti ; salir de la trampa
end
La rutina de inicialización del trap es:
go_mines:
mov r3, #0C7FAh
extp #40h, #1h
mov 0D4h, r3 ; dirección del trap #35h
mov r3, #0FBC0h
extp #40h, #1h
mov 0D6h, r3
calls imprime_r3
jmpr cc_NZ, go_fin
Para probar mi trampa, sé que cuando pulso la tecla '#', originalmente llama a
D6D580: 9A 08 02 00 : jnb mem_FE10.0, loc_D6D588
Entonces sustituyo esos bytes por
9B6ACC00, que significa
trap #35h
nop
Así que
-compilo el parche
-lo meto en el móvil
-activo el nuevo trap mediante el menú buscaminas->Highscore
-verifico que la memoria 0x0000D4 contiene 'jmps 0x0C7FBC0h'
-pulso '#' en el teclado
-y me muestra la direccion D588, que es el offset de D6D588
Bueno, en realidad me muestra D6D588+2 , pues ésta es la dirección
a donde volveré despues de la trampa.
Tengo que modificar el parche para además del offset tambien imprima el
segmento (0x00D6), pero eso es un detalle menor.
Recordar que una instrucción
trap
mete en la pila los valores:
-PSW: flags (Carry, Overflow, ...)
-CSP: Code Segment Pointer
-IP: Instruction Pointer
Me ayudaría todavía más si imprimiera los últimos 10 valores de
la pila, y las variables r0, r1, r2, ...
Pero eso (más o menos) es lo que hacen los programas 'Siemens Debugger' o
el 'ATDebugger'. Me permiten llamar a una subrutina, y me dicen el
estado de las variables.
Lo bueno es que yo lo he implementado haciendo que el teléfono sea el punto de
partida. Ellos hacen que sea el ordenador el que tenga que invocar al móvil.
Para sacarle todo el partido que yo quiero, empiezo a sustituir un
montón de instrucciones en la Flash.
Por ejemplo, puedo cambiar todas las instrucciones
mov r9, [r0+]
por
trap #35h
Y anadir un ultimo paso en
mi_trap:
......
mov r3, [r0+]
mov r9, [r0+] ; <-nueva instruccion
reti
end
Mejor todavía es sustituir
rets
por
trap #35h
Así, cada rutina, en vez de salir, me llamará a mi_trap, donde puedo
mirar los valores que retorna a la subrutina llamante.
Entonces tengo que hacer que vuelva a su sitio original.
Voy a explicarlo con más detalle.
Suponer que en 0xCC1110 hay:
0xCC1110 : E6 FC 11 00 : mov r12, #11h
0xCC1114 : DA 22 22 22 : calls 022h, loc_222222
0xCC1118 : 46 FC 22 00 : cmp r12, #22h
y en 0x222222 hay:
0x222222 : E6 FC 22 00 : mov r12, #22h
0x222226 : DB 00 : rets
Cuando 0x111110 se ejecuta, primero se pone r12=#11h, y la instrucción calls
hace que en la pila se guarde el valor 0x111118 , pues ésta es la siguiente
instrucción que se ejecutará cuando 0x222222 retorne.
Para activar mi trampa pongo a partir de 0x222222 :
0x222222 : mov r12, #22h ; esto no cambia
0x222226 : trap #35h ; originalmente decia rets
Y cambio mi trap para que no vuelva a la instrucción que sigue al trap, sino
a la siguiente instrucción de la llamada inicial:
mi_trap:
......
mov [-r0], r6
mov [-r0], r5
mov [-r0], r4
mov [-r0], r3
pop r3 ; en la pila esta IP: 0x2226+2. No lo necesito
pop r3 ; en la pila esta CSP: 0x0022. Tampoco lo necesito
pop r6 ; en la pila esta PSW
pop r5 ; en la pila esta el IP de la dirección que ha
; llamado a la rutina que me ha llamado: 0x1114+4
pop r4 ; en la pila esta el CSP de la dirección que ha
; llamado a la rutina que me ha llamado: 0x00CC
push r6 ; mete PSW de nuevo
push r4 ; mete CSP (0x00CC) de nuevo
push r5 ; mete IP (0x1114+4) de nuevo
mov r3, r4
calls imprime_r3
mov r3, r5
calls imprime_r3
mov r3, [r0+]
mov r4, [r0+]
mov r5, [r0+]
mov r6, [r0+]
reti ; esto sacara IP, CSP, PSW
end
Llamo a la rutina 0xCC1110 y efectivamente al retornar desde loc_222222, llama
a mi trampa e imprime 0xCC1118 .
Parece que la trampa funciona bien.
DESMONTANDO TRAMPAS VIEJAS
**************************
Para tracear una función necesito que caiga en alguna de mis trampas.
Como no tengo ni idea de donde comenzará la función, lo mejor es poner
muchas trampas y confiar en que tarde o temprano caiga en alguna.
Pero si pongo una trampa al azar en una instrucción rets cualquiera, tengo
que asegurarme de que la trampa está en buenas condiciones.
En otras palabras: si sustituyo rets por trap #35h , tengo que
estar seguro de que el trap #35h va a saltar a 0xC7FBC0. Esto es, que la
dirección 0x0000D4 contiene 'jmps 0x0C7FBC0h'.
Si miro esta zona de memoria, veo que hay
0000D4: FA CC EE FF : jmps 0CCh, loc_CCFFEE
?Quien ha puesto esos datos? Porque yo también los voy a poner, y
el último que los ponga, gana la carrera!
Busco en la Flash algo que tenga que ver con CCFFEE y veo que los
bytes EE FF (Recordar little-indian) aparecen 25 veces. Los desensamblo
todos en 25 minutos, y veo que uno de ellos hace
D48B02: E6 FC EE FF : mov r12, #0FFEEh
D48B06: E6 FD CC 00 : mov r13, #0CCh
D48B0A: DA D3 7E 38 : calls 0D3h, loc_D3387E
Humm, tambien aparece cerca el dato #0CCh , lo cual es bastante interesante.
Desensamblo loc_D3387E para ver lo que hace:
D3387E: 1E E7 : bclr DP6.1
D33880: 88 C0 : mov [-r0], r12
D33882: 88 D0 : mov [-r0], r13
D33884: E6 FC 35 00 : mov r12, #35h
D33888: 98 E0 : mov r14, [r0+]
D3388A: 98 D0 : mov r13, [r0+]
D3388C: DA D4 BA 93 : calls 0D4h, loc_D493BA
D33890: D1 80 : extr #1
D33892: E6 B5 0F 00 : mov CRIC, #0Fh
D33896: 0A 92 30 30 : bfldl CCM5, #30h, #30h
D3389A: DB 00 : rets
Fantástico: usa r12 y r13, y ademas hace algo con #35h , que es
justamente la trampa que yo intento poner.
Investigando un poco más veo que loc_D493BA pone r1 y r13 en la memoria
apuntada por r12*4. Eso es coherente con mis datos, y loc_D493BA resulta
ser una rutina genérica para estabecer el salto de las trampas.
Así que tengo que deshabilitar esta rutina, o bien cambiar
D48B02: mov r12, #0FFEEh
D48B06: mov r13, #0CCh
por
D48B02: mov r12, #0FBC0h
D48B06: mov r13, #0C7h
pues mi_trap está en org 0C7FBC0h.
Ahora me enfrento con otro problema: ?que pasa si sustituyo
un rets por trap #35h , pero todavía no se ha puesto la trampa?
O sea, ?y si todavía no se ha pasado por D48B02?
Pues que saltará a 0x0000D4, que apuntará a la nada sideral. Cuelgue asegurado.
Para solucionarlo tengo que hacer que mi trampa se ponga realmente pronto.
Lo primero que se me ocurre es que el trap #0h debe ponerse muy pronto, ya
que es la rutina del Watchdog.
Tras un rato de búsqueda encuentro:
D44714: 88 80 : mov [-r0], r8
D44716: E0 08 : mov r8, #0
D44718: F0 C8 : loc_144718:
D44718: F0 C8 : mov r12, r8
D4471A: E6 FD 04 47 : mov r13, #4704h
D4471E: E6 FE D4 00 : mov r14, #0D4h
D44722: DA D4 BA 93 : calls 0D4h, loc_D493BA
que establece la dirección 0xD44704 como destino del trap #0h.
Por supuesto, hace uso de la rutina loc_D493BA para establecer la trampa.
Ahora imagina el flujo del programa como una gran red. Existe un punto de
partida P0 y un destino P9 (la pulsación de la tecla '#'). En algún
punto P4 del camino voy a establecer la trampa. Tengo que evitar que
los puntos P2 (entre P0 y P4) llame a la trampa, pero también tengo
que hacer que algún P6 (entre P4 y P9) la llame.
Empiezo a analizar el código inversamente desde 0xD44714 y después de
unas cuantas horas de estudio he aprendido mucho, pero no he llegado
a ninguna conclusión clara sobre cual es el comienzo.
Lo siguiente que se me ocurre es que una dirección útil podría ser
la primera de la Flash: 0xC00000
Desensamblando:
C00000: mov r12, #32F8h
C00004: mov r13, #3D7h
C00008: calls 0C0h, loc_C06866
C0000C: jmps 0C0h, loc_C090D4
C00010: rets
y
C06866: mov r13, r13
C06868: mov r12, r12
C0686A: mov [-r0], r13
C0686C: mov DPP0, #40h
C06870: mov [-r0], r12
C06872: mov r14, mem_100396
C06876: mov [-r0], r14
................
parece prometedor porque establece todas las variables que usa; no asume que
tengan valores válidos. Además pone DPP0=#40h que es el primer segmento.
No sólo eso, sino que más tarde define los otros segmentos.
Hay otros factores que no me convencen: el primero es que no hace nada con
las interrupciones ni el watchdog. Yo esperaría que una rutina de
inicialización establecería unos criterios rígidos para que nade la interrumpa.
Otro aspecto que me intriga es que use mem_100396 . Allí no hay nada, pues
ninguna rutina ha puesto ningun dato.
La mejor forma de probarlo es modificando
C00000: mov r12, #32F8h
para que llame a imprime_r3 , ponga r12=#32F8h , y retorne.
Lo hago, y veo que lamentablmente no se llama cuando yo espero.
EL PRIMER DIA CREO EL CIELO Y LA TIERRA
***************************************
Una de las primeras cosas que se hace cuando se inicializa el micro es poner
con valores buenos las variables importantes.
Entre ellas se pueden incluir:
DPP0 = Data Page Pointer, almacenado en FE00 si 16 bits, o en 0x00 si 8 bits.
CSP = Code Segment Pointer, en FE08 o 0x04
CP = Context Pointer CP, almacenado en FE10 si 16 bits, o en 0x08 si 8 bits.
SYSCON = System Control Register, en FF12 o 0x89
SP = Stack Pointer Register, en FE12 o 0x09
STKOV = Stack Overflow Register, en FE14 o 0x0A
STKUN = Stack Underflow Register, en FE16 o 0x0B
Se pone la pila SP con la instrucción
mov SP, xxyy
que se codifica como
E6 09 yy xx
Normalmente también se pondrán STKOV y STKUN para definir cuánto puede
crecer la pila.
Hay 2 asignaciones de la pila en 0xD449A0 y 0xD449AE, y otra en 0xC7FC40
Desensamblando cerca de 0xD449A0:
D4498E: B7 48 B7 B7 : srst
D44992: 9A D6 03 F0 : jnb TFR.15, loc_D4499C
D44996: E6 47 00 00 : mov CC7, #0
D4499A: 0D 29 : jmpr cc_UC, loc_D449EE
;------------------------------------------------------------
D4499C: 9A D6 05 E0 : loc_D4499C:
D4499C: 9A D6 05 E0 : jnb TFR.14, loc_D449AA
D449A0: E6 09 00 FC : mov SP, #0FC00h
D449A4: E6 47 01 00 : mov CC7, #1
D449A8: 0D 22 : jmpr cc_UC, loc_D449EE
;------------------------------------------------------------
D449AA: 9A D6 05 D0 : loc_D449AA:
D449AA: 9A D6 05 D0 : jnb TFR.13, loc_D449B8
D449AE: E6 09 00 FC : mov SP, #0FC00h
D449B2: E6 47 02 00 : mov CC7, #2
D449B6: 0D 1B : jmpr cc_UC, loc_D449EE
..................
D449EE: 76 47 00 02 : loc_D449EE:
D449EE: 76 47 00 02 : or CC7, #200h
D449F2: F2 D6 1C FF : mov TFR, ZEROS
D449F6: FA 00 C0 00 : jmps 0, loc_0000C0
Paso a paso:
-srst hace un reset del micro. Esto limpia algunas variables para
que el micro pueda trabajar en un entorno seguro.
-mira si el bit 15 de TFR esta puesto. TFR=Trap Flag Register.
Es decir, si está procesando una trampa provocada
por una Interruption No Enmascarable (NMI) externa
-si es así, hace CC7=0 y la procesa en loc_D449EE
-mira si está activado el bit 14.
Es decir, si está procesando un Stack Overflow
-si es así, pone la pila SP=FC00, hace CC7=1 y la procesa en loc_D449EE
-mira si está activado el bit 13.
Es decir, si está procesando un Stack Underflow
-si es así, pone la pila SP=FC00, hace CC7=2 y la procesa en loc_D449EE
-en loc_D449EE limpia los flags de trampas TFR y salta a 0000C0
Recordar que en 0000C0 está la tabla de saltos, así que
es equivalente a trap #30 pues 0x30*4=0xC0
Lo importante es que la rutina 0xD449A0 parece un lugar bueno para llamar
a la rutina que coloca la trampa.
La otra asignación de la pila se produce en 0xC7FC40
C7FC1A: A5 5A A5 A5 : diswdt
C7FC1E: E6 03 03 00 : mov DPP3, #3
C7FC22: E6 02 42 00 : mov DPP2, #42h
C7FC26: E6 01 41 00 : mov DPP1, #41h
C7FC2A: E6 00 00 00 : mov DPP0, #0
C7FC2E: E6 08 00 FC : mov mem_FE10, #0FC00h
C7FC32: CC 00 : nop
C7FC34: E6 89 46 14 : mov SYSCON, #1446h
C7FC38: E6 0B 00 FC : mov STKUN, #0FC00h
C7FC3C: E6 0A 0C FA : mov STKOV, #0FA0Ch
C7FC40: E6 09 00 FC : mov SP, #0FC00h
C7FC44: CC 00 : nop
C7FC46: DA 87 C2 FC : calls 87h, loc_87FCC2
?Que más se puede pedir?
-Deshabilita el watchdog para que nadie le interrumpa.
-Pone todos los DPP a valores de segmentos conocidos.
-Establece SYSCON a un valor que permite control total de la memoria.
-Asigna buenos valores para la pila y sus correspondientes límites.
Sin duda esta es una rutina que se llama bastante pronto.
Cualquier cosa que haga aqui se va a ejecutar muy temprano y con todo
el control a mi merced.
Pero también es una gran responsabilidad. Cualquier fallo en esta rutina
y el móvil no sera' capaz de inicializarse. Esto incluye que es posible
que no se pueda re-cargar la Flash para deshacer el error. Yo aviso.
Perfecto; ahora ya puedo sustituir todos los rets que quiera porque
sé que la trampa está puesta.
DONDE MONTAR TRAMPAS
********************
Buscando en la flash veo que rets aparece unas 70.000 veces.
Si sustituyo todos por trap #35h corro el riesgo de que el sistema vaya
muy lento debido a todos los datos que tiene que sacar.
Además, no todas las veces que sale la cadena DB 00 significa que es rets .
Es posible que haya una instrucción del tipo
mov r14, #000DBh
que se codifica como
E6 FE DB 00
pero en realidad no es un rets . Esta situación no la puedo evitar a
no ser que analice/desensamble el código.
Otro caso es que aparezca en una posición impar. Ya que el C166 es
de 16 bits ls instrucciones sólo pueden aparecer en posiciones pares.
Hago un pequeño programa (llamado di_calls ) que me extraiga de
la flash la lista de subrutinas que son llamadas, y quien las llama:
#include <stdio.h>
#include <string.h>
main(int argc, char *argv[])
{
FILE *ap, *ap2;
unsigned char c1=0, c0, c2, c3, c;
int i, j;
long posi0=-1, posi10;
long llamados[20000];
int frec[20000], total=0, encontrado;
ap=fopen(argv[1],"rb");
if(ap==NULL)
{
printf("No encuentro archivo flash %s \n", argv[1]);
exit(1);
}
ap2=fopen(argv[1],"rb");
while(!feof(ap))
{
c0=getc(ap); posi0++;
c1=getc(ap); posi0++;
if(c0==0xDA || c0==0xFA) /* calls XXXXXX o jumps XXXXXX */
{
c2=getc(ap);
posi0++;
c3=getc(ap);
posi0++;
posi10=c1*65536+c3*256+c2;
if(c2%2==1) /* debe ser posicion par */
continue;
if(c1<0xC0) /* debe llamar a una rutina de la flash */
continue;
/* la direccion destino no puede ser 0000 o FFFF */
fseek(ap2, posi10-0xC00000, SEEK_SET);
c0=getc(ap2);
c1=getc(ap2);
if(c0==0 && c1==0)
continue;
if(c0==0xFF && c1==0xFF)
continue;
/* miro si ha sido llamada anteriormente */
encontrado=-1;
for(i=0;i<total;i++)
if(llamados[i]==posi10)
{ /* si: indica que ha sido llamado una vez mas */
frec[i]++;
encontrado=i;
}
if(encontrado<0)
{ /* no: indica que ha sido llamado por primera vez */
llamados[total]=posi10;
frec[total]=1;
total++;
}
/* imprime llamador y llamado */
printf("+%0.6X>%0.6X\n", 0xC00000+posi0-3, posi10 );
}
}
/* imprime aquellas subrutinas llamadas mas de 300 veces */
encontrado=300;
printf("*** %i - %i\n", encontrado, total );
for(i=0;i<total;i++)
if(frec[i]>encontrado)
printf("+%0.6X * %i\n", llamados[i], frec[i] );
return 1;
}
Lo invoco con
di_calls.exe C35_18_From_00.bin >di_calls.txt
que genera un fichero di_calls.txt con 63.000 llamadas, y veo que
hay 10 rutinas que se llaman mas de 300 veces !
Debo evitar poner trampas en esas rutinas, y las subrutinas llamadas por ellas.
Por ejemplo: 0xD31140 es llamado 1304 veces, y se desensambla:
D31140: mov [-r0], r15
D31142: mov [-r0], r14
..........................
D3115C: calls 0D3h, loc_D309A2
...........................
D311F2: rets
Para no sobrecargar el debug , NO tengo que sustituir
D311F2: rets
por
D311F2: trap #35h
Y en la rutina en loc_D309A2 tampoco debo sustituir su rets por trap #35h.
Igualmente en las rutinas llamadas desde loc_D309A2 tampoco sustituyo.
Lo que me doy cuenta es que estoy otra vez en el problema de la red: unas
rutinas llaman a otras y al final me pierdo.
Lo primero que tengo que hacer es crear un flag para que la trampa
atrape a sus victimas o las libere.
Decido que la rutina mi_trap sólo llamara a imprime_r3 cuando
la memoria 0x100800 valga 1.
Tendré que ampliar mi_trap :
.............
push r5 ; mete IP (0x1114+4) de nuevo
extp #40h, #1h
mov r3, 0800h ; lee la memoria
cmp r3, #1 ; mira el flag
jmpr cc_NZ, go_fin_trap; no está activado
mov r3, r4
calls imprime_r3
mov r3, r5
calls imprime_r3
go_fin_trap:
mov r3, [r0+]
.............
reti ; esto sacara IP, CSP, PSW
Así al menos no perderé tanto tiempo mandando los datos al puerto.
Para escribir en la memoria 0x100800 lo puedo hacer desde el 'Siemens Debugger'
o habilitar el menú Juegos->Buscaminas->Highscore para que lo ponga.
La primera vez lo pruebo con el parche anterior del número de
emergencia 112, que sé que en algun momento pasa por
021AC6: DB 00 : rets
el cual sustituyo por
021AC6: 9B 6A : trap #35h
Meto el parche, activo el flag en 0x100800 , bloqueo el teclado, y cuando
intento escribir el numero '1' , aparece en el puerto serie el dato 021AC8.
Perfecto. Ahora me lanzo a la aventura y sustituyo 200 rets al azar.
Parcheo la Flash, activo el flag, y el puerto serie empieza a volcar las
direcciones de memoria por las que voy pasando.
Mando y recibo SMS, hago llamadas, navego por los menús, y obtengo
un montón de direcciones interesantes.
Quizás demasiadas. Algunas rutinas se llaman demasiadas veces, y decido
restaurarlas por el rets original.
Como mejora podría crear una lista de rutinas que sí deseo imprimir, y
otras que no. La rutina de mi_trap tendrá que ver si la rutina interceptada
est'a en una lista u otra, y actuar en consecuencia.
NOTAS FINALES
*************
Esto es sólo un acercamiento al Sistema Operativo de los teléfonos Siemens
y su microprocesador interno.
En otro artículo seguiré investigando y desarrollaré más parches.
*EOF*