samedi 16 février 2019


0. Table des matières

Table des matières

introduction

Code: libsuperuser

Comment appeler su
3.1 Pièges courants
3.2 Faire l'appel
3.3 Vérifier la disponibilité de la sous-traitance
3.4 Vérification de la version su
3.5 Les espaces de noms Mount

Quand appeler su
4.1 Quand ne pas appeler su
4.2 Détection du fil principal
4.3 Utiliser AsyncTask
4.4 Utiliser IntentService

SELinux / SEAndroid ( 27 novembre 2014 )
5.1 Introduction
5.2 Qu'est-ce que cela signifie pour vous?
5.3 Détection de SELinux
5.4 Contextes
5.4.1 Principes de base ( 22 décembre 2016 )
5.4.2 init vs init_shell vs shell vs supersu ( 22 déc. 2016 )
5.4.3 Pourquoi changer de contexte?
5.4.4 Comment changer de contexte
5.4.5 Quand changer de contexte
5.4.6 Contexte de système de fichiers et de socket
5.4.7 Manipulation directe du contexte
5.5 Politiques
5.5.1 L'outil supolicy
5.5.2 Patchs par défaut ( 22 décembre 2016 )
5.5.3 Soyez prudent
5.5.4 Audits
5.5.5 Autoriser
5.5.6 Autres déclarations de politique générale ( 22 déc. 2016 )

Enrobage ( 27 nov. 2014 )
6.1 Fichiers
6.2 ROM personnalisées
6.3 Exploits

Mises à jour diverses
X.1 Gobbling ( 17 décembre 2012 )
X.2 Enregistrement de contenu complet ( 23 janvier 2013 )
Paramètres X.3 pour su ( 23 janvier 2013 )
X.4 Autorisation ACCESS_SUPERUSER DEPRECATED ( 26 nov. 2014 )
X.5 Protection contre le détournement de bande et superpositions ( 10 oct. 2014 )
X.6 su.d ( 22 déc. 2016 ) 



1. Introduction

Depuis que j'ai écrit SuperSU, j'ai rencontré beaucoup de problèmes d'implémentation. Des problèmes avec mon propre code dans SuperSU, des anomalies non documentées dans Android et des problèmes dans les applications des autres utilisateurs nécessitant un accès root.

