
توثيق سليم ومنظَّم جيداً للبدء في اختراق Chrome واختراق V8
توثيق مناسب ومنظم جيداً للبدء في اختراق كروم واختراق v8
كيف تم تنظيم هذا المستند
المتصفحات هي واحدة من أكثر التقنيات استخداماً اليوم. على كل حاسوب جاهز، إذا قمنا فقط بالتوصيل والتشغيل سنرى متصفحاً مثبتاً. لهذا السبب، من منظور المهاجم ومن منظور نموذج التهديد، من المكافئ جداً إذا تمكن المهاجم من اختراق المتصفح عبر صفحة خبيثة. بالنظر إلى الحجج السابقة، اخترت دراسة محرك جافاسكربت الخاص بجوجل، وتحديداً v8.
نظراً للطبيعة الضخمة لمشروع v8، اخترت كنقطة بداية المفسّر، وتحديداً d8. على الرغم من أن أبحاثاً واسعة قد أُجريت بالفعل على d8، فإننا نأمل في العثور على خطأ واحد على الأقل، وإذا لم نتمكن من ذلك، أن نتمكن من المضي قدماً في البحث حول استغلال المتصفحات، لأن v8 يوفر أرضية دخول لاستراتيجيات تطوير الاستغلال الأساسية المستخدمة في استغلال المتصفحات.
سبب آخر لاختياري v8 كهدف هو أنه يُستخدم عبر متصفحات متعددة.
إذا ألقينا نظرة تحت الغطاء، نرى أن المحرك يُستخدم في MicrosoftEdge أيضاً، لذلك هناك فرصة لمكافآت متعددة لصيد الثغرات. بالنسبة لنظام التشغيل الأساسي، سيستخدم الباحث مزيجاً من ويندوز ولينكس، لأنه لا يوجد قيد من حيث الحصول على شل، لأن ثغرات v8 تسمح بتنفيذ الكود عبر صفحات wasm وهي غير مرتبطة بأي منصة محددة.
لسوء الحظ، بينما كانت الثغرة المستغلة في v8 ستؤدي إلى تنفيذ كود، لن نتمكن من تنفيذ أي كود بسبب صندوق الحماية (sandbox)، وبالتالي سنحصل على تنفيذ كود في سياق العارض (renderer)، وهو ما لن يسمح لنا بتنفيذ كود على الجهاز. لهذا سنحتاج إلى استغلال آخر لصندوق الحماية، وبالتالي سنحتاج إلى سلسلة كاملة لاستغلال النظام.
وبالتالي نحدد الأهداف التالية لنكون قادرين على البدء في اختراق المتصفحات:
في المرحلة الأولى من المشروع، من الضروري جمع أكبر قدر من المعرفة حول بنية كروم وكيف يتفاعل كل مكوّن مع الآخر. لفهم ذلك بشكل أفضل، نحتاج إلى تقسيم مشروع Chromium إلى مكوّنات فرعية متعددة حتى نتمكن من عزل كل شيء وتحليله بشكل صحيح. وبشكل أكثر دقة، إلى كم مكوّن فرعي ينقسم كل من المكونات التالية:
الآن الخطوة الأكثر منطقية كخطوة أولى هي فهم بنية كروم. حسناً، نريد استغلال المتصفح، ولكن ماذا يحدث عندما نبدأ تشغيل المتصفح لأول مرة؟ حسناً، بعد النقر على ملف chromium التنفيذي، يبدأ الملف التنفيذي بعض العمليات.
ترتيبها وأسماؤها كما يلي:
أول عملية تسمى content process. ماذا تفعل هذه العملية؟
الآن بعد أن عرفنا باختصار ماذا تفعل، حان الوقت للتعمق أكثر:
إذن الطريقة التي يعمل بها Chromium على ويندوز على الأقل، هي أنه يترجم الملفات إلى dll، وبعد ذلك يقوم بتحميلها في الذاكرة. لذا فإن المنطق الأساسي لمتصفح Chromium موجود في chromium.dll.
هذا مؤكد أيضاً بالكود
.
هذا مأخوذ من chrome_exe_main_win.cc وإذا كنت فضولياً لقراءة الكود كاملاً فهو موجود في chromium/src/chrome/app. حسناً، بالمتابعة في التنفيذ نرى أنه يستدعي MakeMainDllLoader() لاستدعاء فئة تحميل الـ dll، بعد ذلك يشغّل "اللودر" أي يقوم بتحميل chrome.dll، وإذا كان ضرورياً يعيد تشغيله مع أسطر الأوامر اللازمة. لتحليل الـ Loader بشكل أعمق، نحتاج إلى فهم كوده، الموجود في نفس الدليل، في الملف mail_dll_loader_win.cc. عند التمرير حتى نهاية الملف، نرى الاستدعاء إلى MakeMainDllLoader الذي يقوم بدوره باستدعاء ChromeDllLoader أو ChromiumDllLoader بناءً على الإصدار الذي لديك.

