{"id":352,"date":"2025-05-03T20:34:47","date_gmt":"2025-05-03T20:34:47","guid":{"rendered":"https:\/\/tolva.fr\/?p=352"},"modified":"2025-05-03T20:34:47","modified_gmt":"2025-05-03T20:34:47","slug":"mais-que-fait-magisk","status":"publish","type":"post","link":"https:\/\/tolva.fr\/index.php\/2025\/05\/03\/mais-que-fait-magisk\/","title":{"rendered":"Mais que fait Magisk ?"},"content":{"rendered":"\n<p>Dans les deux articles pr\u00e9c\u00e9dents, nous avons vu comment rooter un t\u00e9l\u00e9phone Android avec Magisk avant de d\u00e9cortiquer le contenu du firmware d&rsquo;un Pixel, constatant au passage que le contenu de certains fichiers <code>img<\/code> correspondait peu ou prou au contenu de certaines partitions du t\u00e9l\u00e9phone, notamment pour les partitions <code>boot<\/code>, <code>init_boot<\/code>, <code>system<\/code> et <code>vendor<\/code>.<\/p>\n\n\n\n<p>Mais tout ceci ne nous renseigne pas sur la <em>magie<\/em> de Magisk et comment il permet d&rsquo;obtenir un acc\u00e8s root.<\/p>\n\n\n\n<p>Pour obtenir quelques \u00e9l\u00e9ments de r\u00e9ponse, commen\u00e7ons par revenir sur la proc\u00e9dure suivie pour rooter un terminal.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Modification de init_boot.img<\/strong><\/h4>\n\n\n\n<p>Pour examiner les diff\u00e9rences entre l&rsquo;<code>init_boot.img<\/code> initial et la version patch\u00e9e par Magisk, on proc\u00e8de comme dans l&rsquo;article pr\u00e9c\u00e9dent, c&rsquo;est-\u00e0-dire que l&rsquo;on utilise successivement <code>android_system_tools_mkbootimg<\/code>, <code>lz4<\/code> et <code>cpio<\/code>.<\/p>\n\n\n\n<p>On obtient deux arborescences &#8211; une par fichier img &#8211; que l&rsquo;on peut comparer avec un outil comme <code>meld<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1080\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2025\/05\/meld_init_boot_img.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-353\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2025\/05\/meld_init_boot_img.png 1920w, https:\/\/tolva.fr\/wp-content\/uploads\/2025\/05\/meld_init_boot_img-300x169.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2025\/05\/meld_init_boot_img-768x432.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2025\/05\/meld_init_boot_img-1536x864.png 1536w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/figure>\n\n\n\n<p>Deux diff\u00e9rences apparaissent :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>L&rsquo;ex\u00e9cutable <code>init<\/code> diff\u00e8re entre les deux <code>init_boot.img<\/code><\/li>\n\n\n\n<li>Un r\u00e9pertoire <code>overlay.d<\/code>, contenant <code>magisk64.xz<\/code> et <code>stub.xz<\/code>, ajout\u00e9 par Magisk<\/li>\n<\/ul>\n\n\n\n<p>Regardons les tailles des fichiers pr\u00e9sents dans l&rsquo;<code>init_boot<\/code> patch\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>thomas@ankou:\/tmp\/plop\/output_magisk$ ls -l\ntotal 5948\ndrwxr-xr-x 2 root   root      4096 11 mars  22:35 debug_ramdisk\ndrwxr-xr-x 2 root   root      4096 11 mars  22:35 dev\ndrwxr-xr-x 9 root   root      4096 11 mars  22:35 first_stage_ramdisk\n-rwxr-x--- 1 root   root    680904 11 mars  22:35 init\n-rw-r--r-- 1 thomas thomas       0 11 mars  22:31 kernel\ndrwxr-xr-x 2 root   root      4096 11 mars  22:35 metadata\ndrwxr-xr-x 2 root   root      4096 11 mars  22:35 mnt\ndrwxr-x--- 3 root   root      4096 11 mars  22:35 overlay.d\ndrwxr-xr-x 2 root   root      4096 11 mars  22:35 proc\ndrwxr-xr-x 2 root   root      4096 11 mars  22:35 second_stage_resources\ndrwxr-xr-x 2 root   root      4096 11 mars  22:35 sys\ndrwxr-xr-x 4 root   root      4096 11 mars  22:35 system<\/code><\/pre>\n\n\n\n<p>Et dans la version initiale :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>thomas@ankou:\/tmp\/plop\/output$ ls -l\ntotal 9732\ndrwxr-xr-x 2 root   root      4096 11 mars  20:49 debug_ramdisk\ndrwxr-xr-x 2 root   root      4096 11 mars  20:49 dev\ndrwxr-xr-x 9 root   root      4096 11 mars  20:49 first_stage_ramdisk\n-rwxr-x--- 1 root   root   3192656 11 mars  20:49 init\n-rw-r--r-- 1 thomas thomas       0 11 mars  20:22 kernel\ndrwxr-xr-x 2 root   root      4096 11 mars  20:49 metadata\ndrwxr-xr-x 2 root   root      4096 11 mars  20:49 mnt\ndrwxr-xr-x 2 root   root      4096 11 mars  20:49 proc\ndrwxr-xr-x 2 root   root      4096 11 mars  20:49 second_stage_resources\ndrwxr-xr-x 2 root   root      4096 11 mars  20:49 sys\ndrwxr-xr-x 4 root   root      4096 11 mars  20:49 system<\/code><\/pre>\n\n\n\n<p>L&rsquo;ex\u00e9cutable <code>init<\/code> patch\u00e9 est donc sensiblement plus petit (environ 680 Ko contre environ 3,2 Mo) que l&rsquo;<code>init<\/code> pr\u00e9sent dans l&rsquo;<code>init_boot<\/code> original.<\/p>\n\n\n\n<p>Si l&rsquo;on calcule le hach\u00e9 md5 du binaire init modifi\u00e9 par Magisk :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>thomas@ankou:\/tmp\/test\/normal\/output$ md5sum ..\/..\/magisk\/output\/init \nb6f881244cb3e9a912d0bcf82b2a2bb2  ..\/..\/magisk\/output\/init<\/code><\/pre>\n\n\n\n<p>On retrouve un binaire, <code>\/debug_ramdisk\/magiskinit<\/code>, de m\u00eame hach\u00e9 md5 sur le terminal root\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>lynx:\/ # ls -lh \/debug_ramdisk\/magiskinit\n-rwxr-x--- 1 root root 665K 1970-01-01 01:00 \/debug_ramdisk\/magiskinit\nlynx:\/ # ls -l \/debug_ramdisk\/magiskinit\n-rwxr-x--- 1 root root 680904 1970-01-01 01:00 \/debug_ramdisk\/magiskinit\nlynx:\/ # md5sum \/debug_ramdisk\/magiskinit\nb6f881244cb3e9a912d0bcf82b2a2bb2  \/debug_ramdisk\/magiskinit<\/code><\/pre>\n\n\n\n<p>Il y a donc deux binaires <code>init<\/code> sur le terminal !<\/p>\n\n\n\n<p>Le premier <code>init<\/code> (version patch\u00e9e ou non <code>init_boot.img<\/code>) est un binaire statique :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>thomas@ankou:\/tmp\/test\/normal\/output$ file ..\/..\/magisk\/output\/init\n..\/..\/magisk\/output\/init: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, BuildID&#91;sha1]=85102ca3bf7424bc5e0b9af32cf228041f941b43, stripped\nthomas@ankou:\/tmp\/test\/normal\/output$ file init\ninit: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, BuildID&#91;md5\/uuid]=2993eb986c6854343f67603d49ccd6c9, stripped<\/code><\/pre>\n\n\n\n<p>\u00c0 contrario, le deuxi\u00e8me est un ex\u00e9cutable dynamique :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>lynx:\/ # file \/system\/bin\/init\n\/system\/bin\/init: ELF shared object, 64-bit LSB arm64, dynamic (\/system\/bin\/bootstrap\/linker64), for Android 34, BuildID=ed3a94b65330c9e44f2523b0f35a796d, stripped\nlynx:\/ #<\/code><\/pre>\n\n\n\n<p>Que contient le r\u00e9pertoire overlay.d ajout\u00e9 par Magisk ?<\/p>\n\n\n\n<p>Le fichier <code>magisk64.xz<\/code> est compress\u00e9 avec <code>xz<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>root@ankou:\/tmp\/plop\/output_magisk\/overlay.d\/sbin# file magisk64.xz \nmagisk64.xz: XZ compressed data<\/code><\/pre>\n\n\n\n<p>On le d\u00e9compresse :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>root@ankou:\/tmp\/plop\/output_magisk\/overlay.d\/sbin# unxz magisk64.xz <\/code><\/pre>\n\n\n\n<p>On obtient un ex\u00e9cutable <code>magisk64<\/code>.<\/p>\n\n\n\n<p>Examinons maintenant <code>stub.xz<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>root@ankou:\/tmp\/plop\/output_magisk\/overlay.d\/sbin# ls\nmagisk64  stub.xz<\/code><\/pre>\n\n\n\n<p>D\u00e9compressons-le :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>root@ankou:\/tmp\/plop\/output_magisk\/overlay.d\/sbin# unxz stub.xz \nroot@ankou:\/tmp\/plop\/output_magisk\/overlay.d\/sbin# ls\nmagisk64  stub<\/code><\/pre>\n\n\n\n<p>Le fichier <code>stub<\/code> est une archive zip :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>root@ankou:\/tmp\/plop\/output_magisk\/overlay.d\/sbin# file *\nmagisk64: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter \/system\/bin\/linker64, BuildID&#91;sha1]=b4c7d85e83f5b20e40d07cc293f9467b13eb1132, stripped\nstub:     Zip archive data, at least v0.0 to extract<\/code><\/pre>\n\n\n\n<p>D\u00e9compressons-la :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>root@ankou:\/tmp\/plop\/output_magisk\/overlay.d\/sbin# unzip stub \nArchive:  stub\nversion=27.0\nversionCode=27000\nstubVersion=38\n  inflating: AndroidManifest.xml     \n  inflating: META-INF\/CERT.RSA       \n  inflating: META-INF\/CERT.SF        \n  inflating: META-INF\/MANIFEST.MF    \n  inflating: classes.dex             \nroot@ankou:\/tmp\/plop\/output_magisk\/overlay.d\/sbin#<\/code><\/pre>\n\n\n\n<p>On voit que l&rsquo;on a quelque chose qui ressemble fortement \u00e0 un apk !<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>magisk64 et magisk sur le t\u00e9l\u00e9phone<\/strong><\/h4>\n\n\n\n<p>On constate que le hach\u00e9 du binaire <code>magisk64<\/code> pr\u00e9sent dans le <code>init_boot.img<\/code> patch\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>thomas@ankou:\/tmp\/test\/magisk\/output\/overlay.d\/sbin$ md5sum magisk64 \n73cd4fe8227b60125c391a6af01a376f  magisk64<\/code><\/pre>\n\n\n\n<p>et le hach\u00e9 du binaire <code>magisk<\/code> pr\u00e9sent sur le t\u00e9l\u00e9phone root\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>lynx:\/ # md5sum \/system\/bin\/magisk\n73cd4fe8227b60125c391a6af01a376f  \/system\/bin\/magisk<\/code><\/pre>\n\n\n\n<p>sont identiques.<\/p>\n\n\n\n<p>On retrouve donc dans <code>\/system\/bin<\/code> l&rsquo;ex\u00e9cutable <code>magisk<\/code> pr\u00e9sent dans le <code>init_boot.img<\/code> patch\u00e9 sur le t\u00e9l\u00e9phone root\u00e9.<\/p>\n\n\n\n<p>Que fait ce binaire ?<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>lynx:\/ # \/system\/bin\/magisk\nMagisk - Multi-purpose Utility\n\nUsage: magisk &#91;applet &#91;arguments]...]\n   or: magisk &#91;options]...\n\nOptions:\n   -c                        print current binary version\n   -v                        print running daemon version\n   -V                        print running daemon version code\n   --list                    list all available applets\n   --remove-modules &#91;-n]     remove all modules, reboot if -n is not provided\n   --install-module ZIP      install a module zip file\n\nAdvanced Options (Internal APIs):\n   --daemon                  manually start magisk daemon\n   --stop                    remove all magisk changes and stop daemon\n   --&#91;init trigger]          callback on init triggers. Valid triggers:\n                             post-fs-data, service, boot-complete, zygote-restart\n   --unlock-blocks           set BLKROSET flag to OFF for all block devices\n   --restorecon              restore selinux context on Magisk files\n   --clone-attr SRC DEST     clone permission, owner, and selinux context\n   --clone SRC DEST          clone SRC to DEST\n   --sqlite SQL              exec SQL commands to Magisk database\n   --path                    print Magisk tmpfs mount path\n   --denylist ARGS           denylist config CLI\n   --preinit-device          resolve a device to store preinit files\n\nAvailable applets:\n    su, resetprop<\/code><\/pre>\n\n\n\n<p>Concr\u00e8tement, lorsque l&rsquo;on ouvre un shell avec <code>adb<\/code> sur un t\u00e9l\u00e9phone root\u00e9 et que l&rsquo;on se <code>su<\/code>, c&rsquo;est le binaire <code>magisk<\/code> qui est ex\u00e9cut\u00e9 derri\u00e8re.<\/p>\n\n\n\n<p>Pour r\u00e9sumer :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>L&rsquo;<code>init<\/code> de la partition init_boot est patch\u00e9<\/li>\n\n\n\n<li>un binaire <code>magisk<\/code> y est ajout\u00e9<\/li>\n\n\n\n<li>une application android y est ajout\u00e9e<\/li>\n<\/ul>\n\n\n\n<p>Une question subsiste : quel lien existe entre l&rsquo;action de patcher l&rsquo;<code>init<\/code> de la partition <code>init_boot<\/code> et l&rsquo;obtention d&rsquo;un acc\u00e8s root ?<\/p>\n\n\n\n<p>Pour cela, nous allons nous int\u00e9resser \u00e0 l&rsquo;arborescence des processus dans Android et en particulier au processus <code>init<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>L&rsquo;arborescence des processus sur un terminal Android<\/strong><\/h4>\n\n\n\n<p>Pour se faire une id\u00e9e de quel processus lance quel autre processus, on ex\u00e9cute la commande <code>ps -elf<\/code> sur un t\u00e9l\u00e9phone non-root\u00e9, en l&rsquo;occurrence un Fairphone 4 sous Android 13.<\/p>\n\n\n\n<p>Les principales observations sont les suivantes :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Le processus 1 est <code>init second_stage<\/code>. Son p\u00e8re est le processus 0<\/li>\n\n\n\n<li>Le processus 0 est aussi p\u00e8re de multiples <code>[kthreadd]<\/code>, qui est un thread noyau<\/li>\n\n\n\n<li>Le processus 2 est le p\u00e8re de multiples <code>[quelquechose]<\/code>, de threads noyau, donc<\/li>\n\n\n\n<li>Le processus 1 est le p\u00e8re de nombreux processus :\n<ul class=\"wp-block-list\">\n<li><code>init subcontext u:r:vendor_init:s0 14<\/code>,<\/li>\n\n\n\n<li><code>ueventd<\/code>,<\/li>\n\n\n\n<li><code>prng_seeder<\/code><\/li>\n\n\n\n<li><code>logd<\/code><\/li>\n\n\n\n<li><code>lmkd<\/code><\/li>\n\n\n\n<li><code>servicemanager<\/code><\/li>\n\n\n\n<li><code>hwservicemanager<\/code><\/li>\n\n\n\n<li><code>vndservicemanager<\/code><\/li>\n\n\n\n<li><code>keystore2 \/data\/misc\/keystore<\/code><\/li>\n\n\n\n<li><code>netd<\/code>, p\u00e8re de <code>iptables-restore --noflush -w -v<\/code> et <code>ip6tables-restore --noflush -w -v<\/code><\/li>\n\n\n\n<li><code>zygote64<\/code><\/li>\n\n\n\n<li><code>zygote<\/code><\/li>\n\n\n\n<li><code>ssgtzd<\/code><\/li>\n\n\n\n<li>un grand nombre de <code>android.quelquechose<\/code> et de <code>vendor.quelquechose<\/code><\/li>\n\n\n\n<li>du d\u00e9mon <code>adbd<\/code><\/li>\n\n\n\n<li>de <code>wpa_supplicant<\/code><\/li>\n\n\n\n<li>etc<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><code>zygote64<\/code> est le p\u00e8re de :\n<ul class=\"wp-block-list\">\n<li><code>system_server<\/code><\/li>\n\n\n\n<li>d&rsquo;un grand nombre d&rsquo;autres processus<\/li>\n\n\n\n<li>des applications proprement dites :\n<ul class=\"wp-block-list\">\n<li><code>org.thoughtcrime.securesms<\/code><\/li>\n\n\n\n<li><code>tv.twitch.android.app<\/code><\/li>\n\n\n\n<li><code>com.google.android.youtube<\/code><\/li>\n\n\n\n<li><code>com.fullsix.android.labanquepostale.accountaccess<\/code><\/li>\n\n\n\n<li><code>com.android.chrome<\/code><\/li>\n\n\n\n<li><code>com.whatsapp<\/code><\/li>\n\n\n\n<li><code>org.mozilla.firefox<\/code><\/li>\n\n\n\n<li>\u2026<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>En conclusion, <code>init<\/code> est le premier binaire lanc\u00e9, et est le p\u00e8re, direct ou indirect, de la tr\u00e8s grande majorit\u00e9 des processus ex\u00e9cut\u00e9.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong><code>init<\/code> et s\u00e9quence de d\u00e9marrage<\/strong><\/h4>\n\n\n\n<p>Dixit <a href=\"https:\/\/android.googlesource.com\/platform\/system\/core\/+\/master\/init\/README.md\" data-type=\"link\" data-id=\"https:\/\/android.googlesource.com\/platform\/system\/core\/+\/master\/init\/README.md\">https:\/\/android.googlesource.com\/platform\/system\/core\/+\/master\/init\/README.md<\/a>, la s\u00e9quence de boot d&rsquo;android est une fus\u00e9e \u00e0 3 \u00e9tages :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>first stage init<\/code>, <code>init<\/code> de la partition <code>init_boot<\/code>, qui r\u00e9alise diverses op\u00e9rations essentielles permettant d&rsquo;obtenir un \u00e9tat fonctionnel, parmi lequelles monter la partition system sur <code>\/<\/code><\/li>\n\n\n\n<li>une fois que le <code>first stage init<\/code> a fini son travail, il ex\u00e9cute <code>\/system\/bin\/init<\/code> (c&rsquo;est-\u00e0-dire l&rsquo;<code>init<\/code> de la partition system) avec l&rsquo;argument <code>selinux_setup<\/code> : Cet \u00e9tage charge la politique SELinux.<\/li>\n\n\n\n<li>enfin, <code>\/system\/bin\/init<\/code> est ex\u00e9cut\u00e9 avec l&rsquo;argument <code>second_stage<\/code>. Cet instance d&rsquo;init termine le processus de boot suivant la configuration sp\u00e9cifi\u00e9e par les diff\u00e9rents scripts <code>init.rc<\/code><\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Que fait l&rsquo;init vanilla ?<\/strong><\/h4>\n\n\n\n<p>En lan\u00e7ant <code>strings<\/code> sur <code>\/system\/bin\/init<\/code>, on tombe sur la cha\u00eene de caract\u00e8re <code>SELINUX_STARTED_AT<\/code>.<\/p>\n\n\n\n<p>Sur <code>cs.android.com<\/code>, on peut voir que cette cha\u00eene est d\u00e9clar\u00e9e dans <code>system\/core\/init\/selinux.h<\/code>.<\/p>\n\n\n\n<p>Comme <code>\/system\/bin\/init<\/code> \u00e9tant un ex\u00e9cutable dynamique, une cha\u00eene de caract\u00e8res pr\u00e9sente dans ce binaire fait n\u00e9cessairement partie du code du binaire en question.<\/p>\n\n\n\n<p>Le code de <code>\/system\/bin\/init<\/code> est donc dans le r\u00e9pertoire system\/core\/init du d\u00e9p\u00f4t de code.<\/p>\n\n\n\n<p>On regarde donc <code>system\/core\/init\/main.cpp<\/code> : Ce fichier contient une fonction <code>main<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>int main(int argc, char** argv) {\n#if __has_feature(address_sanitizer)\n    __asan_set_error_report_callback(AsanReportCallback);\n#elif __has_feature(hwaddress_sanitizer)\n    __hwasan_set_error_report_callback(AsanReportCallback);\n#endif\n    \/\/ Boost prio which will be restored later\n    setpriority(PRIO_PROCESS, 0, -20);\n    if (!strcmp(basename(argv&#91;0]), \"ueventd\")) {\n        return ueventd_main(argc, argv);\n    }\n\n    if (argc &gt; 1) {\n        if (!strcmp(argv&#91;1], \"subcontext\")) {\n            android::base::InitLogging(argv, &amp;android::base::KernelLogger);\n            const BuiltinFunctionMap&amp; function_map = GetBuiltinFunctionMap();\n\n            return SubcontextMain(argc, argv, &amp;function_map);\n        }\n\n        if (!strcmp(argv&#91;1], \"selinux_setup\")) {\n            return SetupSelinux(argv);\n        }\n\n        if (!strcmp(argv&#91;1], \"second_stage\")) {\n            return SecondStageMain(argc, argv);\n        }\n    }\n\n    return FirstStageMain(argc, argv);\n}<\/code><\/pre>\n\n\n\n<p>Le binaire <code>init<\/code> est d&rsquo;abord ex\u00e9cut\u00e9 avec l&rsquo;argument <code>selinux_setup<\/code>, ce qui provoque l&rsquo;ex\u00e9cution de la fonction <code>SetupSelinux<\/code> (<code>system\/core\/init\/selinux.cpp<\/code>).<\/p>\n\n\n\n<p>Cette fonction initialise SELinux et ex\u00e9cute <code>init<\/code> avec l&rsquo;argument <code>second_stage<\/code> (je paraphrase le commentaire de la d\u00e9claration de la fonction dans <code>system\/core\/init\/selinux.h<\/code>).<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>int SetupSelinux(char** argv) {\n    SetStdioToDevNull(argv);\n    InitKernelLogging(argv);\n\n    if (REBOOT_BOOTLOADER_ON_PANIC) {\n        InstallRebootSignalHandlers();\n    }\n\n    boot_clock::time_point start_time = boot_clock::now();\n\n    SelinuxSetupKernelLogging();\n\n    \/\/ TODO(b\/287206497): refactor into different headers to only include what we need.\n    if (IsMicrodroid()) {\n        LoadSelinuxPolicyMicrodroid();\n    } else {\n        LoadSelinuxPolicyAndroid();\n    }\n\n    SelinuxSetEnforcement();\n\n    if (IsMicrodroid() &amp;&amp; android::virtualization::IsOpenDiceChangesFlagEnabled()) {\n        \/\/ We run restorecon of \/microdroid_resources while we are still in kernel context to avoid\n        \/\/ granting init `tmpfs:file relabelfrom` capability.\n        const int flags = SELINUX_ANDROID_RESTORECON_RECURSE;\n        if (selinux_android_restorecon(\"\/microdroid_resources\", flags) == -1) {\n            PLOG(FATAL) &lt;&lt; \"restorecon of \/microdroid_resources failed\";\n        }\n    }\n\n    \/\/ We're in the kernel domain and want to transition to the init domain.  File systems that\n    \/\/ store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,\n    \/\/ but other file systems do.  In particular, this is needed for ramdisks such as the\n    \/\/ recovery image for A\/B devices.\n    if (selinux_android_restorecon(\"\/system\/bin\/init\", 0) == -1) {\n        PLOG(FATAL) &lt;&lt; \"restorecon failed of \/system\/bin\/init failed\";\n    }\n\n    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);\n\n    \/\/ SetupOverlays does not return if overlays exist, instead it execs overlay_remounter\n    \/\/ which then execs second stage init\n    SetupOverlays();\n\n    const char* path = \"\/system\/bin\/init\";\n    const char* args&#91;] = {path, \"second_stage\", nullptr};\n    execv(path, const_cast&lt;char**&gt;(args));\n\n    \/\/ execv() only returns if an error happened, in which case we\n    \/\/ panic and never return from this function.\n    PLOG(FATAL) &lt;&lt; \"execv(\\\"\" &lt;&lt; path &lt;&lt; \"\\\") failed\";\n\n    return 1;\n}<\/code><\/pre>\n\n\n\n<p>Le binaire <code>init<\/code> est ensuite ex\u00e9cut\u00e9 avec l&rsquo;argument <code>second_stage<\/code>, c&rsquo;est alors la fonction <code>SecondStageMain<\/code> (<code>system\/core\/init\/init.cpp<\/code>) qui est ex\u00e9cut\u00e9e.<\/p>\n\n\n\n<p>Cette fonction :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Monte les syst\u00e8mes de fichiers n\u00e9cessaires au second stage et non encore mont\u00e9s :<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>    \/\/ Mount extra filesystems required during second stage init\n    MountExtraFilesystems();<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fait diverses actions avant d&rsquo;entrer dans une boucle infinie :<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>    while (true) {\n        \/\/ By default, sleep until something happens. Do not convert far_future into\n        \/\/ std::chrono::milliseconds because that would trigger an overflow. The unit of boot_clock\n        \/\/ is 1ns.\n        const boot_clock::time_point far_future = boot_clock::time_point::max();\n        boot_clock::time_point next_action_time = far_future;\n\n        auto shutdown_command = shutdown_state.CheckShutdown();\n        if (shutdown_command) {\n            LOG(INFO) &lt;&lt; \"Got shutdown_command '\" &lt;&lt; *shutdown_command\n                      &lt;&lt; \"' Calling HandlePowerctlMessage()\";\n            HandlePowerctlMessage(*shutdown_command);\n        }\n\n        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {\n            am.ExecuteOneCommand();\n            \/\/ If there's more work to do, wake up again immediately.\n            if (am.HasMoreCommands()) {\n                next_action_time = boot_clock::now();\n            }\n        }\n        \/\/ Since the above code examined pending actions, no new actions must be\n        \/\/ queued by the code between this line and the Epoll::Wait() call below\n        \/\/ without calling WakeMainInitThread().\n        if (!IsShuttingDown()) {\n            auto next_process_action_time = HandleProcessActions();\n\n            \/\/ If there's a process that needs restarting, wake up in time for that.\n            if (next_process_action_time) {\n                next_action_time = std::min(next_action_time, *next_process_action_time);\n            }\n        }\n\n        std::optional&lt;std::chrono::milliseconds&gt; epoll_timeout;\n        if (next_action_time != far_future) {\n            epoll_timeout = std::chrono::ceil&lt;std::chrono::milliseconds&gt;(\n                    std::max(next_action_time - boot_clock::now(), 0ns));\n        }\n        auto epoll_result = epoll.Wait(epoll_timeout);\n        if (!epoll_result.ok()) {\n            LOG(ERROR) &lt;&lt; epoll_result.error();\n        }\n        if (!IsShuttingDown()) {\n            HandleControlMessages();\n            SetUsbController();\n        }\n    }<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Que fait l&rsquo;init patch\u00e9 ?<\/strong><\/h4>\n\n\n\n<p>Dans le cas d&rsquo;un t\u00e9l\u00e9phone root\u00e9, c&rsquo;est <code>magiskinit<\/code> qui est ex\u00e9cut\u00e9 en premier lieu.<\/p>\n\n\n\n<p>Cl\u00f4nons le d\u00e9p\u00f4t github de Magisk (https:\/\/github.com\/topjohnwu\/Magisk) et recherchons-y les occurrences de \u00ab\u00a0second_stage\u00a0\u00bb.<\/p>\n\n\n\n<p>Examinons la fin de la m\u00e9thode <code>start<\/code> dans init.rs : Si <code>init<\/code> est ex\u00e9cut\u00e9 avec l&rsquo;argument <code>selinux_setup<\/code>, la m\u00e9thode <code>second_stage<\/code> est ex\u00e9cut\u00e9e :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>        let argv1 = unsafe { *self.argv.offset(1) };\n        if !argv1.is_null() &amp;&amp; unsafe { CStr::from_ptr(argv1) == c\"selinux_setup\" } {\n            self.second_stage();<\/code><\/pre>\n\n\n\n<p>Que fait cette m\u00e9thode <code>second_stage<\/code> ?<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>    pub(crate) fn second_stage(&amp;mut self) {\n        info!(\"Second Stage Init\");\n        unsafe {\n            umount2(raw_cstr!(\"\/init\"), MNT_DETACH);\n            umount2(raw_cstr!(\"\/system\/bin\/init\"), MNT_DETACH); \/\/ just in case\n            path!(\"\/data\/init\").remove().ok();\n\n            \/\/ Make sure init dmesg logs won't get messed up\n            *self.argv = raw_cstr!(\"\/system\/bin\/init\") as *mut _;\n\n            \/\/ Some weird devices like meizu, uses 2SI but still have legacy rootfs\n            let mut sfs: statfs = std::mem::zeroed();\n            statfs(raw_cstr!(\"\/\"), &amp;mut sfs);\n            if sfs.f_type == 0x858458f6 || sfs.f_type as c_long == TMPFS_MAGIC {\n                \/\/ We are still on rootfs, so make sure we will execute the init of the 2nd stage\n                let init_path = path!(\"\/init\");\n                init_path.remove().ok();\n                path!(\"\/system\/bin\/init\").symlink_to(init_path).log_ok();\n                self.patch_rw_root();\n            } else {\n                self.patch_ro_root();\n            }\n        }\n    }<\/code><\/pre>\n\n\n\n<p>Ainsi, la fonction <code>second_stage<\/code> ex\u00e9cute <code>patch_ro_root<\/code> de <code>rootdir.cpp<\/code>. Cette fonction patche les scripts rc :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    load_overlay_rc(ROOTOVL);\n    if (access(ROOTOVL \"\/sbin\", F_OK) == 0) {\n        \/\/ Move files in overlay.d\/sbin into tmp_dir\n        mv_path(ROOTOVL \"\/sbin\", \".\");\n    }\n\n   \/\/ Patch init.rc\n    bool p;\n    if (access(NEW_INITRC_DIR \"\/\" INIT_RC, F_OK) == 0) {\n        \/\/ Android 11's new init.rc\n        p = patch_rc_scripts(NEW_INITRC_DIR, tmp_dir.data(), false);\n    } else {\n        p = patch_rc_scripts(\"\/\", tmp_dir.data(), false);\n    }\n    if (p) patch_fissiond(tmp_dir.data());<\/code><\/pre>\n\n\n\n<p>Comme son nom le sugg\u00e8re, la fonction <code>patch_rc_scripts<\/code> modifie les scripts rc :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>        \/\/ Inject custom rc scripts\n        for (auto &amp;script : rc_list) {\n            \/\/ Replace template arguments of rc scripts with dynamic paths\n            replace_all(script, \"${MAGISKTMP}\", tmp_path);\n            fprintf(dest.get(), \"\\n%s\\n\", script.data());\n        }\n        rc_list.clear();\n\n        \/\/ Inject Magisk rc scripts\n        rust::inject_magisk_rc(fileno(dest.get()), tmp_path);\n\n        fclone_attr(fileno(src.get()), fileno(dest.get()));\n    }\n\n    \/\/ Then patch init.zygote*.rc<\/code><\/pre>\n\n\n\n<p>La fonction <code>patch_ro_root<\/code> modifie ensuite la configuration SELinux via la fonction <code>handle_sepolicy<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>    handle_sepolicy();\n    unlink(\"init-ld\");\n\n    \/\/ Mount rootdir\n    mount_overlay(\"\/\");\n\n    chdir(\"\/\");\n}<\/code><\/pre>\n\n\n\n<p>On a donc ici deux vecteurs possibles pour conf\u00e9rer des droits \u00e9lev\u00e9s \u00e0 un processus arbitraire : <\/p>\n\n\n\n<p>Patcher la politique SELinux et les scripts rc utilis\u00e9s par <code>init<\/code>.<\/p>\n\n\n\n<p>Revenons sur la m\u00e9thode <code>start<\/code>. Avant de lancer (ou non, en fonction de <code>argv[1]<\/code>) <code>second_stage<\/code>, cette m\u00e9thode commence par monter, si cela n&rsquo;a pas \u00e9t\u00e9 fait, diff\u00e9rents syst\u00e8mes de fichiers :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>    fn start(&amp;mut self) -&gt; LoggedResult&lt;()&gt; {\n        if !path!(\"\/proc\/cmdline\").exists() {\n            path!(\"\/proc\").mkdir(0o755)?;\n            unsafe {\n                mount(\n                    raw_cstr!(\"proc\"),\n                    raw_cstr!(\"\/proc\"),\n                    raw_cstr!(\"proc\"),\n                    0,\n                    null(),\n                )\n            }\n            .as_os_err()?;\n            self.mount_list.push(\"\/proc\".to_string());\n        }\n        if !path!(\"\/sys\/block\").exists() {\n            path!(\"\/sys\").mkdir(0o755)?;\n            unsafe {\n                mount(\n                    raw_cstr!(\"sysfs\"),\n                    raw_cstr!(\"\/sys\"),\n                    raw_cstr!(\"sysfs\"),\n                    0,\n                    null(),\n                )\n            }\n            .as_os_err()?;\n            self.mount_list.push(\"\/sys\".to_string());\n        }<\/code><\/pre>\n\n\n\n<p>Apr\u00e8s ces op\u00e9rations, <code>start<\/code> ex\u00e9cute <code>self.config.init();<\/code>, soit la fonction <code>BootConfig::init()<\/code> de <code>getinfo.cpp<\/code>.<\/p>\n\n\n\n<p>Enfin, la fonction <code>exec_init<\/code> est lanc\u00e9e :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>        \/\/ Finally execute the original init\n        self.exec_init();<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Conclusions<\/strong><\/h4>\n\n\n\n<p>Examiner le contenu des partitions system et init_boot, le code de l&rsquo;<code>init<\/code> vanilla et de la version patch\u00e9e aura permis de se faire une petite id\u00e9e du fonctionnement de Magisk.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dans les deux articles pr\u00e9c\u00e9dents, nous avons vu comment rooter un t\u00e9l\u00e9phone Android avec Magisk avant de d\u00e9cortiquer le contenu du firmware d&rsquo;un Pixel, constatant au passage que le contenu de certains fichiers img correspondait peu ou prou au contenu de certaines partitions du t\u00e9l\u00e9phone, notamment pour les partitions boot, init_boot, system et vendor. Mais [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-352","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/352","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/comments?post=352"}],"version-history":[{"count":1,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/352\/revisions"}],"predecessor-version":[{"id":354,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/352\/revisions\/354"}],"wp:attachment":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/media?parent=352"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/categories?post=352"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/tags?post=352"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}