[ASM] GBA | Divertirse con ASM: Lección 1!

Avisos


Like Tree30Gracias
Respuesta
 
Herramientas Desplegado
  #11  
26/08/2018
Predeterminado Re: GBA| ASM | Divertirse con ASM: Lección 1!
Wow, que buen tuto mas tutoriales tan orientativos como este realmente ayudan.
  #12  
Hace 4 Semanas
Predeterminado Respuesta: GBA| ASM | Divertirse con ASM: Lección 1!
He restaurado el tema en su totalidad, no fue nada fácil debido a que la wayback machine no me deja copiar los bbcodes pero con paciencia logré poner cada cosa como debe estar, espero les sirva.

Dejo el tema en un spoiler, por si debemos restaurarlo nuevamente:

Spoiler

Tenga en cuenta que este tutorial fue escrito originalmente por HackMew. Solamente lo he mejorado y modernizado ligeramente. Tabien si se ve raro este thred es porque no esta completamente formatado para un thred. Si usted no ve las imagenes es una restricion de el host que uso. El post original esta aqui: http://gamer2020.ga/?p=98&lang=es

Requisitos

No$gba, No$gba debug, ensamblador de ASM, editor de hexadecimal, y algun ROM.

Introducción

¿Cuántas veces has escuchado la palabra “ASM”? No importa cuántos, ¿qué significa “ASM“?

ASM significa asamblea, que es un lenguaje de programación de bajo nivel. En términos generales, los lenguajes de programación pueden ser divididos en 4 categorías principales. Al nivel más bajo tenemos el código máquina: números en bruto (binarios) que la CPU decodifica en instrucciones a ejecutar. En el siguiente nivel tenemos asamblea. Cada instrucción de asamblea corresponde a una instrucción de código de máquina. De hecho hay una relación 1: 1 entre el código de la máquina y de asamblea. Los seres humanos no están hechos para programar en código de máquina, donde todo lo que tienes es una larga serie de números binarios. Es por eso que se ha creado de programación en ensamblador: para que los programadores interactúan con la CPU en el nivel más bajo aún usando un código de fácil comprensión. Un paso hacia arriba se compilan lenguajes como C, que utilizan un elemento de lenguaje estructurado para ser más como Inglés, pero necesita ser compilado a código de máquina con el fin de ser capaz de correr. Por último, hay secuencias de comandos lenguajes como VB o Java que se ejecutan a través de intérpretes diseñados para ejecutar los tipos adecuados de código de máquina para conseguir los efectos deseados.

Cuando se trata de ASM, básicamente estamos programando. Por tanto, necesitamos saber cómo funciona realmente un procesador y escribir código que puede comprender. Al ser un programador ya estará seguro que ayuda aquí, porque los principales conceptos son los mismos.

ARM y THUMB

Hablamos de ASM en general, pero de ahora en adelante nos referiremos a el GBA. El GBA tiene un procesador personalizado creado por Nintendo, llamado ARM7TDMI. La CPU tiene 2 conjuntos de instrucciones: ARM y THUMB. Para nuestros propósitos, vamos a utilizar THUMB 99.9% de el veces. THUMB ocupa menos espacio en el ROM (la mitad del tamaño en comparación con ARM) y se ejecuta mucho más rápido si se encuentran en el ROM. Hay algunas desventajas, de hecho, pero nada hay que preocuparse por ahora.

Empezando

Como he escrito en los requisitos, se requiere un ensamblador de ASM. Como la mayoría de los ensambladores, si no todos ellos, son el mando de línea basados en tendrá que utilizar una línea de comandos. Si usted no tiene idea de lo que es la línea de comandos, es mejor que parar aquí por ahora y obtenga más información al respecto antes de continuar.

Una vez que esté familiarizado con la línea de comandos, puede descargar el archivo thumb.zip, que contiene el ensamblador que vamos a utilizar. Extraer todos a los 3 ficheros dentro de una carpeta (por ejemplo, en el Escritorio) y abra una ventana de indicador de mandatos. Aparecerá algo similar:

Código:
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All right reserved.
 C:\Users\YourName>
Ahora vaya al directorio donde el archivo se extrajeron anteriormente:

Código:
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All right reserved.
 C:\Users\YourName>cd Desktop