يمكننا أن نرى أن ChromiumDllLoader هو فئة ترث من . نرى من التعريف أن ما تفعله هذه الفئة ببساطة هو تحميل الـ dll بناءً على الوسائط الممررة إلى سطر الأوامر ونوع العملية
.
من خلال تحليل طريقة Launch يمكننا فهم بعض الأشياء التي تحدث قبل بدء تشغيل chrome.dll: يمكننا أن نفهم من هذا التعليق أن "// Launching is a matter of loading the right dll and calling the entry point. // Derived classes can add custom code in the OnBeforeLaunch callback." أن إطلاق chrome هو مسألة تحميل مجموعة من ملفات الـ dll التي تقوم بالعمل الفعلي. ثانياً، يأخذ الوسائط الممررة إلى سطر الأوامر ويقوم أيضاً بتهيئة خدمات صندوق الحماية.
.
أولاً يتحقق مما إذا كان المتصفح هو من يستدعي تهيئة صندوق الحماية، ثم يتحقق مما إذا كانت العملية التي استدعت تهيئة صندوق الحماية قد استُدعيت كخدمة طباعة سحابية. كما يتحقق مما إذا كان قد مُرر إلى الملف التنفيذي، وهو ما يخبر الملف التنفيذي أساساً بعدم العمل داخل صندوق حماية. يتحقق مما إذا كان أي من هذه الخيارات مضبوطاً، وإذا كان أي منها صحيحاً، يستدعي صندوق الحماية مع الخيارات المعنية. ثم في النهاية نصل إلى
وهو ما نهتم به، وهذا يمثل الغلاف لاستدعاء .
تتبع مرحلة من مراحل دورة حياة العملية والتي تُعرَّف بأنها```
// 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,
سجّل بأسرع وقت ممكن عند خروج عملية، واحفظ المعلومات المتعلقة بالوحدة النمطية، أي عندما يتم تحميل وحدة نمطية (أو مكوّن من مكونات كروميوم)، وفي الأساس تتكرر الوظيفة نفسها ولكن بشكل مختلف لخيوط وفئات مختلفة. في حال كنت مهتمًا، يمكنك العثور عليها في chromium/src/base/debug/activity_tracker.h
بعد ذلك يتحقق مما إذا كانت "Main()" قد استُدعيت من قبل. هنا أعتقد أنهم يقصدون ما إذا كانت ChromeMain() قد استُدعيت من قبل. إنهم يتحققون بشكل أساسي مما إذا كانت هناك أي وسائط قد مُررت إلى عملية المحتوى عند استدعائها. وفي حال كان الأمر كذلك، فإنهم يتأكدون من أن عملية المتصفح ستحصل على نفس الوسائط. ثم يتحققون من الخصائص المحددة للمنصة، وفي حال تم العثور على Windows يقومون بتهيئة معالجهم الخاص عبر CreateATLModuleIfNeeded، ثم إنها القصة نفسها؛ يتحققون من خصائص المنصة ويمررون الوسائط باستخدام SetupCRT، ونصل إلى الجزء الذي نهيئ فيه IPC حتى نتمكن من التحدث إلى العمليات الأخرى بعد أن أنشأناها. إليك الكود المسؤول عن ذلك. لن نخوض في تفاصيله الآن، لأننا سنعود لاحقًا لمناقشة آلية IPC في كروم بمزيد من العمق. فقط اعلم في هذه اللحظة أن هذه هي الآلية التي تسهّل التواصل بين عمليات كروم متعددة البنى. وفي حال كنت لا تعرف ماذا يعني اختصار IPC، فهو يعني الاتصال بين العمليات.
.
ثم ما يفعله هو أنه "يعيد التوجيه" بدلاً من مجرد تعيين الوسائط لواجهة المستخدم باستخدام ui::RegisterPathProvider، ويستدعي المتتبع ويحصل على النتيجة باستخدام content_main_runner->Initialize(std::move(params))، وينشئ وحدة تحكم أبوية، والتي أعتقد أنهم يقصدون بها أنهم ينشئون عملية أبوية، ثم يقوم ببعض الفحوصات الإضافية، ومن ثم نصل إلى الجزء المهم وهو
.
هنا نرى استدعاءً لدالة تُسمى IsSubprocess.
ما تفعله بشكل أساسي لتجنب الكود النمطي القديم، هو أنها تتحقق في دالة واحدة من نوع العملية التي تستلمها من سطر الأوامر، وفي حال تم تمرير خيار، فإنها تختار من بين التالي وتنشئ العملية المناظرة.```
return type == switches::kGpuProcess ||
type == switches::kPpapiPluginProcess ||
type == switches::kRendererProcess ||
type == switches::kUtilityProcess || type == switches::kZygoteProcess;
من هناك نصل إلى `content_main_runner->Run();` الذي يقوم في جوهره بكل السحر. دعونا نفحصه من الداخل. إذن `content_main_runner` هو من الكلاس `ContentMainRunner` بالطبع.. ويمكن العثور عليه في `content\app\content_main_runner_impl.cc`
لكي نتمكن فعليًا من فهم تدفق الكود لهذا، علينا أن نقرأه من الأسفل إلى الأعلى. وبعد قول هذا، نمرر للأسفل مرة أخرى ونجد أن `ContentMainRunner::Create()` يستدعي في الواقع `ContentMainRunnerImpl::Create()`؛ مما يجعلنا ندرك أننا في الحقيقة سنبحث عن تعريف `ContentMainRunnerImpl` وليس `ContentMainRunner`، والذي نجده في `content_main_runner_impl.h` في المجلد المذكور نفسه.

نرى أنه يرث من `ContentMainRunner` ويمكننا رؤية طُرقها الأساسية. في الحقيقة، لا يوجد الكثير هنا لأن سلوكه يُعاد تعريفه في معظمه داخل `content_main_runner_impl.cc`
داخل `content_main_runner_impl.cc`، داخل دالة run، يقوم أولاً ببعض الفحوصات باستخدام DCHECK (ما هذه الدالة بحق الجحيم؟(```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).``` اقتباس من وثائق جوجل :)) للتأكد من أن is_initialized وcontent_main_params_ وis_shutdown_ معيّنة.

بعد ذلك، يحصل على الوسائط ويحدد النوع الذي ذكرته سابقًا
ثم في حال لم نتمكن من العثور على ذلك، نستدعي InitializeFieldTrialAndFeatureList() delegate_->PostFieldTrialInitialization(); ونرسله كرسالة باستخدام mojo.
 .
