
Une documentation bien structurée pour débuter avec le pwning de Chrome et le pwning de V8.
Une documentation bien structurée pour démarrer avec le pwning de Chrome & le pwning de v8
Comment ce document est organisé
Les navigateurs sont l’une des technologies les plus utilisées aujourd’hui. Sur chaque ordinateur du commerce, si l’on branche et joue, on voit un navigateur installé. C’est pourquoi, du point de vue d’un attaquant et d’un modèle de menaces, il est très gratifiant pour l’attaquant de pouvoir compromettre le navigateur via une page malveillante. Compte tenu des arguments ci-dessus, j’ai choisi d’étudier le moteur JavaScript de Google, en particulier v8.
Étant donné la nature gigantesque du projet v8, j’ai choisi comme point de départ l’interpréteur, à savoir d8. Bien que des recherches approfondies aient déjà été menées sur d8, nous espérons trouver au moins un bug et, sinon, pouvoir avancer dans la recherche sur l’exploitation de navigateurs, car v8 constitue un terrain d’entrée pour les stratégies de base du développement d’exploits utilisées dans l’exploitation de navigateurs.
Une autre raison pour laquelle j’ai choisi v8 comme cible est qu’il est utilisé dans plusieurs navigateurs.
Si l’on regarde sous le capot, on constate que le moteur est également utilisé dans MicrosoftEdge, il y a donc une chance de recevoir plusieurs récompenses de bug bounty. Pour le système d’exploitation sous-jacent, le chercheur utilisera un mélange de Windows et Linux, car il n’y a pas de restriction en termes d’obtention d’un shell : les bugs de v8 permettent l’exécution de code via les pages wasm et ne sont liés à aucune plateforme spécifique.
Malheureusement, bien qu’un bug exploité dans v8 aboutisse à une exécution de code, nous ne pourrons exécuter aucun code à cause du sandbox. Nous obtiendrions donc une exécution de code dans le contexte du renderer, ce qui ne nous permettrait pas d’exécuter du code sur la machine. Pour cela, il nous faudrait un autre exploit pour le sandbox ; il nous faudrait donc une chaîne complète pour exploiter le système.
C’est pourquoi nous définissons les objectifs suivants afin de pouvoir démarrer le hacking de navigateurs :
Au premier stade du projet, il est nécessaire de rassembler un maximum de connaissances sur l’architecture de Chrome et sur la façon dont chaque composant interagit avec les autres. Pour mieux comprendre cela, il faut décomposer le projet Chromium en plusieurs sous-composants afin de pouvoir tout isoler et l’analyser correctement. Plus précisément, en combien de sous-composants chacun des composants suivants se divise :
L’étape la plus logique pour commencer est de comprendre l’architecture de Chromium. Alors, ok, nous voulons exploiter le navigateur, mais que se passe-t-il lorsque nous démarrons le navigateur ? Eh bien, après avoir cliqué sur l’exécutable Chromium, l’exécutable lance certains processus.
L’ordre et leur nom sont les suivants :
Le premier s’appelle processus de contenu. Que fait ce processus ?
Maintenant que nous savons brièvement ce qu’il fait, il est temps d’entrer plus en détail :
Ainsi, la façon dont Chromium fonctionne, du moins sous Windows, consiste à compiler les fichiers dans une dll, puis à la charger en mémoire. La logique centrale du navigateur Chromium se trouve donc dans chromium.dll.
Ceci est également confirmé par le code
.
Ceci est tiré de chrome_exe_main_win.cc et si vous êtes curieux de lire tout le code, il se trouve dans chromium/src/chrome/app. Ok, en poursuivant l’exécution, on voit qu’il appelle MakeMainDllLoader() pour invoquer la classe de chargement de la dll ; après cela, il lance le « loader », c’est-à-dire qu’il charge chrome.dll et, si nécessaire, le redémarre avec les lignes de commande nécessaires. Pour analyser plus en détail le Loader, il faut comprendre son code, qui se trouve dans le même répertoire, dans le fichier mail_dll_loader_win.cc. En parcourant le fichier jusqu’en bas, on voit l’appel à MakeMainDllLoader qui, selon la version que vous avez, appelle ChromeDllLoader ou ChromiumDllLoader.