C:\Users\YourName\Desktop>
Ahora que está en la carpeta correcta, ya está listo para ensamblar su primera (THUMB) rutina de de ASM. ¿Qué exactamente? Buena pregunta. Como es la primera lección he preparado para usted una rutina simple. Aquí está el archivo: lesson1.zip. Incluye 3 versiones diferentes, dependiendo del juego que está utilizando. Descargarlo y pon el fichero asm justo en el mismo directorio en que el ensamblador está. A continuación, cambie el nombre a lesson1.asm. Para ensamblarlo se hace lo siguiente:

Código:
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All right reserved.
 C:\Users\YourName>cd Desktop
C:\Users\YourName\Desktop>thumb lesson1.asm lesson1.bin
Assembled successfully.
Si todo va bien, obtendrá el mensaje “Assembled successfully” como se muestra arriba. En caso de que no se especifica el “lesson1.bin”, el archivo resultante sería el mismo nombre que el origen asm, excepto con una extensión .bin. Ahora abre el archivo lesson1.bin a través de un editor de hexadecimal, copia su contenido y pegarlo en un área de espacio libre en el interior de su ROM, presta atención a sobrescribir el espacio libre existente, y no insertar nuevos bytes. En este último caso, de hecho, todo se desplaza hacia abajo, y el tamaño del ROM aumentó. Sin duda no es lo que queremos. También, Asegúrese de que los extremos offset con 0, 4, 8 o C. Por ejemplo, 0x800000 es un desplazamiento mientras 0x91003D no es bueno. Eso es lo que se necesita para que el juego se ejecute la rutina correctamente. Anote el desplazamiento (o simplemente memorizarla, si se siente hacerlo), ya que la necesitará pronto.

Probándolo

La rutina de ASM se ha insertado correctamente en el ROM. Ahora es el momento de probarlo. Prepare un script como el de abajo, donde su offset debe ser reemplazado por el offset que anotó antes + 1. Ejemplo: 0x871034 + 1 = 0x871035. ¿Por qué? Lo necesita el ROM para correr correctamente nuestra rutina utilizando el conjunto de instrucciones THUMB. Si no se agrega 1, la rutina será tratado como ARM, y por lo tanto no va a funcionar. Vamos a suponer que usted está utilizando XSE, así que si estás usando un editor de scripts diferente le tendrá que pagar la atención acerca de la sintaxis necesaria. El script en sí es bastante simple y ya que este no es un tutorial de scripting, no voy a explicarlo más.

Código:
#dynamic 0x800000
#org @start
callasm YourOffset
buffernumber 0x0 LASTRESULT
msgbox  @secret  0x2
end
#org  @secret 
= Sshh! I'll tell you a secret[.]\nYour Secret ID is [buffer1]!
Compila el script y asignarlo a una persona. A continuación, cargue el emulador y hablar con la persona.

Si usted hizo todo correctamente obtendrá algo como esto:


Nuestro script en acción.


Como Trabaja

Hasta ahora hemos solamente insertamos una rutina y la llamamos desde un script. No tenemos idea de lo que sucede detrás de las escenas, todavia. Lo que sí sabemos una cosa: la rutina se utiliza para recuperar el ID secreto. Bueno, no es verdaderamente secreta más, ¿eh? El ID secreto se guarda en la memoria RAM, junto con otra información sobre el jugador. La estructura es la siguiente:

Código:
[Nombre (8 bytes)] [Sexo (1 byte)] [??? (1 byte)] [ID Trainer (2 bytes)] [ID Secreto (2 bytes)] [Horas de juego (2 bytes)] [Minutos (1 byte)] [segundos (1 byte)] [Cuadros (1 byte)] [??? (1 byte)] [Opciones (2 bytes)]
Veamos el asm en detalle ahora. Recuerda que es un fichero de texto plano por lo que cualquier editor de texto será capaz de leerlo bien. Como las 3 rutinas son en su mayoría lo mismo, voy a explicar la versión de FireRed primero a continuación, las diferencias entre las distintas versiones.

En la primera línea podemos ver:

Código:
.text
Esto es un directivo que le dirá al ensamblador para ensamblar las siguientes líneas en la sección de código .text del fichero objeto temporal que se crea.