Au fil du temps, j'ai parcouru de nombreux codes sources d'applications (ou inversé les fichiers binaires) pour comprendre la racine des problèmes, et j'ai travaillé avec divers auteurs d'applications (de l'inconnu au célèbre) pour les résoudre.

Grâce à ces efforts, il est devenu évident que la plupart des blocages et des blocages liés à l'accès su - à la fois avec SuperSU et Superuser - découlent de deux problèmes fondamentaux: comment s'appelle su et quand le su est-il appelé. Ces cas ne sont pas aussi simples qu'ils peuvent paraître.

Un certain nombre de développeurs m'ont demandé de rédiger ce guide afin de fournir des lignes directrices et des informations supplémentaires sur la manière de résoudre tous ces problèmes - et c'est ce que vous lisez. Comme cela est important pour toutes les applications root, j'ai également demandé à Adam "ChainsDD" Shanks (auteur de Superuser) d'examiner ce document, ce qu'il a fait.

Je ne m'attends pas à ce que ce soit le dernier mot sur la question ou que les exemples de code soient parfaits. J'espère que ce document et le code vous fourniront les informations nécessaires et constitueront généralement un bon début.
- Jorrit "Chainfire" Jongma, auteur de SuperSU

2. Code: libsuperuser

Il existe un code source accompagnant ce document, sous la forme de [libsuperuser @ GitHub] (un projet de bibliothèque contenant du code réutilisable pour appeler su) et de [libsuperuser_example @ GitHub] (un exemple de projet utilisant cette bibliothèque, et montrant quelques techniques pour appeler su en l'arrière-plan).

L’objectif de ces deux projets n’est spécifiquement pas de fournir une bibliothèque parfaite pour répondre à tous vos besoins fondamentaux.
L'objectif est de présenter les techniques que vous pouvez réutiliser dans votre application racine pour résoudre des problèmes courants, avec le moins de code possible. Des techniques avancées telles que, par exemple, maintenir une session en arrière-plan et utiliser cette session lorsque cela est nécessaire ne sont pas couvertes par cet article, afin de rester simple. La bibliothèque inclut toutefois la classe Shell.Interactive pour rendre cela possible. Le code est court, je vous conseillerais simplement de lire la source des deux projets.

Veuillez noter que cette bibliothèque se comporte légèrement différemment en mode débogage (APK non signé), fournissant ainsi une journalisation supplémentaire et des exceptions.
Certaines versions de l'ADT ne définissent / désactivent pas le mode de débogage correctement lors de la signature et de l'exportation du fichier APK final. Vous devez absolument vérifier que vos fichiers APK exportés ne sont pas toujours en train de consigner tous les appels shell avant de les publier!

3. Comment appeler su

3.1. Pièges communs


Runtime.exec () et ProcessBuilder

Il est tentant d'utiliser Runtime.getRuntime (). Exec ("su -c [commande]"); , mais vous devez savoir que [commande] doit être un paramètre unique et peut donc nécessiter une citation.

Malheureusement, les deux citant le paramètre [command] et transmettant les paramètres en tant que variables distinctes à Runtime.exec () ou à ProcessBuilder ne fonctionnent pas de manière cohérente dans toutes les versions d'Android. Cette construction doit donc être entièrement évitée.
Il n’est pas impossible de le faire correctement, mais le risque de problèmes est élevé.

Produire un script pour que su s'exécute

Pour résoudre ce problème, divers auteurs d’applications racine ont pris l’écriture de scripts, puis ont appelé su -c / path / to / scriptpour exécuter toutes les commandes.

Cela évite des problèmes potentiels de transmission de paramètres, mais vous créez un fichier temporaire inutile et nécessite que votre chemin d'accès en écriture ne contienne aucun espace.

Et bien que ce dernier soit vrai pour le moment, c’est une mauvaise idée de dépendre de cela. Il peut également y avoir des problèmes liés à SELinux lors de l’utilisation de cette méthode (voir la section SELinux ci-dessous).

Appels su successifs rapides

L'appel su est une opération coûteuse et implique généralement l'exécution d'un peu de code, ainsi que l'exécution d'E / S. C’est une bonne pratique que de bonnes performances de regrouper vos commandes autant que possible. Lancez le moins de processus su possible et exécutez autant de commandes que possible par processus.

De nombreux appels su tout au long du cycle de vie de l'application

Certaines applications doivent passer de nombreux appels su tout au long de leur cycle de vie, mais ne peuvent pas regrouper ces commandes par lot.
Dans un tel cas, vous devriez envisager de démarrer un shell su interactif et de le maintenir actif aux côtés de votre application afin de pouvoir y diriger les commandes si nécessaire. Cela pourrait avoir des effets positifs sur les performances et la réactivité de votre application.

Contrôles codés en dur

L'application de gestion des droits n'est pas toujours /system/app/Superuser.apk . Le nom du paquet n'est pas une constante non plus.L'emplacement du binaire su n'est pas toujours / system / xbin / su . Beaucoup d'applications ont des vérifications codées en dur comme celles-ci pour trouver su.

C'est une mauvaise idée et complètement peu fiable.

En supposant que le binaire su accepte les paramètres
Tous les fichiers binaires ne prennent pas en charge tous les paramètres.

Pire, il y a de bonnes chances que le programme binaire démarre un shell interactif au lieu de générer une erreur si un paramètre inconnu est présent (le paramètre -v pour vérifier la version en est un bon exemple).

Si vous ne le prévoyez pas, votre processus risque de ne jamais reprendre le contrôle de l'appel su et de rester bloqué.

3.2. Faire l'appel

Une méthode courante pour appeler su, qui évite les problèmes connus énumérés ci-dessus, consiste à créer un shell interactif et des commandes de canalisation.

Ceci est fait en appelant Runtime.getRuntime (). Exec ("su"); et extraire les flux d’entrée et de sortie à partir de l’objet Process renvoyé. Faire ceci est un morceau de code assez simple, mais inclure les journaux de débogage et les vérifications est un peu long à reproduire ici.

Le code principal se trouve ici: [libsuperuser :: Shell.java @ GitHub] . Shell.run () est un appel générique pour exécuter du code shell. Les fonctions utilitaires plus spécifiques (statiques) suivantes sont celles que vous allez probablement utiliser:
  List  Shell.SH.run (commande String)
  List  Shell.SH.run (Commandes List )
  List  Shell.SH.run (commandes String [])

  List  Shell.SU.run (commande String)
  List  Shell.SU.run (Commandes List )
  List  Shell.SU.run (commandes String [])
Les variantes SH sont utilisées pour un shell non root, les variantes SU étant utilisées pour un shell racine. Ces appels renvoient une liste contenant la sortie des commandes du shell.

S'il n'y a pas eu de sortie, la liste est vide, mais pas nulle.

Le résultat n'est nul si une erreur survient. un accès refusé peut ou non déclencher la valeur null. Ce sont des appels bloquants .

Notez que dans les compilations de débogage, tous les shell STDIN / STDOUT / STDERR seront consignés dans logcat, et ces appels planteront (intentionnellement) votre application si elle est appelée à partir du thread principal. La raison en est expliquée à la section 4. "Quand appeler su" .

3.3. Vérification de la disponibilité du sous-sol

Il existe de nombreuses façons de vérifier si un accès superutilisateur est disponible ou non. Les deux méthodes les plus populaires sont soit de détecter l’existence du paquet su binary ou superuser, soit d’essayer de faire fonctionner su et de voir ce qui se passe.

Je préfère cette dernière méthode, car exécuter su est ce que vous recherchez, peu importe où il se trouve et si vous savez le trouver - aussi longtemps que le système le permet.

En guise de test, exécutez la commande id , qui affiche les identifiants de l'utilisateur et du groupe actuels. Vous pouvez ainsi utiliser la sortie pour vérifier si le shell que vous avez démarré possède également des droits root (la sortie contient uid = 0 ).

Un problème avec la commande id est que cela dépend d'un binaire externe qui doit être présent.

Je ne me suis jamais trouvé dans la même situation, mais pour être sûr que nous émettons également une commande d' écho que nous vérifions, de sorte que si la commande id n'est pas disponible, nous pouvons toujours savoir si un shell a été exécuté.

Dans ce dernier cas, nous supposons que le shell a également des privilèges root - si cela s'avère être faux, l'utilisateur a des problèmes plus graves que votre application qui ne dispose pas d'un accès root.

Si vous voulez être absolument sûr, même dans ce cas distant d'un périphérique mal enraciné, vous devrez inclure son propre fichier binaire natif pour effectuer la vérification. Bien sûr, si vous incluez des fichiers binaires natifs de toute façon, il est certainement conseillé de les faire vérifier s'ils fonctionnent en tant que root avant de faire autre chose.

[libsuperuser :: Shell.java @ GitHub] fournit la fonction utilitaire (statique) suivante qui effectue ce test pour vous:
 booléen Shell.SU.available ()
Ceci est un appel bloquant .

3.4. Vérification de la version su

Bien que ce ne soit pas quelque chose que la plupart des applications root doivent faire, et que tous les sous-fichiers binaires ne le prennent même pas en charge, cela peut être quelque chose que vous souhaitez faire.

Les sous-fichiers superutilisateur (récents) et les sous-fichiers superSU prennent en charge les paramètres -v (pour l'affichage) et -V (pour la comparaison interne) afin de vérifier le numéro de version.

Comme indiqué ci-dessus, un problème potentiel est qu'un sous-programme qui ne prend pas en charge ces paramètres peut démarrer un shell interactif à la place.

Pour contourner ce problème, vous devez diriger "exit \ n" vers le processus su. Cela assurera que votre application reprend le contrôle de su.

[libsuperuser :: Shell.java @ GitHub] fournit la fonction d'utilitaire (statique) suivante qui extrait ces numéros de version (pour le binaire su, pas pour le package d'interface graphique), ou renvoie null si impossible:
 String Shell.SU.version (booléen interne)
Ceci est un appel bloquant .

3.5. Monter les espaces de noms

Lorsque les versions 1.50 et supérieures de SuperSU sont exécutées en mode démon (Android 4.3+ et d'anciennes versions OEM Android rares avec SELinux paramétré pour l' application ), chaque shell su de processus reçoit un espace de noms de montage isolé.

Cela signifie que les montages appliqués dans un shell su peuvent ne pas être visibles par d'autres processus, à l'exception de la plupart des autres shells su lancés par le même processus parent (votre application).

Dans ce cas, SuperSU tente également de mettre les montages sdcards et les montages spécifiques à l'utilisateur à la disposition du shell su , ce qui facilite, espérons-le, votre travail.

L’une des caractéristiques de cette isolation d’espace de noms de montage est qu’elle empêche les applications d’interférer avec les montages respectifs, de sorte que les conditions de concurrence entre deux applications essayant de manipuler le même montage (basculement / système entre lecture-écriture et lecture-écriture répétées, par exemple) ne peuvent pas.

Ce produit, et tout fonctionne comme le développeur d’application s’attend.

Android a lui-même proposé une isolation d'espace de noms de montage similaire pour les applications de la version 4.2.

Les versions 1.93 et supérieures de SuperSU prennent en charge l'option --mount-master qui (si elle est exécutée en mode démon) connecte votre application à un shell spécial, les commandes de montage étant appliquées à tous les processus.

Si vous devez appliquer "publiquement" un montage, utilisez cette option.

4. Quand appeler su

4.1. Quand ne pas appeler su


Le fil conducteur

N'appelez pas su lors de l'exécution sur le thread principal de l'application.
La principale raison des blocages et des blocages lorsque les applications demandent un accès racine est que su est appelé à partir du thread principal.

Vous devez considérer que la commande su est équivalente à un appel d' E / S bloquant , tel qu'un accès au disque ou au réseau.

Celles-ci ne doivent pas non plus être effectuées à partir du thread principal. 

En fait, sur les versions plus récentes d'Android, effectuer des E / S réseau dans le thread principal plantera (intentionnellement) votre application et, en mode strict, l'écran clignotera en rouge si vous utilisez un disque. I / O sur le thread principal pour vous avertir de votre erreur.

Le blocage d' appels d' E / S sur le thread principal est mauvais car il dépend entièrement de facteurs externes de la durée de l'appel.L'appel peut prendre 100 millisecondes, 30 secondes ou une heure.

Même si vous pensez qu'une commande d'E / S mineure ne devrait jamais prendre plus de quelques millisecondes, vous ne devriez pas le faire depuis le fil principal - vous garantissez que vous ne pouvez pas tenir le coup et vous réduisez probablement la réactivité de votre application.

Si le thread principal de l'application bloque de lasorte pendant plus de quelques secondes, un blocage ANR (Application Not Responding) est généré par le système.

Parce que l'appel su est quasiment garanti d'effectuer le blocage d'E / S lui-même et qu'il peut même avoir besoin d'afficher son interface graphique et d'attendre les entrées de l'utilisateur, vous ne pouvez en déduire le temps qu'il faudra au retour de l'appel. ne devrait jamais l'appeler du thread principal.

J'ai souvent reçu des plaintes d'utilisateurs SuperSU concernant le compte à rebours dans la fenêtre contextuelle de demande d'accès root activée (au moment de la rédaction de cet article) par défaut et refusant automatiquement l'accès root après un certain nombre de secondes.

La seule raison pour laquelle ce minuteur a été créé est que le nombre d'applications root appelant su à partir du thread principal est beaucoup trop important.

Si la fenêtre contextuelle n'arrive pas à expiration et que l'utilisateur n'accorde ou ne refuse pas l'accès root assez rapidement, l'application qui a demandé l'accès su tomberait en panne avec un ANR.

La minuterie aide à réduire(mais pas à éliminer) les chances que cela se produise, mais il ne fait que traiter les symptômes, pas résoudre les problèmes.

Une note sur su étant un appel bloquant

La plupart des méthodes de démarrage d'un nouveau processus sont en réalité non bloquantes et le processus enfant s'exécute dans un autre thread. Cependant, la plupart des exemples et du code de bibliothèque appellent Process.waitFor ou lisent / écrivent à partir de / dans le flux STDIN, STDOUT ou STDERR du processus.

Toutes ces possibilités transforment le code qui appelle su en code bloquant , en attente du processus su .

Bien qu'il soit en fait possible d'appeler su de manière non bloquante à partir du thread principal, il est cependant facile de faire des erreurs de cette façon, et il est souvent plus simple de créer un seul bloc de code qui gère l'appelant su (comme avec l'exemple de shell .XX.run fourni) et exécutez-le simplement à partir d’un autre thread.

Il existe cependant des situations dans lesquelles il est préférable d'appeler su de manière non bloquante (par exemple, garder une session su ouverte en arrière-plan et lire et écrire en continu à partir de celle-ci).

L'exemple de code fournit la classe Shell.Interactive qui peut être utilisée de cette manière à partir du thread principal, des commandes en file d'attente et des rappels, mais l'utilisation de cette classe n'est pas documentée par cet article (lisez plutôt la documentation intégrée dans le code source). ), afin de garder les choses simples.