On voit que ChromiumDllLoader est une classe qui hérite de . D’après la définition, la classe se contente de charger la dll en fonction des arguments passés à la ligne de commande et du type de processus
.
En analysant la méthode Launch, on comprend certaines choses qui se passent avant que chrome.dll démarre : on comprend à partir de ce commentaire que « // Launching is a matter of loading the right dll and calling the entry point. // Derived classes can add custom code in the OnBeforeLaunch callback. » lancer chrome consiste à charger un ensemble de dll qui font réellement le travail. Ensuite, il prend les arguments passés à la ligne de commande et initialise en outre les services du sandbox.
.
D’abord, il vérifie si c’est le navigateur qui appelle l’initialisation du sandbox, puis il vérifie si le processus qui a appelé l’initialisation du sandbox a été lancé en tant que service d’impression cloud. Il vérifie aussi si a été passé au binaire, ce qui indique en gros au binaire de ne pas s’exécuter dans un sandbox. Il vérifie si l’une de ces options est définie et, si c’est le cas, il appelle le sandbox avec les options correspondantes. Puis, à la fin, nous arrivons à
ce qui nous intéresse, cela représentant le wrapper d’appel à .
le suivi d'une étape du cycle de vie d'un processus qui est définie comme```
// The phases are generic and may have meaning to the tracker.
PROCESS_PHASE_UNKNOWN = 0,
PROCESS_LAUNCHED = 1,
PROCESS_LAUNCH_FAILED = 2,
PROCESS_EXITED_CLEANLY = 10,
PROCESS_EXITED_WITH_CODE = 11,
// Add here whatever is useful for analysis.
PROCESS_SHUTDOWN_STARTED = 100,
PROCESS_MAIN_LOOP_STARTED = 101,
journalise dès que possible lorsqu'un processus se termine, enregistre les informations concernant le module, autrement dit quand un module (un composant de chromium) est chargé, et fondamentalement la même fonctionnalité se répète, mais différemment pour différents threads et classes. Si vous êtes intéressé, vous pouvez le trouver dans chromium/src/base/debug/activity_tracker.h
Ensuite, il vérifie si « Main() » a déjà été appelé. Ici, je pense qu'ils veulent dire si ChromeMain() a déjà été appelé. Ils vérifient essentiellement si des commandes ont été passées au processus de contenu avec des arguments. Et si c'est le cas, ils s'assurent que le processus navigateur recevra les mêmes arguments. Ils vérifient ensuite les spécificités de la plateforme et, si Windows est détecté, ils initialisent leur propre gestionnaire en appelant CreateATLModuleIfNeeded, puis c'est la même histoire : vérification des spécificités de la plateforme et ils passent les arguments via SetupCRT et nous arrivons à la partie où nous initialisons l'IPC afin de pouvoir parler aux autres processus après les avoir engendrés. Voici le code responsable de cela. Nous n'entrerons pas dans les détails, car nous y reviendrons plus tard en discutant plus en profondeur du mécanisme IPC de chrome. Sachez juste pour le moment qu'il s'agit du mécanisme qui facilite la communication entre les processus multi-architectures de chrome. Et si vous ne savez pas ce que signifie IPC, c'est pour inter process communication.
.
Ensuite, ce qu'il fait, c'est qu'il « transfère » plutôt qu'il ne définit les arguments pour l'interface utilisateur en utilisant ui::RegisterPathProvider, appelle le tracker et obtient le résultat via content_main_runner->Initialize(std::move(params)), crée une console parente, ce qui, je suppose, signifie qu'ils créent un processus parent, effectue quelques vérifications supplémentaires et nous arrivons à la partie importante qui est
.
Ici, on voit un appel à une fonction appelée IsSubprocess.
ce qu'elle fait essentiellement, pour éviter l'ancien code passe-partout, c'est vérifier dans une seule fonction le type de processus qu'elle reçoit sur la ligne de commande et, si une option a été passée, elle choisit parmi les suivantes et crée le processus respectif.```
return type == switches::kGpuProcess ||
type == switches::kPpapiPluginProcess ||
type == switches::kRendererProcess ||
type == switches::kUtilityProcess || type == switches::kZygoteProcess;
===============================================================================================
À partir de là, nous arrivons à `content_main_runner->Run();` qui, en substance, fait toute la magie. Examinons cela sous le capot. Donc `content_main_runner` est de la classe `ContentMainRunner`, évidemment.. que l'on trouve dans `content\app\content_main_runner_impl.cc`
Pour pouvoir réellement comprendre le flux du code, nous devons le prendre de bas en haut. Ceci dit, nous faisons défiler à nouveau et nous constatons que `ContentMainRunner::Create()` appelle en fait `ContentMainRunnerImpl::Create()`; ce qui nous fait réaliser qu'en réalité nous devons rechercher la définition de `ContentMainRunnerImpl` et non pas de `ContentMainRunner`, que nous trouvons dans `content_main_runner_impl.h` dans le même dossier déjà mentionné.

