τTau SolutionsHerramientas

Desensambladores smali

panorama · Revisado el 12 de agosto de 2026

1. Qué es y por qué existe

smali es una sintaxis de texto para el bytecode Dalvik, y también el nombre del ensamblador que la convierte en DEX. Su inverso, el desensamblador, se llama baksmali. Entre los dos forman el único camino de ida y vuelta que existe para modificar el código de una aplicación de la que no se tiene el fuente.

La diferencia con el documento anterior es de naturaleza, no de grado. Decompilar a Java es reconstruir: se adivina qué texto Java habría producido esas instrucciones, y la respuesta puede ser buena, mala o silenciosamente falsa. Desensamblar a smali es transcribir: cada instrucción del DEX se escribe como una línea de texto, con sus registros y sus operandos, sin interpretar nada.

De ahí la propiedad que define este documento entero:

El smali es fiel. El Java es una hipótesis.

Un método puede fallar al decompilar; no puede fallar al desensamblar, salvo que el DEX esté corrupto. Si el fichero es válido, hay smali. Ese es el suelo sobre el que se apoya todo el trabajo de modificación.

Este documento cubre la sintaxis, las herramientas y —lo que más duele en la práctica— qué se puede editar a este nivel y qué no. El juego de instrucciones subyacente, con sus opcodes, formatos y descriptores, está en Bytecode Dalvik y no se repite aquí.

2. Cuándo hay que bajar a smali obligatoriamente

Cuatro situaciones. Fuera de ellas, leer Java es más cómodo y basta.

1. Cuando hay que modificar y reempaquetar. No existe ninguna herramienta que recompile a DEX el Java que salió de un decompilador. Ese Java no compila: le faltan tipos, tiene referencias sintéticas, y la sección 2 de Decompiladores a Java explica por qué nunca compilará. El smali, en cambio, vuelve a DEX byte a byte por construcción. Toda modificación real de una aplicación pasa por aquí.

2. Cuando el decompilador ha fallado. Si jadx escribió JADX ERROR en el método o lo marcó con Code decompiled incorrectly, el smali es lo único que queda. Y es suficiente: dice exactamente lo que la máquina va a ejecutar.

3. Cuando hay que verificar una afirmación. Un informe que dice «esta aplicación envía el IMEI a este dominio» se sostiene sobre lo que el smali muestra, no sobre lo que el Java sugiere. En cualquier trabajo con consecuencias —auditoría, peritaje, análisis de malware— la cita es del desensamblado.

4. Cuando el comportamiento depende de un detalle que el Java no muestra. Orden de evaluación, aritmética de enteros con desbordamiento, comparaciones de referencias frente a equals, el tipo exacto de una constante. El decompilador normaliza estas cosas; el desensamblador no puede.

3. La sintaxis, directiva a directiva

Un fichero .smali describe una clase, y su nombre y ruta reproducen el nombre completo de la clase: com/ejemplo/Utilidades.smali.

3.1 Directivas de clase

Directiva Qué declara
.class Modificadores y descriptor de tipo de la clase. Siempre la primera línea
.super Descriptor de la superclase
.implements Una interfaz implementada. Se repite una vez por interfaz
.source Nombre del fichero fuente original, si el DEX lo conserva
.class public final Lcom/ejemplo/Utilidades;
.super Ljava/lang/Object;
.implements Ljava/io/Serializable;
.source "Utilidades.java"