BroadcastReceivers

La plupart du temps, BroadcastReceiver s s'exécute sur le thread principal (quelque chose est souvent négligé), et donc aucun appel d' E / S bloquant comme su ne devrait être effectué. En plus de s'exécuter sur le thread principal, l'appel su lui-même peut avoir besoin de diffuser une intention de communiquer avec l'interface graphique.

Cela pose un problème parce que la dernière diffusion peut attendre la fin de la diffusion précédente, ce qu’elle ne fera pas tant que la méthode onReceive n’aura pas terminé, ce qui attend à son tour la fin de l’appel su. Cela provoquera un ANR.

Prestations de service

Comme dans le cas de BroadcastReceiver , de nombreux développeurs oublient tout d'abord qu'un service de base s'exécute également sur le thread principal et est donc également sensible aux ANR. Autrement dit, à moins que vous n'utilisiez une sous-classe de servicespéciale (telle que IntentService ) qui utilise un thread en arrière-plan, ou que vous ajoutiez vous -même un thread en arrière-plan au service.

4.2. Détecter le fil principal

La détection de l'exécution de votre code sur le thread principal s'effectue généralement comme suit:


 if (Looper.myLooper () == Looper.getMainLooper ()) {
   // en cours d'exécution sur le thread principal
  } autre {
   // ne fonctionne pas sur le thread principal
  }

Vous avez peut-être remarqué ce code dans l'appel Shell.run () dans [libsuperuser :: Shell.java @ GitHub] . S'il est détecté que vous exécutez sur le thread principal et que le projet Android est compilé en mode débogage ( BuildConfig.DEBUG == true ), une exception est générée et votre application se bloque. J'espère que cela vous convaincra d'essayer d'exécuter votre code shell dans un thread d'arrière-plan - et de ne pas supprimer la vérification!

4.3. Utiliser AsyncTask

La classe AsyncTask est souvent utilisée pour effectuer un traitement en arrière-plan rapide et facile pour des opérations relativement courtes. Vous pouvez appeler en toute sécurité su à partir de la méthode doInBackground de l'AsyncTask . Voici un exemple d'implémentation minimale dans une activité :


 Classe publique MainActivity étend Activity {
   classe privée Startup étend AsyncTask  {
    @Passer outre
    protected Void doInBackground (Void ... paramètres) {
     // cette méthode est exécutée dans un thread en arrière-plan
     // pas de problème en appelant su ici

     return null;
    }
   }

   @Passer outre
   public void onCreate (Bundle savedInstanceState) {
    super.onCreate (savedInstanceState);
    setContentView (R.layout.activity_main);

    // lance la tâche en arrière-plan
    (new Startup ()). execute ();
   }
  }

Bien entendu, vous souhaiterez généralement exécuter du code avant et après l'exécution de la tâche en arrière-plan, par exemple pour afficher et masquer un ProgressDialog afin que l'utilisateur sache qu'une action en arrière-plan est en cours d'exécution et que l'interface graphique ne pourra pas être utilisée tant que cette action ne sera pas terminée: libsuperuser_example :: MainActivity.java @ GitHub] contient un exemple de base.

Veuillez noter que rien n'est parfait et qu'AsyncTask a également quelques problèmes. Par exemple, l' AsyncTask continue à s'exécuter jusqu'à ce qu'il soit terminé, même si l' Active propriétaire est fermée, sauf si vous appelez la méthode cancel de l' AsyncTask et le gérez dans votre méthode doInBackground (en vérifiant périodiquement l'état annulé et / ou en vous assurant que méthode est interruptible ).Par exemple, l'utilisateur faisant pivoter le périphérique peut entraîner la fermeture et la recréation de votre activité , en relançant la tâche Async pendant que l'ancienne tâche est toujours en cours d'exécution.

Ce ne sont pas des problèmes insurmontables, mais comme pour tout outil que vous utilisez, vous devez savoir quand, comment et quand ne pas l'utiliser.

4.4. Utiliser IntentService


Bien que AsyncTask soit une classe très utile que vous utiliserez sans doute souvent, ce n'est parfois pas l'outil idéal pour le travail.

Par exemple, vous ne pouvez pas utiliser directement une tâche asynchrone à partir d'un BroadcastReceiver , car une fois la méthode onReceive terminée, votre processus peut ne plus avoir aucun composant actif et donc être tué. La bonne chose à faire pour toute entrée / sortie bloquante - y compris les appels su - depuis un BroadcastReceiver est de démarrer un service et d’exécuter le code à partir de là.

Cependant, un service standard est également exécuté sur le thread principal, à moins que vous ne fassiez le travail supplémentaire nécessaire pour exécuter le code dans un thread en arrière-plan. Cependant, la classe IntentService est une sous-classe de servicefacile à utiliser , conçue spécifiquement pour exécuter des tâches (exprimées par Intentit ) dans un fil d’arrière-plan et s’arrêter automatiquement quand elle est à court de travail. Parfait pour les tâches incessantes.

De nombreuses applications utilisent un BOOT_COMPLETED BroadcastReceiver pour effectuer certains traitements après le démarrage du périphérique, sans interaction de l'utilisateur - un cas parfait pour IntentService.

Votre fichier AndroidManifest.xml ressemblera à ceci: un BroadcastReceiver public (exporté) et un IntentService privé (non exporté):
 
   ...
    </ intent-filter>
   </ receiver>
   ...
  </ application>
Ensuite, nous créons un IntentService très basique dans BackgroundIntentService.java :
 La classe publique BackgroundIntentService s'étend à IntentService {
   public statique void launchService (contexte de contexte) {
    if (context == null) return;
    context.startService (nouvelle intention (context, BackgroundIntentService.class));
   }

   public BackgroundIntentService () {
    super ("BackgroundIntentService");
   }

   @Passer outre
   protected void onHandleIntent (Intention Intention) {
    // le code que vous avez mis ici sera exécuté dans un fil de fond
   }
  }

Il ne reste plus qu’à lancer ce service d’arrière-plan à partir de votre BroadcastReceiver , dans BootCompleteReceiver.java :


 Classe publique BootCompleteReceiver étend BroadcastReceiver {
   @Passer outre
   public void onReceive (contexte de contexte, intention d'intention) {
    BackgroundIntentService.launchService (contexte);
   }
  }

C'est tout ce qu'il y a à faire - une fois que vous savez comment faire, c'est incroyablement facile. Bien entendu, cet IntentServicen'effectue qu'une seule action et ne prend aucun paramètre: [libsuperuser_example :: BackgroundIntentService.java @ GitHub] contient un exemple plus élaboré.

Au lieu de s'exécuter dans votre BroadcastReceiver sur le thread principal, votre code s'exécute maintenant en toute sécurité dans un thread d'arrière-plan sans risquer un blocage de l'ANR. Il y a cependant un petit problème. De nombreuses applications envoient Toast s depuis leur BroadcastReceiver s - cela est un peu plus difficile à faire à partir d’un fil d’arrière-plan en raison de quelques bugs mineurs dans le cadre Android.
Reportez-vous à [libsuperuser :: Application.java @ GitHub] pour une solution de contournement.