Nous voyons qu'elle hérite de `ContentMainRunner` et nous pouvons voir ses méthodes principales. En réalité, il n'y a pas grand-chose ici car son comportement est surtout redéfini dans `content_main_runner_impl.cc`.
Dans `content_main_runner_impl.cc`, à l'intérieur de la méthode Run, elle commence par faire quelques vérifications à l'aide de DCHECK (c'est quoi cette fonction ? (```The CHECK() macro will cause an immediate crash if its condition is not met. DCHECK() is like CHECK() but is only compiled in when DCHECK_IS_ON is true (debug builds and some bot configurations, but not end-user builds).``` citations des docs google :) ) pour voir si is_initialized, content_main_params_, is_shutdown_ sont définis.

Ensuite, elle récupère les arguments et détermine le type dont j'ai parlé plus tôt.
Puis, dans le cas où nous ne pouvons pas le trouver, nous appelons InitializeFieldTrialAndFeatureList() delegate_->PostFieldTrialInitialization(); et nous le publions comme message via mojo.
 .
Ensuite, elle configure quelques éléments pour l'interface utilisateur.
toujours en fonction de ce qui a été passé en ligne de commande, puis appelle RegisterMainThreadFactories qui est un wrapper pour RegisterUtilityMainThreadFactory, qui ne fait que définir g_utility_main_thread_factory, qui, d'après son nom, crée le thread principal, c'est-à-dire le thread qui surveille le reste des threads, et lance enfin le processus navigateur. 
Si vous ne me faites pas confiance, voici Chromium désassemblé dans Binja et nous verrons aussi une analyse en mémoire de cela.
Chanceux que nous sommes, nous avons `chrome.exe.pdb` qui nous permet d'avoir les symboles de débogage et nous n'aurons aucune difficulté à rétro-concevoir le binaire.
Comme nous travaillons sur Chrome sous Windows, le point d'entrée principal du programme est `wWinMain` :
Voici à quoi ressemble le graphe
.
La fonction en elle-même est énorme et je ne montrerai donc que les parties nécessaires. Après un tas d'initialisations, nous arrivons à un point où elle appelle `MakeMainDllLoader()`
, dont nous avons expliqué le fonctionnement. Bon, alors comment déboguer Chrome dynamiquement et prouver tout ce que j'ai déjà mentionné ? D'abord, nous le chargeons dans windbg, puis `lm` et cherchons le nom de l'exécutable. Ensuite, nous cherchons une fonction appelée `chrome!MakeMainDllLoader`, nous plaçons un point d'arrêt et laissons l'exécution continuer.. Nous entrons dedans , nous le laissons s'exécuter jusqu'à ce qu'il atteigne `ret` et nous en sortons, pour arriver dans `chrome!wWinMain+0x764` . Nous le laissons ensuite s'exécuter jusqu'à `chrome!MainDllLoader::Launch` et nous rentrons dedans. De là, nous plaçons un bp sur `chrome!MainDllLoader::Load` afin de voir comment `chrome.dll` est chargée en mémoire. Et comme nous pouvons le voir, parmi les premières choses chargées se trouvait `chrome.dll` . À partir de là, le flux d'analyse est le même et l'analyse complète en mémoire sera laissée comme exercice au lecteur. Pour une raison particulière, je ferai l'analyse jusqu'au point où le processus de contenu est presque terminé et juste avant le démarrage du processus navigateur, je m'arrêterai. Le but est simplement de montrer comment l'IPC commence à communiquer entre les processus. Maintenant, après le chargement de la dll, nous devons chercher une instruction `call rax`. Ce que j'ai fait, c'est lister les 0x40 instructions à partir de l'eip courant après le chargement de la dll.. à cette adresse se trouve en fait ce que nous cherchons, à savoir `chrome!ChromeMain`.
De là, nous voulons nous arrêter à , qui est essentiellement une grosse vérification avant de sauter dans `content!content::ContentMain`. De là, nous voulons avancer de quelques instructions et atteindre `content!content::ContentMain`. Nous voulons entrer dedans et nous arrêter à `content!content::RunContentProcess`, nous voulons entrer, placer un bp à `content!content::ContentMainRunnerImpl::Run+0x430` et dès que nous passons par-dessus , nous voyons l'IPC mojo démarrer et nous concluons que le processus suivant, à savoir le processus navigateur, est responsable de l'implémentation réelle du navigateur et de la gestion de tous les autres processus.
===============================================================================================
2.
La dernière fois, nous nous étions arrêtés après avoir compris le processus de contenu. Aujourd'hui, nous allons nous intéresser au processus navigateur.
C'est le processus qui démarre après le processus de contenu et c'est le deuxième des processus lancés par Chrome.
Décrivons-le brièvement :
* nous pouvons le considérer comme le processus principal, car le processus de contenu est davantage une routine d'initialisation et de vérification des dépendances, et il est démarré par le processus de contenu
* il reste actif pendant toute la durée de vie du navigateur.
* C'est le coordinateur central de tous les processus, et il opère au niveau de privilège le plus élevé disponible pour le navigateur.
* comme il s'exécute au niveau de privilège le plus élevé, si d'autres processus ont besoin d'effectuer une opération de niveau supérieur, cette demande est traitée par le processus navigateur.
* Il contrôle des fonctionnalités telles que la barre d'adresse, les marque-pages et les boutons précédent/suivant/actualiser. Étant le processus le plus privilégié, il ne fait pas confiance aux données qui lui sont fournies par les autres processus.
* il gère également les opérations privilégiées telles que l'interface utilisateur, le réseau ou le stockage du système de fichiers pour les autres processus lorsque cela est nécessaire.
Maintenant que nous savons brièvement ce qu'il fait, il est temps d'entrer plus en détail :
Notre parcours commence dans `src\content\app`, dans un fichier nommé content_main_runner_impl.cc, au niveau de la fonction int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params,bool start_minimal_browser). La première opération qu'elle exécute, nous voyons, est un TRACE_EVENT_INSTANT0 .
Qu'est-ce que c'est ? Avant cela, qu'est-ce qu'une fonction de trace, même ? Eh bien, si nous allons sur https://lwn.net/Articles/379903/ nous pouvons voir qu'ils la définissent comme , en faisant un peu d'abstraction nous pouvons conclure qu'une fonction de trace est une fonction qui enregistre des données à un point précis d'une pile d'appels. Elle fait plus que d'enregistrer la pile d'exécution des fonctions, elle peut enregistrer les variables locales de la dernière fonction exécutée. Maintenant que nous savons ce qu'est une fonction de trace, voyons ce que fait la nôtre.
Si nous allons sur https://chromium.googlesource.com/chromium/src/base/trace_event/common/+/refs/heads/main/trace_event_common.h nous voyons qu'ils la définissent comme une fonction dont le seul but est de « tracking application performance and resource usage » (suivi des performances applicatives et de l'utilisation des ressources). En approfondissant un peu pour comprendre ce qu'elle fait, nous voyons que c'est une macro et elle est définie comme . Nous pouvons voir une courte définition au-dessus de la macro qui dit qu'elle enregistre immédiatement un seul événement appelé « name », avec 0, 1 ou 2 arguments. Et si la catégorie de l'événement concerné n'est pas activée, elle ne fait rien. Nous pouvons voir que comme paramètres nous avons « startup » comme catégorie principale et comme sous-système nous avons « ContentMainRunnerImpl::RunBrowser(begin) » et le troisième paramètre est TRACE_EVENT_SCOPE_THREAD qui est « #define TRACE_EVENT_SCOPE_THREAD (static_cast<unsigned char>(2 << 2)) » ce qui, je pense, d'après la valeur, est un identifiant pour l'événement instantané respectif. Donc en substance, cela trace (journalise) simplement le fait que nous sommes entrés avec notre exécution dans la fonction RunBrowser. Nous avons ensuite une vérification pour voir si la boucle principale du navigateur a déjà démarré et, dans ce cas, nous sortons de la fonction  . Nous définissons ensuite un indicateur et après cela, nous arrivons à une partie plutôt intéressante du code. Nous vérifions si nous avons le support du mécanisme mojo_ipc et si c'est le cas, nous utilisons ShouldCreateFeatureList qui crée une liste de fonctionnalités avec différents processus, tente de les initialiser et enfin tente d'initialiser les fonctionnalités mojo.. Nous créons ensuite un threadpool. C'est quoi un threadpool ?!?! Citation de Wikipédia : « a software design pattern for achieving concurrency of execution in a computer program » (un patron de conception logiciel pour atteindre la concurrence d'exécution dans un programme informatique). Plus précisément, « a thread pool maintains multiple threads waiting for tasks to be allocated for concurrent execution by the supervising program » (toujours une citation de Wikipédia). Plus précisément, imaginez deux lignes de personnes travaillant dans une usine. Appelons-les ligne a et ligne b. Elles sont toutes supervisées par un patron. Appelons-le ligne c. La ligne b doit attendre que la ligne a finisse son travail et être notifiée par la ligne c pour travailler. Et il en va de même pour la ligne a. Et c'est ce qu'est un threadpool. Voici aussi un petit exemple en C . Il est honteusement tiré de https://stackoverflow.com/questions/15752659/thread-pooling-in-c11 . Ensuite, nous avons un appel à PreBrowserMain(); qui fait une initialisation spécifique à la plateforme et après cela, nous arrivons à un point où nous appelons `BrowserTaskExecutor::Create()`;
. Plongeons en profondeur dans ce qu'elle fait, car le nom est assez intéressant et en se fiant à une supposition éclairée, nous pouvons réaliser que cela pourrait être intéressant. Notre détour commence dans content/browser/scheduler/browser_task_executor.h où nous constatons que BrowserTaskExecutor est une classe qui est censée « to map base::TaskTraits to actual task queues for the browser process » (mapper base::TaskTraits vers les files de tâches réelles du processus navigateur). En descendant un peu dans le fichier, nous voyons d'abord  qu'elle hérite de BaseBrowserTaskExecutor. Donc pour comprendre BrowserTaskExecutor, nous devons comprendre BaseBrowserTaskExecutor. Nous voyons qu'elle hérite de TaskExecutor. Heureusement pour nous, nous voyons qu'elle redéfinit les méthodes de TaskExecutor avec une propriété redéfinissable, ce qui signifie qu'elles seront écrasées par d'autres appels de méthodes plus tard. Mais juste pour les curieux, nous pouvons la trouver dans base/task/task_executor.h et si nous l'inspectons, nous découvrons que TaskExecutor est une classe qui « can execute Tasks with a specific TaskTraits extension id » (peut exécuter des tâches avec un identifiant d'extension TaskTraits spécifique)  .
Qu'est-ce qu'une tâche et que sont les TaskTraits ? Nous avons déjà mentionné ce qu'est une tâche, mais pour rafraîchir, c'est l'un des processus des processus de Chrome, et maintenant que sont les TaskTraits ? Ils sont situés dans base/task/task_traits.h et ils sont définis comme suit : « encapsulate information about a task that helps the thread pool make better scheduling decisions » (encapsulent des informations sur une tâche qui aident le thread pool à prendre de meilleures décisions d'ordonnancement). Revenons à notre BrowserTaskExecutor. La méthode que nous appelons est Create() et son analyse est la suivante 
d'abord, vérifier si la tâche actuelle doit être exécutée à partir d'un SingleThreadTaskRunner, c'est-à-dire si nous devons exécuter cette tâche en utilisant un thread indépendant. Nous faisons cela en obtenant un pointeur vers le tls. Nous initialisons ensuite un ui et un thread scheduler. En gros, ici nous initialisons un ordonnanceur pour les futurs événements liés à l'interface utilisateur..
Et c'est à peu près tout pour cette fonctionnalité. En poursuivant l'analyse de content_main_runner_impl.cc, nous arrivons ici.
En cherchant le fichier de la classe variations ids provider, nous le trouvons dans `components/variations/variations_ids_provider.h` ; là, nous voyons quelque chose d'assez intéressant. Il inclut un fichier `.mojom.h`. Nous pouvons trouver sa définition dans `Debug/gen/components/variations/` variations.mojom.h, ce qui signifie que cela a à voir avec le mécanisme IPC. En examinant la définition réelle de la classe, nous voyons un commentaire qui l'annote comme « A helper class for maintaining client experiments and metrics state transmitted in custom HTTP request headers » (une classe d'assistance pour maintenir l'état des expériences client et des métriques transmis dans des en-têtes de requête HTTP personnalisés). En regardant son comportement et sa définition source, nous concluons qu'elle est simplement utilisée comme un marqueur pour une fonction, indiquant que « signed-in parameter supplied to GetClientDataHeaders() » (paramètre connecté fourni à GetClientDataHeaders()), ce qui signifie que quelque part dans la pile d'exécution, il y a un appel à GetClientDataHeaders qui sera plus tard appelé avec un paramètre. Nous faisons ensuite delegate_->PostEarlyInitialization(!!main_params.ui_task); qui publie simplement sur l'IPC de mojo qu'il doit démarrer les tâches d'interface utilisateur. Nous faisons plus d'initialisations  puis nous arrivons à l'appel de RunBrowserProcessMain.  . Si vous espériez comme moi voir ici le démarrage de l'interface graphique, vous avez tort. Une citation du maître Oogway . Nous continuons ensuite avec quelques vérifications supplémentaires et nous atteignons BrowserMain. En parcourant cela, elle fait un peu de tracing puis nous arrivons à, qui est l'endroit où nous démarrons réellement l'interface graphique. Maintenant, si vous ne me faites pas confiance, il faudra être un peu plus patient jusqu'à l'analyse dynamique. Nous arrivons ensuite à la méthode Run, qui est ce qui nous intéresse vraiment ; ensuite, nous arriverons à notre prochain point pour comprendre le processus navigateur. Mais pour l'instant, faisons un autre court détour et examinons comment la méthode Initialize est créée. Dès le départ, nous voyons qu'elle trace l'exécution de la méthode init et avant cela, elle crée un histogramme. Nous vérifions ensuite l'indicateur initialization_started_ pour voir si nous avons atteint l'étape d'initialisation et si ce n'est pas le cas, nous initialisons Skia, la bibliothèque graphique utilisée par Chrome, démarrons un « timer » pour compter les secondes écoulées pour l'exécution de cette méthode, pour une utilisation ultérieure dans un histogramme, vérifions si un paramètre a été passé au binaire pour attendre que le débogueur s'attache au processus et enfin démarrons un notification_service_ qui, par supposition éclairée, notifiera au watcher principal du threadpool quand démarrer un service. . Nous initialisons ensuite les polices nécessaires pour Chrome et créons la boucle principale du navigateur, qui est le coordinateur de tous les processus, et nous sautons par-dessus trois méthodes qui ne présentent aucun intérêt pour nous : main_loop_->CreateStartupTasks();
int result_code = main_loop_->GetResultCode(); . D'ici, ce qui nous intéresse, c'est d'entrer dans CreateStartupTasks. De là, nous regardons content\browser\browser_main_loop.cc ; nous sommes intéressés par startup_task_runner_->RunAllTasksNow(); qui se trouve dans la méthode createstartuptask. starup_task_runner_ est un StartupTaskRunner situé dans le même répertoire, dans le fichier startup_task_runner.cc. Maintenant, si nous inspectons la méthode RunAllTasksNow, nous voyons qu'elle fait simplement une itération sur toutes les tâches et les exécute
==========================================================================================
Place à une analyse dynamiqueMaintenant, afin d'attraper le renderer, il faudra démarrer le binaire dans windbg. C'est simplement réalisable en faisant Fichier->Ouvrir un exécutable et en passant --renderer-startup-dialog --no-sandbox --wait-for-debugger-children=renderer --renderer-process-limit=1 comme arguments.. Nous définissons ensuite un point d'arrêt sur content!content::StartupTaskRunner::RunAllTasksNow+0x88 afin de pouvoir capturer les messages IPC et plus tard le renderer. Pour référence, voici à quoi votre débogueur devrait ressembler après l'avoir exécuté une fois  . Vous devriez voir le message « chrome is running in full browser mode ». À partir de là, vous exécutez en comptant de un à cinq, donc vous l'exécutez encore quatre fois. Ensuite, vous devriez voir quelque chose comme . Je recommande d'utiliser procmon pour surveiller le processus renderer lorsqu'il démarre. Après cela, vous l'attachez à un autre débogueur, et si vous avez utilisé les arguments ci-dessus, vous devriez voir une boîte de dialogue apparaître vous indiquant le PID du renderer. À partir de là, vous voudrez mettre un point d'arrêt sur base!base::RunLoop::Run. Malheureusement, vous ne pouvez pas vous arrêter sur content!content::RendererMain pour une raison quelconque. Peut-être parce que nous attachons le processus juste après qu'il quitte la fonction RendererMain et qu'il est exécuté par un thread. Quoi qu'il en soit, voici à quoi ressemble l'IPC après quatre exécutions.. Cela indique que l'interface graphique est démarrée. Et voici à quoi ressemble l'IPC après le démarrage du processus renderer.. Nous définissons ensuite un point d'arrêt sur
content!content::StartupTaskRunner::RunAllTasksNow+0x88 et content!content::RunOtherNamedProcessTypeMain. Laissez-le s'exécuter
============================================================================================================
3.Maintenant, pour la deuxième partie de l'analyse du processus navigateur, nous sommes arrivés au point où nous serons capables de comprendre et de déboguer le renderer, mais nous n'y allons pas encore. J'ai explicitement laissé une fonction de plus à analyser après RunBrowserProcessMain ; théoriquement, c'est après RunBrowser, mais comme vous vous en souvenez peut-être, RunBrowser est un wrapper pour RunBrowserProcessMain. Ce que j'ai laissé de côté, c'est que dans le fichier appelé content_main_runner_impl.cc dans le dossier src/content/app, il y a une autre fonction qui est appelée après le lancement du renderer et qui s'appelle RunOtherNamedProcessTypeMain. Ce processus est chargé d'exécuter tous les autres processus.. Maintenant, comprenons ce qui se passe dans le code. Nous voyons que son prototype est , ce qui indique qu'il prendra les arguments passés à la ligne de commande, le type de processus attendu et un delegate Chrome. Nous arrivons ensuite au début de la fonction où nous avons une macro-définition pour vérifier les spécificités de la plate-forme et vérifier pour quel processus instancier un gestionnaire d'événements. C'est-à-dire vérifier s'il exécute certaines fonctions pour le processus console ou pour le processus navigateur. . Nous itérons ensuite sur les processus passés et les comparons à une liste de processus connus, puis exécutons le processus correspondant.  Si nous n'obtenons pas le processus correspondant, il s'agit alors d'un processus personnalisé implémenté par quelqu'un
Voici un lien pour implémenter un processus personnalisé et nous utiliserons cet exemple pour la deuxième partie de l'analyse dynamique . https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial .
=====================================================================
Partie Analyse Dynamique
La première partie de l'analyse dynamique commence comme suit : lancez d'abord, depuis une cmd.exe avec les droits administrateur, la commande suivante : windbg.exe chrome.exe -G -o --renderer-startup-dialog --no-sandbox --wait-for-debugger-children=renderer --renderer-process-limit=1 --allow-pre-commit-input --allow-sandbox-debugging . Après cela, définissez .childdbg 1 afin de pouvoir déboguer les nouveaux processus enfants créés. En substance, la commande ci-dessus fait « assurez-vous que vous êtes attaché à tous les processus enfants ». Un grand merci à @spoofyroot et @_coreDump pour m'avoir orienté dans la bonne direction avec le débogage multiprocessus. Voici à quoi cela est censé ressembler après avoir défini .childdbg 1  Comme je n'ai pas pu capturer d'une autre manière ce qui se passe ensuite, j'ai fait une vidéo que j'expliquerai. https://streamable.com/9t4iof . Fondamentalement, après avoir exécuté pour la dernière fois content!content::StartupTaskRunner::RunAllTasksNow+0x88, nous créons de nouveaux processus qui gèrent des trucs IPC, et nous devrons continuer à définir un point d'arrêt sur content!content::RunContentProcess jusqu'à ce qu'il soit atteint. C'est ce que j'appelle un essai-erreur éclairé :)) .
=====================================================================
4.Analyse du Renderer
!Avertissement
Pendant que nous ferons l'analyse du code du Renderer, nous plongerons un peu aussi dans le code de blink afin de comprendre ce qui se passe correctement dans le renderer
En écrivant cette partie du cours, je me suis rendu compte que j'ai oublié de décrire brièvement ce que cela fait et pourquoi même on y jette un œil. Comme nous le savons, c'est le troisième processus démarré par chromium et il s'appelle le renderer. Mais pourquoi est-il appelé le « renderer » ? Il est appelé ainsi parce que son travail est de rendre (dessiner) tout ce que nous voyons sur un site web. Fondamentalement, c'est la raison pour laquelle votre tableau ressemble à un tableau lorsque vous allez sur un site web, ou bien votre CSS permet de personnaliser un morceau de texte. Ou pourquoi votre JS est capable de faire de la magie noire*. Une autre raison importante pour laquelle nous analysons cela est que c'est de là que viennent la plupart des bugs. Que nous parlions de HTML, CSS, JS ou de tout autre composant, vous les verrez tous sous blink sur chrome bugs.chromium.org
* Il existe plus de processus renderer. Un processus entièrement séparé pour chaque onglet que le navigateur a actuellement ouvert.
* Ce processus contrôle tout ce qui se trouve à l'intérieur de l'onglet du site web réel
* à partir de 2018, les iframes ont reçu une mise à niveau où elles peuvent toutes avoir des onglets. Ainsi, chaque onglet d'une iframe possède un processus renderer individuel. Cela s'appelle l'isolation de site (Site-Isolation)
* Sa responsabilité est d'analyser un site web, de dessiner sur votre écran ce que le site contient, par ex. tableaux, images, exécuter du JavaScript
* il est sandboxé
* à la base, il utilise un moteur de rendu appelé Blink.
* crée et gère des schémas d'URL tels que : chrome://, devtools://, chrome-error://
* initialise le moteur Blink qui fera en réalité tout l'analyse et le gros du travail pour le processus renderer.
La dernière fois, nous avions terminé l'analyse du processus navigateur et maintenant, c'est le moment que nous attendions tous : aller faire la partie analyse du renderer. Bon, quand je jouais avec chromium, j'ai réussi à le faire planter et j'ai obtenu la trace de pile suivante.  Sur cette base, nous savons que notre voyage commence à content::RendererMain qui se trouve dans `\content\renderer\renderer_main.cc` . Vous l'avez deviné, le point de départ est RendererMain. Commençons par analyser comment c'est défini et un peu à l'intérieur  . Nous voyons qu'il accepte un paramètre de type MainFunctionParams, ce qui signifie que cette fonction acceptera les arguments passés au binaire. Ensuite, nous ajoutons un point de trace pour savoir que nous sommes arrivés à l'appel de RendererMain, puis nous déréférençons la valeur de parameters et la sauvegardons dans command_line. Ensuite, nous avons quelques macros qui vérifient l'architecture spécifique à la plate-forme, que nous pouvons ignorer , nous vérifions la valeur passée à kTimeZoneForTesting qui est un fuseau horaire à utiliser pour les tests, puis nous rencontrons une nouvelle classe et un type de données appelé icu.