Código:
.align 2
Esto es un directivo que alineará el siguiente código por 2 bytes. Ya que es una rutina THUMB debe ser 2.

Código:
.thumb
Esto es un directivo para informar al ensamblador que las siguientes líneas son parte del conjunto de instrucciones THUMB.

Código:
.thumb_func
Este directivo indicará que estamos usando THUMB. Se necesitan Ambos .thumb y .thumb_func.

Código:
.global lesson1
Opcional. Esto puede ser lo que decida llamar a la rutina.

Código:
main:
Esto es un sello llamado “main”. Es un buen comportamiento para llamar a la main como tal.

Código:
push {r0-r1, lr}
Aquí el verdadero código THUMB comienza. Esta instrucción empujará registros de r0 a r1, junto con el registro de enlace en la pila. “¿Qué diablos son los registros? Stack ??”

Los registros son zonas de memoria especiales que son 32 bits de ancho, por tanto, pueden aguantar los números de hasta 4 bytes. Se puede acceder simplemente llamando a su nombre. Hay un total de 16 registros, desde r0 a r15. Un poco más en detalle:
  • R0-R12: Estos 13 registros son los llamados registros de propósito general, lo que significa que pueden ser utilizados por cualquier razón que puedas tener. Sin embargo, en el modo de THUMB, R0 – R7 (Low Registros) se pueden utilizar siempre mientras que R8 – R12 (registros altos) pueden ser utilizadas únicamente por algunas instrucciones.
  • R13: En el modo de ARM el usuario puede optar por utilizar r13 o otro registro como un puntero de pila, en el modo THUMB este registro siempre se utiliza como puntero de pila.
  • R14: Esto se utiliza como enlace de registro. Al llamar a un sub-rutina por una rama con una instrucción de enlace, la dirección de retorno se guarda en este registro. Guardando la dirección de retorno es mucho más rápido que empujarla a la memoria, sin embargo, sólo hay un registro LR para cada modo así que el usuario debe empujar manualmente su contenido antes de emitir subrutinas “anidados”.
  • R15: Esto se utiliza como contador de programa, cuando se lee de r15 devolverá un valor de PC + n debido a la prelectura (pipelining), mientras que “n” depende de la instrucción y en el estado de la CPU (THUMB o ARM).
  • Pila: además de registros, hay otra área de memoria especial llamada “Pila”. Se utiliza para guardar el valor de los registros en ella por lo que se puede modificar de forma segura. Cuando se guarda algo en la pantalla, eso se llama “pushing”. Cuando haya terminado, harás lo contrario. Es decir, “popping”. Cuando haces un pop a un registro, su valor será restaurado a su estado anterior.
Dicho por Wikipedia
Una metáfora frecuentemente utilizada es la idea de una pila de platos en una pila cafetería resorte. En tal pila, sólo el plato superior es visible y accesible para el usuario, todos los demás platos permanecen ocultos. A medida que se añaden nuevos platos, cada nuevo plato se convierte en la parte superior de la pila, que oculta por debajo de cada plato, empujando la pila de platos hacia abajo. A medida que el plato superior se elimina de la pila, ellos se pueden utilizar, las platos suben otra vez, y el segundo plato se convierte en la parte superior de la pila. Dos principios importantes son ilustrados por esta metáfora: lo último en entrar, primero en salir principio es uno; la segunda es que el contenido de la pila están ocultos. Sólo el plato superior es visible, porque para ver lo que está en el tercer plato, el primer y segundo plato tendrán que ser eliminado.
Para resumir, cuando utilizamos push {r0-r1, lr}, estamos almacenando – o mejor, pushing – registros de r0 a R1 y el registro de enlace en la pila. Así que, siguiendo la metáfora anterior, r0, r1 y LR se convertiría en el plato superior.

Código:
ldr r0, .PLAYER_DATA
Esta instrucción THUMB cargará el valor de nuestro símbolo personalizado llamado .PLAYER_DATA en el registro r0.

Código:
ldr r0, [r0]
Esta instrucción THUMB se cargará en r0 el valor indicado por el valor real de R0. Tal vez, lo has adivinado: .PLAYER_DATA es una dirección de memoria que contiene un puntero a los datos de el jugador. Primero cargamos la dirección en r0, a continuación, cargamos en el mismo registro el valor ubicado en la dirección almacenada en el propio registro.