5. SELinux / SEAndroid

5.1. introduction


SELinux est l'abréviation de NSA Security-Enhanced Linux et fournit un contrôle d'accès détaillé allant au-delà des limites du contrôle d'accès basé sur uid / gid. SEAndroid est son port Android, que cet article appellera également SELinux. Il est utilisé dans l'un des deux modes suivants: mode permissif où les violations de stratégie sont consignées mais aucune action n'est entreprise, et mode d' application où les violations de stratégie sont empêchées.

SELinux est présent en stock sur Android depuis la 4.3 (API niveau 18, JELLY_BEAN_MR2 ) en mode permissif , et a été basculé en mode de mise en application sous Android 4.4 (API niveau 19, KITKAT ).

Cependant, vous ne devriez pas dépendre de ces niveaux d'API pour détecter la présence ou le mode de SELinux, car il existe même des firmwares de type 4.2 (!) Dans la nature avec SELinux intégré et configuré pour être appliqué , ainsi que dans le cas beaucoup plus courant de 4.4 firmwares en cours d'exécution mode permissif .

5.2. Qu'est-ce que cela signifie pour vous


En tant que développeur d'applications root, vous devez apprendre à gérer SELinux. Vous n'avez pas besoin de connaître tous les détails de ce système très complexe et des règles utilisées par les actions, ainsi que des versions personnalisées OEM, mais en fonction du type d'application racine que vous créez, vous devrez peut-être passer plus de temps tester sur différents firmwares et appareils pour que les choses fonctionnent de manière fiable.

Pour aggraver les choses, SELinux est une cible mouvante, les règles changeant entre les versions Android et même les constructeurs OEM. Les règles applicables (par exemple) à un appareil Samsung peuvent être très différentes de celles appliquées à un appareil Nexus.

Si SELinux est défini sur le mode permissif , vous aurez relativement peu à vous inquiéter, mais lorsqu'il est configuré pour appliquer la loi , la partie de votre application exécutée en tant que root peut être soumise à toutes sortes de restrictions inattendues.

Les versions 2.11 et supérieures de SuperSU corrigent activement les stratégies SELinux à partir d'Android 4.4, contournant ainsi un grand nombre de problèmes qu'une application racine rencontrerait autrement sur un système d' application . À partir de la version 2.23 de SuperSU , la plupart des cas où vous auriez besoin d'écrire un code spécial sont couverts par les derniers correctifs de stratégie.Néanmoins, il est bon de lire le reste de cette section pour savoir comment cela fonctionne si vous rencontrez un cas particulier.

Notez que divers noyaux et firmwares personnalisés commutent SELinux sur Permissive pour Android 4.4 et les versions plus récentes.Cela désactive complètement toutes les nouvelles fonctionnalités de sécurité apportées par SELinux, plutôt que de relâcher uniquement les zones dont nous avons absolument besoin pour que nos applications fonctionnent.

Il est rare qu'une application racine exige des correctifs supplémentaires dans la stratégie actuelle de SELinux, à part ce que SuperSU fait déjà pour vous, mais si cela est nécessaire, une API est fournie à cet effet. Il appartient bien entendu à l'utilisateur de décider si SELinux doit être facultatif ou non, mais il est certainement recommandé de veiller à ce que vos applications fonctionnent sur un système d' application .

On parle beaucoup de différentes versions de SuperSU . Ceci n'est listé que par souci d'exhaustivité, car de nombreux détails ont changé entre la première version Android 4.4 «commerciale» et la première version Android 5.0 «commerciale». Les utilisateurs d'Android 5.0 doivent être considérés comme utilisant la version 2.23 ou une version plus récente.

5.3. Détection de SELinux

Bien qu'il soit probablement sage de vous assurer que votre code s'exécute indépendamment de la présence ou du mode de SELinux, vous devrez parfois détecter si SELinux est présent et définir le respect . Cela peut généralement être fait en lisant / sys / fs / selinux / enforce , qui est un fichier lisible par tout le monde.
Pour un exemple de code, voir l'appel Shell.SU.shell () dans [libsuperuser :: Shell.java @ GitHub]

5.4. Les contextes


5.4.1. Les bases


Le contexte SELinux actuel définit les stratégies de sécurité applicables à votre processus. Le contexte de mode utilisateur 'le plus élevé' est généralement u: r: init: s0 .

Contrairement à ce à quoi vous pouvez vous attendre, cela ne signifie pas nécessairement que ce contexte a accès à tout. Certains contextes courants avec lesquels vous pouvez entrer en contact:
  1. u: r: kernel: s0 - Contexte du noyau
  2. u: r: init: s0 - Le plus haut contexte init usermode
  3. u: r: init_shell: s0 - Shell démarré depuis init
  4. u: r: shell: s0 - shell non privilégié (comme un shell adb)
  5. u: r: serveur_système: s0 - serveur_système , u: r: système: s0 sur certains firmwares
  6. u: r: system_app: s0 - Applications système
  7. u: r: platform_app: s0 - Applications système
  8. u: r: untrusted_app: s0 - Applications tierces
  9. u: r: recovery: s0 - Récupération
  10. u: r: supersu: s0 - Le propre contexte de SuperSU, v2.79-SR1 + sur Android 7.0+
Les numéros 4 à 9 peuvent être utilisés avec l'option de contexte -cn / - de SuperSU . Avec v2.60 + sur Android 5.0+, vous pouvez basculer vers tous les contextes avec cette option.

Les stratégies par défaut qui composent ces contextes se trouvent sous external / sepolicy dans l’ arborescence de votre source Android. Notez que les constructeurs ont tendance à modifier ces stratégies et que ces stratégies changent entre les versions Android.

Pour assurer la compatibilité, votre application doit être testée sur toutes les versions d'API et, si possible, sur les appareils phares de tous les principaux constructeurs. Si vous ne pouvez pas le faire vous-même, dépendez de vos utilisateurs principaux.

5.4.2. init vs init_shell vs shell vs supersu

Sur les firmwares qui utilisent SELinux, su est généralement implémenté en tant que proxy pour un démon démarré à partir d' init . Il est important de noter que les coquilles su peuvent fonctionner comme suit: init: s0 , u: r: init_shell: s0 ou u: r: supersu: s0 . SuperSU lui-même devrait toujours fonctionner comme suit : u: r: init: s0 ou u: r: supersu: s0 si SELinux est configuré pour faire respecter , mais tous les shells de superutilisateur ne le font pas.

Il existe différentes manières de changer de contexte. Pour ce cas particulier, passer de u: r: init: s0 à u: r: init_shell: s0 peut être effectué en lançant un deuxième sh (faites attention à ne pas utiliser mksh car il est obsolète). . Si vous avez une commande à exécuter en tant que u: r: init_shell: s0 , enroulez-la simplement dans sh -c "... commande ..." , pour vous en assurer.

Adb utilise le contexte u: r: shell: s0 , qui a des politiques très différentes. Attention à ne pas les confondre!

5.4.3. Pourquoi changer de contexte?

Vous vous demandez peut-être pourquoi - si nous fonctionnons déjà en tant que contexte init , en tant qu'utilisateur root et avec SuperSU corrigeant activement les stratégies SELinux - avons-nous encore besoin de changer de contexte?

Si vous exécutez la commande su sur un système exécutant SuperSU , vous vous retrouvez dans un shell totalement illimité. Hormis les manipulations de bas niveau restreintes par le chargeur de démarrage et / ou TrustZone (des histoires totalement différentes), il n'y a rien que vous ne puissiez pas faire dans ce shell.

Android dans son ensemble est un système complexe englobant de nombreux processus, fonctionnant sous différents utilisateurs et différents contextes. La politique SELinux définit les règles pour les transitions et la communication entre elles. Le fait qu’il n’y ait pas de restrictions pour notre shell ne signifie pas qu’aucune restriction ne s’applique aux autres processus que nous devons traiter.