En cherchant une source de définition pour cela, nous arrivons à https://unicode-org.github.io/icu-docs . Si nous regardons le début du fichier où se trouve la directive include, nous pouvons voir que c'est une bibliothèque tierce. Donc jusqu'à présent, nous savons que c'est une bibliothèque tierce qui traite des composants internationaux pour Unicode, autrement dit nous l'utilisons pour le support Unicode. Ok, mais que fait cette fonction ? En allant sur https://unicode-org.github.io/icu-docs nous voyons , c'est-à-dire qu'elle définit le fuseau horaire par défaut en fonction de ce que nous avons défini comme paramètres. Nous initialisons ensuite la bibliothèque skia et gérons --renderer-startup-dialog
. Nous rencontrons ensuite une nouvelle classe RendererMainPlatformDelegate.
Qu'est-ce que cela fait ? Eh bien, d'abord, nous devons préciser qu'il s'agit d'une classe abstraite spécifique à la plate-forme. Pour notre cas, elle sera située dans /content/renderer à l'intérieur du fichier appelé renderer_main_platform_delegate_win.cc . Elle ressemble à ceci
. Nous pouvons donc conclure que tout cela n'est qu'une fonction d'aide qui active le sandbox, et si nous passons --no-sandbox, elle fait les actions nécessaires, et c'est tout. Nous définissons ensuite le nom de notre thread sur CrRendererMain. Nous rencontrons ensuite encore une nouvelle classe appelée RenderThread. 
Encore une fois, comme nous ne savons pas ce qu'elle fait, explorons-la un peu. Notre quête pour comprendre à quoi ressemble RendererThread commence à content/public/renderer/render_thread.h. En regardant à l'intérieur du fichier render_thread.h, nous voyons qu'elle est définie comme . Dans tout ce fichier, nous nous intéressons à IsMainThread qui est définie dans le fichier rendere_thread.cc et elle ressemble à ceci  (à ajouter : une longue description sur ce mécanisme). Maintenant, en poursuivant l'analyse du code du renderer, nous pouvons voir le moment le plus attendu : nous voyons enfin du code blink. Le premier du genre, et ce qu'il fait, c'est initialiser la bibliothèque.. Derrière les coulisses, la fonction ressemble à ceci . Maintenant, la question naturelle qui se pose est : c'est quoi ces classes ? Qu'est-ce que WTF et Platform ? Heureusement pour nous, les docs de blink nous disent ce qu'elles sont.
https://docs.google.com/document/d/1aitSOucL0VHZa9Z2vbRJSyAIsAz24kX8LFByQ5xQnUg/edit . En regardant « Directory structure and dependencies », nous pouvons en déduire que platform est une classe qui aide avec la géométrie et les graphiques. Maintenant, à propos des classes WTF et Partitions. Les docs de Blink disent aussi à propos de WTF : cela vient de Web Template Framework et c'est en grande partie un « wrapper » de la bibliothèque STL, comme dans « est une bibliothèque de base pour Blink fournissant une variété de fonctionnalités de base, comme des conteneurs, des bibliothèques de chaînes, des mécanismes de comptage de références, des foncteurs, des primitives de threading, etc. » (citations des docs Blink (https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/platform/wtf/README.md)). Ajouter plus de détails sur Blink
Ok, maintenant, en poursuivant l'analyse de renderer_main.cc, nous voyons quelque chose dont nous ne savons pas encore ce que ça fait, et c'est  À ajouter : détails demain. Nous appelons ensuite . Nous vérifions ensuite si nous avons activé le plugin lors de la compilation et, le cas échéant, nous les chargeons.
 Nous effectuons ensuite quelques vérifications supplémentaires que j'ai décidé de sauter parce que l'explication devient assez longue et qu'elles ne sont pas nécessaires à ce stade. Mais en version TL;DR, il s'agit de savoir s'il faut activer ou non le sandbox avant l'initialisation du processus RenderProcess. Et nous arrivons finalement à .(à ajouter : le reste des détails sur le reste des fonctions). Bien que ce ne soit pas la fin de l'analyse du renderer, nous pouvons la considérer comme le point de départ de l'analyse réelle du renderer, car, comme nous le verrons plus tard, c'est ici que se passe la plupart des choses intéressantes, nous pouvons donc la considérer comme le code du renderer. Nous nous intéressons à RenderThreadImpl, qui ressemble à . Dans tout ce code, nous nous intéressons à la fonction Init(), qui ressemble à ceci :
