Introducción
El objetivo de esta entrada es recoger a modo de borrador algunas notas
que he ido tomando durante mi investigación con los opcodes de Dalvik,
explicando algunas nociones básicas sobre el funcionamiento de los
mismos.
¿Qué es Dalvik?
Extraído de la Wikipedia:
"Dalvik is the process virtual machine (VM) in Google’s Android
operating system. It is the software that runs the apps on Android
devices. Dalvik is thus an integral part of Android, which is typically
used on mobile devices such as mobile phones and tablet computers as
well as more recently on embedded devices such as smart TVs and media
streamers.
Programs are commonly written in Java and compiled to bytecode.
They are then converted from Java Virtual Machine-compatible .class
files to Dalvik-compatible .dex (Dalvik Executable) files before
installation on a device. The compact Dalvik Executable format is
designed to be suitable for systems that are constrained in terms of
memory and processor speed."
Para información más detallada sobre cómo está diseñada la arquitectura y
cuál es su funcionamiento interno, puedes leer el siguiente artículo
[1].
Dalvik Bytecode
Instrucciones utilizadas para interactuar con la máquina virtual Dalvik. [2] [3] [4]
Formato de las instrucciones
Cada opcode de Dalvik está asociado a un formato de instrucción, encargado de moldear el comportamiento del mismo.
Dicho formato está compuesto por varias palabras (16 bits) separados
por un espacio, donde cada carácter representa cuatro bits, leídos desde
el bit más significativo al menos, utilizando el carácter “|”
intercalado para facilitar su lectura.
De esta forma, encontramos:
- Mayúsculas secuencialmente ordenadas identifican los campos dentro del formato.
- El término “op” es utilizado para indicar la posición de un opcode de 8 bits
- El símbolo “Ø” determinad que los bits restantes han de ser cero.
Pongamos un ejemplo
Para el formato "B|A|op CCCC":
- Está compuesta por dos palabras de 16 bits cada una, siendo la primera "B|A|op" y la segunda "CCCC"
- La primera palabra está compuesta por el opcode posicionado en
los 8 bits menos significativos y un par de valores de cuatro bits
localizados en los 8 bits más significativos.
- La segunda palabra consiste en un único valor de 16 bits.
Un ejemplo es la instrucción "
if-eq vA, vB, +CCCC" donde "
vA" representa el primer registro a comprobar (4 bits), "
vB" representa el segundo registro (4 bits), y "
+CCCC"
representa el desplazamiento a realizar (16 bits). Siendo su cometido
el desplazarse a otra zona de la memoria, dada por el posicionamiento
actual más el valor del offset, si el contenido de los dos registros son
iguales.
Para el formato "
ØØ|op"
- Tenemos una única palabra de 16 bits.
- Los 8 bits menos significativos están establecidos a 0.
- Los restantes 8 bits, están reservados para el opcode
Asociado a instrucciones como "nop" o "return-void".
Para el formato "ØØ|op AAAA BBBB"
- Está compuesto por tres palabras (16 bits/palabra), siendo la primera "ØØ|op", la segunda "AAAA", y la tercera "BBBB".
- La primera palabra los 8 bits menos significativo están
establecidos a 0, mientras que los más significativos almacenan el
opcode
- La segunda palabra almacena un único valor de 16 bits.
- Otro valor único de 16 bits es almacenado en la tercera y última.
Un ejemplo de instrucción puede ser "
move/16 vAAAA, vBBBB" donde se desplaza el contenido del registro "
vBBBB" a "
vAAAA".
A su vez, cada formato está asociado a un "short ID", compuesto
normalmente por tres caracteres (salvo casos excepcionales), dos digitos
y una letra. Donde el primer dígito simboliza el número de palabras que
integran el formato, el segundo determina el número de registros que
hay, o viene expresado por el carácter "r" que expresa el uso de un
rango de registros dentro del formato , y por último el tercer carácter
representado por una letra, es utilizado para inidicar cualquier tipo de
dato extra codificado en el propio formato.
De estos podemos distinguir la siguiente tabla:

Utilicemos unos ejemplos, el short ID "
12x" establece
que se está utilizando una palabra de 16 bits, junto a dos registros de
4 bits cada uno y que no se está codificando ningún tipo de datos
adicional "(
x)", siendo por tanto
"B|A|op" el formato especificado.
Para el short ID "
22s", podemos discernir que se
están utilizando dos palabras de 16 bits, dos registros, y como dato
adicional, que se incluye un ‘signed short’ de 16 bits. En este caso
estamos hablando de "
B|A|op CCCC".
Por último, con la intención de orientar todo esto a una sintáxis más
legible, se propone utilizar para cada instrucción una construcción
mucho más sencilla. Donde se comienza utilizando el nombre de cada
opcode, y una lista de parámetros aceptados por el mismo, utilizando el
carácter "," como separador, teniendo en consideración los siguientes
aspectos:
- Todo argumento etiquetado en el formato, recibirá la misma etiqueta en la sintáxis. Para "AA|op BB|CC" su homólogo será "op vAA, vBB, vCC" (23x)
- Aquellos registros que tengan la forma "vX", utilizando el prefijo "v" intencionadamente en lugar de "r", es para evitar conflictos con arquitecturas (no virtuales) de Dalvik que sí lo utilicen.
- Los argumentos que indican un valor inmediato presentan la forma "#+X".
- Aquellos que indican un desplazamiento relativo se muestran con "+X".
- Los que muestran un "literal constant pool index", lo hacen utilizando "kind@X" donde "kind" señaliza qué "constant pool" es indicada, existiendo: "string" (string pool index) | "type" (type pool index) | "field" (field pool index | "meth" (meth pool index).
- También es posible indicar "prelinked offsets" existiendo de dos tipos: "vtaboff" (vtable offsets). | "fieldoff" (field offsets).
Por último, en aquellos formatos cuyo "short ID" es
"35c",
"35ms” y “
35mi“
Poniendo como ejemplo, la sintáxis “op vAA, #+BBBB” cuyo ‘short ID’ es “21s” y su formato “AA|op BBBB“, a su vez también acepta otras construcciones como “op vAA, vBBBB” para el ‘short ID’ “22x” o “op vAA, +BBBB” para 21t.
Todas ellas tienen el mismo formato inicial (AA|op BBBB) a pesar de que su representación es distinta:
“
op vAA, vBBBB” en este caso vBBBB hace referencia a un registro.
“
op vAA, +BBBB” aquí BBBB representa el offset añadido a la posición base.
"
op vAA, #+BBBB” es interpretado como un valor inmediato.
REFERENCIAS
Fuente: http://blog.seguesec.com
[1] The Dalvik Virtual Machine Architecture [Eng]
[2] Dalvik Opcodes.
[3] Dalvik VM Instruction Formats
[4] Bytecode for the Dalvik VM
Autor: Sebastián Guerrero
Departamento de Auditoría