Windows10 (14316) Analyse du sous-système Linux

0. Contexte

Lors de la conférence Build 2016, Microsoft a annoncé que bash serait pris en charge de manière native dans Windows 10 et a prétendu ajouter un sous-système Linux (Windows Subsystem for Linux) à Windows au lieu d'une machine virtuelle. Ensuite, la fonction bash a été activée dans la mise à jour publiée win10 14316 (x64), mais la version 32 bits n'a pas bash.

L'auteur a immédiatement téléchargé 14316 et voulait expérimenter l'exécution des commandes bash sur Windows, et voulait comprendre la mise en œuvre de ce mécanisme de sous-système Linux.

 

1. Introduction

Ouvrez d'abord system32 \ bash.exe, puis entrez une commande ls avec désinvolture. Par le biais de procmon, un processus a accédé au fichier C: \ Users \ xxxxx \ AppData \ Local \ lxss \ rootfs \ bin \ ls.

Image 1.png

Vous avez ensuite constaté que le répertoire racine de linux est monté dans C: \ Users \ xxxxx \ AppData \ Local \ lxss \ rootfs, vous pouvez voir que la structure du répertoire est fondamentalement la même que ubuntu.

Image 2.png

Inversement, il est étrange de voir le processus d'accès au fichier elf de ls. Procmon ne peut pas afficher le nom du processus, et le débogueur trouvera également qu'il n'y a pas de nom, section, peb et autres informations dans cet objet de processus.

二. Processus Pico

Ce processus sans nom est appelé le processus pico, qui est le processus hôte de l'ELF. À propos de l'introduction de picoprocess:  http://research.microsoft.com/en-us/projects/drawbridge/#Picoprocess . En termes simples, Pico Process est un conteneur, un mécanisme pour mettre en œuvre la technologie du bac à sable du pont-levis, et le projet "Project Astoria", qui avait été précédemment tué par Microsoft, utilise également la technologie lxcore + picoprocess. Désormais, Microsoft applique cette technologie au sous-système win10 linux.

Picoprocess est créé de manière uniforme par PspCreatePicoProcess et le travail principal dans PspCreatePicoProcess est réalisé par PsCreateMinimalProcess. Le nom de la fonction PsCreateMinimalProcess est utilisé pour créer un processus rationalisé. En fait, il est également vrai. PsCreateMinimalProcess appellera PspAllocateProcess, la fonction de la fonction PspAllocateProcess est utilisée pour créer un objet EPROCESS, un espace d'adressage de processus, un PEB et d'autres informations.

Le prototype de la fonction PspAllocateProcess est approximativement le suivant:

NTSTATUS PspAllocateProcess( void *ParentProcess, 
             int PreviousMode, 
             void *ObjectAttributes, 
             char Protection,
             char SignatureLevel, 
             char SectionSignatureLevel, 
             void *SectionObject,
             void *TokenObject, 
             int Flags, 
             void *UserProcessParameters, 
             int bSystemToken, 
         OUT int a12, 
         OUT void *Process) 

Les paramètres d'appel de PspAllocateProcess dans PsCreateMinimalProcess sont illustrés dans la figure suivante. ObjectAttributes, SectionObject, etc. sont tous NULL. Cela montre également que nous avons vu que picoprocess n'a aucun nom de processus et aucune information d'espace utilisateur.

Image 3.png

En regardant la pile d'appels de la fonction PspCreatePicoProcess, nous pouvons voir que l'appel de ring3 est distribué au pilote lxcore par la fonction PsPicoSystemCallDispatch. Ici, nous devons introduire une autre chose: PicoProvider

Image 4.png

3. PicoProvider 

Nt a exporté une fonction PsRegisterPicoProvider pour enregistrer un PicoProvider. Actuellement, seul lxss.sys appellera cette interface. Les deux pilotes, lxss.sys et lxcore.sys, sont responsables de l'implémentation de la plupart des fonctions du sous-système Linux. Lxss.sys, en tant que pilote de démarrage, appellera lxcore.sys-> PsRegisterPicoProvider lors de l'initialisation et PsRegisterPicoProvider ne peut être appelé qu'une seule fois après le démarrage du système. Une fois tous les pilotes de démarrage initialisés, nt définira PspPicoRegistrationDisabled sur TRUE, empêchant ainsi tout futur Tous les appels PsRegisterPicoProvider, ce qui signifie qu'un seul fournisseur est autorisé dans le système actuel.

Image 5.png

Notez que parce que le dernier symbole est actuellement 14295, le symbole de 14316 n'a pas été publié par Microsoft, mais le fichier lié à lxss.sys existe à 14295 et la plupart des fonctions ont été utilisées dans nt, mais le mécanisme de couche supérieure n'a pas été implémenté. Par conséquent, j'utilise le symbole 14295 et le fichier 14316 pour analyser le mécanisme lié au noyau lxss. S'il est incorrect, veuillez le signaler.