Les stratégies SELinux ne sont pas bidirectionnelles. Par conséquent, même si nous pouvons parler à d'autres processus, ces processus restreints risquent de ne pas pouvoir nous répondre. La solution consiste à nous déguiser pour permettre à ces processus de nous parler: changer de contexte.

Bien sûr, nous pourrions également libérer ces autres processus de leurs chaînes SELinux, mais poursuivre dans cette voie finira par annuler complètement tous les avantages en termes de sécurité que SELinux peut apporter.

La ligne doit être tracée quelque part, et pour SuperSU, la ligne a été tracée sur la base de la nécessité. Il n'est pas nécessaire d'assouplir davantage les stratégies SELinux pour l'exécution de ces commandes. Par conséquent, nous n'assouplissons pas davantage les stratégies SELinux, même si cela constitue un léger inconvénient pour les développeurs d'applications root.

5.4.4. Comment changer de contexte

Il y a plusieurs façons de changer de contexte. Parfois, l’exécution d’un certain binaire provoque automatiquement un changement (comme aller de u: r: init: s0 à u: r: init_shell: s0 en exécutant sh depuis init ). La plupart du temps, vous devrez utiliser une forme plus explicite de changement de contexte.

La commande runcon (disponible dans la boîte à outils depuis certaines versions d'Android 4.1) exécute une commande en tant qu'argument de contexte fourni, en supposant que vous êtes autorisé à effectuer cette transition de contexte.

La commande run-as (disponible depuis Android 2.2.3) imitera de la même manière un package spécifique (non-système) et son contexte.

Malheureusement, ces deux commandes ne peuvent généralement pas être utilisées, car elles ne fonctionnent que dans des circonstances très spécifiques. Par conséquent, les versions 1.90 et supérieures de SuperSU prennent en charge le paramètre -cn ou --context pour exécuter vos appels su dans un certain contexte.

Voir l'appel Shell.SU.shell () dans [libsuperuser :: Shell.java @ GitHub]pour savoir comment construire une commande de shell interactive avec cette syntaxe. À l’heure actuelle, seul le SuperSU est d’accord avec cette idée (merci de me faire savoir si cette situation change, comme je l’attendais).

Les versions 1.97 et supérieures de SuperSU sont requises pour la compatibilité de cette fonctionnalité avec Android 4.4 ou ultérieure.Le serveur: system: s0 , u: r: system_app: s0 , u: r: platform_app: s0 et u: r: untrusted_app: s0 sont actuellement pris en charge à partir de v1.97 et v2.00 ajoute u: r: shell: s0 et s u: r: récupération: s0 (si disponible). Mon conseil est d'utiliser la variante untrusted_app autant que possible car c'est la variante la moins privilégiée et la plus susceptible de rester disponible à long terme. Si vous rencontrez des problèmes, essayez la variante system_app . Notez que la variante system_server ne fonctionne déjà pas sur tous les périphériques, utilisez-la uniquement lorsque vous en avez absolument besoin et testez-la correctement.

Veuillez noter que l'option de contexte -cn / - est conçue pour fonctionner quelles que soient la version et l'état de la plate-forme. Si SELinux n'est pas présent ou défini sur permissif , la commande sera simplement exécutée en tant que contexte u: r: init: s0 .

5.4.5. Quand changer de contexte

La commutation de contexte est un processus compliqué, et je conseille de l'utiliser avec parcimonie. Non seulement plusieurs processus sont impliqués, mais le traitement des entrées et des sorties est également effectué différemment des autres commandes exécutées via su . Ne soyez pas surpris lorsque vous utilisez un shell adb à des fins de test, si votre invite de terminal disparaît - le contexte que vous venez de basculer peut ne pas prendre en charge l'accès au terminal, par exemple.

Vous aurez besoin de découvrir par vous-même ce qui ne fonctionne pas exactement comme le contexte u: r: init: s0 et qui nécessite un changement de contexte. Il n’existe pas de liste de commandes problématiques. Traditionnellement, toutes les commandes qui lancent du code Java doivent cependant être exécutées dans l'un des contextes * _app , bien que nombre d'entre elles fonctionnent correctement sans le faire.

Comme exemple (étrange), effaçons la carte sd interne, désinstallez le paquet com.example.app et le cache Dalvik. Les commandes suivantes seraient toutes dirigées vers un shell su et seraient donc exécutées en tant que root:


 boîte à outils rm -rf / data / media / *
 su --context u: r: system_app: s0 -c "pm désinstaller com.example.app" </ dev / null
 boîte à outils rm -rf / data / dalvik-cache / *

Des extraits intéressants sur cet exemple:
  • su est appelé à nouveau depuis l'intérieur d'un shell su afin de minimiser le risque de rencontrer des restrictions pour les autres commandes, et un changement de contexte n'est effectué qu'en cas de besoin.
  • u: r: system_app: s0 est utilisé à la place de u: r: untrusted_app: s0 car ce dernier peut ne pas être autorisé à désinstaller une application.
  • Le flux d’entrée du shell su de commutation de contexte est acheminé depuis / dev / null , afin d’empêcher que cette commande engloutisse toutes les données du flux d’entrée (du fait que l’E / S a traversé plusieurs processus), empêchant ainsi l’effacement de / data / dalvik-cache de se produire. Cela n’est nécessaire que dans certaines circonstances: lorsque vous transmettez des commandes au shell su et que vous envoyez la commande suivante avant d’attendre le résultat de la commande précédente.
Remarque: cet exemple spécifique n'est en réalité plus pertinent à partir de la version 2.23 de SuperSU , car la commande pm fonctionne désormais à partir du contexte u: r: init: s0 , mais il montre toujours comment exécuter des commandes dans un contexte différent, le cas échéant. survenir.

5.4.6. Contexte de système de fichiers et de socket

Les mentions précédentes de changement de contexte ont été appliquées aux processus. Les objets de système de fichiers et les sockets ont également un contexte SELinux associé. Dans le cas du système de fichiers, ceux-ci peuvent facilement être modifiés à tout moment à l'aide de la commande toolcon chcon . Les sockets, cependant, sont un autre problème - leur contexte ne peut être défini que lors de la création du socket.

Par exemple, si vous écrivez votre propre service démon, il est possible que vous communiquiez entre deux contextes qui ne peuvent normalement pas accéder aux sockets de chacun. Vous pouvez lancer le démon à partir d'un contexte différent, mais cela peut entraîner d'autres problèmes.

Une autre option consiste à modifier le contexte du socket en un élément pouvant être utilisé par les deux processus. Cela peut être fait via procfs comme indiqué ci-dessous. Définissez le contexte juste avant de créer le socket et définissez-le ensuite.

5.4.7. Manipulation directe du contexte

Vous pouvez manipuler différents contextes SELinux pour votre processus directement via procfs : voir / proc / self / attr / * . De même, les paramètres globaux SELinux peuvent être accessibles via sysfs : / sys / fs / selinux / * .

De loin, la plupart des développeurs d’application root ne devraient jamais avoir besoin d’accéder manuellement à ces éléments.

5.5. Politiques


5.5.1. L'outil supolicy


Le supolicy outil est livré avec SuperSU versions 2.11 et fonctionne sur 4.4 et plus récents firmwares. Son utilisation principale est de modifier la politique actuelle de SELinux, bien qu'elle fournisse également d'autres fonctionnalités (qui vont au-delà de la portée de ce document).
SuperSU exécute l' outil supolicy lorsque le démon est démarré au démarrage. Ensuite, il exécute tous les exécutables de su.d et appelle setprop supolicy.loaded 1 .
Le paramètre --live de la commande supolicy corrige la stratégie SELinux actuelle avec les correctifs principaux pour SuperSU et toute modification supplémentaire que vous ajoutez sur la ligne de commande. Corriger et recharger les politiques SELinux est un appel très coûteux et doit être effectué le moins possible. Il est conseillé de surveiller si vous avez exécuté vos correctifs à l'aide d'un booléen statique , car cela maintiendra son état entre les lancements d'applications, tant qu'Android n'effacera pas complètement votre application de la mémoire.
Le paramètre --live prend autant d'instructions de stratégie (expliquées ci-dessous) que vous pouvez lui donner , tant que vous ne dépassez pas la longueur maximale de la ligne de commande, ce qui est garanti => 4096 octets. Chaque déclaration de politique doit cependant être dans un paramètre unique. Vous devez donc les envelopper entre "guillemets". Vous pouvez séparer plusieurs instructions de politique à l'intérieur des guillemets avec un point-virgule; ou utilisez simplement plusieurs paramètres cités. Il est possible de diviser les correctifs en plusieurs appels Supolicy , mais en raison de la nature coûteuse de l'appel, vous ne devez pas le faire à moins que vous n'ayez une très bonne raison de le faire. Une fois l'appel renvoyé, les stratégies sont actives.
En outre, il est extrêmement rare de devoir appliquer des correctifs aux stratégies . Neuf cent fois sur cent, vous pouvez accomplir ce que vous voulez faire sans appliquer de correctifs, veuillez donc vérifier de manière approfondie si vous devez appliquer des correctifs.
Il y a eu un débat sur la question de savoir si les correctifs de politique ont besoin d'une notification contextuelle spéciale ou d'une notification dans SuperSU . Cela ne se produit pas car il n'y a rien de spécial à propos des stratégies de correction. Tout processus s'exécutant en tant que root dans le contexte u: r: init: s0 peut le faire. Ainsi, si une application reçoit la racine, elle peut utiliser son propre code pour appliquer des correctifs aux stratégies plutôt que d'utiliser l' outil supolicy , et l'utilisateur final continue Je ne le saurais pas. En guise de compromis, l' outil Supolicy enregistre tous les correctifs de stratégie dans les journaux Android (logcat).

5.5.2. Patchs par défaut

Mis à part divers petits correctifs de stratégie qui ouvrent différents chemins de communication entre les processus su , les modifications majeures apportées à la stratégie de stock proposée par supolicy permettent de rendre init , init_shell (v2.22 +) et la récupération permissive .
La logique derrière cela est que, mis à part les processus su et init et recovery, rien ne devrait fonctionner dans ces contextes, nous ne rendons donc rien d'autre plus exploitable qu'il ne l'était déjà (contrairement à ce qui rend tout le système permissif ).
Si vous vous demandez pourquoi nous n'utilisons pas le contexte su -standard AOSP , c'est parce qu'il est filtré sur de nombreux firmwares 'de détail'.
À partir de la version 2.7-SR1 + sur Android 7.0+, au lieu des modifications énumérées ci-dessus, tout ce qui concerne SuperSUs'exécute dans son propre contexte supersu . Le contexte init est seulement suffisamment modifié pour permettre au démon de basculer sur le contexte supersu selon les besoins.

5.5.3. Faites attention

En théorie, assouplir les mesures de sécurité ouvre toujours un trou quelque part. La gravité d'un tel trou doit être soigneusement prise en compte avant d'apporter des modifications - il est assez facile d'ouvrir des trous majeurs. En règle générale, si vous ajoutez permettre des politiques avec un * _APP classe comme source ou classe cible, vous êtes très probablement faire quelque chose que vous ne devriez pas, et vous devez faire preuve de prudence.

5.5.4. Audits

Sur la plupart des firmwares, si vous essayez de faire quelque chose qui est bloqué par SELinux, un message d’audit apparaît dans dmesg , logcat ou dans un fichier journal quelque part sur / data. Vous pouvez les utiliser pour repérer les stratégies que vous devrez peut-être appliquer, ou au moins pour obtenir des indications sur les problèmes de votre application. Notez que les messages d'audit sont également produits pour des contextes permissifs , même si rien n'est vraiment bloqué.
Si quelque chose ne fonctionne pas comme prévu dans votre application racine, vous devez d'abord vérifier les messages d'audit. Un message d’audit typique peut ressembler à ceci (exemple de StickMount ):
 W / sdcard (216): type = 1400 audit (0.0: 53): avc: refusé {getattr} pour chemin = "/ data / media / 0 / usbStorage / sda1" dev = "sda1" ino = 1 scontext = u: r: sdcardd: s0 tcontext = u: object_r: sans étiquette: s0 tclass = dir permissive = 0
La carte SD processus, avec le contexte de la source u: r: sdcardd: s0 n'a pas la getattr permission (de la classe dir ) sur un objet ( / data / media / 0 / usbstorage / sda1 ) avec u: object_r: non marqué: s0 contexte . Les quatre variables que vous devez lire dans cette ligne sont:
  • classe source: sdcardd (scontext = ...)
  • classe cible: sans étiquette (tcontext = ...)
  • classe de permission: dir (tclass = ...)
  • permission: getattr ({...})

5.5.5. Permettre

L'exemple ci-dessus provient de StickMount , une application racine qui vous permet de monter des clés USB dans un sous-répertoire de votre stockage interne. Toutes les lectures et écritures de stockage internes à partir d'applications tierces passent par le processus démon sdcard , qui s'exécute dans le contexte sdcardd . La plupart des montages se déroulent sans incident, car lorsque vous montez un stockage, vous pouvez indiquer au système quelle étiquette de sécurité (contexte) utiliser pour les fichiers. Cependant, si un système de fichiers n'est pas pris en charge par le noyau lui-même (dans ce cas, exFAT sur un Nexus 9), le système de fichiers doit être monté via FUSE, qui peut ne pas prendre en charge la définition d'une étiquette de sécurité. Et ainsi, les fichiers apparaissent comme non étiquetés , ce que sdcarddle contexte n'est pas autorisé à toucher. Lorsque nous essayons d'accéder au répertoire monté avec un explorateur de fichiers, le message d'audit indiqué ci-dessus est généré.
Ce que nous voulons maintenant, c’est élaborer une déclaration de politique relative à l’ autorisation permettant de résoudre ce problème. Nous pouvons le faire sans trop de risque, comme non étiquetés fichiers ne sont pas censés exister nulle part ailleurs sur le système, et seulement sdcard fonctionne en tant sdcardd contexte (il est pas comme nous rapiéçage untrusted_app , qui devrait être évité en général). Autoriser les déclarations de stratégie sous cette forme (si vous êtes familier avec les fichiers de stratégie source .te, c'est très similaire):
 autoriser l'autorisation classe classe classe source
classe-source , classe - cible et permission (donc non -classe-permission ) peuvent être des collections, comme indiqué par des accolades:
 allow {source1 source2} {cible1 cible2} permission-class {permission1 permission2}
Ceci est étendu à:
 autoriser source1 cible1 classe de permission permission1
 autoriser source1 cible1 permission-classe permission2
 autoriser source1 cible2 classe de permission permission1
 autoriser source1 cible2 classe de permission permission2
 autoriser source2 cible1 permission-classe permission1
 autoriser source2 cible1 permission-classe permission2
 autoriser source2 cible2 classe de permission permission1
 autoriser source2 cible2 classe d'autorisation permission2
Dans notre exemple, l'instruction allow devient:
 autorise sdcardd sans étiquette dir getattr
Et cela peut être appliqué comme ceci:
 supolicy --live "autoriser sdcardd sans étiquette dir getattr"
Si vous essayez à nouveau d'accéder à notre répertoire monté avec un explorateur de fichiers, le message d'audit suivant s'affiche:
 W / sdcard (215): type = 1400 audit (0.0: 60): avc: denied {lecture} pour nom = "/" dev = "sda1" ino = 1 scontext = u: r: sdcardd: s0 tcontext = u: object_r: unlabeled: s0 tclass = dir permissive = 0
Une autre permission manquante ( lecture ) de la même classe ( dir ), nous mettons à jour notre déclaration de politique pour:
 supolicy --live "autorise sdcardd sans étiquette dir {getattr read}"
Nous pouvons continuer ce processus pendant un certain temps et nous aurons une liste de règles à corriger. Dans ce cas, vous pouvez simplement vouloir ajouter toutes les autorisations de la classe dir (et, comme vous le découvrirez plus tard, le fichierégalement). Mais comment les trouvez-vous? Vous pouvez parcourir le dossier external / sepolicy de votre arborescence source AOSP, qui les répertorie tous, ou vous pouvez utiliser la commande supolicy --dumpav , qui répertorie toutes les stratégies en cours, et dérober les autorisations correspondantes. En fin de compte, voici les règles appliquées par StickMount :
 supolicy --live
  "allow sdcardd unlabeled dir {ajouter create execute write relabelfrom link unlink ioctl getattr setattr lecture rename verrouille monte quotaon swapon rmdir audit_access add_name reparent execmod recherche ouverte}"
  "autoriser sdcardd fichier non étiqueté {ajouter créer écriture relabelfrom lien lier ioctl getattr setattr lecture lire renommer verrouiller mounton quotaon swapon audit_access ouvert}"
  "autoriser l'association d'un système de fichiers sans étiquette sans étiquette"

5.5.6. Autres déclarations de politique

Les autres déclarations de politique soutenues par supolicy à ce moment sont:
    refuser l'autorisation classe d'accès classe classe source
    classe permissive
    classe d'exécution
    attribut de classe attradd
    attribut de classe attrdel    

v2.79-SR1 +:    
    créer une classe
    auditallow classe-source classe-cible permission-classe
    auditdeny classe-source classe-cible autorisation-classe
    allowxperm ioctl plage-classe classe-cible classe-classe autorisation
Tous les paramètres peuvent être des collections, à l'exception du paramètre permission-class . Le paramètre permission peut être *pour tout sélectionner (v2.79-SR1 +). Il est très peu probable que vous ayez besoin d'une déclaration autre que celle de permettre des tests. Cela étant dit, si vous proposez une raison valable pour utiliser l'un de ces éléments, il est toujours conseillé d'utiliser uniquement les instructions qui assouplissent la sécurité ( autoriser , autoriser , attradd ) plutôt que de l'appliquer davantage ( nier , appliquer , attrdel). . Avec ce dernier, vous pouvez par inadvertance interrompre d’autres applications root exécutées sur le périphérique.
Si vous lisez à partir du dossier external / sepolicy de votre arborescence AOSP, il convient également de noter que les règles non autorisées sont une opération de compilation et qu'elles n'apparaissent pas dans les fichiers de stratégie. SELinux refuse par défaut et n'autorise que ce que vous déclarez explicitement autorisé. La plupart des instructions source utilisent des collections qui sont finalement étendues à un grand ensemble de règles. L' instruction neverallow s'assure simplement que si une instruction allow existe, elle est supprimée. L' instruction Neverallow n'est ni stockée ni appliquée dans le fichier de règles résultant. Il n'est pas nécessaire de les contrer autrement que de permettre tout ce dont votre application a besoin.



6. Enrobage


6.1. Des dossiers

Tous les fichiers dont vous avez besoin se trouvent dans le dernier fichier ZIP compressible SuperSU. La dernière version "stable" peut toujours être récupérée sur mon serveur . Pour la dernière version "bêta", vous devez consulter le fil de discussion bêta dans les forums SuperSU .
Le script d'installation dans le ZIP est META-INF / com / google / android / update-binary . Ce n'est pas un binaire comme son nom l'indique, mais un script shell et le fichier de script de mise à jour de la mise à jour standard de ZIP est juste un mannequin.
Ce script contient des commentaires indiquant quels fichiers vont où, sur quel niveau d'API et quels droits d'accès, propriétaires et étiquettes de sécurité doivent être. Si vous avez fait exactement cela correctement, SuperSU ne se plaindra pas lorsque vous ouvrez l'application après le démarrage.

6.2. ROM personnalisées

Il n’est pas anodin d’inclure SuperSU exactement de la même manière. Il est donc souvent plus facile d’inclure le dernier ZIP SuperSU dans votre propre ZIP flashable et de chaîner son installation.
Ajouter le dernier zip SuperSU en tant que supersu / supersu.zip dans la ZIP de votre ROM et ajouter les lignes suivantes à la fin de votre script de mise à jour fera exactement cela:
 package_extract_dir ("supersu", "/ tmp / supersu");
 run_program ("/ sbin / busybox", "unzip", "/tmp/supersu/supersu.zip", "META-INF / com / google / android / *", "-d", "/ tmp / supersu") ;
 run_program ("/ sbin / busybox", "sh", "/ tmp / supersu / META-INF / com / google / android / update-binary", "dummy", "1", "/ tmp / supersu / supersu. Zip *: français");
Vous pouvez télécharger ici un exemple de fichier ZIP, qui installe SuperSU v2.30 à partir de celui-ci, testé avec TWRP 2.8.2.1 sur un Nexus 9 fonctionnant sous Android 5.0.
De plus, vous devriez voir la section sur l'outil supolicy , car elle décrit les propriétés définies et les scripts appelés une fois que SuperSU a terminé de corriger les stratégies et que les appels racine proviennent du contexte init sans restriction.

6.3. Exploits

Au cours des dernières années, de nombreux exploits ont installé SuperSU comme moyen de racine persistante. L'exploit laisse souvent le système dans un état instable, et une installation correcte et longue peut ne pas être possible. L'APK peut corriger une installation partielle tant que la racine de base fonctionne. Au moment d'écrire ces lignes, cela signifie au moins que ces fichiers doivent être présents, ainsi que la bonne architecture et la quantité de bits pour le microprogramme (voir le script ZIP pour les autorisations et les niveaux d'API):
  • / system / xbin / su
  • / system / xbin / daemonsu
  • / system / xbin / supolicy
  • /system/lib(64)/libsupol.so
De plus, daemonsu --auto-daemon doit être lancé d’une manière ou d’une autre au démarrage. Cela se fait généralement par install-recovery.sh , 99SuperSUDaemon ou détournement d' avion app_process ([32 | 64]) .
Vous pouvez également inclure le fichier ZIP et exécuter le script d'installation de SuperSU. Pour que cela fonctionne, au moment d'écrire ces lignes, les commandes suivantes doivent être disponibles sur le PATH:
 chat, chmod, chown, cp, dd, écho, grep, ln, losetup, ls, mkdir, mknod, montage, mv, readlink, rm, rmdir, sh, sommeil, sync, décomposer, décompresser
De plus, sh a besoin d' un support de test (les crochets [] fonctionnent dans les instructions if ). En plus de décompresser , tous ces éléments doivent être présents sur un appareil Android 4.3+ pleinement démarré. Si ce n'est pas le cas, vous pouvez fournir une boîte à outils (busybox) (compatible SELinux) et symlink ces commandes quelque part sur le chemin PATH. Dernier point mais non le moindre, / tmp devrait être accessible en écriture. Si toutes ces dépendances sont remplies, vous pouvez installer le fichier ZIP comme suit:
 décompressez /path/to/supersu.zip META-INF / com / google / android / * -d / tmp
 sh / tmp / META-INF / com / google / android / dummy-update-binary 1 /path/to/supersu.zip
En raison du fait que le script essaie de différentes manières de prendre en charge différents systèmes et versions de récupération, il génère des erreurs, que l’installation soit réussie ou non. Ignorez-les, redémarrez et voyez si l'interface graphique de SuperSU se plaint lorsque vous l'ouvrez.



X. Mises à jour


X.1. Gober

Le 17 décembre 2012, [libsuperuser @ GitHub] a été mis à jour avec Gobblers pour consommer STDOUT et STDERR. Ce ne sont rien de plus que des threads d'arrière-plan qui consomment aussi rapidement que possible les sorties STDOUT et STDERR. Le comment et le pourquoi sont une longue histoire (si cela vous intéresse, lisez [When Runtime.exec () sera @ JavaWorld] ), mais cela évite des blocages potentiels en cas de sortie excessive sur STDOUT ou STDERR. Si vous utilisez ma bibliothèque, assurez-vous que vous utilisez la dernière version. Si vous n'exécutez pas ma bibliothèque, il peut être judicieux de lire l'article lié et de voir s'il y a un problème avec votre code.

X.2. Enregistrement complet du contenu

SuperSU a une fonctionnalité pour enregistrer tout le contenu de la commande su. Bien que cela fonctionne correctement pour la plupart des applications, certaines applications peuvent rencontrer des problèmes inattendus lorsque cette fonctionnalité est utilisée. Les émulateurs de terminaux en sont un exemple. Ils ne s'afficheront pas à l'invite de commande si un shell su est démarré. SuperSU v0.97 (publié le 29 novembre 2012) et les versions plus récentes prennent en charge un moyen de faire savoir à SuperSU que votre application ne fonctionne pas correctement avec la journalisation complète du contenu activée. Si vous utilisez cette méthode, SuperSU n'active pas la journalisation de contenu complet pour votre application si SuperSU n'a été configuré que pour se connecter par défaut . Si l'utilisateur passe en configuration spécifique à une application, il peut toujours activer manuellement la journalisation de contenu complet pour votre application.Dans ce cas, l'utilisateur recevra un avertissement.
Pour que SuperSU sache que votre application n'est pas totalement compatible avec la journalisation complète du contenu, ajoutez les éléments suivants à une activité , un service ou un BroadcastReceiver :
 
Par exemple:
 
 
  
   
    
      ...
    </ intent-filter>
    
   </ activity>
  </ application>
 </ manifest>
L'ajouter à une seule activité , service ou BroadcastReceiver suffit pour exclure l'intégralité du package de la journalisation complète du contenu. Il n'est pas nécessaire de l'ajouter plusieurs fois.
Veuillez noter que je ne tolérerai pas les abus de cette fonctionnalité. La journalisation complète du contenu est là pour l'utilisateur final et ne devrait pas être désactivée de cette manière sans motif valable. Je peux avoir recours à la liste noire de votre paquet à partir de l'accès root si vous en abusez délibérément.
Autant que je sache, depuis SuperSU v1.39 (publié le 3 juillet 2013), il n'y a plus de problèmes de journalisation complète du contenu et de certaines applications. En tant que tel, cette section peut être obsolète.

X.3. Paramètres pour su

SuperSU l' origine a pris le paramètre de l' analyse syntaxique de super - utilisateur ChainsDD su le binaire. Le 11 janvier 2012, les modifications concernant l'analyse des paramètres ont été transmises à GitHub [fc7479fab2 @ GitHub] de ChainsDD . SuperSU a paramètre mis à jour pratiquement identique de l' analyse syntaxique v1.00 . Bien que cela autorise certaines constructions intéressantes dans l'appel de su , vous devez savoir que toutes les constructions possibles avec l' analyse de paramètre d' origine ne seront pas interprétées de la même manière avec l' analyse de nouveau paramètre.
Je voudrais également souligner spécifiquement que (1) la commande à exécuter, à la suite du paramètre -c ou --command , devrait être un paramètre unique , (2) ce paramètre n'est même pas pris en charge par toutes les variantes su disponibles dans la nature. , et (3) le moyen le plus fiable d’exécuter des commandes en tant que root continue de démarrer su en tant que commandes et sorties shell et piping.
Certaines variantes de su sur certains appareils ne prennent en charge que le démarrage en tant que shell interactif. Paramètre exact analyse syntaxique des plus fonctionnels su binaires diffère par auteur et par la version, parfois très subtilement. Plus la version d'Android de votre application peut fonctionner sur, plus la chance de courir dans une espèce exotique ou incompatible su binaire. Vous seriez surpris de ce que votre application peut rencontrer dans la nature.
En tant que tel, à mon avis, il est toujours plus sage et plus compatible de simplement exécuter su en tant que shell interactif et de diriger les commandes et les sorties. Si vous devez vous écarter de cela, vous devez au moins tester minutieusement votre application avec (1) le plus récent superutilisateur, (2) un superutilisateur (et binaire) de 2011, (3) SuperSU v0.99 ou plus ancien et (4) SuperSU v1.00 ou plus récent.

X.4. ACCESS_SUPERUSER permission DECONSEILLE

En raison de changements dans Android 5.0 Lollipop, cette autorisation est obsolète et est complètement ignorée à partir de SuperSU v2.30.
À partir de la version 1.20 de SuperSU, l' autorisation android.permission.ACCESS_SUPERUSER est déclarée par SuperSU. Toutes les applications root doivent désormais déclarer cette autorisation dans leur fichier AndroidManifest.xml :
 
Si cette autorisation n'est pas présente, SuperSU présentera un avertissement dans sa fenêtre contextuelle de demande de superutilisateur (configurable dans les paramètres SuperSU). Au moment d'écrire ces lignes, cette autorisation n'était pas appliquée, mais il est prévu qu'elle le sera un jour, et les applications demandant à la racine qui ne possèdent pas cet ensemble d'autorisations seront refusées en silence .
Si cette autorisation est déclarée, l'utilisateur pourra voir dans la liste des autorisations de l'application que l'application demande un accès superutilisateur.

X.5. Protection contre le détournement et les superpositions

SuperSU v2.04 a introduit la protection contre le détournement de bande dans l'invite d'accès du superutilisateur. Cette protection est utilisée dans de nombreuses boîtes de dialogue liées à la sécurité sous Android. Cela revient à dire que le popup ne réagira pas à la saisie, il est masqué par toute autre activité, vue, incrustation, etc. Ceci empêche une application malveillante d'afficher une image tactile au dessus de la popup qui, par exemple, basculer entre les boutons accepter et refuser.
Il existe cependant des utilisations légitimes pour les superpositions. Une utilisation courante consiste à ajuster les couleurs d'affichage. Une application affichant de telles superpositions doit masquer la superposition lorsque la fenêtre contextuelle SuperSU devient visible et l'afficher à nouveau. Pour faciliter cela, SuperSU enverra une diffusion aux applications pour lesquelles un accès root a déjà été accordé . Ainsi, une application comme celle-ci devrait en effet désactiver ses superpositions et demander un accès root lors de son premier lancement / configuration, même si elle ne nécessite pas root!
Le BroadcastReceiver suivant peut être utilisé pour recevoir des informations sur le moment où masquer les superpositions. Voir [libsuperuser :: HideOverlaysReceiver.java @ GitHub] pour une classe de base expliquant les détails.
 
  
   
   
  </ intent-filter>
 </ receiver>

X.6. su.d

À partir de la v2.22, une fois que supolicy a été appelé pour appliquer des correctifs aux stratégies SELinux, tous les exécutables de /system/su.d/ et /su/su.d/ sont exécutés. Les répertoires ainsi que les scripts et les fichiers binaires doivent être chmod 0700 . Certains firmwares ont une fonctionnalité similaire dans /system/(etc/)init.d/ , bien que l'exécution de ces scripts dépende du noyau et puisse s'exécuter avant que les stratégies SELinux ne soient corrigées et / ou que su ne soit disponible.
À partir de la v2.76, ces scripts sont exécutés avant le démarrage de zygote et sont donc utilisés à temps pour la plupart des opérations de montage de liens, à moins que les scripts ne s'exécutent trop longtemps.
Ce sont les garanties faites pour su.d :
  • Tous les correctifs SELinux sont terminés avant l' exécution des scripts su.d
  • Tous les scripts à l'intérieur de su.d sont exécutés dans l'ordre alphabétique (0-9a-z)
  • Les scripts sont exécutés en série. Le prochain ordre ne commence pas avant la fin du précédent.
  • Le démarrage ne continue pas tant que tous les scripts n'ont pas été exécutés
    • sauf si votre image de démarrage ne supporte pas exec (très rare)
    • sauf si les scripts durent plus longtemps que le délai d'expiration (4 secondes à l'origine, 60 secondes à compter de v2.78-SR1)
Comme les scripts sont exécutés en série, si votre script n'a pas besoin de retarder le processus de démarrage, le script lui-même doit s'assurer que ses actions sont exécutées de manière asynchrone. Par exemple:
#! / su / bin / sush
 (
    dormir 300
    écho fait
) Et
    
Cet exemple concerne root sans système, qui doit utiliser / su / bin / sush comme interpréteur de shell. Les parenthèses () entraînent la création d'un sous-processus qui exécute les commandes qu'il contient. & Esperluette après la parenthèse fermante entraîne l'exécution de ce sous-processus de manière asynchrone. Cela permet à tous les autres scripts de su.d de continuer à être exécutés et au démarrage de continuer. 'echo done' sera appelé après 5 minutes, heure à laquelle Android est probablement opérationnel depuis longtemps.
--- EOF ---

acidburn0zzz.github.io