Ich habe einen ausführbaren Build aus einem ndk-build
Programm. Ich möchte es auf einem gerooteten Android-Gerät ausführen. Wie groß ist die Wahrscheinlichkeit, dass Android meine ausführbare Datei tötet?
Mit adb shell
kann ich meine ausführbare Datei mit folgenden Befehlen ausführen:
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
Meine Anforderung ist, diese ausführbare Datei beim Gerätestart auszuführen.
Folgendes habe ich probiert:
./executable
ein init.rc
.
init.rc
auf den ursprünglichen Inhalt zurückgesetzt. Ich habe herausgefunden, dass Magisk dies getan hat../executable
service custom /system/executable
on boot
./system/dhandler/diag_revealer
Keines der oben genannten Dinge funktioniert.
Wie groß ist die Wahrscheinlichkeit, dass Android meine ausführbare Datei tötet?
Privilegierte native Prozesse werden normalerweise nicht von Android beendet, es sei denn, sie können einen darin aufgetretenen Fehler nicht verarbeiten, z dh läuft unter zygote
. Um Ressourcen für native Prozesse zu verwalten, verwendet Android cgroups
.
Prozesse werden beendet, wenn sie SIGNALE vom Kernel oder anderen Userspace-Programmen erhalten (z. B. mit kill
dem Befehl) ( 1 , 2 ) . Der Kernel ist das eigentliche Betriebssystem, das für uns nicht sichtbar ist, aber alles verarbeitet, was wir mit dem Gerät tun. Ein Entwickler kann seinen Code so programmieren, wie er auf ein bestimmtes empfangenes Signal reagiert oder es komplett ignoriert ( 3 ) . Außer SIGKILL . Was nicht vom Programm gehandhabt werden kann, keine Warnung vom Kernel, keine Gnadenfrist zum sicheren Beenden, nur sofortige Beendigung. Aber der Kernel wird Ihre Anwesenheit nicht stören, es sei denn, ihm gehen die Hardware-Ressourcen aus oder Sie fangen an, sich schlecht zu benehmen. Deshalb ist Programmieren wichtig.
Programme können sich gegenseitig Signale senden (einschließlich KILL), die vom Kernel weitergeleitet werden, geregelt durch UID ( 4 ) . Allerdings init
ist der allererste Prozess im Userspace, der vom Kernel gestartet wird, der liebe, der Kernel leitet niemals gefährliche Signale an weiter init
. Und wenn dies aus irgendeinem Grund passiert, gerät der Kernel in Panik und startet neu ( 5 ) .
Zusammenfassend lässt sich sagen, dass es möglich ist, das Töten (AMAP) programmgesteuert zu vermeiden oder einige Skripttricks zu verwenden, wie @alecxs erwähnt hat. Wenn Sie jedoch sicherstellen möchten, dass Ihr Prozess neu gestartet wird, wenn er beendet wird, definieren Sie einen Android- init
Dienst.
Beim Neustart
init.rc
auf den ursprünglichen Inhalt zurückgesetzt. Ich habe herausgefunden, dass Magisk dies getan hat.
Nein, Magisk hat das nicht getan. Android rootfs
ist ein temporäres Dateisystem (kein persistentes wie on /system
oder /data
), das bei jedem Neustart gelöscht wird. Der Inhalt des Stammverzeichnisses ( /
) wird aus einer anderen Partition mit dem Namen extrahiert, boot
die kernel
und enthält ramdisk
(obwohl sich die Dinge mit system-as-root geändert haben ). Sie können also nicht init.rc
dauerhaft ändern, es sei denn, Sie extrahieren, modifizieren, neu packen und neu flashen boot.img
.
Um jedoch einen neuen Init-Dienst zu definieren, init.rc
ist keine Änderung erforderlich. Android parst alle .rc
Dateien aus /etc/init
Verzeichnissen unter /system
und /vendor
( 6 ) . Sie können also Ihre eigene .rc
Datei erstellen.
HINWEIS: Um echte Root-Rechte zu erhalten und mit SELinux umzugehen, hängen alle unten angegebenen Optionen von Magisk ab. Siehe diese Antwort für Details.
init.d
Sie können die traditionelle -ähnliche Funktion von Magisk verwenden, um einen Prozess beim Booten zu starten. Skript erstellen /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..."
* ist das PseudosignalEXIT
der Shell .
* Android /system/bin/pkill
(von toybox
) ist fehlerhaft, verwenden Sie besser busybox
Applet.
Platzieren Sie die ausführbare Datei unter /system/bin
und legen Sie die Berechtigungen fest:
~# chown 0.0 /system/bin/executable /data/adb/service.d/custom.sh
~# chmod 0755 /system/bin/executable /data/adb/service.d/custom.sh
Sie können auch ein Skript unter platzieren, /data/adb/post-fs-data.d/
aber das wird etwas früher ausgeführt. Stellen Sie sicher, dass Dateisystempfade (und ggf. andere erforderliche Ressourcen) zu diesem Zeitpunkt verfügbar sind.
Eine andere Möglichkeit besteht darin, die Binärdatei direkt von init auszuführen. custom.rc
Datei erstellen :
#/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
Berechtigungen festlegen:
~# chown 0.0 /etc/init/custom.rc
~# chmod 0644 /etc/init/custom.rc
~# chcon u:object_r:system_file:s0 /etc/init/custom.rc
Und das ist alles! Starten Sie das Gerät neu, damit die Änderungen wirksam werden.
Es handelt sich jedoch um eine einmalige Ausführung, die nicht neu gestartet wird. Außerdem gibt es einige Shell-Scripting-Funktionen, die in .rc
Dateien nicht verfügbar sind. Beispielsweise können Sie stdout/stderr nicht in eine Datei umleiten, dies muss vom ausführbaren Programm selbst gehandhabt werden. Wir können also versuchen, beides zu nutzen; Shell-Skript und .rc
Datei:
Anstatt die Binärdatei direkt aus .rc
der Datei auszuführen, führen Sie ein Shell-Skript aus. Skript erstellen /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
Dienst erstellen init
:
#/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
Setzen Sie die Berechtigungen auf executable
und custom.sh
wie custom.rc
oben angegeben und starten Sie neu.
Andere Parameter ( 7 ) wie user
, group
, capabilities
sind erforderlich, wenn Sie den Dienst als nicht privilegierter Benutzer ausführen möchten. Das Gewähren der am wenigsten erforderlichen Berechtigungen ist aus Sicherheitssicht der empfohlene Ansatz. Weitere Informationen zu Funktionen und SELinux finden Sie in dieser Antwort .
init
wird den Dienst alle 5 Sekunden (standardmäßig) neu starten, wenn er beendet wird. Sie können den Dienst mit beenden setprop ctl.stop custom
. Ersetzen Sie stop
durch, start
um neu zu beginnen.
So sehen Sie, was mit dem Service passiert: dmesg | grep init: | tail
.
VERWANDTE :
/data/adb/service.d
wäre großartig, aber aus irgendeinem Grund scheinen diese Skripte nicht mit Root-Rechten zu laufen: using mount ...
gibt mir "mount: Permission denied" und using su -c "mount ..."
gibt mir "su: Permission denied". Fehlt mir etwas?
Andreas T.