بعد ذلك، ما يفعله هو تعيين بعض الأشياء لواجهة المستخدم
مرة أخرى بناءً على ما تم تمريره عبر سطر الأوامر، ويستدعي RegisterMainThreadFactories وهو غلاف لـ RegisterUtilityMainThreadFactory الذي يعيّن فقط g_utility_main_thread_factory الذي أعتقد من اسمه أنه ينشئ الخيط الرئيسي، وهو الخيط الذي يعد بمثابة المراقب لبقية الخيوط، وأخيرًا يشغّل عملية المتصفح. 
في حال كنت لا تثق بي، هنا الكروميوم مفكك في binja وسنرى أيضًا بعض التحليل في الذاكرة لذلك.
لحسن حظنا، لدينا chome.exe.pdb الذي يتيح لنا رموز التصحيح ولن نعاني أي ألم أثناء عكس الباينري.
بما أننا نعمل على كروم في ويندوز، فإن نقطة الدخول الرئيسية للبرنامج هي wWinMain:
هكذا يبدو الرسم البياني
.
الدالة نفسها ضخمة، ولذلك سأعرض فقط الأجزاء الضرورية منها. بعد مجموعة من التهيئات نصل إلى نقطة تستدعي `MakeMainDllLoader()`
، وهو ما سبق أن شرحنا ما يفعله. حسنًا، كيف نقوم بتصحيح أخطاء كروم ديناميكيًا وإثبات كل ما ذكرته سابقًا؟ أولاً نحمّله في windbg، ثم نستخدم الأمر lm ونبحث عن اسم الملف التنفيذي. بعد ذلك نبحث عن دالة اسمها chrome!MakeMainDllLoader، ونضع نقطة توقف ونترك التنفيذ يستمر.. نحصل على الدخول إلى داخلها  نتركها تعمل حتى تصل إلى ret ونخرج منها، فنصل إلى chrome!wWinMain+0x764 . ثم نتركها تعمل حتى chrome!MainDllLoader::Launch وندخل إليها خطوة بخطوة. ومن هناك نضع نقطة توقف عند chrome!MainDllLoader::Load لكي نرى كيف يتم تحميل chrome.dll في الذاكرة. وكما نرى، من بين أول الأشياء التي تم تحميلها كان chrome.dll  من هنا، مسار التحليل هو نفسه، وسأترك التحليل الكامل في الذاكرة كتمرين للقارئ. ولغرض خاص، سأقوم بالتحليل حتى النقطة التي تكتمل فيها عملية المحتوى تقريبًا، وقبل بدء عملية المتصفح تمامًا سأتوقف هناك. الغرض من هذا هو فقط إظهار كيف تبدأ ipc بالتواصل فيما بينها. الآن بعد تحميل الـ dll، سيتعين علينا البحث عن تعليمة call rax. ما فعلته هو سرد 0x40 تعليمة من عنوان eip الحالي بعد تحميل الـ dll.. عند ذلك العنوان يوجد بالفعل ما نبحث عنه وهو chrome!ChromeMain.
من هناك نريد أن نتوقف عند  وهو في الأساس فحص كبير قبل القفز إلى content!content::ContentMain. من هناك نريد تجاوز بضع تعليمات والوصول إلى content!content::ContentMain. نريد الدخول إلى داخلها والتوقف عند content!content::RunContentProcess نريد الدخول إلى داخلها، ونضع نقطة توقف عند content!content::ContentMainRunnerImpl::Run+0x430 وبمجرد أن نتجاوزها  نرى أن mojo ipc تبدأ، ونستنتج أن العملية التالية، أي عملية المتصفح، هي المسؤولة عن تنفيذ المتصفح الفعلي والتعامل مع جميع العمليات الأخرى.
===============================================================================================
2.
في المرة السابقة توقفنا بعد فهم عملية المحتوى. اليوم سنتناول عملية المتصفح.
إنها العملية التي تبدأ بعد عملية المحتوى، وهي الثانية بين العمليات التي يبدأها كروم.
دعونا نصفها بإيجاز:
* يمكننا اعتبارها العملية الرئيسية، لأن عملية المحتوى هي مجرد روتين تهيئة وبعض فحوصات التبعيات، وهي تبدأ بواسطة عملية المحتوى
* تبقى حية طوال فترة حياة المتصفح بالكامل.
* هي المنسق المركزي لجميع العمليات، وتعمل بأعلى مستوى صلاحيات متاح للمتصفح.
* نظرًا لأنها تعمل بأعلى مستويات الصلاحيات، ففي حال احتاجت العمليات الأخرى إلى تنفيذ عملية بمستوى أعلى، تتم معالجة هذا الطلب بواسطة عملية المتصفح.
* تتحكم في ميزات مثل شريط العنوان والإشارات المرجعية وأزرار الرجوع/التقدم/إعادة التحميل. وبما أنها العملية الأكثر صلاحية، فإنها لا تثق في البيانات المقدمة إليها من أي من العمليات الأخرى.
* كما تتولى العمليات ذات الصلاحيات الخاصة مثل واجهة المستخدم أو الشبكات أو تخزين نظام الملفات للعمليات الأخرى عند الحاجة.
الآن بعد أن عرفنا بشكل موجز ما تفعله، حان الوقت للتعمق أكثر فيها:
تبدأ رحلتنا في `src\content\app` في ملف يسمى content_main_runner_impl.cc عند الدالة int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params,bool start_minimal_browser) أول عملية تنفذها نراها هي TRACE_EVENT_INSTANT0.
ما هذا؟ وقبل ذلك، ما هي دالة التتبع أصلًا؟ حسنًا، إذا ذهبنا إلى https://lwn.net/Articles/379903/ يمكننا أن نرى أنهم يعرّفونها على النحو التالي ، ومع قليل من التجريد يمكننا أن نصل إلى استنتاج مفاده أن دالة التتبع هي دالة تسجّل البيانات عند نقطة محددة في إطار المكدس. إنها لا تقتصر على تسجيل مكدس استدعاءات الدوال فحسب، بل يمكنها أيضًا تسجيل المتغيرات المحلية لآخر دالة تم تنفيذها. والآن بعد أن عرفنا ما هي دالة التتبع، دعونا نرى ماذا تفعل خاصتنا.
إذا ذهبنا إلى https://chromium.googlesource.com/chromium/src/base/trace_event/common/+/refs/heads/main/trace_event_common.h نرى أنهم يعرّفونها كدالة غرضها الوحيد هو "تتبع أداء التطبيق واستخدام الموارد". وبالتعمق أكثر لفهم ما تفعله، نجد أنها ماكرو ويُعرَّف كما يلي . يمكننا رؤية تعريف قصير أعلى الماكرو يقول إنه يسجّل حدثًا واحدًا يُسمى "name" فورًا، مع 0 أو 1 أو 2 من الوسائط. وفي حال لم تكن فئة الحدث المعني مفعّلة، فإنه لا يفعل شيئًا. يمكننا أن نرى أن لدينا كمعاملات "startup" كفئة رئيسية وكنظام فرعي لدينا "ContentMainRunnerImpl::RunBrowser(begin)" والمعامل الثالث هو TRACE_EVENT_SCOPE_THREAD وهو "#define TRACE_EVENT_SCOPE_THREAD (static_cast<unsigned char>(2 << 2))" والذي أعتقد بناءً على القيمة أنه معرّف للحدث اللحظي المعني. إذن في جوهره، هذا ببساطة يتتبع (يسجّل) حقيقة أننا دخلنا بالتنفيذ إلى دالة RunBrowser. ثم لدينا فحص لمعرفة ما إذا كانت الحلقة الرئيسية للمتصفح قد بدأت بالفعل، وفي حال كانت قد بدأت بالفعل نخرج من الدالة . ثم نضبط علامة، وبعد ذلك نصل إلى جزء مثير للاهتمام إلى حد ما من الكود. نفحص ما إذا كان لدينا دعم لآلية mojo_ipc، وفي حال كان لدينا، نستخدم ShouldCreateFeatureList الذي ينشئ قائمة ميزات مع عمليات مختلفة ويحاول تهيئتها، وأخيرًا يحاول تهيئة ميزات mojo.. ثم ننشئ مجموعة خيوط. ما هي مجموعة الخيوط بحق الجحيم؟! اقتباسًا من ويكيبيديا: "نمط تصميم برمجي لتحقيق التوازي في التنفيذ داخل برنامج حاسوبي". وبشرح أكثر دقة، "مجموعة الخيوط تحتفظ بعدة خيوط تنتظر تخصيص مهام لها لتنفيذها بشكل متوازٍ بواسطة البرنامج المشرف" (وهذا أيضًا اقتباس من ويكيبيديا). ولشرح أوضح، تخيل خطّين من الأشخاص يعملون في مصنع. دعنا نسميهما الخط (أ) والخط (ب). وكلاهما يُشرف عليه رئيس، دعنا نسميه الخط (ج). الآن، على الخط (ب) أن ينتظر حتى يُنهي الخط (أ) مهمته ويتم إخطاره من الخط (ج) لكي يعمل. وينطبق الأمر نفسه على الخط (أ). وهذا هو مفهوم مجموعة الخيوط. إليك أيضًا مثال صغير بلغة C . هذا مأخوذ بلا خجل من https://stackoverflow.com/questions/15752659/thread-pooling-in-c11 . بعد ذلك لدينا استدعاء إلى PreBrowserMain(); الذي يقوم ببعض التهيئة الخاصة بالمنصة، وبعد ذلك نصل إلى نقطة نستدعي فيها `BrowserTaskExecutor::Create()`;
. دعونا نتعمق في ما يفعله، لأن الاسم مثير للاهتمام حقًا، واعتمادًا على تخمين مدروس يمكننا أن ندرك أن هذا قد يكون مثيرًا للاهتمام. يبدأ تحويلنا من داخل content/browser/scheduler/browser_task_executor.h حيث نجد أن BrowserTaskExecutor هو صنف من المفترض أن "يقوم بتعيين base::TaskTraits إلى قوائم الانتظار الفعلية للمهام في عملية المتصفح". وبنزولنا قليلًا في الملف، نجد أولًا  أنه يرث من BaseBrowserTaskExecutor. لذا الآن، لكي نفهم BrowserTaskExecutor، نحتاج إلى فهم BaseBrowserTaskExecutor. يمكننا أن نرى أنه يرث من TaskExecutor. ولحسن حظنا، نرى أنه يعيد تعريف دوال TaskExecutor بخصائص قابلة لإعادة التعريف، مما يعني أنه سيتم تجاوزها بواسطة استدعاءات دوال أخرى لاحقًا. ولكن فقط للمهتمين من القراء، يمكننا العثور عليه في base/task/task_executor.h، وفي حال فحصنا نجد أن TaskExecutor هو صنف "يمكنه تنفيذ المهام مع معرّف امتداد محدد لـ TaskTraits" .
ما هي المهمة وما هي TaskTraits؟ لقد ذكرنا سابقًا ما هي المهمة، لكن للتذكير، هي إحدى مهام عمليات كروم. والآن، ما هي TaskTraits؟ إنها موجودة في base/task/task_traits.h ويتم تعريفها على النحو التالي: "تغليف معلومات حول مهمة تساعد مجموعة الخيوط على اتخاذ قرارات جدولة أفضل". بالعودة إلى BrowserTaskExecutor، الدالة التي نستدعيها هي Create() وتحليلها كما يلي  أولاً، نتحقق مما إذا كانت المهمة الحالية بحاجة إلى التشغيل من SingleThreadTaskRunner، أي إذا كنا بحاجة إلى تشغيل هذه المهمة باستخدام خيط مستقل. نفعل ذلك عن طريق الحصول على مؤشر إلى tls. ثم نهيئ واجهة مستخدم وجدولة خيوط. بشكل أساسي، هنا نهيئ جدولة للأحداث المستقبلية المتعلقة بواجهة المستخدم..
وهذا تقريبًا كل ما يخص هذه الميزة. ونكمل مع تحليل content_main_runner_impl.cc ونصل إلى هنا.
عند البحث عن ملف صنف موفّر معرّفات التغييرات، نجده في `components/variations/variations_ids_provider.h`، وهناك نرى شيئًا مثيرًا للاهتمام. إنه يتضمن ملف `.mojom.h`. يمكننا العثور على تعريفه في `Debug/gen/components/variations/` variations.mojom.h ، مما يعني أن له علاقة بآلية ipc. والآن، عند مراجعة التعريف الفعلي للصنف، نرى تعليقًا يصفه بأنه "فئة مساعدة للحفاظ على حالة تجارب العملاء والمقاييس المرسلة في ترويسات طلبات HTTP المخصصة". وبالنظر إلى سلوكه وتعريف مصدره، نستنتج أنه يُستخدم ببساطة كعلامة لدالة، تحدد أن "المعامل الموقّع المُمرَّر إلى GetClientDataHeaders()" مما يعني أنه في مكان ما في مكدس التنفيذ هناك استدعاء إلى GetClientDataHeaders وسيتم استدعاؤه لاحقًا بمعامل. ثم نقوم بـ delegate_->PostEarlyInitialization(!!main_params.ui_task); والذي يرسل ببساطة إلى ipc الخاص بـ mojo أنه يجب عليه بدء مهام واجهة المستخدم. نقوم بمزيد من التهيئة  ثم نصل إلى استدعاء RunBrowserProcessMain. . إذا كنت تأمل مثلي أن هذا هو المكان الذي نرى فيه بدء الواجهة الرسومية، فأنت مخطئ. اقتباس من السيد oogwgay . ثم نذهب ونقوم ببعض الفحوصات الإضافية ونصطدم بـ BrowserMain. وعند مراجعته، نقوم ببعض التتبع ثم نصل إلى  وهو المكان الذي نبدأ فيه الواجهة الرسومية فعليًا. الآن، في حال كنت لا تثق بي، سيتعين عليك التحلي بقليل من الصبر حتى نصل إلى التحليل الديناميكي. ثم نصل إلى دالة Run وهي الشيء الفعلي الذي يهمنا، وبعد ذلك سننتقل إلى نقطتنا التالية لفهم عملية المتصفح. لكن الآن دعونا نقوم باستطراد قصير آخر ونستكشف كيف يتم إنشاء دالة Initialize. فورًا نرى أنها تتعقب تنفيذ دالة init، وقبل ذلك تنشئ مدرجًا تكراريًا. ثم نتحقق من العلم initialization_started_ لمعرفة ما إذا وصلنا إلى مرحلة التهيئة، وفي حال لم نصل، نهيئ skia وهي مكتبة الرسوميات التي يستخدمها كروم، ونبدأ "مؤقتًا" لعدّ الثواني المنقضية لتشغيل هذه الدالة لاستخدامها لاحقًا في مدرج تكراري، ونتحقق مما إذا مررنا معاملًا للباينري لانتظار المصحح للارتباط بالعملية، وأخيرًا نبدأ notification_service_ الذي، بناءً على تخمين مدروس، سيخطر المراقب الرئيسي لمجموعة الخيوط بموعد بدء خدمة. . ثم نهيئ الخطوط اللازمة لكروم وننشئ الحلقة الرئيسية للمتصفح، وهي المنسق لجميع العمليات، ونقفز فوق ثلاث دوال لا تظهر لنا أي أهمية main_loop_->CreateStartupTasks();
int result_code = main_loop_->GetResultCode(); . من هنا، ما يهمنا هو الدخول إلى CreateStartupTasks. من هناك ننظر إلى content\browser\browser_main_loop.cc، ونحن مهتمون بـ startup_task_runner_->RunAllTasksNow(); وهي داخل دالة createstartuptask. starup_task_runner_ هو من نوع StartupTaskRunner الموجود في نفس الدليل داخل ملف startup_task_runner.cc. الآن إذا فحصنا دالة RunAllTasksNow نرى أن ما تفعله هو ببساطة المرور على جميع المهام وتشغيلها
==========================================================================================
حان وقت بعض التحليل الديناميكيالآن، من أجل الإمساك بعملية الـ renderer، سيتعين علينا تشغيل الملف الثنائي داخل windbg. يمكن تحقيق ذلك ببساطة عبر File->Open Executable وتمرير --renderer-startup-dialog --no-sandbox --wait-for-debugger-children=renderer --renderer-process-limit=1 كوسائط.. بعدها نضع نقطة توقف bp عند content!content::StartupTaskRunner::RunAllTasksNow+0x88 حتى نتمكن من التقاط رسائل IPC ولاحقًا الـ renderer. كمرجع فقط، هكذا يجب أن يبدو المصحح (debugger) بعد تشغيله مرة واحدة  . يجب أن ترى رسالة تفيد بأن chrome يعمل في وضع المتصفح الكامل. من هناك تعدّ من 1 إلى 5، أي تشغّله أربع مرات إضافية. وعندها يجب أن ترى شيئًا كهذا . أنصح باستخدام procmon حتى تتمكن من مراقبة عملية الـ renderer عند بدء تشغيلها. بعد ذلك تقوم بربطه بمصحح أخطاء آخر، وفي حال استخدمت الوسائط أعلاه يجب أن تظهر لك نافذة رسالة تخبرك بمعرّف عملية الـ renderer (pid). من هناك سترغب في وضع نقطة توقف bp عند base!base::RunLoop::Run. لسوء الحظ لا يمكنك التوقف عند content!content::RendererMain لسبب ما. ربما لأننا نربط العملية بعد خروجها مباشرةً من دالة RendererMain وهي تُدار بواسطة خيط. بغض النظر، هكذا يبدو الـ IPC بعد أربع مرات تشغيل.. يشير هذا إلى أن الواجهة الرسومية (gui) قد بدأت. وهكذا يبدو الـ IPC بعد بدء عملية الـ renderer.. بعدها نضع نقطة توقف bp عند
content!content::StartupTaskRunner::RunAllTasksNow+0x88 و content!content::RunOtherNamedProcessTypeMain. ثم نتركه يعمل
============================================================================================================
3. الآن بالنسبة للجزء الثاني من تحليل عملية المتصفح، وصلنا إلى النقطة التي سنتمكن فيها من فهم وتصحيح أخطاء الـ renderer، لكننا لن نذهب إلى هناك بعد. لقد تركت عمدًا دالة واحدة أخرى لتحليلها بعد RunBrowserProcessMain، نظريًا هي بعد RunBrowser، لكن كما قد تتذكر فإن RunBrowser هو غلاف لـ RunBrowserProcessMain. ما تركته خارجًا هو أنه في الملف المسمى content_main_runner_impl.cc داخل المجلد src/content/app توجد دالة أخرى تُستدعى بعد أن نُطلق عملية الـ renderer وتُسمى RunOtherNamedProcessTypeMain. هذه العملية مسؤولة عن تشغيل جميع العمليات الأخرى.. الآن دعونا نفهم ما يحدث في الكود. نرى أن نموذجها الأولي هو ، مما يشير إلى أنها ستأخذ الوسائط الممررة إلى cmdline ونوع العملية المتوقع ومفوّض chrome (chrome delegate). ثم نصل إلى بداية الدالة حيث يوجد تعريف ماكرو للتحقق من خصائص المنصة والتحقق من أي عملية سننشئ لها معالج أحداث، أي للتحقق مما إذا كانت تشغّل بعض الدوال الخاصة بعملية وحدة التحكم أم الخاصة بعملية المتصفح. . بعدها نمرّر على العمليات المُمررة ونقارنها بقائمة العمليات المعروفة ونشغّل العملية المناظرة.  وفي حال لم نحصل على العملية المناظرة فهي عملية مخصصة نفّذها شخص ما
إليك رابطًا لتنفيذ عملية مخصصة وسنستخدم هذا المثال في الجزء الثاني من التحليل الديناميكي. https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial .
=====================================================================
جزء التحليل الديناميكي
يبدأ الجزء الأول من التحليل الديناميكي كالتالي: أولًا، شغّل من cmd.exe بصلاحيات مسؤول الأمر التالي: 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 . بعد ذلك اضبط .childdbg 1 حتى نتمكن من تصحيح أخطاء العمليات الفرعية الجديدة المُنشأة. في الجوهر، ما يفعله الأمر أعلاه هو «تأكد من أنك مرتبط بجميع العمليات الفرعية». تحية خاصة إلى @spoofyroot و @_coreDump لإرشادهما لي في الاتجاه الصحيح فيما يخص تصحيح أخطاء العمليات المتعددة. هكذا يفترض أن يبدو الوضع بعد ضبط .childdbg 1  الآن وبما أنني لم أتمكن من التقاط ما يحدث بعد ذلك بأي طريقة أخرى، فقد صنعت مقطع فيديو سأشرح فيه ما يحدث لاحقًا. https://streamable.com/9t4iof . باختصار، بعد أن نشغّل للمرة الأخيرة content!content::StartupTaskRunner::RunAllTasksNow+0x88، تُنشأ عمليات جديدة تتعامل مع بعض أمور الـ IPC، وسيتعين علينا الاستمرار في وضع نقطة توقف bp عند content!content::RunContentProcess حتى يتم الوصول إليها. هذا ما أسميه التجربة والخطأ المدروس :)) .
=====================================================================
4. تحليل الـ Renderer
!تنويه
بينما سنقوم بتحليل كود الـ Renderer، سنغوص قليلًا أيضًا في كود blink حتى نفهم ما يحدث بشكل صحيح في الـ renderer
أثناء كتابتي لهذا الجزء من الدورة أدركت أنني نسيت أن أصف بإيجاز ما يفعله هذا ولماذا ننظر إليه أصلًا. كما نعرف، هذه هي ثالث عملية يبدأها كروميوم وتُسمى الـ renderer. لكن لماذا تُسمى «renderer»؟ تُسمى كذلك لأن وظيفتها هي رسم (rendering) كل ما نراه على موقع ويب. ببساطة، هذا هو السبب في أن جدولك يبدو كجدول عندما تزور موقعًا، أو أن الـ css الخاص بك يجعل من الممكن تخصيص جزء من النص. أو لماذا يستطيع الـ js الخاص بك فعل السحر الأسود*. سبب مهم آخر لتحليلنا لهذا هو أن معظم الثغرات تأتي من هنا. سواء تحدثنا عن html أو css أو js أو أي مكوّن آخر، ستراهم جميعًا تحت blink في chrome bugs.chromium.org
* هناك المزيد من عمليات الـ renderer. عملية منفصلة تمامًا لكل تبويب يفتحه المتصفح حاليًا.
* تتحكم هذه العملية في كل شيء داخل تبويب الموقع الفعلي
* ابتداءً من 2018، حصلت iframe على ترقية حيث يمكن أن تحتوي جميعها على تبويبات. وبالتالي، لكل تبويب من iframe عملية renderer مستقلة. يُسمى هذا عزل المواقع (Site-Isolation)
* مسؤوليتها هي تحليل موقع ويب، ورسم ما يحتويه الموقع على شاشتك مثل: الجداول، الصور، وتنفيذ جافاسكريبت
* أنها معزولة (sandboxed)
* في جوهرها تستخدم محرك عرض يُسمى Blink
* إنشاء ومعالجة مخططات عناوين URL مثل: chrome:// و devtools:// و chrome-error://
* تهيئة محرك Blink الذي سيقوم فعليًا بكل التحليل والعمل الشاق بالنسبة لعملية الـ renderer
آخر مرة توقفنا فيها كنا قد أنهينا تحليل عملية المتصفح، والآن حان الوقت الذي كنا ننتظره جميعًا للذهاب والقيام بجزء تحليل الـ renderer. صحيح، عندما كنت ألعب مع كروميوم تمكنت من جعله ينهار وحصلت على تتبع المكدس التالي.  بناءً على هذا نعرف أن رحلتنا تبدأ عند content::RendererMain الموجود في `\content\renderer\renderer_main.cc` . لقد خمّنت ذلك، نقطة البداية هي RendererMain. لنبدأ بتحليل كيف يتم تعريفها وقليلًا من داخلها  . يمكننا أن نرى أنها تقبل معاملًا من النوع MainFunctionParams مما يعني أن هذه الدالة ستقبل الوسائط الممررة إلى الملف الثنائي. بعد ذلك نضيف نقطة تتبع لنعرف أننا وصلنا إلى استدعاء RendererMain، ثم نقوم بإلغاء الإشارة عن قيمة parameters ونحفظها في command_line. بعدها لدينا بعض الماكروت التي تتحقق من البنية الخاصة بالمنصة والتي يمكننا تجاهلها ، نتحقق من القيمة الممررة إلى kTimeZoneForTesting وهي منطقة زمنية لاستخدامها في الاختبار، ثم نصل إلى التعرف على كلاس جديد ونوع بيانات جديد يُدعى icu.

