
Eine ordentliche, gut strukturierte Dokumentation für den Einstieg in Chrome Pwning & V8 Pwning
Eine ordentlich strukturierte Dokumentation für den Einstieg in Chrome-Pwning & v8-Pwning
Wie dieses Dokument organisiert ist
Browser gehören heute zu den am häufigsten genutzten Technologien. Auf jedem handelsüblichen Computer, den wir einfach anschließen und einschalten, ist ein Browser installiert. Aus der Perspektive eines Angreifers und aus einer Bedrohungsmodell-Perspektive ist es daher sehr lohnenswert, wenn der Angreifer den Browser durch eine bösartige Seite kompromittieren kann. Aufgrund dieser Argumente habe ich mich entschieden, Googles JavaScript-Engine, insbesondere v8, zu studieren.
Angesichts der gigantischen Größe des v8-Projekts wähle ich als Startpunkt den Interpreter, nämlich d8. Obwohl bereits umfangreiche Forschung an d8 betrieben wurde, hoffen wir, mindestens einen Bug zu finden, und wenn nicht, in der Lage zu sein, mit der Forschung zur Browser-Ausnutzung fortzufahren, da v8 die Grundlage für die grundlegenden Exploit-Entwicklungsstrategien bietet, die bei der Browser-Ausnutzung verwendet werden.
Ein weiterer Grund, warum ich v8 als Ziel gewählt habe, ist, dass es in mehreren Browsern verwendet wird.
Wenn wir unter die Haube schauen, sehen wir, dass die Engine auch in MicrosoftEdge verwendet wird, sodass es eine Chance auf mehrere Bug-Bounty-Prämien gibt. Für das zugrunde liegende Betriebssystem wird der Forscher eine Mischung aus Windows und Linux verwenden, da es keine Einschränkungen hinsichtlich des Erhaltens einer Shell gibt, da v8-Bugs die Codeausführung über wasm-Seiten ermöglichen und nicht an eine bestimmte Plattform gebunden sind.
Leider werden wir, obwohl ein ausgenutzter Bug in v8 zu Codeausführung führen würde, aufgrund der Sandbox keinen Code ausführen können, und daher würden wir eine Codeausführung im Kontext des Renderers erhalten, was uns nicht erlauben würde, Code auf dem Rechner auszuführen. Dafür bräuchten wir einen weiteren Exploit für die Sandbox, also eine vollständige Kette, um das System auszunutzen.
Und so definieren wir die folgenden Ziele, um mit dem Browser-Hacking beginnen zu können:
In der ersten Phase des Projekts ist es notwendig, so viel Wissen wie möglich über die Chrome-Architektur zu sammeln und wie jede Komponente miteinander interagiert. Um dies besser zu verstehen, müssen wir das Chromium-Projekt in mehrere Unterkomponenten aufteilen, sodass wir alles isolieren und richtig analysieren können. Genauer gesagt, in wie viele Unterkomponenten die folgenden Komponenten zerfallen:
Der logischste erste Schritt ist nun, die Chromium-Architektur zu verstehen. Also gut, wir wollen den Browser ausnutzen, aber was passiert, wenn wir den Browser zum ersten Mal starten? Nun, nach dem Klicken auf die Chromium-Ausführbare startet diese einige Prozesse.
Die Reihenfolge und ihre Namen sind wie folgt:
Der erste heißt Content-Prozess. Was macht dieser Prozess?
Nachdem wir nun kurz wissen, was er tut, ist es Zeit, tiefer einzusteigen:
Die Art und Weise, wie Chromium zumindest unter Windows funktioniert, ist, dass es die Dateien in eine DLL kompiliert und sie dann in den Speicher lädt. Die Kernlogik des Chromium-Browsers befindet sich also in chromium.dll.
Dies wird auch durch den Code bestätigt
.
Dies stammt aus chrome_exe_main_win.cc und wenn du neugierig bist, den gesamten Code zu lesen, befindet er sich unter chromium/src/chrome/app. Ok, wenn wir die Ausführung weiterverfolgen, sehen wir, dass es MakeMainDllLoader() aufruft, um die DLL-Ladeklasse aufzurufen, danach startet es den "Loader", d.h. es lädt chrome.dll und startet es bei Bedarf mit den notwendigen Befehlszeilen neu. Um den Loader weiter zu analysieren, müssen wir seinen Code verstehen, der im selben Verzeichnis in der Datei mail_dll_loader_win.cc liegt. Wenn wir ganz nach unten scrollen, sehen wir den Aufruf von MakeMainDllLoader, der wiederum basierend auf der Version, die du hast, ChromeDllLoader oder ChromiumDllLoader aufruft.
Wir sehen, dass ChromiumDllLoader eine Klasse ist, die von erbt. Aus der Definition können wir ersehen, dass die Klasse einfach die DLL basierend auf den an cmdline und Prozesstyp übergebenen Argumenten lädt
.
Wenn wir die Launch-Methode analysieren, können wir einige Dinge verstehen, die passieren, bevor chrome.dll startet: Aus diesem Kommentar können wir entnehmen, dass "// Launching is a matter of loading the right dll and calling the entry point. // Derived classes can add custom code in the OnBeforeLaunch callback." Das Starten von Chrome ist eine Frage des Ladens einer Reihe von DLLs, die tatsächlich die Arbeit erledigen. Zweitens nimmt es die an die Befehlszeile übergebenen Argumente und initialisiert weiter die Sandbox-Dienste.
.
Zuerst prüft es, ob es der Browser ist, der die Sandbox-Initialisierung aufruft, dann prüft es, ob der Prozess, der die Initialisierung der Sandbox aufgerufen hat, als Cloud Print-Dienst aufgerufen wurde. Es prüft auch, ob an die Binärdatei übergeben wurde, was der Binärdatei im Grunde sagt, dass sie nicht in einer Sandbox ausgeführt werden soll. Es prüft, ob eine dieser Optionen gesetzt wurde, und wenn eine davon wahr ist, ruft es die Sandbox mit den entsprechenden Optionen auf. Am Ende gelangen wir zu
was uns interessiert, das den Wrapper zum Aufrufen von darstellt.
Verfolgen einer Prozesslebensphase, die definiert ist als```
// 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,
log asap when a process exits,save the information regarding the module aka when when a module(aka a componenet of chromium) is loaded and basically the same functinolity repeated but different for different threads and classes. In case you are interested you can find it at chromium/src/base/debug/activity_tracker.h
Anschließend wird geprüft, ob "Main()" bereits aufgerufen wurde. Ich denke hier ist gemeint, ob ChromeMain() bereits aufgerufen wurde. Sie prüfen im Grunde, ob dem Content-Prozess irgendwelche Argumente übergeben wurden. Falls ja, stellen sie sicher, dass der Browser-Prozess dieselben Argumente erhält. Dann prüfen sie auf Plattformspezifika und falls Windows erkannt wird, initialisieren sie ihren eigenen Handler durch Aufruf von CreateATLModuleIfNeeded; dann wiederholt sich die Geschichte – Prüfung auf Plattformspezifika – und sie übergeben die Argumente mittels SetupCRT. Dann gelangen wir zu dem Teil, wo wir die IPC initialisieren, um mit den anderen Prozessen zu kommunizieren, nachdem wir sie erzeugt haben. Hier ist der dafür verantwortliche Code. Wir werden nicht ins Detail gehen, da wir später bei der vertieften Diskussion des IPC-Mechanismus von Chrome darauf zurückkommen. Für den Moment sollten Sie nur wissen, dass dies der Mechanismus ist, der die Kommunikation zwischen den Multi-Architektur-Prozessen von Chrome ermöglicht. Falls Sie nicht wissen, wofür IPC steht: Es ist die Interprozesskommunikation.
.
Dann leitet es die Argumente weiter (eher als sie zu setzen) für die UI mittels ui::RegisterPathProvider, ruft den Tracker auf und erhält das Ergebnis mittels content_main_runner->Initialize(std::move(params)), erstellt eine übergeordnete Konsole (wobei ich denke, dass sie damit einen übergeordneten Prozess meinen), führt weitere Prüfungen durch, und dann gelangen wir zum wichtigen Teil, welcher
ist.
Hier sehen wir einen Aufruf einer Funktion namens IsSubprocess.
Sie macht im Grunde, um den alten Boilerplate-Code zu vermeiden, Folgendes: Sie prüft in einer Funktion den Typ des Prozesses, den sie über die Kommandozeile erhält, und falls eine Option übergeben wurde, wählt sie aus den folgenden aus und erstellt den entsprechenden Prozess.```
return type == switches::kGpuProcess ||
type == switches::kPpapiPluginProcess ||
type == switches::kRendererProcess ||
type == switches::kUtilityProcess || type == switches::kZygoteProcess;
Von dort gelangen wir zu `content_main_runner->Run();`, was im Wesentlichen die ganze Magie ausführt. Lass es uns unter der Haube untersuchen. `content_main_runner` ist also vom Typ `ContentMainRunner` – duh –, das in `content\app\content_main_runner_impl.cc` zu finden ist.
Um den Codeablauf hierfür tatsächlich zu verstehen, müssen wir ihn von unten nach oben betrachten. Und damit gesagt: Wir scrollen wieder runter und finden, dass `ContentMainRunner::Create()` tatsächlich `ContentMainRunnerImpl::Create()` aufruft; das bringt uns zu der Erkenntnis, dass wir nach der Definition von `ContentMainRunnerImpl` suchen müssen, nicht nach `ContentMainRunner`, die wir in `content_main_runner_impl.h` im selben bereits genannten Ordner finden.