, mais elle est beaucoup plus longue. :) nous ne pouvons malheureusement pas la capturer en une seule image, et nous avons donc capturé son début. Mis à part cela, tout ce qui se passe après InitializeWebKit() ne nous intéresse pas vraiment, car la plupart n'est que communication IPC avec le processus GPU, qui n'est pas sur notre radar pour le moment. La fortune nous sourit car dans cette fonction, nous avons aussi attrapé l'une des fonctions qui nous intéressent, précisément InitializeWebKit(), qui ressemble encore à . Nous faisons un petit détour hors de notre quête d'explication de renderer_main.cc afin de mieux comprendre ce qui se passe à l'intérieur de InitializeWebKit. Je sais que c'est beaucoup à comprendre, mais suivez-moi pendant que nous essayons de donner un sens à ce chaos. Nous voyons donc InitializeWebKit() commencer par prendre tous les arguments qui ont été passés à ce processus, puis nous vérifions si nous avons activé à la compilation -dENABLE_VTUNE_JIT_INTERFACE, et si c'est le cas, nous vérifions l'option passée à la ligne de commande qui est enable-vtune-support. C'est quoi ce truc ? Nous avons cherché sur Google et nous avons trouvé un lien vers https://www.intel.com/content/www/us/en/develop/documentation/vtune-help/top.html et sur cette page, il est dit : « est un outil d'analyse de performances pour les applications séquentielles et multithreadées ». donc tl;dr quelque chose qui améliore les performances de votre chrome. Nous initialisons ensuite blink . Maintenant, pour comprendre le processus d'initialisation de blink, expliquons brièvement, car nous entrerons dans le détail au prochain chapitre qui examinera blink en profondeur. Nous rencontrons une autre classe qui nous est étrangère et c'est RendererBlinkPlatformImpl, qui ressemble à
 et peut être trouvée dans