الآن بالبحث عن أي مصدر تعريف لذلك نصل إلى https://unicode-org.github.io/icu-docs . إذا نظرنا إلى بداية الملف حيث توجيه include يمكننا أن نرى أنها مكتبة طرف ثالث. إذن حتى الآن نعلم أنها مكتبة طرف ثالث تتعامل مع المكونات الدولية لليونيكود، أي نستخدمها لدعم اليونيكود. حسنًا، لكن ماذا تفعل تلك الدالة؟ بالاطلاع على https://unicode-org.github.io/icu-docs نرى ، أي أنها تضبط المنطقة الزمنية الافتراضية بناءً على ما نضبطه كمعاملات. ثم نهيئ مكتبة skia ونتعامل مع --renderer-startup-dialog
. بعدها يقابلنا كلاس جديد هو RendererMainPlatformDelegate.
ماذا يفعل هذا؟ حسنًا، أولًا يجب أن نحدد أن هذا كلاس مجرد خاص بالمنصة. في حالتنا سيكون موجودًا في /content/renderer داخل الملف المسمى renderer_main_platform_delegate_win.cc . يبدو هكذا
. إذن يمكننا أن نستنتج أن كل ما هو عليه هو دالة مساعدة تقوم بتمكين العزل (sandbox)، وفي حال مررنا --no-sandbox تقوم بالإجراءات اللازمة وهذا كل شيء. ثم نضبط اسم الخيط الخاص بنا إلى CrRendererMain. بعدها نقابل كلاسًا جديدًا آخر يُدعى RenderThread. 
مرة أخرى، وبما أننا لا نعرف أساسًا ما يفعله، دعونا نستكشفه قليلًا. تبدأ رحلتنا في فهم كيف يبدو RendererThread من content/public/renderer/render_thread.h. بالنظر داخل الملف render_thread.h نرى أنه معرّف كالتالي . من ذلك الملف بأكمله نحن مهتمون بـ IsMainThread المعرفة في الملف rendere_thread.cc وتبدو هكذا  (أضف وصفًا طويلًا حول هذه الآلية). الآن وبالمضي قدمًا في تحليل كود الـ renderer يمكننا رؤية اللحظة الأكثر انتظارًا، وهي أننا أخيرًا نصل إلى رؤية بعض كود blink. أول ما يظهر منها وما يفعله هو تهيئة المكتبة.. خلف الكواليس تبدو الدالة هكذا . الآن السؤال الطبيعي الذي يطرح نفسه هو: ما هذه الكلاسات بحق الجحيم؟ ما هما WTF و Platform؟ لحسن حظنا، توثيقات blink تخبرنا فعليًا ما هما.
https://docs.google.com/document/d/1aitSOucL0VHZa9Z2vbRJSyAIsAz24kX8LFByQ5xQnUg/edit . بالنظر إلى «Directory structure and dependencies» يمكننا أن نستنتج أن platform هو كلاس يساعد في الهندسة والرسوميات. الآن بخصوص كلاسي WTF و Partitions. توثيقات blink تقول أيضًا عن WTF: إنها تأتي من Web Template Framework وهي تشبه إلى حد كبير «غلافًا» لمكتبة stl، بمعنى أنها «مكتبة أساسية لـ Blink توفر مجموعة متنوعة من الوظائف الأساسية، مثل الحاويات، ومكتبات السلاسل النصية، وآليات العد المرجعي، والـ functors، وأساسيات الخيوط، إلخ.» (اقتباس من توثيقات blink(https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/platform/wtf/README.md)). أضف المزيد من التفاصيل عن blink
حسنًا، الآن وبالمضي قدمًا في تحليل renderer_main.cc نرى شيئًا مرة أخرى لا نعرف ما يفعله وهو  أضف التفاصيل غدًا. ثم نستدعي . بعدها نتحقق مما إذا كنا قد فعّلنا دعم الإضافات (plugins) عند الترجمة (compilation) وفي حال فعلنا نقوم بتحميلها.
 ثم نقوم ببعض الفحوصات الإضافية التي قررت تخطي شرحها لأن الشرح يطول كثيرًا وهي ليست ضرورية في هذه المرحلة. لكن في نسخة مختصرة جدًا (TL;DR)، الأمر يتعلق بما إذا كان يجب تفعيل العزل (sandbox) أم لا قبل تهيئة RenderProcess. وأخيرًا نصل إلى .(أضف باقي التفاصيل حول باقي الدوال). على الرغم من أن هذه ليست نهاية تحليل الـ renderer، يمكننا اعتبارها نقطة البداية للتحليل الفعلي للـ renderer، لأنه كما سنرى لاحقًا هنا يحدث معظم الأشياء المثيرة للاهتمام، لذا يمكننا اعتبارها كود الـ renderer. نحن مهتمون بـ RenderThreadImpl، الذي يبدو هكذا . من كل ذلك الكود نحن مهتمون بدالة Init()، التي تبدو هكذا:
، لكنها أطول بكثير. :) لسنا قادرين على التقاطها في صورة واحدة للأسف، ولذا التقطنا بدايتها. بصرف النظر عن ذلك، فإن كل ما يحدث بعد InitializeWebKit() ليس في الحقيقة موضع اهتمامنا، لأن معظمه مجرد اتصال IPC مع عملية الـ GPU، وهي ليست على رادارنا حاليًا. لقد ابتسم لنا الحظ لأنه في تلك الدالة التقطنا أيضًا إحدى الدوال التي تهمنا، وتحديدًا InitializeWebKit()، التي تبدو مرة أخرى هكذا . نستطرد قليلًا عن مهمتنا في شرح renderer_main.cc من أجل فهم أفضل لما يحدث داخل InitializeWebKit. أعلم أن هناك الكثير لفهمه، لكن تحلَّ معي بينما نحاول إيجاد بعض المنطق في هذه الفوضى. إذن نرى أن InitializeWebKit() تبدأ بأخذ أي وسائط مُمررة إلى هذه العملية، ثم نتحقق مما إذا كنا قد فعّلنا أثناء الترجمة (compilation) خيار -dENABLE_VTUNE_JIT_INTERFACE، وفي حال فعلنا نتحقق من الخيار الممرر إلى cmdline وهو enable-vtune-support. ما هذا بحق الجحيم؟ بحثنا على جوجل ووجدنا رابطًا إلى https://www.intel.com/content/www/us/en/develop/documentation/vtune-help/top.html وعلى تلك الصفحة تقول إنها: «أداة تحليل أداء للتطبيقات التسلسلية ومتعددة الخيوط». إذن باختصار، شيء يحسّن أداء الـ chrome الخاص بك. بعدها نهيئ blink . الآن، لفهم عملية تهيئة blink دعونا نشرح بإيجاز، حيث سنتعمق في التفاصيل في الفصل التالي الذي سينظر بعمق في blink. نقابل كلاسًا آخر غريبًا علينا وهو RendererBlinkPlatformImpl، الذي يبدو هكذا
 ويمكن العثور عليه في