Wir sehen, dass es von `ContentMainRunner` erbt, und wir können seine Kernmethoden erkennen. Wirklich nicht viel hier, da das Verhalten hauptsächlich in `content_main_runner_impl.cc` überschrieben wird.
Innerhalb von `content_main_runner_impl.cc` führt die `Run`-Methode zunächst einige Prüfungen mit `DCHECK` durch (was zur Hölle ist diese Funktion? (```Das CHECK()-Makro führt zu einem sofortigen Absturz, wenn seine Bedingung nicht erfüllt ist. DCHECK() ist wie CHECK(), wird aber nur kompiliert, wenn DCHECK_IS_ON wahr ist (Debug-Builds und einige Bot-Konfigurationen, aber keine Endbenutzer-Builds).``` Zitate aus Google-Dokumentation :) )), um zu sehen, ob `is_initialized`, `content_main_params_` und `is_shutdown_` gesetzt sind.

Danach holt es die Argumente und bestimmt den Typ, den ich bereits erwähnt habe.
Dann rufen wir, falls wir das nicht finden, `InitializeFieldTrialAndFeatureList()` auf, dann `delegate_->PostFieldTrialInitialization();` und senden es als Nachricht über Mojo.
 .
Als nächstes setzt es einige Dinge für die UI.
Wieder basierend auf dem, was in der Befehlszeile übergeben wurde, ruft es `RegisterMainThreadFactories` auf, was ein Wrapper für `RegisterUtilityMainThreadFactory` ist, das nur `g_utility_main_thread_factory` setzt, was meiner Meinung nach den Hauptthread erstellt, der der Thread ist, der die anderen Threads überwacht, und startet schließlich den Browserprozess. 
Falls du mir nicht vertraust, hier ist Chromium in Binja disassembliert, und wir werden auch einige In-Memory-Analysen davon sehen.
Zum Glück haben wir `chrome.exe.pdb`, das uns Debug-Symbole liefert, und wir werden keine Schmerzen beim Reversen der Binary haben.
Da wir Chrome unter Windows betrachten, ist der Haupteinstiegspunkt des Programms `wWinMain`:
So sieht der Graph aus:
.
Die Funktion selbst ist riesig, daher zeige ich nur die notwendigen Teile. Nach einer Reihe von Initialisierungen erreichen wir einen Punkt, an dem `MakeMainDllLoader()` aufgerufen wird.
, von dem wir erklärt haben, was es tut. Also, wie debuggen wir Chrome dynamisch und beweisen alles, was ich bereits erwähnt habe? Zuerst laden wir es in WinDbg, dann `lm` und suchen nach dem Namen der ausführbaren Datei. Danach suchen wir nach einer Funktion namens `chrome!MakeMainDllLoader`, setzen einen Breakpoint und lassen die Ausführung laufen.. Wir steigen ein. Lassen es laufen, bis es `ret` trifft, und wir kommen raus bei `chrome!wWinMain+0x764`.. Dann lassen wir es laufen bis `chrome!MainDllLoader::Launch` und steigen ein. Von dort setzen wir einen Breakpoint bei `chrome!MainDllLoader::Load`, um zu sehen, wie `chrome.dll` in den Speicher geladen wird. Und wie wir sehen können, war eines der ersten Dinge, die geladen wurden, `chrome.dll`. Von hier ist der Analyseablauf derselbe, und die vollständige In-Memory-Analyse wird dem Leser als Übung überlassen. Aus besonderen Gründen werde ich die Analyse bis zu dem Punkt durchführen, an dem der Content-Prozess fast abgeschlossen ist, und kurz vor dem Start des Browserprozesses stoppen. Der Zweck ist nur, zu zeigen, wie die IPC-Kommunikation zwischen den Prozessen beginnt. Nach dem Laden der DLL müssen wir nach einer `call rax`-Anweisung suchen. Was ich getan habe, war, die 0x40 Instruktionen ab dem aktuellen EIP nach dem Laden der DLL aufzulisten.. An dieser Adresse befindet sich tatsächlich das, wonach wir suchen: `chrome!ChromeMain`.
Von dort wollen wir bei  unterbrechen, was im Grunde eine große Prüfung vor dem Sprung in `content!content::ContentMain` ist. Von dort gehen wir ein paar Instruktionen weiter und treffen auf `content!content::ContentMain`. Wir wollen hineingehen und bei `content!content::RunContentProcess` unterbrechen. Wir steigen ein und setzen einen Breakpoint bei `content!content::ContentMainRunnerImpl::Run+0x430`, und sobald wir drübersteppen  sehen wir, wie die Mojo-IPC startet, und wir schließen daraus, dass der nächste Prozess, der Browserprozess, für die eigentliche Browserimplementierung und die Verwaltung aller anderen Prozesse verantwortlich ist.
===============================================================================================
2.
Beim letzten Mal sind wir nach dem Verständnis des Content-Prozesses stehen geblieben. Heute werden wir uns den Browserprozess ansehen.
Es ist der Prozess, der nach dem Content-Prozess gestartet wird, und der zweite in der von Chrome gestarteten Prozesskette.
Lass es uns kurz beschreiben:
* Wir können ihn als Hauptprozess betrachten, da der Content-Prozess eher eine Initialisierungsroutine und eine Überprüfung der Abhängigkeiten ist und vom Content-Prozess gestartet wird.
* Er bleibt während der gesamten Lebensdauer des Browsers aktiv.
* Er ist der zentrale Koordinator aller Prozesse und arbeitet mit der höchsten Privilegienstufe, die dem Browser zur Verfügung steht.
* Da er auf der höchsten Privilegienstufe läuft, wird, wenn andere Prozesse eine höherstufige Operation ausführen müssen, diese Anfrage vom Browserprozess bearbeitet.
* Er steuert Funktionen wie die Adressleiste, Lesezeichen und die Vor-/Zurück-/Aktualisieren-Schaltflächen. Da dies der privilegierteste Prozess ist, vertraut er den von anderen Prozessen gelieferten Daten nicht.
* Er behandelt auch privilegierte Operationen wie UI, Netzwerk- oder Dateisystemspeicher für die anderen Prozesse, wenn nötig.
Nachdem wir nun kurz wissen, was er tut, ist es Zeit, tiefer einzusteigen:
Unsere Reise beginnt in `src\content\app` in einer Datei namens `content_main_runner_impl.cc` bei der Funktion `int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params,bool start_minimal_browser)`. Die erste Operation, die ausgeführt wird, ist ein `TRACE_EVENT_INSTANT0`. .
Was ist das? Und überhaupt, was ist eine Trace-Funktion? Nun, wenn wir zu https://lwn.net/Articles/379903/ gehen, sehen wir, dass sie definiert ist als ; mit etwas Abstraktion können wir zu dem Schluss kommen, dass eine Trace-Funktion eine Funktion ist, die Daten an einem bestimmten Punkt in einem Stackframe aufzeichnet. Sie zeichnet nicht nur den Ausführungsstack der Funktion auf, sondern kann auch lokale Variablen der zuletzt ausgeführten Funktion speichern. Nachdem wir nun wissen, was eine Trace-Funktion ist, sehen wir uns an, was unsere tut.
Wenn wir zu https://chromium.googlesource.com/chromium/src/base/trace_event/common/+/refs/heads/main/trace_event_common.h gehen, sehen wir, dass sie als Funktion definiert ist, deren einziger Zweck darin besteht, „Anwendungsleistung und Ressourcennutzung zu verfolgen“. Gehen wir etwas tiefer, um zu verstehen, was sie tut: Es ist ein Makro, und es ist definiert als . Wir sehen eine kurze Definition oberhalb des Makros, die besagt, dass es ein einzelnes Ereignis namens „name“ sofort aufzeichnet, mit 0, 1 oder 2 Argumenten. Und falls die Kategorie des zugehörigen Ereignisses nicht aktiviert ist, tut es nichts. Wir sehen, dass als Parameter „startup“ als Hauptkategorie und als Subsystem „ContentMainRunnerImpl::RunBrowser(begin)“ übergeben werden; der dritte Parameter ist `TRACE_EVENT_SCOPE_THREAD`, was „#define TRACE_EVENT_SCOPE_THREAD (static_cast<unsigned char>(2 << 2))“ ist, was meiner Meinung nach basierend auf dem Wert eine ID für das entsprechende Instant-Ereignis ist. Im Wesentlichen zeichnet (loggt) dies einfach die Tatsache, dass wir mit unserer Ausführung in die `RunBrowser`-Funktion eingetreten sind. Dann haben wir eine Prüfung, ob die Browser-Hauptschleife bereits gestartet wurde, und falls ja, verlassen wir die Funktion. . Wir setzen dann ein Flag und gelangen zu einem eher interessanten Teil des Codes. Wir prüfen, ob wir Unterstützung für den Mojo-IPC-Mechanismus haben, und falls ja, verwenden wir `ShouldCreateFeatureList`, das eine Feature-Liste erstellt, mit verschiedenen Prozessen initialisiert und schließlich versucht, Mojo-Features zu initialisieren.. Dann erstellen wir einen Threadpool. WTF ist ein Threadpool?! Zitat aus Wikipedia: „Ein Software-Entwurfsmuster zur Erzielung von Nebenläufigkeit bei der Ausführung in einem Computerprogramm“. Genauer erklärt: „Ein Threadpool unterhält mehrere Threads, die auf Aufgaben warten, die vom überwachenden Programm zur gleichzeitigen Ausführung zugewiesen werden“ (immer noch Zitat aus Wikipedia). Noch genauer erklärt: Stell dir zwei Linien von Arbeitern in einer Fabrik vor. Nennen wir sie Linie A und Linie B. Sie werden alle von einem Chef beaufsichtigt, nennen wir ihn Linie C. Linie B muss warten, bis Linie A ihre Arbeit beendet hat und von Linie C benachrichtigt wird, um zu arbeiten. Und das Gleiche gilt für Linie A. Und das ist ein Threadpool. Hier ist auch ein kleines C-Beispiel . Dies ist schamlos von https://stackoverflow.com/questions/15752659/thread-pooling-in-c11 übernommen. Als nächstes haben wir einen Aufruf von `PreBrowserMain();`, der einige plattformspezifische Initialisierungen durchführt, und danach gelangen wir zu einem Punkt, an dem wir `BrowserTaskExecutor::Create()` aufrufen.
. Lass uns einen tiefen Einblick nehmen, was es tut, da der Name recht interessant ist und auf Basis einer fundierten Vermutung erkennen wir, dass dies interessant sein könnte. Unser Abstecher beginnt in `content/browser/scheduler/browser_task_executor.h`, wo wir finden, dass `BrowserTaskExecutor` eine Klasse ist, die dazu dient, „base::TaskTraits auf tatsächliche Aufgabenwarteschlangen für den Browserprozess abzubilden“. Gehen wir etwas weiter in der Datei runter: Zuerst  erbt es von `BaseBrowserTaskExecutor`. Um also zu verstehen, was `BrowserTaskExecutor` ist, müssen wir `BaseBrowserTaskExecutor` verstehen. Wir sehen, es erbt von `TaskExecutor`. Glücklicherweise sehen wir, dass es die Methoden von `TaskExecutor` mit überschreibbarer Eigenschaft überschreibt, was bedeutet, dass sie später von anderen Methoden überschrieben werden. Aber nur für die Neugierigen unter uns: Wir finden es in `base/task/task_executor.h`, und wenn wir es inspizieren, finden wir heraus, dass `TaskExecutor` eine Klasse ist, die „Tasks mit einer bestimmten TaskTraits-Erweiterungs-ID ausführen kann“. .
Was ist eine Task und was sind TaskTraits? Wir haben bereits erwähnt, was eine Task ist, aber zur Erinnerung: Es ist einer der Prozesse von Chromes Prozessen. Und was sind TaskTraits? Sie befinden sich in `base/task/task_traits.h` und sind wie folgt definiert: „Sie kapseln Informationen über eine Task, die dem Threadpool helfen, bessere Planungsentscheidungen zu treffen.“ Zurück zu unserem `BrowserTaskExecutor`. Die Methode, die wir aufrufen, ist `Create()`, und ihre Analyse sieht wie folgt aus: 
Zuerst wird geprüft, ob die aktuelle Task über einen SingleThreadTaskRunner ausgeführt werden muss, also ob wir diese Task mit einem unabhängigen Thread ausführen müssen. Dies tun wir, indem wir einen Pointer auf TLS holen. Dann initialisieren wir einen UI- und einen Thread-Scheduler. Grundsätzlich initialisieren wir hier einen Scheduler für zukünftige Ereignisse im Zusammenhang mit der UI..
Und das war's auch schon für dieses Feature. Weiter mit der Analyse von `content_main_runner_impl.cc` kommen wir hier an:
Bei der Suche nach der Klassendatei für den Variations-IDs-Provider finden wir sie in `components/variations/variations_ids_provider.h`, dort sehen wir etwas recht Interessantes. Es enthält eine `.mojom.h`-Datei. Wir finden ihre Definition in `Debug/gen/components/variations/` variations.mojom.h, was bedeutet, dass es etwas mit dem IPC-Mechanismus zu tun haben muss. Wenn wir uns die tatsächliche Definition der Klasse ansehen, sehen wir einen Kommentar, der sagt: „Eine Hilfsklasse zum Verwalten von Client-Experimenten und Metrikzuständen, die in benutzerdefinierten HTTP-Request-Headern übertragen werden.“ Wenn wir uns ihr Verhalten und die Quellendefinition ansehen, schließen wir, dass sie einfach als Marker für eine Funktion verwendet wird, die markiert, dass „signed-in parameter supplied to GetClientDataHeaders()“ irgendwo im Stack-Ablauf ein Aufruf von `GetClientDataHeaders` erfolgt, der später mit einem Parameter aufgerufen wird. Dann rufen wir `delegate_->PostEarlyInitialization(!!main_params.ui_task);` auf, was einfach an die Mojo-IPC sendet, dass die UI-Aufgaben gestartet werden sollen. Wir führen weitere Initialisierungen durch:  und dann gelangen wir zum Aufruf von `RunBrowserProcessMain`.  . Falls du gehofft hast wie ich, dass wir hier das GUI-Start sehen, liegst du falsch. Ein Zitat von Meister Oogway: . Dann gehen wir weiter und führen einige weitere Prüfungen durch und treffen auf `BrowserMain`:. Wenn wir es durchgehen, führt es einige Tracing durch und dann gelangen wir zu:, wo wir tatsächlich das GUI starten. Falls du mir nicht vertraust, musst du dich noch ein wenig gedulden, bis wir zur dynamischen Analyse kommen. Dann erreichen wir die `Run`-Methode, die das eigentliche Zeug ist, an dem wir interessiert sind. Darauf folgend gelangen wir zu unserem nächsten Punkt zum Verständnis des Browserprozesses. Aber für jetzt machen wir einen weiteren kurzen Abstecher und untersuchen, wie die `Initialize`-Methode erstellt wird. Gleich zu Beginn sehen wir, dass sie die Ausführung der `init`-Methode verfolgt und davor ein Histogramm erstellt:. Dann prüfen wir das `initialization_started_`-Flag, um zu sehen, ob wir die Initialisierungsphase erreicht haben, und falls nicht, initialisieren wir Skia (eine von Chrome verwendete Grafikbibliothek), starten einen „Timer“, um die vergangenen Sekunden für diese Methode für ein späteres Histogramm zu zählen, prüfen, ob ein Parameter an die Binärdatei übergeben wurde, um auf das Anhängen des Debuggers zu warten, und starten schließlich einen `notification_service_`, der durch fundierte Vermutung den Haupt-Watcher des Threadpools benachrichtigt, wann ein Dienst gestartet werden soll. . Dann initialisieren wir die notwendigen Schriftarten für Chrome und erstellen die `mainbrowserloop`, den Koordinator für alle Prozesse, und überspringen drei Methoden, die für uns nicht interessant sind:
`main_loop_->CreateStartupTasks();`
`int result_code = main_loop_->GetResultCode();` . Von hier aus sind wir daran interessiert, in `CreateStartupTasks` hineinzugehen. Von dort schauen wir in `content\browser\browser_main_loop.cc`, uns interessiert: `startup_task_runner_->RunAllTasksNow();`, das sich innerhalb der `CreateStartupTask`-Methode befindet. `startup_task_runner_` ist ein `StartupTaskRunner`, der sich im selben Verzeichnis in der Datei `startup_task_runner.cc` befindet. Wenn wir uns die `RunAllTasksNow`-Methode ansehen, sehen wir, dass sie einfach über alle Tasks iteriert und sie ausführt:
==========================================================================================
Zeit für etwas dynamische AnalyseUm den Renderer zu erwischen, müssen wir die Binärdatei in WinDbg starten. Dies ist einfach über Datei -> Ausführen erreichbar, wobei wir `--renderer-startup-dialog --no-sandbox --wait-for-debugger-children=renderer --renderer-process-limit=1` als Argumente übergeben.. Dann setzen wir einen Breakpoint bei `content!content::StartupTaskRunner::RunAllTasksNow+0x88`, um die IPC-Nachrichten und später den Renderer abzufangen. Als Referenzpunkt sollte Ihr Debugger nach einmaligem Ausführen so aussehen: . Sie sollten die Meldung sehen, dass Chrome im Vollbildmodus läuft. Von dort aus zählen Sie von eins bis fünf, führen es also weitere viermal aus. Danach sollte etwas Ähnliches erscheinen: . Ich empfehle die Verwendung von Procmon, um den Renderer-Prozess beim Start zu überwachen. Danach haken Sie es in einen anderen Debugger ein. Falls Sie die oberen Argumente verwendet haben, sollte ein MessageBox-Popup erscheinen, das Ihnen die Renderer-PID anzeigt: . Von dort aus möchten Sie einen Breakpoint bei `base!base::RunLoop::Run` setzen. Leider können Sie aus irgendeinem Grund nicht bei `content!content::RendererMain` breaken. Vielleicht, weil wir den Prozess direkt nach dem Verlassen der RenderMain-Funktion anhängen und er von einem Thread ausgeführt wird. Wie auch immer, so sieht der IPC nach vier Läufen aus: . Dies zeigt an, dass die GUI gestartet ist. Und so sieht der IPC aus, nachdem der Renderer-Prozess gestartet ist: . Wir setzen dann einen Breakpoint bei `content!content::StartupTaskRunner::RunAllTasksNow+0x88` und `content!content::RunOtherNamedProcessTypeMain`. Lassen Sie es laufen.
============================================================================================================
3. Nun zum zweiten Teil der Browser-Prozessanalyse: Wir sind an dem Punkt angelangt, an dem wir den Renderer verstehen und debuggen können, aber wir gehen noch nicht dorthin. Ich habe bewusst eine weitere Funktion nach `RunBrowserProcessMain` zur Analyse ausgelassen – theoretisch liegt sie nach `RunBrowser`, aber wie Sie sich erinnern, ist `RunBrowser` ein Wrapper für `RunBrowserProcessMain`. Was ich ausgelassen habe, ist, dass es in der Datei `content_main_runner_impl.cc` im Ordner `src/content/app` eine weitere Funktion gibt, die aufgerufen wird, nachdem wir den Renderer erzeugt haben: `RunOtherNamedProcessTypeMain`. Dieser Prozess ist für die Ausführung aller anderen Prozesse zuständig.. Lassen Sie uns nun verstehen, was im Code passiert. Wir sehen, dass das Prototyp ist: , was darauf hindeutet, dass es die an die Befehlszeile übergebenen Argumente, den erwarteten Prozess-Typ und einen Chrome-Delegaten übernimmt. Dann gelangen wir zum Anfang der Funktion, wo wir eine Makrodefinition haben, um plattformspezifische Details zu prüfen und zu entscheiden, welcher Prozess einen Ereignishandler instanziiert. Das heißt, es wird geprüft, ob die Funktion für einen Konsolenprozess oder den Browser-Prozess ausgeführt wird. . Wir iterieren dann über die übergebenen Prozesse und vergleichen sie mit einer Liste bekannter Prozesse, um den entsprechenden Prozess auszuführen.  Falls wir keinen entsprechenden Prozess finden, handelt es sich um einen benutzerdefinierten Prozess, der von jemandem implementiert wurde:  Hier ist ein Link zur Implementierung eines benutzerdefinierten Prozesses; wir werden dieses Beispiel für den zweiten Teil der dynamischen Analyse verwenden: https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial .
=====================================================================
Dynamische Analyse Teil
Der erste Teil der dynamischen Analyse beginnt wie folgt: Führen Sie von einer cmd.exe mit Administratorrechten den folgenden Befehl aus: `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`. Setzen Sie dann `.childdbg 1`, um neu erzeugte Child-Prozesse debuggen zu können. Im Wesentlichen bewirkt der obige Befehl: "Stelle sicher, dass du an alle Child-Prozesse angehängt bist". Ein Dankeschön geht an @spoofyroot und @_coreDump, die mich in die richtige Richtung beim Multiprozess-Debugging gelenkt haben. So sollte es nach dem Setzen von `.childdbg 1` aussehen:  Da ich mit keiner anderen Methode erfassen konnte, was als Nächstes passiert, habe ich ein Video erstellt, in dem ich erkläre, was passiert: https://streamable.com/9t4iof . Grundsätzlich erzeugen wir, nachdem wir `content!content::StartupTaskRunner::RunAllTasksNow+0x88` zum letzten Mal ausgeführt haben, neue Prozesse, die IPC-Sachen behandeln, und wir müssen weiterhin einen Breakpoint bei `content!content::RunContentProcess` setzen, bis er getroffen wird. Das nenne ich fundiertes Ausprobieren :)) .
=====================================================================
4. Renderer-Analyse
!Haftungsausschluss
Während wir die Analyse des Renderer-Codes durchführen, werden wir auch ein wenig in den Blink-Code eintauchen, um richtig zu verstehen, was im Renderer passiert.
Während ich diesen Teil des Kurses schrieb, wurde mir klar, dass ich vergessen habe, kurz zu beschreiben, was dies tut und warum wir es überhaupt untersuchen. Wie wir wissen, ist dies der dritte von Chromium gestartete Prozess und er wird Renderer genannt. Aber warum heißt er "Renderer"? Er heißt so, weil seine Aufgabe darin besteht, alles, was wir auf einer Website sehen, zu rendern (zu zeichnen). Im Grunde ist das der Grund, warum Ihre Tabelle wie eine Tabelle aussieht, wenn Sie eine Website besuchen, oder Ihr CSS es ermöglicht, einen Textabschnitt zu gestalten. Oder warum Ihr JS in der Lage ist, schwarze Magie zu betreiben*. Ein weiterer wichtiger Grund, warum wir dies analysieren, ist, dass die meisten Fehler von hier kommen. Ob wir über HTML, CSS, JS oder jede andere Komponente sprechen – Sie werden sie alle unter Blink in den Chrome-Bugs auf bugs.chromium.org finden.
* Es gibt mehrere Renderer-Prozesse. Einen völlig separaten Prozess für jeden Tab, den der Browser derzeit geöffnet hat.
* Dieser Prozess steuert alles innerhalb des eigentlichen Website-Tabs.
* Seit 2018 haben iframes ein Upgrade erhalten, bei dem sie alle Tabs haben können. Somit hat jeder Tab eines iframes einen eigenen Renderer-Prozess. Dies wird Site-Isolation genannt.
* Seine Aufgabe ist es, eine Website zu parsen, auf Ihrem Bildschirm zu zeichnen, was die Website darin enthält, z. B. Tabellen, Bilder, und JavaScript auszuführen.
* Er ist in einer Sandbox.
* Im Kern verwendet er eine Rendering-Engine namens Blink.
* Er erstellt und behandelt URL-Schemes wie: chrome://, devtools://, chrome-error://
* Initialisiert die Blink-Engine, die die eigentliche Arbeit des Parsens und der schweren Arbeit für den Renderer-Prozess erledigt.
Beim letzten Mal haben wir die Analyse des Browser-Prozesses abgeschlossen, und jetzt ist es endlich soweit: Wir gehen zum Renderer-Analyse-Teil über. Als ich mit Chromium gespielt habe, brachte ich es zum Absturz und erhielt den folgenden Stacktrace:  Auf dieser Grundlage wissen wir, dass unsere Reise bei `content::RendererMain` beginnt, das sich in `\content\renderer\renderer_main.cc` befindet. Sie haben es erraten: Unser Ausgangspunkt ist RendererMain. Beginnen wir mit der Analyse, wie es definiert ist, und ein wenig über das Innere:  . Wir sehen, dass es einen Parameter vom Typ `MainFunctionParams` akzeptiert, was bedeutet, dass diese Funktion die übergebenen Argumente der Binärdatei annimmt. Als Nächstes fügen wir einen Trace-Punkt hinzu, damit wir wissen, dass wir `RendererMain` erreicht haben, dann dereferenzieren wir den Wert der Parameter und speichern ihn in `command_line`. Dann gibt es einige Makros, die auf plattformspezifische Architektur prüfen, die wir ignorieren können: . Wir prüfen den an `kTimeZoneForTesting` übergebenen Wert, eine zu testende Zeitzone. Dann treffen wir auf eine neue Klasse und einen Datentyp namens `icu`.