Le prototype de PsRegisterPicoProvider est à peu près comme suit:

NTSTATUS PsRegisterPicoProvider(  IN PICO_INTERFACE *PicoInterface,
                               OUT PSP_INTERFACE *PspInterface
                               ); struct PICO_INTERFACE
{
   __int64 cbSize; //目前只支持0x48,不排除以后会有EX    PVOID PicoSystemCallDispatch;
   PVOID PicoThreadExit;
   PVOID PicoProcessExit;
   PVOID PicoDispatchException;
   PVOID PicoProcessTerminate;
   PVOID PicoWalkUserStack;
   PVOID LxpProtectedRanges;
   PVOID PicoGetAllocatedProcessImageName;
} struct PSP_INTERFACE
{
   __int64 cbSize; //目前只支持0x60    PVOID PspCreatePicoProcess;
   PVOID PspCreatePicoThread;
   PVOID PspGetPicoProcessContext;
   PVOID PspGetPicoThreadContext;
   PVOID PspGetContextThreadInternal;
   PVOID PspSetContextThreadInternal;
   PVOID PspTerminateThreadByPointer;
   PVOID PsResumeThread;
   PVOID PspSetPicoThreadDescriptorBase;
   PVOID PsSuspendThread;
   PVOID PspTerminatePicoProcess;
}

Lxss fournit un ensemble d'interfaces de fonctionnement PICO à NT via PsRegisterPicoProvider pour obtenir des messages tels que la distribution des appels système, les entrées et sorties et les accès anormaux, et les gère par lui-même. Et il obtiendra un ensemble d'interface d'opération de thread NT pour fonctionner sur le thread NT. Il y a une interface PICO PicoGetAllocatedProcessImageName, ce qui est plus intéressant, car j'ai mentionné que nous utilisons procmon pour observer que le processus pico n'a pas de nom et que le débogueur du noyau ne peut pas voir le nom. Si vous pouvez savoir quel ELF correspond au processus pico, il sera très gros pour l'analyse. Aide.

Grâce à l'analyse, Pico Provider fournit cette interface à NT. Lorsque NtQuerySystemInformation obtient les informations relatives au nom du processus, il prendra normalement les informations sur le nom du processus à partir d'EPROCESS-> SeAuditProcessCreationInfo, mais s'il s'avère qu'il s'agit d'un processus pico, appelez PicoGetAllocatedProcessImageName pour l'obtenir. Donc, logiquement, NT a fait la compatibilité avec le processus pico, mais pourquoi procmon ne peut-il toujours pas obtenir le nom du processus? Effectivement, je l'ai essayé moi-même avec le plus simple CreateToolhelp32Snapshot, qui peut facilement énumérer les noms liés au processus elf.

Procmon ne peut donc pas utiliser l'interface standard pour obtenir le nom du processus.

Image 6.png

Mais où est enregistré le nom du processus pico? En analysant cette fonction, on constate que les informations de chemin ELF correspondant au processus pico sont stockées dans EPROCESS-> PicoContext + 0x15b0, et le PicoContext est défini par PspCreatePicoProcess lorsque le processus pico est créé et enregistre diverses informations du processus pico. Utilisez un script simple pour parcourir les informations de nom du processus pico du système actuel. Il s'agit des informations du processus pico lorsque j'utilise bash pour effectuer une tâche de téléchargement wget:

Image 7.png

En passant à PsRegisterPicoProvider, PicoSystemCallDispatch dans l'interface PICO est également important. Responsable de la distribution des appels système du pico process pour la communication entre le picoprocess et le fournisseur. Dans un appel système de processus pico, lorsque le code ring3 est sysenter, l'entrée du noyau est KiSystemCall64. Si l'indicateur Minimal est défini dans le thread actuel-> _ DISPATCHER_HEADER, KiSystemCall64 appellera nt! Publié séparément. Lxcore! LxpSyscalls enregistre l'adresse de cette distribution.

Cela fait partie de la fonction de distribution pour l'impression. Similaire à la table SDT, elle est toujours distribuée via rax comme id, mais la transmission des paramètres est différente de celle de sdt. Utilisez rdi, rsi, rdx, r10 pour passer les paramètres.