content/renderer/renderer_blink_platform_impl.cc . بناءً على الاسم يمكننا أن نستنتج أنه كلاس مجرد يُنفَّذ بناءً على المنصة، ويمكننا أيضًا أن نرى أنه يرث من  وبالاطلاع على ما يفعله RendererBlinkPlatformImpl فعليًا، نرى أنه يتحقق من المنصة ثم بناءً على نتائج الفحص يضبط علامة (flag)، ثم يتحقق مما إذا كان الخيط الحالي هو RendererThread عن طريق الحصول على مؤشر إلى الـ TLS
إلخ إلخ، تحقق إذا كنت ستضيف المزيد من المحتوى حول الكلاسات. ثم نصل إلى شيء ربما كنا جميعًا ننتظره، وهي أول قطعة من كود v8. وهي . ماذا يفعل هذا؟ أولًا، نعرف من التوثيقات أن v8::isolate هو نسخة (instance) من محرك V8، أي (نسخة مستقلة من بيئة تشغيل V8، وتشمل مدير الكومة heap manager وجامع القمامة garbage collector، إلخ) وهي غير كافية لتشغيل السكربتات. نرى أن blink::MainThreadIsolate() معرّفة كالتالي
 وهي موجودة في الملف blink/renderer/platform/bindings/v8_per_isolate_data.cc ، والتي تبدو V8PerIsolateData::MainThreadIsolate() فيها هكذا
 ، وV8PerIsolateData التي كما خمّنت هي أيضًا