Wenn wir nach einer Definition dafür suchen, landen wir bei https://unicode-org.github.io/icu-docs . Wenn wir uns den Anfang der Datei ansehen, wo die Include-Direktive ist, sehen wir, dass es sich um eine Drittanbieterbibliothek handelt. Bis jetzt wissen wir also, dass es sich um eine Bibliothek handelt, die sich mit internationalen Komponenten für Unicode befasst – wir verwenden sie für Unicode-Unterstützung. Aber was macht diese Funktion? Wenn wir uns https://unicode-org.github.io/icu-docs ansehen, sehen wir , d.h. sie setzt die Standardzeitzone basierend auf dem, was wir als Parameter festgelegt haben. Wir initialisieren dann die Skia-Bibliothek und behandeln `--renderer-startup-dialog`:
. Als nächstes treffen wir auf eine neue Klasse: `RendererMainPlatformDelegate`.
Was macht diese? Nun, zuerst müssen wir spezifizieren, dass dies eine abstrakte Klasse ist, die plattformspezifisch ist. In unserem Fall wird sie sich in `/content/renderer` in der Datei `renderer_main_platform_delegate_win.cc` befinden. Sie sieht so aus:
. Also können wir schlussfolgern, dass es sich um eine Hilfsfunktion handelt, die das Aktivieren der Sandbox ermöglicht, falls wir `--no-sandbox` übergeben, die notwendigen Aktionen durchführt, und das war's. Wir setzen dann den Namen unseres Threads auf `CrRendererMain`:. Dann treffen wir auf eine weitere neue Klasse namens `RenderThread`. 
Da wir immer noch nicht wissen, was es tut, lasst es uns ein wenig erkunden. Unsere Suche nach dem Verständnis, wie `RendererThread` aussieht, beginnt in `content/public/renderer/render_thread.h`. Wenn wir uns die Datei `render_thread.h` ansehen, sehen wir, dass sie so definiert ist: . Aus der gesamten Datei sind wir an `IsMainThread` interessiert, das in der Datei `render_thread.cc` definiert ist und so aussieht:  (füge eine lange Beschreibung dieses Mechanismus hinzu). Wenn wir nun mit der Analyse des Renderer-Codes fortfahren, sehen wir den am meisten erwarteten Moment: Endlich sehen wir etwas Blink-Code. Der erste seiner Art initialisiert die Bibliothek.. Hinter den Kulissen sieht die Funktion so aus: . Die natürliche Frage, die sich stellt, ist: Was sind diese Klassen? Was ist WTF und Platform? Glücklicherweise sagt uns die Dokumentation von Blink tatsächlich, was das ist.
https://docs.google.com/document/d/1aitSOucL0VHZa9Z2vbRJSyAIsAz24kX8LFByQ5xQnUg/edit . Wenn wir uns "Directory structure and dependencies" ansehen, können wir ableiten, dass `platform` eine Klasse ist, die bei Geometrie und Grafik hilft. Was die Klassen WTF und Partitions betrifft, sagt die Blink-Dokumentation auch über WTF: Es stammt von "Web Template Framework" und ist größtenteils ein "Wrapper" für die STL-Bibliothek, d.h. "eine Basisbibliothek für Blink, die eine Vielzahl grundlegender Funktionen bereitstellt, wie Container, String-Bibliotheken, Referenzzählmechanismen, Funktoren, Threading-Primitiven usw." (Zitat aus der Blink-Dokumentation (https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/platform/wtf/README.md)). Füge weitere Details über Blink hinzu.
Ok, weiter in der Analyse von `renderer_main.cc` sehen wir etwas, von dem wir wieder nicht wissen, was es tut, und zwar:  Füge morgen Details hinzu. Dann rufen wir auf: . Dann prüfen wir, ob wir beim Kompilieren Plugins aktiviert haben, und falls ja, laden wir sie.
 Wir führen dann einige weitere Prüfungen durch, die ich beschlossen habe, nicht zu erklären, da die Erklärung bereits sehr lang ist und sie in diesem Stadium nicht notwendig sind. Aber in einer TL;DR-Version geht es darum, ob die Sandbox vor der RenderProcess-Initialisierung aktiviert werden soll oder nicht. Und schließlich gelangen wir zu: . (Füge die restlichen Details zu den anderen Funktionen hinzu). Obwohl dies nicht das Ende der Renderer-Analyse ist, können wir es als den Ausgangspunkt der tatsächlichen Renderer-Analyse betrachten, denn wie wir später sehen werden, passiert hier das meiste Interessante, sodass wir es als den Code des Renderers betrachten können. Wir sind an `RenderThreadImpl` interessiert, das so aussieht: . Von all dem Code sind wir an der `Init()`-Funktion interessiert, die so aussieht:
, aber sie ist viel länger. :) Wir können sie leider nicht in einem einzigen Bild erfassen, daher haben wir ihren Anfang erfasst. Abgesehen davon ist alles, was weiter von `InitializeWebKit()` passiert, nicht wirklich von Interesse, da es hauptsächlich um IPC-Kommunikation mit dem GPU-Prozess geht, der derzeit nicht auf unserem Radar ist. Das Glück ist auf unserer Seite, denn in dieser Funktion haben wir auch eine der Funktionen erwischt, an denen wir interessiert sind, nämlich `InitializeWebKit()`, das wiederum so aussieht: . Wir schweifen ein wenig von unserem Ziel ab, `renderer_main.cc` zu erklären, um besser zu verstehen, was in `InitializeWebKit` passiert. Ich weiß, es ist viel zu verstehen, aber haltet durch, während wir versuchen, etwas Licht in dieses Chaos zu bringen. Wir sehen, dass `InitializeWebKit()` damit beginnt, alle Argumente, die an diesen Prozess übergeben wurden, zu nehmen. Dann prüfen wir, ob wir beim Kompilieren `-dENABLE_VTUNE_JIT_INTERFACE` aktiviert haben, und falls ja, prüfen wir auf die an die Befehlszeile übergebene Option `enable-vtune-support`. Was ist das? Wir haben bei Google gesucht und einen Link zu https://www.intel.com/content/www/us/en/develop/documentation/vtune-help/top.html gefunden. Auf dieser Seite steht: "ist ein Leistungsanalyse-Tool für serielle und Multithread-Anwendungen". Also kurze Zusammenfassung: etwas, das die Chrome-Leistung verbessert. Wir initialisieren dann Blink: . Um den Prozess der Blink-Initialisierung zu verstehen, lassen Sie es uns kurz erklären, da wir im nächsten Kapitel, das sich eingehend mit Blink befasst, ins Detail gehen werden. Wir treffen auf eine weitere uns fremde Klasse namens `RendererBlinkPlatformImpl`, die so aussieht:
 und in
