
Caja de pruebas de clonación de aplicaciones Android con una CLI estilo Frida que lanza o se adjunta a aplicaciones clonadas e inyecta scripts QuickJS usando hooks de Gum Interceptor y Stalker.

Spindle es el cliente de línea de comandos. Envía un script al host de BBA o a una app clonada y devuelve los logs. Los flags siguen la línea de comandos de Frida: spawn con -f, attach con -n o -p, cargar un archivo con -l.
BBA en sí es un sandbox de clonación de apps de Android. El paquete host es com.android.bba. Una app clonada se ejecuta dentro de ese paquete, no como una instalación normal. Instala el APK, abre el clon desde la UI de BBA y luego usa Spindle. Los scripts se ejecutan solo en arm64, en QuickJS, con el Interceptor y Stalker de Frida Gum.

La máquina de desarrollo nunca se conecta directamente a un clon. Solo habla con com.android.bba:black en 127.0.0.1:2408. El proceso host y cada clon son agentes: se registran por Binder, reciben el script y devuelven los logs. El modo USB ejecuta adb forward tcp:0 tcp:2408 por ti.
Desde la raíz del repo, con un dispositivo conectado:
# Stop the clone on user 0, start it, inject, then let it continue
python3 tools/spindle -U -f com.example.app -l tools/test_js_api.js
# Attach to a clone that is already running
python3 tools/spindle -U -n com.example.app -l tools/test_native_hook.js
# Attach to a pid, including the host process com.android.bba
python3 tools/spindle -U -p 12345 -l tools/test_js_api.js
# One line of script, no file
python3 tools/spindle -U -n com.example.app -e 'console.log("hi")'
# Registered processes, JSON
python3 tools/spindle -U --list
# Same list, as a table
python3 tools/spindle-ps -U
-f es spawn. El cliente envía el script y luego UNLOCK. El clon espera en la ruta de arranque hasta que llega ese frame, así que el script está en su lugar antes de Application.onCreate. -f siempre usa el usuario 0.
-n y -p hacen attach. El proceso ya debe haberse registrado. Un nombre es el nombre de proceso que Spindle registró, normalmente el nombre del paquete clonado. El proceso de UI del host es com.android.bba.
Más de un teléfono:
python3 tools/spindle -D <serial> -n com.example.app -l tools/test_js_api.js
Si el puerto ya está redirigido, omite adb:
python3 tools/spindle -H 127.0.0.1:2408 -n com.example.app -l tools/test_js_api.js
-H, -U y -D eligen la conexión. Pasa solo uno de ellos. Pasa solo uno de -f, -n y -p.
El cliente permanece activo e imprime console.log hasta que el proceso termina, el servidor se cae o pulsas Ctrl-C.
Un script es JavaScript plano. Estos ejemplos están en tools/:
| Archivo | Qué hace |
|---|---|
test_js_api.js | Memory, punteros, Module, Interceptor |
test_native_hook.js | Interceptor.attach("libz.so!deflate", …) |
test_java_hook.js | Java.hook en Activity.onCreate |
test_stalker.js | Stalker.follow desde dentro de un hook |
test_rpc.js | rpc.exports llamado desde la línea de comandos |
console.log vuelve como una línea de log. En armeabi-v7a no hay runtime; un RPC allí responde con js runtime is not in bba.
Llama a un export después de que el script se cargue. --args es un array JSON y se alinea con el --call correspondiente:
python3 tools/spindle -U -n com.example.app -l tools/test_rpc.js --call ping --args '["hi"]'
Eso imprime pong hi y luego sigue esperando logs.
Chat de grupo: openbba. El código expira el 20 de octubre de 2026.