content/renderer/renderer_blink_platform_impl.cc . Sur la base du nom, nous pouvons conclure qu'il s'agit d'une classe abstraite implémentée selon la plate-forme, et nous pouvons aussi voir qu'elle hérite de  En examinant ce que fait réellement RendererBlinkPlatformImpl, nous voyons qu'elle vérifie la plate-forme, puis selon les résultats de la vérification, elle marque un drapeau, puis vérifie si le thread actuel est un RendererThread en obtenant un pointeur vers TLS
blah blah blah, voir si tu ajoutes plus de contenu sur les classes. Nous arrivons ensuite à quelque chose que nous attendions tous : le premier morceau de code v8. Qui est . Qu'est-ce que cela fait ? Tout d'abord, nous savons par les docs qu'un v8::isolate est une instance du moteur V8. c'est-à-dire (une copie indépendante du runtime V8, comprenant un gestionnaire de tas, un ramasse-miettes, etc.) pas suffisant pour exécuter des scripts. nous voyons blink::MainThreadIsolate() défini comme
 qui se trouve dans le fichier blink/renderer/platform/bindings/v8_per_isolate_data.cc , qui à son tour V8PerIsolateData::MainThreadIsolate() ressemble à
 , V8PerIsolateData qui, comme vous l'avez deviné, est aussi une