`content/renderer/renderer_blink_platform_impl.cc` zu finden ist. Basierend auf dem Namen können wir schließen, dass es sich um eine abstrakte Klasse handelt, die plattformabhängig implementiert wird. Wir sehen auch, dass sie von erbt: . Wenn wir uns ansehen, was `RendererBlinkPlatformImpl` tatsächlich tut, sehen wir, dass es auf Plattformbasis prüft und basierend auf den Prüfergebnissen ein Flag setzt: . Dann wird geprüft, ob der aktuelle Thread ein RendererThread ist, indem ein Zeiger auf TLS geholt wird.
Blah blah, sieh nach, ob du mehr Inhalt zu den Klassen hinzufügst. Dann gelangen wir zu etwas, auf das wir vielleicht alle gewartet haben: das erste Stück V8-Code. Das ist: . Was tut das? Zuerst wissen wir aus der Dokumentation, dass ein `v8::Isolate` eine Instanz der V8-Engine ist (d.h. eine unabhängige Kopie der V8-Laufzeit, mit Heap-Manager, Garbage Collector usw.). Es reicht jedoch nicht aus, um Skripte auszuführen. Wir sehen, dass `blink::MainThreadIsolate()` wie folgt definiert ist:
. Dies befindet sich in der Datei `blink/renderer/platform/bindings/v8_per_isolate_data.cc`. `V8PerIsolateData::MainThreadIsolate()` wiederum sieht so aus:
. `V8PerIsolateData` ist, wie Sie sich denken können, ebenfalls eine Klasse, die sich in `bindings/core/v8/V8PerIsolateData.h` befindet und ungefähr so aussieht:  blah blah, füge Details hinzu. Wir prüfen dann, ob wir `kDisableThreadedCompositing` (füge morgen hinzu, wie das Flag tatsächlich aussieht) an die Befehlszeile übergeben haben: . Falls nicht, starten wir einen Compositor-Thread. Was ist das? Zitat von https://frontendmasters.com/courses/web-performance/the-compositor-thread/ : Es ist ein Thread, dessen "einzige Aufgabe es ist, Bitmaps zu zeichnen, die Bitmaps zu nehmen, sie an die GPU zu senden und auf den Bildschirm zu bringen". Wir registrieren dann ein sogenanntes Schema: . Erinnern Sie sich, wenn Sie den Quelltext anzeigen, dass vor der URL etwas wie `view-source:website` steht? Ja, das wird tatsächlich vom Renderer behandelt. Und es gibt noch mehr dieser Schemata. Was bedeutet es, sie zu registrieren? Ich bin mir noch nicht sicher, aber ich denke, es bedeutet so etwas wie: "Hey, das möchte ich, dass du es behandelst, wenn der Benutzer dies tut." Wie auch immer, so sieht es aus:  Füge mehr Details hinzu. Gut, jetzt füge Details zum letzten Code-Stück hinzu: 
. Blah, und schließlich sind wir am letzten Teil von `renderer_main` angelangt: aktualisiere mit Details.