كلاس موجود في bindings/core/v8/V8PerIsolateData.h ويبدو تقريبًا هكذا إلخ إلخ، أضف تفاصيل. ثم نتحقق مما إذا كنا قد مررنا kDisableThreadedCompositing (أضف غدًا كيف يبدو العلم (الـ flag) فعليًا) إلى سطر الأوامر. في حال لم نمرره، نبدأ خيط compositor. ما هذا بحق الجحيم؟ اقتباسًا من https://frontendmasters.com/courses/web-performance/the-compositor-thread/ ، إنه خيط وظيفته الوحيدة هي «رسم خرائط البت (bitmaps)، وأخذ خرائط البت، وإرسالها إلى GPU، ووضعها على الشاشة». بعدها نسجّل ما يُسمى مخططًا (scheme). أساسًا، هل تتذكر عند عرض المصدر أن لديك شيئًا أمام الرابط مثل: «view-source:website». نعم، هذا في الواقع يتم التعامل معه بواسطة الـ renderer. وهناك المزيد من هذه المخططات. ماذا يقصدون بالتسجيل؟ لست متأكدًا بعد، لكن أعتقد أنهم يقصدون شيئًا مثل: «مرحبًا، هذا شيء أريدك أن تتعامل معه عندما يأتي المستخدم ويفعل هذا الشيء». على أي حال، هكذا يبدو الأمر. أضف المزيد من التفاصيل. حسنًا، الآن عند الإضافة، فصّل أيضًا آخر قطعة كود 
. إلخ إلخ، وأخيرًا وصلنا إلى الجزء الأخير من renderer_main، حدّث بالتفاصيل