classe qui se trouve dans bindings/core/v8/V8PerIsolateData.h ressemble à peu près à blah blah blah, ajoute des détails. Nous vérifions ensuite si nous avons passé kDisableThreadedCompositing(à ajouter demain à quoi ressemble le flag en fait)à la ligne de commande. dans le cas contraire, nous démarrons un thread de composition. C'est quoi ce truc ? En citant https://frontendmasters.com/courses/web-performance/the-compositor-thread/ , c'est un thread dont le « seul travail est de dessiner des bitmaps, de prendre les bitmaps, de les envoyer au GPU, de les mettre à l'écran ». Nous enregistrons ensuite ce qu'on appelle un schéma. En gros, vous souvenez-vous que lorsque vous visualisez une source, vous avez quelque chose devant l'URL comme : « view-source:website » . Ouais, c'est en fait géré par le renderer. Et il y a plus de ces schémas. Qu'entendent-ils par « enregistrer » ? Je n'en suis pas encore sûr, mais je pense qu'ils veulent dire : hé, c'est quelque chose que je veux que tu gères quand l'utilisateur vient et fait ce truc. Bref, voilà à quoi ça ressemble. à ajouter : plus de détails. bon, maintenant quand j'ajouterai, il faudra détailler aussi le dernier morceau de code 
. balh balsdld et enfin nous arrivons à la dernière partie de renderer_main à mettre à jour avec des détails