=====================================================================
Dynamische Analyse TeilUm es dynamisch debuggen zu können, falls du so ein Noob bist wie ich, wirst du windbg von der cmd.exe wie folgt ausführen wollen: 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", und setze .childdbg 1, um den erzeugten Kindprozess debuggen zu können. Von dort aus wirst du bp content!content::RendererMain setzen wollen und es etwa 5 oder 6 Mal laufen lassen. (sa modifici safaci referinta la 4 la video ca e mai clar asa) danach gelangen wir zu .





MainDllLoader

--no-sandbox
chrome_mainNun schauen wir uns an, was es tut. Wir öffnen chromium.dll in binja
Basierend auf diesem Bild von https://blogs.igalia.com/jaragunde/files/2019/03/chrome-init-sequence.png haben wir eine grobe Vorstellung, wonach wir in binja suchen sollten
So sieht der Graph in binja aus
.
Um es besser verfolgen zu können, suche nach einer Funktion namens ChromeMain. Dies ist die Hauptlogik von Chrome (der Kern). Darin sehen wir die erwähnten Phasen, wie chrome.dll beim Start läuft:
Hier sehen wir, dass es zuerst einige Funktionen wie sub_180001420, sub_180017020, sub_180017020 aufruft, die einige Überprüfungen durchführen, um Details darüber zu sehen, wie Chrome installiert/kompiliert wurde. Mit dem Quellcode können wir ihre Attribute verfolgen. Die erste Funktion, sub_180001420, entspricht UmaHistogramEnumeration, als nächstes haben wir sub_180017020, was InitializeFromPrimaryModule ist, und drittens und letztens sub_180017020 ist chrome_main_delegate.
Wir wiederholen ständig chrome_main_delegate, haben aber nie definiert, was sein Zweck ist. ChromeMainDelegate ist eine Klasse, die von ContentMainDelegate erbt und hauptsächlich startbezogene Funktionen und Prozessaufrufverarbeitung bereitstellt. Falls du möchtest, kannst du ein benutzerdefiniertes ContentMainDelegate-Interface implementieren, um das Standardverhalten des Content-Moduls zu ändern, und eine benutzerdefinierte ChromeMainDelegate-Klasse in Chromium verwenden, um das Verhalten des Startvorgangs anzupassen.
Darinnen gibt es einige Funktionsaufrufe, die XOR auf einige Datenbereiche anwenden und sie mit etwas Registry verketten, um einige Optionen bezüglich des Chrome-Starts zu erhalten
.
Beachte auch, dass die Funktion sub_180001510 einen scoped pointer reference mit zwei Callbacks erstellt. Nicht wirklich wichtig, was es tut, aber es ist interessant zu lernen, was ein BindStateBase ist. Wir werden ihm im Chrome-Basecode oft begegnen.
Jetzt fragst du dich vielleicht, was die letzten drei Zeilen, genau
, tun. Die erste Zeile erstellt also im Grunde ein std::unique_ptr<> für Closures. WTF ist ein Closure!? Nun, wenn ich es von mozila dev zitieren würde: "closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time." In menschlicher Sprache: Es ist eine Funktion innerhalb einer Funktion, die auf eine Variable in der äußeren Funktion zugreift. Z.B.
.
Im Wesentlichen stellt es also sicher, dass der Closure ausgeführt wird. Und die anderen beiden Zeilen setzen nur ein bestimmtes Verhalten für Dumps, wenn Chrome abstürzt. InstallDetails::Get().VersionMismatch() patchuie aici
Weiter geht es mit der Überprüfung der Version zur Laufzeit, und falls sie nicht übereinstimmt, stürzt es ab, und wir gelangen zur Befehlszeilenanalyse
Wenn wir in sub_184171800 hineintreten, sehen wir, dass es nicht so groß ist
Zuerst nimmt es die an den aktuellen Prozess übergebenen Argumente, dann ruft es eine Funktion auf, die ein StringPiece verwendet, im Grunde eine Klassen-Wrapper für std::string, aber etwas cooler, und diese Funktion prüft im Grunde, ob die Binärdatei headless aufgerufen wurde, vergleicht, ob der Name der ausgeführten Binärdatei chrome ist, prüft, ob USE_HEADLESS_CHROME gesetzt ist, und geht dann weiter zum nächsten Schritt, content main.
Basierend auf den bereitgestellten Argumenten besteht die Aufgabe von content main darin, die entsprechende Shell zu starten.
Wenn wir der Shell keine Argumente übergeben, geht der Ausführungsfluss zu
.
Um zu verstehen, was es tut, müssen wir die Quelle überprüfen. Wir finden sie unter /src/content/app/content_main.cc
Wir müssen ganz nach unten scrollen und dort finden wir die Definition der ContentMain-Funktion. Dort sehen wir, dass sie zwei Funktionen aufruft. Eine, die einen ContentMainRunner initialisiert, die Klasse, die die gesamte "Prozesserstellung" im Kontext der Erstellung des Browser-IPC-Netzes und des Restes handhabt. Und die andere, die im Grunde die Art der Unterprozesse überprüft und sie an die ContentMainRunner-Klasse weitergibt.
Nun machen wir einen Schritt zurück, um den Codefluss zu verstehen. Zuerst untersuchen wir RunContentProcess, da ContentMainRunner recht komplex ist. Der erste Schritt ist die Erstellung eines GlobalActivityTracker. Oh mein Gott!!! Du hast es erraten, Google verfolgt dich :))) Nur ein Scherz, bitte verklage mich nicht, Google. Aber im Ernst, dies verfolgt jeden Thread, der ein Thread-Tracker ist, und es tut ein paar nette Dinge für besseres Debugging, wie:
was im Wesentlichen bedeutet, dass es eine eindeutige ganze Zahl als Kennung für jeden Thread gibt, um besser zu verstehen, was den Prozess zum Absturz gebracht hat.
z.B.:```
kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4
kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3
kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1
kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100,