¿Cómo ejecutar un ejecutable en el arranque y mantenerlo funcionando?

Tengo una compilación ejecutable de un ndk-buildprograma. Quiero ejecutarlo en un dispositivo Android rooteado. ¿Cuál es la posibilidad de que Android mate mi ejecutable?

Usando adb shell, puedo ejecutar mi ejecutable usando los siguientes comandos:

adb push executable /sdcard
adb shell
device:/ $ su
device:/ # cd /system
device:/system # mount -o rw,remount /system
device:/system # mv /sdcard/executable .
device:/system # ./executable

Mi requisito es ejecutar este ejecutable en el arranque del dispositivo.

He intentado lo siguiente:

  1. Escribe ./executableen init.rc.
    • Al reiniciar, init.rcrestablece su contenido original. Descubrí que Magisk hizo esto.
  2. Escriba comandos en /system/etc/init/bootstat.rc
    • ./executable
    • service custom /system/executable
    • on boot ./system/dhandler/diag_revealer

Nada de lo anterior está funcionando.

Los comentarios no son para una discusión extensa; esta conversación se ha movido a chat .

Respuestas (1)

¿Cuál es la posibilidad de que Android mate mi ejecutable?

Los procesos nativos privilegiados generalmente no son eliminados por Android, excepto si no pueden manejar un error interno, como algún recurso del sistema no disponible o permiso denegado debido a SELinux, etc. Para liberar memoria, Android solo elimina los procesos dentro de su marco. es decir, corriendo bajo zygote. Para administrar recursos para procesos nativos, Android usa cgroups.

Los procesos se eliminan cuando reciben SEÑALES del núcleo u otros programas de espacio de usuario (por ejemplo, con killcomando) ( 1 , 2 ) . Kernel es el sistema operativo real, no visible para nosotros pero que maneja todo lo que hacemos con el dispositivo. Un desarrollador puede programar su código para reaccionar ante una señal específica si se recibe, o ignorarla por completo ( 3 ) . Excepto SIGKILL . Lo cual no puede ser manejado por el programa, sin advertencia del núcleo, sin período de gracia para salir de manera segura, solo se termina de inmediato. Pero a Kernel no le importará tu presencia a menos que se quede sin recursos de hardware o empieces a portarte mal. Por eso la programación es importante.

Los programas pueden enviarse señales entre sí (incluyendo KILL), que son reenviadas por el núcleo, gobernadas por UID ( 4 ) . Sin embargo, initel primer proceso en el espacio de usuario iniciado por kernel es el querido, kernel nunca envía señales peligrosasinit a . Y si esto sucede por alguna razón, el kernel entra en pánico y se reinicia ( 5 ) .

Resumiendo las líneas anteriores, es posible evitar ser asesinado (AMAP) mediante programación o usando algunos trucos de secuencias de comandos como ha mencionado @alecxs . Pero si desea asegurarse de que su proceso se reinicie si se cancela, defina un initservicio de Android.

Al reiniciar, init.rcrestablece su contenido original. Descubrí que Magisk hizo esto.

No, Magisk no hizo esto. Android rootfses un sistema de archivos temporal (no persistente como en /systemo /data) que se borra en cada reinicio. El contenido del directorio raíz ( /) se extrae de otra partición llamada bootque contiene kernely ramdisk(aunque las cosas han cambiado con system-as-root ). Por lo tanto, no puede cambiar init.rcpermanentemente a menos que extraiga, modifique, vuelva a empaquetar y actualice boot.img.

Pero para definir un nuevo servicio de inicio, init.rcno es necesario modificarlo. Android analiza todos .rclos archivos de /etc/initlos directorios ubicados bajo /systemy /vendor ( 6 ) . Para que pueda crear su propio .rcarchivo.


NOTA: Para obtener privilegios de root reales y manejar SELinux, todas las opciones que se detallan a continuación dependen de Magisk. Vea esta respuesta para más detalles.

GUIÓN INIT.D

Puede usar init.dla función tradicional de Magisk para iniciar un proceso en el arranque. Crear guión /data/adb/service.d/custom.sh:

#!/system/bin/sh

# write log file if executable throws something at stdout/sterr
exec >>/data/media/0/executable.log 2>&1