Los tipos se escriben con descriptores, no con nombres de Java: I es int, Z es boolean, [I es int[], Ljava/lang/String; es String. La tabla completa está en la sección 9 de Bytecode Dalvik.

3.2 Directivas de campo

Directiva Qué declara
.field Un campo: modificadores, nombre, :, descriptor de tipo y valor inicial si es constante
.field private static final MAX:I = 0x64
.field private nombre:Ljava/lang/String;

El valor inicial solo aparece en campos static final con constante de compilación. Los demás se inicializan en <clinit> o en <init>, y ahí es donde hay que buscarlos.

3.3 Directivas de método

Directiva Qué declara
.method.end method Delimitan el método: modificadores, nombre, firma
.registers Total de registros del método
.locals Registros no dedicados a parámetros. Alternativa a .registers
.param Nombre y anotaciones de un parámetro
.line Número de línea del fuente original, del debug_info
.prologue Fin del preámbulo del método. Emitida por baksmali antiguo; hoy es rara
.annotation.end annotation Una anotación sobre la clase, campo, método o parámetro
.catch Un bloque try/catch sobre un tipo concreto de excepción
.catchall Un bloque try/finally: captura cualquier excepción
.packed-switch.end packed-switch Tabla de saltos de un switch con valores contiguos
.sparse-switch.end sparse-switch Tabla de saltos de un switch con valores dispersos
.array-data.end array-data Datos literales de un array inicializado

Las etiquetas son identificadores precedidos de dos puntos en su definición y referidos sin ellos: :cond_0, :goto_0, :try_start_0, :array_0. baksmali las genera con prefijo según su uso y numeración correlativa. Son locales al método.

.catch referencia dos etiquetas de rango y una de destino:

    :try_start_0
    invoke-static {}, Lcom/ejemplo/Red;->conectar()V
    :try_end_0
    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_0

3.4 v frente a p

Dentro de un método hay un único banco de registros, numerados desde v0. Los parámetros ocupan siempre los últimos, y baksmali les da un nombre alternativo con prefijo p para que no haya que contar:

  • En un método de instancia, p0 es siempre this, p1 el primer parámetro declarado, p2 el segundo…
  • En un método estático, p0 es el primer parámetro declarado.
  • Los tipos anchos —long y doubleocupan dos registros consecutivos. Si p1 es un long, el siguiente parámetro es p3, no p2.

p0 y v1 pueden ser el mismo registro físico. La notación p es azúcar de baksmali, no una categoría distinta de almacenamiento. La sección 5 lo demuestra sobre código real.

3.5 .registers frente a .locals: donde se equivoca todo el mundo

Las dos directivas declaran lo mismo con aritmética distinta, y solo puede aparecer una por método.

.registers N     N = total de registros del método, parámetros incluidos
.locals   M      M = registros que NO son parámetros

N = M + (registros de parámetros)

Los «registros de parámetros» son el campo ins_size del code_item: el número de registros que ocupan los argumentos entrantes, contando this en los métodos de instancia y contando dos por cada long o double.

Método Parámetros ins_size Si .locals 3 Equivale a
static void f() ninguno 0 .locals 3 .registers 3
void f() (instancia) this 1 .locals 3 .registers 4
void f(int, int) (instancia) this, 2 int 3 .locals 3 .registers 6
static void f(long, Object) 1 long, 1 ref 3 .locals 3 .registers 6
void f(long, boolean) (instancia) this, long, bool 4 .locals 3 .registers 7

El error clásico, y el motivo de este apartado: alguien necesita un registro más para su parche, ve .locals 2 y lo cambia a .locals 3 pensando que ha añadido un registro al final. Lo que ha hecho es insertar un registro: como los parámetros viven al final del banco, subir el número de locales desplaza todos los p hacia arriba. Si el método tenía .locals 2 y tres registros de parámetros, p0 era v2; con .locals 3 pasa a ser v3. Todas las referencias explícitas a v2, v3 y v4 que hubiera en el cuerpo apuntan ahora a otra cosa.

Como baksmali escribe los parámetros con notación p, el desplazamiento es transparente para ellos — y por eso el error pasa desapercibido hasta que revienta. Lo que se rompe son las referencias que usan la notación v sobre registros que resultaban ser parámetros, y el código que se haya copiado de otro método.

Regla práctica: subir .locals es correcto y es la forma normal de hacer sitio, siempre que el método esté escrito íntegramente en notación p para los parámetros. Mezclar v y p para el mismo registro es lo que mata.

Hay un segundo límite, más duro: muchas instrucciones solo codifican registros de 4 bits, o sea v0 a v15. Un método que ya usa dieciséis registros no admite uno más sin reescribir las instrucciones afectadas a sus variantes /from16 o /16. La sección 3.2 de Bytecode Dalvik explica esas variantes.

4. .registers sobre código real

Lo que sigue sí está ejecutado. La máquina de referencia no tiene baksmali, pero tiene d8 y dexdump, que muestran los mismos campos del code_item que baksmali traduce a directivas.

Clase de partida, escrita para este documento:

public class Saldo {
    private int saldo;
    public int retirar(int cantidad, boolean forzar) {
        int restante = this.saldo - cantidad;
        if (restante < 0 && !forzar) {
            return -1;
        }
        this.saldo = restante;
        return restante;
    }
}
BT=~/Library/Android/sdk/build-tools/37.0.0
javac -g Saldo.java
$BT/d8 --release --min-api 24 --output . Saldo.class
$BT/dexdump -d classes.dex

Salida real, recortada al método que interesa:

  Virtual methods   -
    #0              : (in LSaldo;)
      name          : 'retirar'
      type          : '(IZ)I'
      access        : 0x0001 (PUBLIC)
      code          -
      registers     : 4
      ins           : 3
      outs          : 0
      insns size    : 12 16-bit code units
000108:                                        |[000108] Saldo.retirar:(IZ)I
000118: 5210 0000                              |0000: iget v0, v1, LSaldo;.saldo:I // field@0000
00011c: b120                                   |0002: sub-int/2addr v0, v2
00011e: 3b00 0600                              |0003: if-gez v0, 0009 // +0006
000122: 3903 0400                              |0005: if-nez v3, 0009 // +0004
000126: 12f1                                   |0007: const/4 v1, #int -1 // #ff
000128: 0f01                                   |0008: return v1
00012a: 5910 0000                              |0009: iput v0, v1, LSaldo;.saldo:I // field@0000
00012e: 0f00                                   |000b: return v0
      catches       : (none)
      positions     :
        0x0000 line=4
        0x0009 line=8
      locals        :
        0x0000 - 0x000c reg=1 this LSaldo;
        0x0000 - 0x000c reg=2 (null) I
        0x0000 - 0x000c reg=3 (null) Z

Tres lecturas, todas verificables en ese volcado:

La aritmética. registers : 4 e ins : 3 dan .registers 4, y por tanto .locals 1 — porque 4 − 3 = 1. Los tres registros de entrada son this, el int y el boolean. El único local es v0, que sostiene restante.

El mapa vp. El bloque locals lo confirma sin ambigüedad: reg=1 es this, o sea p0 es v1; reg=2 es el int, o sea p1 es v2; reg=3 es el boolean, o sea p2 es v3. Y v0, que no aparece en la lista, es el local.

La prueba de que p no protege nada. En el offset 0007 está const/4 v1, #int -1. v1 es p0, es decir this. El compilador ha decidido que en esa rama this ya no hace falta y reutiliza su registro para el valor de retorno, que devuelve en la instrucción siguiente. Un registro de parámetro es un registro normal: se puede pisar en cuanto su valor deja de ser necesario, y D8 lo hace de forma rutinaria.

Un detalle más, que enlaza con la sección 2.3 de Decompiladores a Java: la clase se compiló con javac -g, así que el .class llevaba los nombres de los parámetros. En el volcado aparecen como (null). Los ha borrado d8 --release. Los números de línea sí sobrevivieron —positions conserva line=4 y line=8—, de modo que un DEX puede traer información de depuración parcial: las líneas sí y los nombres no.

5. La misma clase en smali, comentada

⚠️ No ejecutado localmente: baksmali no está instalado en la máquina de referencia. El smali que sigue está transcrito a mano desde el volcado real de dexdump de la sección 4, aplicando las reglas de las secciones 3.4 y 3.5. Las instrucciones, los registros, los offsets y los números de línea son los reales; el nombre exacto de las etiquetas (:cond_0) es la convención de baksmali y podría numerarse de otro modo.

.class public LSaldo;                      # clase pública, descriptor de tipo con L…;
.super Ljava/lang/Object;                  # extiende Object
.source "Saldo.java"                       # nombre del fuente, del debug_info


# instance fields
.field private saldo:I                     # private int saldo — I es el descriptor de int


# direct methods
.method public constructor <init>()V
    .registers 1                           # 1 registro total; ins=1 (this) ⇒ .locals 0

    .line 1
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V   # super()

    return-void
.end method


# virtual methods
.method public retirar(IZ)I                # (int, boolean) → int
    .registers 4                           # 4 totales, ins=3 ⇒ equivale a .locals 1
                                           #   p0 = v1 = this
                                           #   p1 = v2 = cantidad (I)
                                           #   p2 = v3 = forzar   (Z)
                                           #   v0 = el único local

    .line 4
    iget v0, p0, LSaldo;->saldo:I          # v0 = this.saldo
                                           #   iget lee un campo de instancia int

    sub-int/2addr v0, p1                   # v0 = v0 - cantidad
                                           #   /2addr: destino y primer operando son el mismo
                                           #   registro. Ocupa 2 bytes en vez de 4

    if-gez v0, :cond_0                     # si v0 >= 0, saltar a :cond_0
                                           #   es la negación de (restante < 0) del fuente

    if-nez p2, :cond_0                     # si forzar != 0 (true), saltar a :cond_0
                                           #   negación de (!forzar). Los dos saltos juntos
                                           #   implementan el && con cortocircuito

    const/4 p0, -0x1                       # p0 (= v1 = this) pasa a valer -1
                                           #   se reutiliza el registro de this: ya no se usa

    return p0                              # return -1

    .line 8
    :cond_0
    iput v0, p0, LSaldo;->saldo:I          # this.saldo = v0
                                           #   ¡ojo! aquí p0 vuelve a ser this: este camino
                                           #   no pasó por el const/4 de arriba

    return v0                              # return restante
.end method

Merece la pena detenerse en el const/4 p0, -0x1. Escrito con notación p, parece que se está machacando this, y eso es exactamente lo que ocurre — pero solo en la rama que retorna inmediatamente después, donde this ya no se necesita. La rama de :cond_0 nunca ejecuta esa instrucción, así que allí p0 sigue siendo this y el iput funciona.

Esta es la clase de razonamiento que hay que hacer para editar smali con seguridad, y la razón por la que un parche que «solo añade una línea» puede romper un método que parecía no tener nada que ver.

6. baksmali y smali: las herramientas

6.1 El cambio de manos, que conviene conocer

El proyecto lo creó Ben Gruver (JesusFreke) en 2010 y fue la referencia absoluta durante más de una década. Ese repositorio original, JesusFreke/smali, está hoy archivado: último push el 17 de enero de 2024, 6.631 estrellas, y sin desarrollo.

El proyecto vivo es google/smali, un fork que mantiene Google. Su README lo dice explícitamente: el original ya no se mantiene activamente y Google se hizo cargo del código para poder aplicarle los parches que necesitaba, cambiando el espacio de nombres de org.jf a com.android.tools.smali. La primera release bajo Google fue la 3.0.0, construida sobre la 2.5.2 del original.

Quien busque documentación antigua se encontrará con clases org.jf.* y con URLs que ya no llevan a ninguna parte. Es el mismo software.

6.2 Ficha de google/smali

Dato Valor
Repositorio https://github.com/google/smali
Licencia BSD de 3 cláusulas (Ben Gruver, 2010), con partes de AOSP bajo Apache-2.0
Lenguaje Java
Versión estable 3.0.6, publicada el 8 de mayo de 2024
Último push 14 de julio de 2026
Creado (el fork) 9 de diciembre de 2022
Tracción 390 ★ · 67 forks · 24 issues abiertas
Archivado No

Consultado el 12 de agosto de 2026 vía la API de GitHub. La API reporta la licencia como NOASSERTION porque el fichero LICENSE no es un texto estándar sin modificar: empieza con «The majority of smali/baksmali is written and copyrighted by me (Ben Gruver) and released under the following license», seguido de una BSD de 3 cláusulas, y añade el aviso de Apache-2.0 para el código procedente de AOSP.

Lectura del mantenimiento: hay actividad reciente en el repositorio —commits en julio de 2026— pero la última versión publicada es de mayo de 2024, hace más de dos años. Es el mismo patrón que CFR: desarrollo sin publicación. La diferencia es que aquí el respaldo es Google y el componente está integrado en su propia cadena de herramientas, así que el riesgo de abandono es bajo.

El proyecto también publica dexlib2, la biblioteca de lectura y escritura de DEX que hay debajo. Es la que usan apktool y buena parte del ecosistema; a efectos prácticos, es la implementación de referencia del formato fuera de AOSP.

6.3 Opciones principales

⚠️ No ejecutado localmente: baksmali y smali no están instalados. La sintaxis procede de la documentación del proyecto y del uso establecido, consultada el 12 de agosto de 2026. ⚠️ sin verificar: el README de google/smali no documenta las opciones de línea de órdenes —remite a --help y a documentación externa—, así que la lista de flags de abajo no se ha podido contrastar contra una fuente oficial que los enumere.

Desensamblar un DEX a un árbol de ficheros .smali:

java -jar baksmali.jar disassemble classes.dex -o salida/

Volver a ensamblar:

java -jar smali.jar assemble salida/ -o classes.dex

Opciones que importan:

Opción Efecto
-o, --output <dir> Directorio de salida
-a, --api <n> Nivel de API. Determina qué opcodes se aceptan como válidos
--parameter-registers / --no-parameter-registers Usar la notación p o solo v
--sequential-labels Etiquetas numeradas correlativamente en vez de por offset
-l, --use-locals Emitir .locals en lugar de .registers
-b, --no-debug-info No emitir .line, .local ni .param
--register-info Anotar cada instrucción con el estado de tipos de los registros

--register-info es la opción infravalorada del lote: anota en comentarios qué tipo tiene cada registro en cada punto. Es justo la información que hace falta para no romper la verificación de bytecode al insertar código, y evita tener que deducirla a mano.

7. El crate smali de azw413

La única implementación de esta funcionalidad en Rust, y una pieza que condiciona decisiones de arquitectura por su licencia.

Dato Valor
Repositorio https://github.com/azw413/smali
Crate https://crates.io/crates/smali
Licencia declarada en crates.io GPL-3.0-only
Versión estable 0.6.2, publicada el 5 de agosto de 2026
Último push 5 de agosto de 2026
Creado 22 de septiembre de 2022
Descargas totales 4.745
Tracción 23 ★ · 8 forks · 0 issues abiertas

Consultado el 12 de agosto de 2026 vía la API de GitHub y la API de crates.io. Activo: las versiones 0.5.3, 0.6.0, 0.6.1 y 0.6.2 se publicaron todas entre el 4 y el 5 de agosto de 2026, y la 0.5.2 el 10 de junio de 2026. Es desarrollo reciente y vivo.

Qué cubre. Según su propio README: un parser, escritor y conjunto de tipos en Rust puro para el formato smali, cubriendo la estructura completa de una clase de la VM Dalvik incluidas todas sus instrucciones; lectura y escritura de ficheros DEX; soporte de multidex; manejo de anotaciones de metadatos de Kotlin; edición de manifiestos de APK y de AAB; y manejo de errores pensado para analizar ficheros no confiables sin romperse. Se declara completamente autocontenido, sin dependencias de Java, y con el objetivo de ser 100 % compatible con la implementación de smali/baksmali de Ben Gruver.

La licencia, que es el punto. crates.io declara GPL-3.0-only en todas sus versiones publicadas, y el repositorio publica junto a la GPL un fichero LICENSE-commercial: es decir, licenciamiento dual con opción comercial de pago. ⚠️ sin verificar: no se han podido leer los términos concretos del fichero LICENSE-commercial; la consulta directa de su contenido no fue posible en esta revisión. Lo verificado es que el fichero existe y que la licencia libre es GPL-3.0-only.

Eso es lo que importa. La GPL-3.0-only es una licencia copyleft fuerte: enlazar este crate dentro de un motor propio obliga a distribuir ese motor entero bajo GPL-3.0, lo que arrastra consigo el resto del código y descarta cualquier módulo propietario.

La consecuencia es la misma que con jadx: quien quiera integrarlo en un producto con licencia permisiva tiene que usarlo como binario aislado, en proceso separado, con frontera de licencia limpia —o adquirir la licencia comercial.

El punto ciego. 23 estrellas, 4.745 descargas y un solo autor. Es un proyecto personal, técnicamente ambicioso y activo, pero con un bus factor de uno. Apoyar sobre él una función crítica sin un plan de contingencia sería imprudente; usarlo tras una frontera de proceso acota el daño a tener que sustituir un ejecutable.

8. Qué se puede editar a nivel smali, y qué no

Aquí es donde la teoría se cobra el peaje.

8.1 Barato y seguro

Cambiar el valor de una constante. Sustituir const/4 v0, 0x0 por const/4 v0, 0x1 es la edición canónica. No cambia el tamaño de la instrucción, no toca registros, no altera el flujo.

Cuidado con el rango: const/4 codifica un literal de 4 bits con signo, o sea de −8 a 7. Un valor mayor exige const/16 o const, que ocupan más bytes — lo cual sigue siendo correcto, porque el ensamblador recalcula todos los offsets, pero deja de ser una sustitución de un carácter.

Invertir una condición. Cambiar if-eqz por if-nez, o if-gez por if-ltz. Mismo tamaño, mismo formato, efecto opuesto. Es cómo se neutraliza una comprobación.

Eliminar una llamada sin retorno útil. Sustituir un invoke-* a un método void por nop, o simplemente borrar la línea. El ensamblador recalcula offsets y etiquetas.

Cambiar el destino de un salto. Reapuntar una etiqueta a otra ya existente.

Editar el string de una constante. const-string v0, "..." admite cualquier contenido: el ensamblador añade la entrada a la tabla de strings.

8.2 Caro, y por qué

Cambiar la firma de un método. Parece un cambio local y no lo es. La firma es parte de la identidad del método en el DEX: aparece en el method_id_item, que referencia un proto_id_item con la lista de tipos. Cambiarla implica que todas las invocaciones desde todas las clases dejan de resolver — y esas invocaciones están repartidas por todos los classes*.dex del APK, incluidos los que ni se han desensamblado.

Añadir un parámetro tiene un segundo efecto: sube ins_size, lo que desplaza todo el banco de registros del método con el mecanismo de la sección 3.5. Hay que revisar el cuerpo entero.

Si el método sobrescribe uno de una superclase o implementa una interfaz, cambiar la firma rompe además el enlace y ART lo detecta al cargar.

Añadir un campo. Hay que crear el .field, y luego inicializarlo — lo que significa tocar <init> en todos sus constructores, o <clinit> si es estático. Un iput sobre un campo que no existe en la clase produce error de verificación en tiempo de carga.

Añadir un método. Más fácil que lo anterior: un .method nuevo con .registers correcto suele bastar. Pero si hay que llamarlo desde otra clase, esa clase también se toca.

Insertar código en medio de un método. El problema real, y merece su propio apartado.

8.3 El problema de los registros al insertar código

Insertar instrucciones requiere registros donde poner los valores intermedios. Y ahí empiezan las tres restricciones que se acumulan:

1. No hay registros libres. El compilador asigna el mínimo necesario. Un método con .locals 2 tiene exactamente dos, y los dos están en uso. Hay que subir el número — con el efecto de desplazamiento de la sección 3.5.

2. El techo de 4 bits. La mayoría de las instrucciones codifican sus registros en 4 bits: solo alcanzan v0 a v15. Si el método ya usa dieciséis, subir .locals empuja los parámetros por encima de v15, y todas las instrucciones que los referencian dejan de poder codificarse. Hay que reescribirlas a /from16 o /16, lo que cambia sus tamaños.

La salida habitual es no tocar el banco: guardar el registro que se va a usar en uno alto con move/16, usarlo, y restaurarlo. Funciona, y es tedioso.

3. La verificación de tipos de ART. Es la restricción que sorprende. ART verifica al cargar que cada registro tiene un tipo coherente en cada punto del programa, y en particular que en los puntos donde dos ramas confluyen el tipo coincide por ambos caminos. Un parche que deja un registro conteniendo un int en una rama y una referencia en la otra produce un error de verificación.

Y el error no ocurre donde está el bug: ocurre al cargar la clase, con un mensaje sobre tipos de registro que no señala la línea que se editó. La sección 10 de Bytecode Dalvik cubre el verificador; el diagnóstico de estos fallos está en Diagnóstico de fallos.

La consecuencia operativa: un DEX editado a mano que no supere la verificación falla al cargar la clase, no al ejecutar la línea. La aplicación arranca y muere al tocar la funcionalidad parcheada, con una traza que apunta a otro sitio.

8.4 Lo que no se toca desde aquí

Fuera del alcance del smali, aunque estén en el mismo APK:

  • Los recursos. resources.arsc y los AXML son otro formato. Se editan con las herramientas de Desempaquetado y reempaquetado.
  • El código nativo. Lo que está en lib/<abi>/*.so es ELF, y se trata en Análisis binario y estático. Mover lógica ahí es precisamente una técnica para escapar del alcance de este documento.
  • La firma. Cualquier cambio la invalida, y hay que rehacerla: Firma y empaquetado.

Fuentes

  1. google/smali — repositorio oficial — https://github.com/google/smali Consultado el 12 de agosto de 2026 vía https://api.github.com/repos/google/smali. De aquí salen la fecha de creación del fork, el último push, los recuentos de estrellas, forks e issues, y la versión 3.0.6 del 8 de mayo de 2024 de la ficha de la sección 6.2.
  2. google/smali — README — https://raw.githubusercontent.com/google/smali/main/README.md Consultado el 12 de agosto de 2026. De aquí sale el relato del cambio de manos de la sección 6.1: que el original de JesusFreke ya no se mantiene, que Google se hizo cargo, el cambio de espacio de nombres de org.jf a com.android.tools.smali y que la 3.0.0 se construyó sobre la 2.5.2.
  3. google/smali — fichero LICENSE — https://api.github.com/repos/google/smali/contents/LICENSE Consultado el 12 de agosto de 2026. De aquí sale la licencia exacta: BSD de 3 cláusulas de Ben Gruver, más Apache-2.0 para el código de AOSP, y la explicación de por qué la API de GitHub la reporta como NOASSERTION.
  4. JesusFreke/smali — repositorio original — https://github.com/JesusFreke/smali Consultado el 12 de agosto de 2026 vía la API de GitHub. De aquí sale que está archivado, con último push el 17 de enero de 2024 y 6.631 estrellas.
  5. azw413/smali — repositorio del crate de Rust — https://github.com/azw413/smali Consultado el 12 de agosto de 2026. De aquí salen la descripción de capacidades de la sección 7, la existencia del fichero LICENSE-commercial junto a la GPL-3.0, y los recuentos de estrellas, forks e issues.
  6. crate smali en crates.io — https://crates.io/api/v1/crates/smali Consultado el 12 de agosto de 2026. De aquí salen la licencia GPL-3.0-only declarada en todas las versiones, la versión 0.6.2 del 5 de agosto de 2026, el historial de versiones recientes y las 4.745 descargas totales.
  7. Volcado de dexdump sobre una clase compilada al efecto — ejecutado el 12 de agosto de 2026 con build-tools 37.0.0 (d8, dexdump) y OpenJDK 21.0.10 sobre la máquina de referencia. De aquí sale íntegra la sección 4: los valores registers : 4 e ins : 3, las ocho instrucciones con sus offsets, el bloque locals con el mapa de registros, y la evidencia de que d8 --release borra los nombres de parámetros pero conserva los números de línea.