=====================================================================
Partie Analyse DynamiqueMaintenant, afin de pouvoir le déboguer dynamiquement, si vous êtes aussi noob que moi, vous voudrez lancer windbg depuis cmd.exe comme suit : windbg.exe chrome.exe -G -o --renderer-startup-dialog --no-sandbox --wait-for-debugger-children=renderer --renderer-process-limit=1 --allow-pre-commit-input --allow-sandbox-debugging --time-zone-for-testing="US/Pacific", et définir .childdbg 1 afin de pouvoir déboguer le processus enfant généré. À partir de là, vous voudrez définir un point d'arrêt avec bp content!content::RendererMain et le laisser tourner environ 5 ou 6 fois. (modifie-le pour faire référence à 4 dans la vidéo, c'est plus clair comme ça) après ça, on arrive à .





MainDllLoader

--no-sandbox
chrome_mainVoyons maintenant ce qu’il fait. Nous ouvrons chromium.dll dans binja
Sur la base de cette image provenant de https://blogs.igalia.com/jaragunde/files/2019/03/chrome-init-sequence.png, nous avons une idée approximative de ce que nous devons chercher dans binja
Voici à quoi ressemble le graphe dans binja
.
Pour mieux le suivre, cherchez une fonction appelée ChromeMain. C’est la logique principale (cœur) de chrome. À l’intérieur, on voit les phases mentionnées ci-dessus de la façon dont chrome.dll s’exécute au démarrage :
Ici, on voit qu’il appelle d’abord certaines fonctions telles que sub_180001420,sub_180017020,sub_180017020 qui effectuent des vérifications pour voir certains détails concernant la façon dont chrome a été installé/compilé. En utilisant le code source, nous pouvons suivre leurs attributs. La première fonction, sub_180001420, correspond à UmaHistogramEnumeration ; ensuite nous avons sub_180017020, qui est InitializeFromPrimaryModule, et la troisième et dernière, sub_180017020, est chrome_main_delegate.
Nous répétons sans cesse chrome_main_delegate, mais nous n’avons jamais défini quel est son but. ChromeMainDelegate est une classe qui hérite de ContentMainDelegate et fournit principalement des fonctions liées au démarrage et le traitement des appels de processus. !Si vous le souhaitez, vous pouvez implémenter une interface ContentMainDelegate personnalisée pour modifier le comportement par défaut du module Content, et utiliser une classe ChromeMainDelegate personnalisée dans Chromium pour personnaliser le comportement du processus de démarrage.

À l’intérieur, il y a certains appels de fonction qui effectuent des xor sur certaines régions de données et les concatènent avec certaines entrées de registre pour obtenir des options concernant le démarrage de chrome
.
Notez également que la fonction sub_180001510 crée une référence de pointeur scopé avec deux callbacks. Ce n’est pas vraiment important, mais il est intéressant d’apprendre ce qu’est un BindStateBase. Nous le rencontrerons souvent dans le code de base de chrome.
Vous vous demandez peut-être ce que font les trois dernières lignes, précisément
? La première ligne crée en gros un std::unique_ptr<> pour les Closures. WTF, qu’est-ce qu’une closure !? Eh bien, pour citer Mozilla Dev : « la closure est la combinaison d’une fonction regroupée (enclose) avec des références à son état environnant (l’environnement lexical). En d’autres termes, une closure vous donne accès à la portée d’une fonction externe depuis une fonction interne. En JavaScript, les closures sont créées à chaque fois qu’une fonction est créée, au moment de la création de la fonction. » Si l’on devait utiliser un langage humain, c’est une fonction dans une fonction qui accède à une variable de la fonction externe. Ex :
.
Donc, en substance, cela garantit que la closure s’exécutera. Et les deux autres lignes définissent simplement un comportement spécifique pour les dumps lorsque chrome crashes.InstallDetails::Get().VersionMismatch() patche ici
En allant plus loin, il vérifie sa version au moment de l’exécution et, si elle ne correspond pas, il plante et nous arrivons à l’analyse de la ligne de commande 
En entrant dans sub_184171800, on voit qu’elle n’est pas si grande
D’abord, elle prend les arguments passés au processus courant, puis appelle une fonction qui prend une StringPiece, essentiellement une classe wrapper pour std::string mais un peu plus sympa, et cette fonction vérifie en gros si le binaire a été appelé en mode headless, compare si le nom du binaire exécuté est chrome, vérifie si USE_HEADLESS_CHROME est défini, puis passe à l’étape suivante, à savoir content main.
Sur la base des arguments fournis, le travail de content main est de démarrer le shell correspondant.
Or, si nous ne fournissons aucun argument au shell, le flux d’exécution va vers
.
Afin de comprendre ce qu’elle fait, nous devons vérifier sa source. Nous pouvons la trouver dans /src/content/app/content_main.cc
Nous devrons faire défiler jusqu’en bas et là nous trouverons la définition de la fonction ContentMain. On y voit qu’elle appelle deux fonctions. L’une initialise un ContentMainRunner, qui est la classe qui gère toute la « création de processus » dans le contexte de la création du navigateur, de l’IPC, du réseau, etc. L’autre vérifie en gros le type de sous-processus et le transmet à la classe ContentMainRunner.
Prenons maintenant un peu de recul afin de comprendre le flux du code. Examinons d’abord RunContentProcess, car ContentMainRunner est assez complexe. La première étape consiste à créer un GlobalActivityTracker. Omg !!! vous l’avez deviné, Google vous suit :))) Non, je plaisante, ne me poursuivez pas en justice, Google. Mais plus sérieusement, il suit chaque thread ; c’est un tracker de threads et il fait quelques petites choses sympas pour mieux déboguer, comme :
ce qui signifie en substance qu’un entier unique sera utilisé comme identifiant pour chaque thread afin de mieux comprendre ce qui a causé la rupture du processus.
par exemple :```
kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4
kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3
kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1
kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100,