# run script in background to avoid blocking boot chain
[ -n "$BG" ] || { BG=Y "$0" & exit; }

# try to ignore signals as much as possible
for i in $(seq 64); do trap '' "$i"; done

# execute script whenever exits e.g. when executable gets killed
trap "sleep 5; exec $0" EXIT

# avoid multiple instances e.g. if script killed but executable is running
pkill -9 -x /system/bin/executable

# execute the binary, should run in foreground, otherwise get in loop
echo "$(date): Starting program..."
/system/bin/executable

# program is killed, won't reach here if script is killed
echo "$(date): Re-executing script..."

* es la pseudo-señalEXIT de shell .
* Android /system/bin/pkill(de toybox) tiene errores, es mejor usar busyboxel applet.

Coloque el ejecutable debajo /system/biny configure los permisos:

~# chown 0.0 /system/bin/executable /data/adb/service.d/custom.sh
~# chmod 0755 /system/bin/executable /data/adb/service.d/custom.sh

También puede colocar un script debajo /data/adb/post-fs-data.d/, pero eso se ejecuta un poco antes. Asegúrese de que las rutas del sistema de archivos (y otros recursos necesarios, si los hay) estén disponibles en esa etapa.

EJECUTAR PROGRAMA DESDE INIT

Otra forma es ejecutar directamente el binario desde init. Crear custom.rcarchivo:

#/etc/init/custom.rc

# execute the binary when boot is completed
on property:sys.boot_completed=1
    exec_background u:r:magisk:s0 -- /system/bin/executable

Establecer permisos:

~# chown 0.0 /etc/init/custom.rc
~# chmod 0644 /etc/init/custom.rc
~# chcon u:object_r:system_file:s0 /etc/init/custom.rc

¡Y eso es todo! Reinicie el dispositivo para que los cambios surtan efecto.

Sin embargo, es una ejecución única, no se reiniciará. También hay algunas funciones de secuencias de comandos de shell que no están disponibles en .rclos archivos. Por ejemplo, no puede redirigir stdout/stderr a un archivo, esto debe ser manejado por el propio programa ejecutable. Así que podemos intentar hacer uso de ambos; script de shell y .rcarchivo:

SERVICIO DE INICIO

En lugar de ejecutar directamente el binario desde .rcel archivo, ejecute un script de shell. Crear guión /system/bin/custom.sh:

#!/system/bin/sh

# write log file if executable throws something at stdout/sterr
exec >>/data/media/0/executable.log 2>&1

# execute the binary, should run in foreground, otherwise get in loop
echo "$(date): Starting program..."
exec /system/bin/executable

Crear initservicio:

#/etc/init/custom.rc

# define service, use executable here if script not needed
service custom /system/bin/custom.sh

    # don't start unless explicitly asked to
    disabled

    # only execute once, don't restart if exited
    # don't add if you want to restart service when killed
    #oneshot

    # run with unrestricted SELinux context to avoid avc denials
    # it's required if SELinux is enforcing and service needs access
    # to some system resources not allowed by default sepolicy
    seclabel u:r:magisk:s0

# start the service when boot is completed
on property:sys.boot_completed=1
    start custom

Establezca permisos en executable, custom.shy custom.rccomo se indicó anteriormente y reinicie.

Se requieren otros parámetros ( 7 ) como user, group, capabilitiessi desea ejecutar el servicio como usuario sin privilegios. Otorgar los privilegios mínimos requeridos es el enfoque recomendado desde la perspectiva de la seguridad. Consulte esta respuesta para obtener más detalles sobre las capacidades y SELinux.

initseguirá reiniciando el servicio cada 5 segundos (por defecto) si se elimina. Puede detener el servicio con setprop ctl.stop custom. Reemplazar stopcon startpara comenzar de nuevo.
Para ver que pasa con el servicio: dmesg | grep init: | tail.

RELACIONADO :

Una secuencia de comandos /data/adb/service.dsería genial, pero por alguna razón, estas secuencias de comandos no parecen ejecutarse con privilegios de root: el uso mount ...me da "mount: Permiso denegado" y el uso su -c "mount ..."me da "su: Permiso denegado". ¿Algo que me esté perdiendo?