Código:
ldr r1, .VAR
Esta instrucción THUMB cargar en r1 el valor de el .VAR símbolo, que es la dirección de memoria de el variable 0x800D, LASTRESULT.

Código:
ldrh r0, [r0, #0xC]
Ahora mismo en r0 tenemos la dirección de memoria de los datos de el jugador. Si usted empieza a contar desde el primer byte del nombre hasta que el ID secreto, usted va a terminar con 12 bytes (0xc). Así que esta instrucción THUMB cargar una media palabra almacenada en la dirección r0 + 0xC. No es sorprendente, eso es exactamente donde se almacena el ID secreto. ¿Por qué media palabra? Y, lo más importante: ¿Que son medias palabras? A excepción de “byte” que siempre es de 8 bits, no hay una convención estricto sobre sus múltiplos. Cuando se habla de de ASM, de todos modos, se define un valor de palabra de 32 bits. Por lo tanto, una media palabra (como su nombre indica) es de 16 bits. Y el ID secreto toma 2 bytes o 16 bits.

Código:
strh r0, [r1]
Esta instrucción THUMB guarda el valor en poder de r0 (que es nuestro secreto ID) en la dirección apuntada por r1, que es LASTRESULT. Tenga en cuenta que estamos utilizando el sufijo “h” una vez más. De hecho estamos guardando una media palabra desde que las variables son de 16 bits de ancho (de 0x0 a 0xFFFF).

Código:
pop {r0-r1, pc}
Esta instrucción THUMB revertirá el efecto de push de nuestra anterior. Recuerde que cuando se push una variable y cambia su valor, siempre se debe pop de nuevo.

Código:
.align 2
Directiva de ensamblador. Nada nuevo en realidad. Pero que no se olvide.

Código:
.PLAYER_DATA:
Esto es un marcador usado para definir el símbolo .PLAYER_DATA utilizado por la rutina.

Código:
.word 0x0300500C
Asigna la palabra (32 bits) 0x300500C a .PLAYER_DATA.

Código:
.VAR:
Esto es un marcador usado para definir el símbolo .VAR utilizado por la rutina.

Código:
.word 0x020270B6 + (0x800D * 2)
Asigna la palabra (32 bits) 0x020270B6 + (0x800D * 2) = 0x020370D0 a .VAR. Si se pregunta sobre el formato “raro”, hice eso para hacerlo que sea más fácil cambiar la variable utilizada. Obsérvese, sin embargo esto funcionaría sólo para variables temporales, 0x800D en adelante. Para los variables temporales anteriores, sólo aumentará la dirección principal por 2 (en el ejemplo anterior, se cambia a .word 0x020270B8 + (0x8000 * 2), si se va a utilizar la variable 0x8000).

Por el bien de la precisión, te voy a explicar cómo la memoria es utilizada por el GBA.
  • ROM del sistema / BIOS: comienza en 0x0000000 con una longitud de 16KBs, esta sección contiene la memoria del BIOS que es estrictamente sólo lectura.
  • RAM de Trabajo Externo / EWRAM: comienza a 0x2000000 y tiene una longitud de 256 KB. Ya que contiene un bus de datos de 16 bits, el THUMB se utiliza mejor aquí. Permite 8, 16 y 32 bits de lectura / escritura.
  • RAM de trabajo interno / IWRAM: comienza en 0x3000000 y tiene una longitud de 32 KB con un bus de datos de 32 bits por lo que resulta rápido para el código de ARM. Permite 8, 16 y 32 bits de lectura / escritura.
  • Registro de memoria I/O: comienza en 0x4000000 yendo hasta 1 KB. Esto es donde el control de gráficos, sonido, tiempo, presión de una tecla, etc. Además del nombre, no tiene absolutamente nada que ver con los registros reales: R0-R15.
  • Paleta de memoria: se inicia en 0x5000000 yendo hasta 1 KB. Esta área contiene 2 gamas de colores: amarillo y sprites, respectivamente.
  • Memoria de vídeo / VRAM: comienza en 0x6000000 Aquí se almacenan los datos gráficos (baldosas, sprites, tilemaps). Sprites normalmente se almacenan empenzando de 0x6010000.
  • Memoria de atributo de objeto / OAM: comienza en 0x7000000 con una longitud de 1 KB. Esto es donde el control de los sprites como anchura, la altura, o la ubicación de datos de Sprite gráficos.
  • el ROM: comienza en 0x8000000, yendo a un máximo de 32 MB. THUMB es la mejor opción aquí.

Si se compara la rutina Rojo Fuego y Esmeralda verá que son más o menos lo mismo, excepto los valores .PLAYER_DATA y .VAR. Como Rojo Fuego y Esmeralda son juegos diferentes, eso es predecible si no es obvio. A excepción de los diferentes valores el de Rubí parece que falta algo sin embargo. En particular, el r0 LDR, [R0] línea. ¿Cual es la razón?

La normalidad del hacking de RAM, se basa principalmente en el hecho – si te das cuenta o no – que los datos que está buscando es estáticos (permanecen en el mismo lugar), al menos durante la duración de la búsqueda. Para luchar contra los hackers, algunos datos en FR / LG y Esmeralda se guarda en lugares dinámicos – es decir, que se está moviendo constantemente, por ejemplo, cada vez que se abre un menú o sale de un mapa. El método se llama DMA o asignación de memoria dinámica. Este esquema es en realidad débil sin embargo: en algún lugar de la memoria RAM, tiene que haber un valor que indica al juego en el que se guarda actualmente los datos en cuestión: un puntero. Ruby tiene ningún tipo de protecciones por eso pueden cargar la dirección de memoria directamente en el registro. Cuando se trata de FR / LG y / o esmeralda en cambio, hay que cargar primero la dirección que contiene el puntero a los datos reales, y luego cargar el propio puntero.

Vamos debug

La rutina que utilizamos fue (relativamente) simple, pero tan pronto como las rutinas crecen y se vuelven más y más complicado, no se puede confiar sólo en el simple uso de un callasm para ver si funcionan o no. Especialmente en el último caso. Entonces es cuando un debugger viene muy bien. Así que si no lo ha descargado aún, este es el momento adecuado para hacerlo: no$gba.



Cargar su el ROM (File – Cartridge Menu). A continuación, haga clic en la zona con el código ensamblador. Haga clic en “Goto …”, introduzca el offset de la rutina, y haga clic en OK. No se olvide de añadir 0x08000000 a su desplazamiento, ya que se encuentra en la memoria del ROM. Puede hacer doble clic en una línea de código para establecer un punto de interrupción. Pon un punto de interrupción en el offset de la rutina.



Juega el juego y habla con la persona que le dio el script. Si define el punto de interrupción correctamente el juego se detendrá y te llevará de nuevo a la ventana de código.



Si miras a la derecha de la ventana de código usted notará que se muestra el contenido de los registros. Puede hacer clic en el botón llamado “Ejecutar Siguiente” para ejecutar la siguiente línea de código. Usando esto puede seguir rutinas para asegurarse de que funcionan correctamente. Antes de pulsar el botón, siempre es mejor hacer suposiciones sobre el posible contenido de los registros después de cada instrucción. De esta manera usted será capaz de comparar los resultados que está obteniendo con los resultados esperados, y por lo tanto se puede ver si la rutina está funcionando correctamente o no.

Descargas

Buenas referencias

  • Wikipedia
  • Whirlwind Tour of ARM Assembly
  • Official ARM7DTMI Technical Manual
  • GBATek
  • THUMB Quick Reference
  • Assembler Quick Reference
  • Assembler Manual
Conclusión

Espero que esto ayudará a aquellos que son nuevos en ASM. La siguiente lección será una reescritura de la lessson 2 de HackMew y será un poco más avanzada. Por favor, siéntase libre de comentar con cualquier pregunta que tengas!


Saludos!!!
Gracias: Andrea, Turambar, Helio y 1 más.
Respuesta

Herramientas
Desplegado

Permisos para publicar mensajes
No puedes crear nuevos temas
No puedes responder mensajes
No puedes subir archivos adjuntos
No puedes editar tus mensajes

Los BB code están Activado
Los Emoticones están Activado
El código [IMG] está Activado
El Código HTML está Desactivado
Trackbacks are Activado
Pingbacks are Activado
Refbacks are Desactivado



Extra
Estilo clásico
La franja horaria es GMT +1. Ahora son las 13:51.