=====================================================================
جزء التحليل الديناميكيالآن لكي تتمكن من تصحيح أخطائه ديناميكيًا، إذا كنت مبتدئًا مثلي، فستحتاج إلى تشغيل windbg من cmd.exe على النحو التالي 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"، وضبط .childdbg 1 لتتمكن من تصحيح أخطاء العملية الفرعية المنبثقة. من هناك ستحتاج إلى تعيين نقطة توقف bp content!content::RendererMain وتركها تعمل حوالي 5 أو 6 مرات. (عدّل بحيث تشير إلى 4 في الفيديو لأن ذلك أوضح) بعد ذلك نصل إلى .





MainDllLoader

--no-sandbox
chrome_mainالآن دعنا نرى ماذا يفعل. نفتح chromium.dll في binja
بناءً على هذه الصورة من https://blogs.igalia.com/jaragunde/files/2019/03/chrome-init-sequence.png لدينا فكرة تقريبية عما يجب أن نبحث عنه في binja
هذا هو شكل الرسم البياني في binja
.
لكي نتمكن من تتبعه بشكل أفضل، ابحث عن دالة تسمى ChromeMain. هذا هو المنطق الرئيسي (أي الأساسي) لكروم. بداخله يمكننا رؤية المراحل المذكورة آنفاً لكيفية عمل chrome.dll عند بدء التشغيل:
هنا نرى أنه أولاً يستدعي بعض الدوال مثل sub_180001420، sub_180017020، sub_180017020 والتي تقوم ببعض الفحوصات لمعرفة بعض التفاصيل حول كيفية تثبيت/ترجمة كروم. باستخدام الكود المصدري يمكننا تتبع خصائصها. الدالة الأولى، sub_180001420 تقابل UmaHistogramEnumeration، ثم لدينا sub_180017020 والتي هي InitializeFromPrimaryModule، والثالثة والأخيرة sub_180017020 هي chrome_main_delegate.
نظل نكرر chrome_main_delegate لكننا لم نحدد أبداً ما هو الغرض منه. ChromeMainDelegate هو فئة ترث من ContentMainDelegate ويوفر بشكل أساسي الوظائف المتعلقة ببدء التشغيل ومعالجة استدعاءات العملية. إذا أردت، يمكنك تنفيذ واجهة ContentMainDelegate مخصصة لتغيير السلوك الافتراضي لوحدة Content، واستخدام فئة ChromeMainDelegate مخصصة في Chromium لتخصيص سلوك عملية بدء التشغيل.

بداخله هناك بعض استدعاءات الدوال التي تقوم بعملية xor على بعض مناطق البيانات وتدمجها مع بعض مفاتيح الريجستري للحصول على بعض الخيارات المتعلقة ببدء تشغيل كروم
.
لاحظ أيضاً أن الدالة sub_180001510 تقوم بإنشاء مرجع مؤشر مملوك (scoped pointer) مع استدعاءين رجعيين (callbacks). ليس أمراً مهماً حقاً ما تفعله، لكن من المثير للاهتمام معرفة ما هو BindStateBase. سنقابله كثيراً في الكود الأساسي لكروم.
الآن قد تتساءل ماذا تفعل الأسطر الثلاثة الأخيرة، وتحديداً

إذن السطر الأول يقوم أساساً بإنشاء std::unique_ptr<> لـ Closures. ما هو الإغلاق (closure) بحق الجحيم!؟ حسناً، إذا اقتبسنا من مطوري موزيلا "الإغلاق هو دالة مدمجة (مغلقة) مع مراجع لحالتها المحيطة (البيئة المعجمية). بعبارة أخرى، الإغلاق يمنحك الوصول إلى نطاق الدالة الخارجية من دالة داخلية. في جافاسكربت، يتم إنشاء الإغلاقات في كل مرة يتم فيها إنشاء دالة، في وقت إنشاء الدالة." إذا استخدمنا لغة بشرية، فهو دالة داخل دالة تصل إلى متغير داخل الدالة الخارجية. مثال:
.
إذن في جوهره، يضمن أن الإغلاق سيُنفذ. والسطران الآخران يضبطان بعض السلوكيات المحددة لملفات التفريغ (dumps) عندما يتعطل كروم crashes.InstallDetails::Get().VersionMismatch() patchuie aici
بالمضي قدماً، يتحقق من إصداره في وقت التشغيل، وفي حال عدم تطابقه، يتعطل ونصل إلى تحليل سطر الأوامر 
عند الدخول إلى sub_184171800 نرى أنه ليس كبيراً
أولاً يأخذ الوسائط الممررة إلى العملية الحالية، ثم يستدعي دالة تأخذ StringPiece وهو أساساً غلاف فئة لـ std::string لكنه أفضل قليلاً، وتتحقق تلك الدالة أساساً مما إذا كان الملف التنفيذي قد استُدعي بوضع headless، وتقارن ما إذا كان اسم الملف التنفيذي الذي تم تشغيله هو chrome، وتتحقق مما إذا كان USE_HEADLESS_CHROME مضبوطاً، ثم تمضي قدماً إلى الخطوة التالية وهي content main.
بناءً على الوسائط المقدمة، فإن وظيفة content main هي بدء الغلاف (shell) المعني.
الآن في حال لم نقدم أي وسائط للغلاف، فإن تدفق التنفيذ يذهب إلى
.
لكي نفهم ماذا يفعل، نحتاج إلى التحقق من مصدره. يمكننا العثور عليه في /src/content/app/content_main.cc
سيتعين علينا التمرير حتى النهاية، وهناك سنجد تعريف دالة ContentMain. هناك نرى أنها تستدعي دالتين. واحدة تهيئ ContentMainRunner وهي الفئة التي تتعامل مع كل "إنشاء العمليات" في سياق إنشاء متصفح ipc sqli net والباقي. والأخرى تتحقق أساساً من نوع العمليات الفرعية وتغذيها لفئة ContentMainRunner.
الآن دعنا نتراجع خطوة إلى الوراء لفهم تدفق الكود. أولاً دعنا نفحص RunContentProcess لأن ContentMainRunner معقد للغاية. أول خطوة تقوم بها هي إنشاء GlobalActivityTracker. يا إلهي!!! خمنتها، جوجل تتعقبك :))) لا، أمزح فقط، أرجو ألا تقاضوني يا جوجل. لكن بجدية، هذا يتتبع كل خيط (thread) وهو متتبع للخيوط، ويقوم ببعض الأشياء اللطيفة لتحسين التصحيح مثل:
وهو ما يعني في جوهره أنه سيكون هناك عدد صحيح فريد يُستخدم كمعرّف لكل خيط لفهم أفضل لما تسبب في تعطل العملية.
مثال:```
kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4
kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3
kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1
kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100,