(Dans le processus d'analyse, il a également été constaté qu'après 14316, une nouvelle table SDT KeServiceDescriptorTableFilter a été ajoutée. Lorsqu'un indicateur de thread a été défini sur RestrictedGuiThread, la table KeServiceDescriptorTableFilter a été utilisée. Cette table restreindra de nombreux appels d'API natifs, principalement les bords, etc. Le processus appelle win32k et d'autres fonctions pour effectuer une isolation de sécurité plus stricte, mais le contenu de KeServiceDescriptorTableFilter est toujours le même que le SDT ordinaire et ne doit pas encore être utilisé)

Image 8.png

Jetons un coup d'œil à l'implémentation de certaines fonctions fournies par lxcore au sous-système Linxu. Par exemple, lorsque nous utilisons bash pour créer un processus ELF, le processus appelant de lxcore est probablement: LxpSyscall_FORK-> LxpThreadGroupFork-> LxpThreadGroupCreate-> PspCreatePicoProcess. PspCreatePicoProcess a été obtenu de NT avant PsRegisterPicoProvider, donc lxsscore peut fonctionner nt dans le thread, mais qu'en est-il des autres fonctions qui n'ont pas d'interface de réponse de NT?

Pour le fonctionnement du système de fichiers, l'api nt correspondante est directement appelée au lieu de traiter directement avec le système de fichiers. Par exemple, lorsque la commande rm est utilisée pour supprimer un fichier, le processus d'appel est: LxpSyscall_UNLINK-> LxpUnlinkHelper-> VfsPerformUnlink-> VfsUnlinkChild-> LxDrvFsRemoveChild-> LxDrvFsDeleteFile-> ZwSetInformationFile, qui ouvrira le fichier à l'aide de IoCreateFile via LxDrvFsDeleteFile, puis le supprimera. Un ensemble d'interfaces VfsXXXX est implémenté dans lxcore.sys pour virtualiser VFS vers le haut et utiliser un ensemble d'API de LxDrvFsXXXX vers le bas pour fournir des capacités d'accès aux fichiers.

Pour le fonctionnement du réseau, il est divisé en cluster de protocoles UNIX et cluster de protocoles INET. Les deux ont des distributions différentes. Par exemple, lorsque la couche supérieure envoie un paquet UDP, le processus d'appel de lxcore: LxpSyscall_SENDMMSG-> LxpSocketSendMultipleMessages-> LxpSocketInetSendMessage-> LxpSocketInocket LxpSocketInetDatagramSend-> afd! WskProAPISendTo. Entrera éventuellement afd pour exécuter la fonction correspondante. Il s'agit de la fonction liée à Afd utilisée par le lxcore imprimé:

Image 9.png

Pour d'autres, comme l'obtention des informations système actuelles, lxcore appelle également directement la fonction correspondante de Nt à obtenir, comme la commande date, lxcore est obtenue via LxpSyscall_CLOCK_GETTIME-> KeQuerySystemTimePrecise.

4. Injecter dans le processus Pico       

Enfin, j'ai essayé d'injecter dans le processus pico, dans l'intention de l'injecter pour appeler la distribution du processus pico. Comme le processus pico n'a pas d'objet section, il ne peut pas être injecté avec un thread distant. Enfin, il a été injecté dans le processus du processus pico à l'aide de la méthode SetThreadContext. Bien qu'il puisse être injecté avec succès, windbg ne peut pas déboguer le processus pic, car lxcore prend en charge la distribution anormale du processus pico. Si une exception est trouvée dans le thread pico dans nt! KiDispatchException, PicoDispatchException est utilisée pour gérer l'exception et le lxcore utilise APC pour simuler le mécanisme du signal. Communication de processus et distribution anormale.

V. Résumé

À l'heure actuelle, LxpSyscalls contient actuellement 0 × 138 appels, et certains appels n'ont actuellement aucune implémentation de logique interne, donc Microsoft améliorera progressivement la prise en charge de diverses commandes à l'avenir. Ce qui précède analyse brièvement le support de lxcore pour le fonctionnement du sous-système Linux. Bien sûr, ce n'est qu'une partie de l'opération, et certains tels que le chargement ELF, la gestion de la mémoire, la gestion des périphériques, etc. n'ont pas été analysés. Cependant, à partir des résultats qui ont été analysés jusqu'à présent, Microsoft implémente un ensemble de prise en charge de sous-système Linux, pas une machine virtuelle Linux, donc l'efficacité d'exécution sera bien meilleure, comme l'allocation de tranche de temps du processus de processus pico et du processus de base. Cohérent. Mais il y a aussi des inconvénients. Après avoir ajouté un ensemble de mécanismes lxss, cela augmente également la complexité. C'est-à-dire qu'après win10, il peut être confronté au double test de la sécurité binaire win et linux, ce qui peut ajouter de nouvelles garanties de sécurité pour windows. Puzzles.

Publié 766 articles originaux · loué 474 · 2,54 millions de vues

Je suppose que tu aimes

Origine blog.csdn.net/u010164190/article/details/105695938
conseillé
Classement