
Una documentación adecuada y bien estructurada para iniciarse en el pwning de Chrome y el pwning de V8
Una documentación adecuada y bien estructurada para comenzar con chrome pwning & v8 pwning
Cómo está organizado este documento
Los navegadores son una de las tecnologías más utilizadas hoy en día. En cualquier computadora estándar, si simplemente conectamos y usamos, veremos un navegador instalado. Es por eso que, desde la perspectiva de un atacante y desde una perspectiva de modelo de amenazas, es muy gratificante si el atacante puede comprometer el navegador a través de una página maliciosa. Dados los argumentos anteriores, elegí estudiar el motor de JavaScript de Google, particularmente v8.
Dada la gigantesca naturaleza del proyecto de v8, elegí como punto de partida el intérprete, es decir, d8. Aunque ya se han realizado investigaciones extensas sobre d8, esperamos encontrar al menos un bug y, si no, poder avanzar con la investigación sobre la explotación de navegadores, ya que v8 proporciona el terreno de entrada para las estrategias básicas de desarrollo de exploits utilizadas en la explotación de navegadores.
Otra razón por la que elegí v8 como objetivo es que se utiliza en múltiples navegadores.
Si miramos bajo el capó, podemos ver que el motor también se utiliza en MicrosoftEdge, por lo que hay una oportunidad de múltiples recompensas por bugs. Para el sistema operativo subyacente, el investigador utilizará una combinación de Windows y Linux, ya que no hay restricción en cuanto a obtener una shell, porque los bugs de v8 permiten la ejecución de código a través de páginas wasm y no están ligados a ninguna plataforma específica.
Desafortunadamente, aunque un bug explotado en v8 resultaría en ejecución de código, no podremos ejecutar ningún código debido al sandbox, y como tal obtendríamos ejecución de código en el contexto del renderer, lo que no nos permitiría ejecutar código en la máquina. Para eso necesitaríamos otro exploit para el sandbox, por lo que necesitaríamos una cadena completa para explotar el sistema.
Y así definimos los siguientes objetivos para poder comenzar con el hacking de navegadores
En la primera etapa del proyecto es necesario recopilar tanto conocimiento como sea posible sobre la arquitectura de Chrome y cómo interactúa cada componente entre sí. Para entender mejor esto, debemos dividir el Proyecto Chromium en múltiples subcomponentes para poder aislar todo y analizarlo adecuadamente. Más precisamente, en cuántos subcomponentes se divide cada uno de los siguientes componentes:
Ahora, el paso más lógico para un primer paso es entender la arquitectura de Chromium. Así que, vale, queremos explotar el navegador, pero ¿qué sucede cuando iniciamos el navegador por primera vez? Bueno, después de hacer clic en el ejecutable de Chromium, el ejecutable inicia algunos procesos.
El orden y sus nombres son los siguientes:
El primero se llama content process. ¿Qué hace este proceso?
Ahora que sabemos brevemente qué hace, es hora de profundizar en ello:
La forma en que funciona Chromium, al menos en Windows, es que compila los archivos en una dll y después la carga en memoria. Así que la lógica central del navegador Chromium está en chromium.dll.
Esto también se confirma con el código
.
Esto está tomado de chrome_exe_main_win.cc y si tienes curiosidad por leer el código completo, se encuentra en chromium/src/chrome/app. Vale, bajando con la ejecución podemos ver que llama a MakeMainDllLoader() para llamar a la clase del cargador de dll; después de eso, lanza el "loader", es decir, carga el chrome.dll y, si es necesario, lo reinicia con las líneas de comando necesarias. Para analizar más a fondo el Loader, necesitamos entender su código, que está en el mismo directorio, en el archivo mail_dll_loader_win.cc. Desplazándonos hasta el final del archivo podemos ver la llamada a MakeMainDllLoader, que a su vez llama, según la versión que tengas, a ChromeDllLoader o ChromiumDllLoader.
Podemos ver que ChromiumDllLoader es una clase que hereda de . Podemos ver en la definición que la clase simplemente carga la dll basándose en los argumentos pasados a la línea de comandos y al tipo de proceso.
.
Analizando el método Launch podemos entender algunas cosas que suceden antes de que chrome.dll se inicie: Podemos entender de este comentario 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." lanzar chrome es cuestión de cargar un montón de dll que realmente hacen el trabajo. En segundo lugar, toma los argumentos pasados a la línea de comandos y además inicializa los servicios del sandbox.
.
Primero comprueba si es el navegador quien llama a la inicialización del sandbox; luego comprueba si el proceso que llamó a la inicialización del sandbox fue llamado como un servicio de impresión en la nube. También comprueba si se pasó al binario, lo que básicamente le indica al binario que no se ejecute en un sandbox. Comprueba si alguna de estas opciones se estableció y, si alguna es verdadera, llama al sandbox con las opciones respectivas. Luego, al final llegamos a
que es lo que nos interesa, esto representa el envoltorio para llamar a .
el seguimiento de una etapa del ciclo de vida de un proceso que se define como```
// 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,
registra tan pronto como un proceso termina, guarda la información relativa al módulo, es decir, cuando se carga un módulo (aka un componente de chromium), y básicamente la misma funcionalidad repetida pero diferente para distintos hilos y clases. Por si te interesa, lo puedes encontrar en chromium/src/base/debug/activity_tracker.h
Después comprueba si "Main()" fue llamado antes. Aquí creo que se refiere a si ChromeMain() fue llamado antes. Básicamente comprueban si se pasó algún comando al proceso de contenido, es decir, si el proceso de contenido fue invocado con algún argumento. Y en caso de que así fuera, se aseguran de que el proceso del navegador reciba los mismos argumentos. Luego comprueban las particularidades de la plataforma y, en caso de que se detecte Windows, inicializan su propio manejador llamando a CreateATLModuleIfNeeded; después es la misma historia: comprueban las particularidades de la plataforma y pasan los argumentos usando SetupCRT, y llegamos a la parte donde inicializamos el IPC para poder hablar con los otros procesos una vez los hemos lanzado. Aquí está el código responsable de eso. No entraremos en detalles sobre ello, ya que volveremos más adelante para discutir con mayor profundidad el mecanismo de IPC de chrome. Por ahora solo ten en cuenta que este es el mecanismo que facilita la comunicación entre los procesos de la arquitectura multiproceso de chrome. Y por si no sabes qué significa IPC, es por Inter Process Communication (comunicación entre procesos).
.
Después, lo que hace es "reenviar" — más bien que establecer — los argumentos para la interfaz de usuario usando ui::RegisterPathProvider, llama al tracker y obtiene el resultado usando content_main_runner->Initialize(std::move(params)), crea una consola padre, que supongo que quiere decir que crean un proceso padre, hace algunas comprobaciones más y llegamos a la parte importante, que es
.
Aquí vemos una llamada a una función llamada IsSubprocess.
lo que hace básicamente, para evitar el viejo código repetitivo, es comprobar en una sola función el tipo de proceso que recibe en la línea de comandos y, en caso de que se pase una opción, elige entre las siguientes y crea el proceso correspondiente.```
return type == switches::kGpuProcess ||
type == switches::kPpapiPluginProcess ||
type == switches::kRendererProcess ||
type == switches::kUtilityProcess || type == switches::kZygoteProcess;
Desde ahí llegamos a content_main_runner->Run(); que en esencia hace toda la magia. Vamos a examinarlo bajo el capó. content_main_runner es de la clase ContentMainRunner, obviamente... que se puede encontrar en `content\app\content_main_runner_impl.cc`
Para poder entender el flujo de código de esto, tenemos que recorrerlo de abajo hacia arriba. Dicho esto, bajamos de nuevo y encontramos que en realidad `ContentMainRunner::Create()` llama a `ContentMainRunnerImpl::Create()`, lo que nos hace darnos cuenta de que tendremos que buscar la definición de ContentMainRunnerImpl, no la de ContentMainRunner, que encontramos en `content_main_runner_impl.h`, en la misma carpeta ya mencionada.

Vemos que hereda de ContentMainRunner y podemos ver sus métodos principales. En realidad no hay mucho aquí, ya que su comportamiento está mayormente sobrescrito en `content_main_runner_impl.cc`.
Dentro de `content_main_runner_impl.cc`, en el método run, primero hace algunas comprobaciones usando DCHECK(¿qué demonios es esta función?(```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).``` citas de los documentos de google :) ) para ver si is_initialized, content_main_params_, is_shutdown_ están establecidos.

Después obtiene los argumentos y determina el tipo que he mencionado antes.
Entonces, en caso de que no podamos encontrar eso, llamamos a InitializeFieldTrialAndFeatureList() delegate_->PostFieldTrialInitialization(); y lo publicamos como mensaje usando mojo.
 .
A continuación, lo que hace es configurar algunas cosas para la interfaz de usuario
de nuevo según lo que se le haya pasado en la línea de comandos, y llama a RegisterMainThreadFactories, que es un envoltorio para RegisterUtilityMainThreadFactory, que solo establece g_utility_main_thread_factory. Por el nombre, creo que crea el hilo principal, que es como el hilo que vigila al resto de hilos, y finalmente lanza el proceso del navegador. 
En caso de que no me creas, aquí tienes chromium desensamblado en binja y también veremos algo de análisis en memoria de eso.
Por suerte, tenemos chome.exe.pdb, que nos permite tener símbolos de depuración y no tendremos ningún dolor al invertir el binario.
Como trabajamos con Chrome en Windows, el punto de entrada principal del programa es wWinMain:
Así se ve el grafo
.
La función en sí es enorme y tal; solo mostraré las partes necesarias. Después de un montón de inicializaciones llegamos a un punto donde llama a `MakeMainDllLoader()`
, cuyo funcionamiento ya hemos explicado. Muy bien, ¿cómo depuramos Chrome dinámicamente y demostramos todo lo que ya he mencionado? Primero lo cargamos en windbg, luego lm y buscamos el nombre del ejecutable. Después buscamos una función llamada chrome!MakeMainDllLoader, ponemos un bp y dejamos que la ejecución continúe.. Entramos dentro de ella , la dejamos correr hasta que llega a ret y salimos de ella, entrando en chrome!wWinMain+0x764 . Luego la dejamos correr hasta chrome!MainDllLoader::Launch y entramos en ella. Desde allí ponemos un bp en chrome!MainDllLoader::Load para poder ver cómo se carga chrome.dll en memoria. Y como podemos ver, entre las primeras cosas que se cargaban estaba chrome.dll . Desde aquí el flujo de análisis es el mismo y el análisis completo en memoria se dejará como ejercicio para el lector. Por un propósito especial, haré el análisis hasta el punto donde el proceso de contenido casi está terminado y justo antes de que comience el proceso del navegador; allí me detendré. El propósito de esto es solo mostrar cómo el ipc comienza a comunicarse entre sí. Ahora, después de la carga de la dll, tendremos que buscar una instrucción call rax. Lo que hice fue listar 0x40 instrucciones desde el eip actual después de cargar la dll.. En esa dirección está en realidad lo que buscamos, que es chrome!ChromeMain.
Desde allí queremos detenernos en , que es básicamente una gran comprobación antes de saltar a content!content::ContentMain. Desde allí queremos avanzar unas pocas instrucciones y llegar a content!content::ContentMain. Queremos entrar y detenernos en content!content::RunContentProcess. Queremos entrar y establecer un bp en content!content::ContentMainRunnerImpl::Run+0x430, y tan pronto como pasamos por encima  vemos que mojo ipc comienza y concluimos que el siguiente proceso, también conocido como proceso del navegador, es responsable de la implementación real del navegador y del manejo de todos los demás procesos.
===============================================================================================
2.
La última vez nos quedamos después de entender el proceso de contenido. Hoy iremos por el proceso del navegador.
Es el proceso que se inicia después del proceso de contenido y es el segundo de los procesos iniciados por Chrome.
Describámoslo brevemente:
* podemos considerarlo el proceso principal, ya que el proceso de contenido es más una rutina de inicialización y comprobación de dependencias, y es iniciado por el proceso de contenido
* permanece vivo durante toda la vida del navegador.
* Es el coordinador central de todos los procesos y opera en el nivel de privilegios más alto disponible para el navegador.
* dado que se ejecuta con los niveles de privilegios más altos, en caso de que otros procesos necesiten lograr una operación de mayor nivel, esa solicitud es manejada por el proceso del navegador.
* Controla funciones como la barra de direcciones, los marcadores y los botones de atrás/adelante/recargar. Dado que es el proceso más privilegiado, no confía en los datos que le dan cualquiera de los otros procesos.
* también maneja operaciones privilegiadas como la interfaz de usuario, la red o el almacenamiento del sistema de archivos para los otros procesos cuando es necesario.
Ahora que sabemos brevemente lo que hace, es hora de profundizar en ello:
Nuestro viaje comienza en `src\content\app`, en un archivo llamado content_main_runner_impl.cc, en la función int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params,bool start_minimal_browser). La primera operación que ejecuta, vemos, es un TRACE_EVENT_INSTANT0 .
¿Qué es eso? Antes de eso, ¿qué es siquiera una función de traza? Bueno, si vamos a https://lwn.net/Articles/379903/ podemos ver que la definen como . Haciendo un poco de abstracción, podemos llegar a la conclusión de que una función de traza es una función que registra datos en un punto específico de un stackframe. No solo registra la pila de ejecución de la función; también puede registrar variables locales de la última función ejecutada. Ahora que sabemos qué es una función de traza, veamos qué hace la nuestra.
Si vamos a https://chromium.googlesource.com/chromium/src/base/trace_event/common/+/refs/heads/main/trace_event_common.h vemos que la definen como una función cuyo único propósito es "realizar el seguimiento del rendimiento de la aplicación y el uso de recursos". Yendo un poco más a fondo para entender qué hace, vemos que es una macro y se define como . Podemos ver una breve definición encima de la macro que dice que registra un único evento llamado "name" inmediatamente, con 0, 1 o 2 argumentos. Y en caso de que la categoría del evento al que pertenece no esté habilitada, no hace nada. Podemos ver que como parámetros tenemos "startup" como categoría principal y como subsistema tenemos "ContentMainRunnerImpl::RunBrowser(begin)" y el tercer parámetro es TRACE_EVENT_SCOPE_THREAD, que es "#define TRACE_EVENT_SCOPE_THREAD (static_cast<unsigned char>(2 << 2))", que creo que, basándome en el valor, es un id para el respectivo evento instantáneo. Así que, en esencia, lo que hace es simplemente trazar (registrar) el hecho de que hemos entrado con nuestra ejecución en la función RunBrowser. Luego tenemos una comprobación para ver si el bucle principal del navegador ya ha comenzado y, en caso de que ya haya comenzado, salimos de la función . Luego establecemos una marca y después llegamos a una parte bastante interesante del código. Comprobamos si tenemos soporte para el mecanismo mojo_ipc y, en caso de tenerlo, usamos ShouldCreateFeatureList, que crea una lista de características con diferentes procesos, intenta inicializarlos y finalmente intenta inicializar las características de mojo.. Después creamos un threadpool. ¿Qué diablos es un threadpool??! Citando a Wikipedia: "un patrón de diseño de software para lograr concurrencia de ejecución en un programa de computadora". Explicado más propiamente, "un grupo de hilos mantiene múltiples hilos esperando que se les asignen tareas para su ejecución concurrente por parte del programa supervisor" (sigue siendo una cita de Wikipedia). Explicado más adecuadamente, imagina dos líneas de personas trabajando en una fábrica. Llamémoslas línea a y línea b. Todas están supervisadas por un jefe. Llamémoslo línea c. La línea b tiene que esperar a que la línea a termine su trabajo y ser notificada por la línea c para poder trabajar. Y lo mismo aplica para la línea a. Y esto es lo que es un threadpool. Aquí hay también un pequeño ejemplo en c . Esto está descaradamente tomado de https://stackoverflow.com/questions/15752659/thread-pooling-in-c11 . A continuación tenemos una llamada a PreBrowserMain(); que hace alguna inicialización específica de la plataforma y después de eso llegamos a un punto donde llamamos a `BrowserTaskExecutor::Create()`;
. Tomemos una inmersión profunda en lo que hace, ya que el nombre es bastante interesante y, basándonos en una suposición fundamentada, podemos darnos cuenta de que esto podría ser interesante. Nuestro desvío comienza dentro de content/browser/scheduler/browser_task_executor.h, donde encontramos que BrowserTaskExecutor es una clase que supuestamente "mapea base::TaskTraits a colas de tareas reales para el proceso del navegador". Bajando un poco en el archivo, primero  vemos que hereda de BaseBrowserTaskExecutor. Entonces, para entender qué es BrowserTaskExecutor, necesitamos entender BaseBrowserTaskExecutor. Podemos ver que hereda de TaskExecutor. Afortunadamente para nosotros, vemos que sobrescribe los métodos de TaskExecutor con la propiedad de ser sobrescritos, lo que significa que serán sobrescritos por otros métodos llamados más tarde. Pero solo para los curiosos, podemos encontrarlo en base/task/task_executor.h y, si lo inspeccionamos, podemos descubrir que TaskExecutor es una clase que "puede ejecutar tareas con un id de extensión TaskTraits específico  .
¿Qué es una tarea y qué son los tasktraits? Ya hemos mencionado qué es una tarea, pero para refrescarlo, es una de las unidades de ejecución de los procesos de Chrome; y ahora, ¿qué son los TaskTraits? Se encuentran en base/task/task_traits.h y se definen de la siguiente manera: "encapsulan información sobre una tarea que ayuda al grupo de hilos a tomar mejores decisiones de planificación". Volviendo a nuestro BrowserTaskExecutor. El método que llamamos es Create() y su análisis es el siguiente 
primero comprueba si la tarea actual necesita ejecutarse desde un SingleThreadTaskRunner, es decir, si necesitamos ejecutar esta tarea usando un hilo independiente. Hacemos esto obteniendo un puntero a tls. Luego inicializamos una ui y un programador de hilos. Básicamente, aquí inicializamos un programador para futuros eventos relacionados con la interfaz de usuario..
Y eso es todo en cuanto a esa característica. Continuando con el análisis de content_main_runner_impl.cc, llegamos aquí.
Buscando el archivo de la clase variations ids provider, lo encontramos en `components/variations/variations_ids_provider.h`; allí vemos algo bastante interesante. Incluye un archivo `.mojom.h`. Podemos encontrar su definición en `Debug/gen/components/variations/` variations.mojom.h, lo que significa que tiene algo que ver con el mecanismo ipc. Ahora, repasando la definición real de la clase, vemos un comentario que la anota como "Una clase auxiliar para mantener el estado de los experimentos de clientes y las métricas transmitidas en los encabezados de solicitudes HTTP personalizados". Examinando su comportamiento y la definición de su fuente, concluimos que simplemente se usa como marcador para una función, que marca que "parámetro de usuario iniciado con sesión suministrado a GetClientDataHeaders()", lo que significa que en algún lugar de la pila de ejecución hay una llamada a GetClientDataHeaders y luego se llamará con un parámetro. Luego hacemos delegate_->PostEarlyInitialization(!!main_params.ui_task); que simplemente publica en el ipc de mojo que tiene que iniciar las tareas de la interfaz de usuario. Hacemos más inicializaciones  y luego llegamos a llamar a RunBrowserProcessMain.  . Si esperabas, como yo, que aquí es donde vemos la interfaz gráfica comenzando, estás equivocado. Una cita del maestro oogwgay . Luego hacemos algunas comprobaciones más y llegamos a BrowserMain. Revisándolo, hace algo de trazado y luego llegamos a , que es donde realmente iniciamos la interfaz gráfica. Ahora, en caso de que no me creas, tendrás que ser un poco más paciente hasta que lleguemos al análisis dinámico. Luego llegamos al método Run, que es lo que realmente nos interesa; después de eso llegaremos a nuestro siguiente punto para entender el proceso del navegador. Pero por ahora, hagamos otro pequeño desvío e investiguemos cómo se crea el método Initialize. Directamente, vemos que traza la ejecución del método init y, antes de eso, crea un histograma. Luego comprobamos la marca initialization_started_ para ver si llegamos a la etapa de inicialización y, en caso de no haberlo hecho, inicializamos skia, que es la biblioteca gráfica utilizada por Chrome, iniciamos un "temporizador" para contar los segundos transcurridos al ejecutar este método para su uso posterior en un histograma, comprobamos si pasamos un parámetro al binario para esperar a que el depurador se adjunte al proceso, y finalmente iniciamos un notification_service_ que, usando una suposición fundamentada, notificará al observador principal del threadpool cuándo iniciar un servicio. . Luego inicializamos las fuentes necesarias para Chrome y creamos el mainbrowserloop, que es el coordinador de todos los procesos, y saltamos sobre tres métodos que no nos interesan: main_loop_->CreateStartupTasks();
int result_code = main_loop_->GetResultCode(); . Desde aquí, lo que nos interesa es entrar en CreateStartupTasks. Desde allí, miramos content\browser\browser_main_loop.cc, y nos interesa startup_task_runner_->RunAllTasksNow(); que está dentro del método createstartuptask. starup_task_runner_ es un StartupTaskRunner que se encuentra en el mismo directorio, dentro del archivo startup_task_runner.cc. Ahora, si inspeccionamos el método RunAllTasksNow, vemos que lo que hace es simplemente iterar sobre todas las tareas y ejecutarlas
==========================================================================================
Hora de un poco de análisis dinámicoAhora, para capturar el renderer tendremos que iniciar el binario en windbg. Eso se consigue simplemente con File->Open Executable y pasando --renderer-startup-dialog --no-sandbox --wait-for-debugger-children=renderer --renderer-process-limit=1 como argumentos.. Luego ponemos un bp en content!content::StartupTaskRunner::RunAllTasksNow+0x88 para poder capturar los mensajes ipc y más tarde el renderer. Solo como referencia, así es como debería verse tu dbg después de ejecutarlo una vez . Deberías ver el mensaje de que chrome se está ejecutando en modo navegador completo. A partir de ahí, cuentas del uno al 5, es decir, lo ejecutas otras cuatro veces más. Y entonces deberías ver algo como . Recomiendo usar procmon para poder monitorizar el proceso del renderer cuando se inicia. Después lo enganchas a otro debugger y, en caso de que hayas usado los argumentos anteriores, deberías ver un mensaje emergente que te dice el pid del renderer. Desde ahí querrás poner un bp en base!base::RunLoop::Run. Desafortunadamente, no puedes romper en content!content::RendererMain por alguna razón. Quizás porque enganchamos el proceso justo después de que salga de la función renderemain y lo ejecuta un hilo. Independientemente, así es como se ve el ipc después de cuatro ejecuciones.. Esto indica que la gui está iniciada. Y así es como se ve el ipc después de que el proceso del renderer comienza.. Luego ponemos un bp en
content!content::StartupTaskRunner::RunAllTasksNow+0x88 y content!content::RunOtherNamedProcessTypeMain. Déjalo correr.
============================================================================================================
3.Ahora, para la segunda parte del análisis del proceso del navegador, llegamos al punto en el que podremos entender y depurar el renderer, pero no iremos allí todavía. Dejé explícitamente una función más para analizar después de RunBrowserProcessMain; bueno, teóricamente es después de RunBrowser, pero como recordarás, RunBrowser es un envoltorio de RunBrowserProcessMain. Lo que dejé fuera es que en el archivo llamado content_main_runner_impl.cc, en la carpeta src/content/app, hay otra función que se llama después de que generamos el renderer y se llama RunOtherNamedProcessTypeMain. Este proceso se encarga de ejecutar todos los demás procesos.. Ahora entendamos qué ocurre en el código. Vemos que su prototipo es , lo que indica que tomará los argumentos pasados a cmdline, qué tipo de proceso esperar y un delegate de chrome. Llegamos al inicio de la función donde hay una definición de macro para comprobar las especificidades de la plataforma y verificar para qué proceso instanciar un manejador de eventos. O sea, comprobar si ejecuta algunas funciones para el proceso de consola o para el proceso del navegador. . Luego iteramos sobre los procesos pasados y los comparamos con una lista de procesos conocidos y ejecutamos el proceso respectivo.  En caso de que no obtengamos el proceso respectivo, entonces es un proceso personalizado implementado por alguien
Aquí hay un enlace para implementar procesos personalizados y usaremos este ejemplo para la segunda parte del análisis dinámico. https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial .
=====================================================================
Parte de análisis dinámico
La primera parte del análisis dinámico comienza de la siguiente manera: primero, desde un cmd.exe con derechos de administrador, ejecuta el siguiente comando: 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 . Después de eso, establece .childdbg 1 para poder depurar los procesos hijos recién creados. En esencia, lo que hace el comando anterior es "asegurarse de que estás adjunto a todos los procesos hijos". Un agradecimiento a @spoofyroot y @_coreDump por señalarme la dirección correcta con la depuración multiproceso. Así es como se supone que debe verse después de establecer .childdbg 1  Ahora, como no pude capturar de ninguna otra manera lo que sucede después, hice un video donde explico lo que ocurre a continuación. https://streamable.com/9t4iof . Básicamente, después de que ejecutamos por última vez content!content::StartupTaskRunner::RunAllTasksNow+0x88, generamos nuevos procesos que manejan algunos asuntos de ipc y tendremos que seguir poniendo un bp en content!content::RunContentProcess hasta que lo alcance. Esto es lo que yo llamo prueba y error educado:)) .
=====================================================================
4.Análisis del Renderer
!Aviso
Mientras hacemos el análisis del código del Renderer, nos sumergiremos un poco también en el código de blink para poder entender correctamente qué ocurre en el renderer
Mientras escribía esta parte del curso me di cuenta de que olvidé describir brevemente qué hace esto y por qué siquiera lo analizamos. Como sabemos, este es el tercer proceso iniciado por chromium y se llama renderer. Pero, ¿por qué se llama "renderer"? Se llama así porque su trabajo es renderizar (dibujar) todo lo que vemos en un sitio web. Básicamente, esta es la razón por la que tu tabla se ve como una tabla cuando visitas un sitio web, o por la que tu css permite personalizar un fragmento de texto. O por la que tu js es capaz de hacer magia negra*. Otra razón importante por la que analizamos esto es porque es de donde provienen la mayoría de los bugs. Ya sea que hablemos de html, css, js o cualquier otro componente, los verás todos bajo blink en chrome bugs.chromium.org
* Hay más procesos de renderer. Un proceso completamente separado para cada pestaña que el navegador tiene abierta actualmente.
* Este proceso controla todo lo que hay dentro de la pestaña real del sitio web
* Desde 2018, los iframes recibieron una mejora por la que todos pueden tener pestañas. De este modo, cada pestaña de un iframe tiene un proceso de renderer individual. Esto se llama Site-Isolation
* Su responsabilidad es analizar un sitio web, dibujar en tu pantalla lo que el sitio web tiene dentro, p. ej. tablas, imágenes, ejecutar javascript
* está en sandbox
* en su núcleo usa un motor de renderizado llamado Blink.
* crea y maneja esquemas de url como: chrome://, devtools://, chrome-error://
* Inicializa el motor Blink, que realmente hará todo el análisis sintáctico y el trabajo pesado para el proceso del renderer.
La última vez que lo dejamos, terminamos el análisis del proceso del navegador y ahora ha llegado el momento que todos estábamos esperando para ir a hacer la parte del análisis del renderer. Bien, cuando estaba jugando con chromium conseguí hacer que se estrellara y obtuve el siguiente stack trace.  Basándonos en esto, sabemos que nuestro viaje comienza en content::RendererMain, que se encuentra en `\content\renderer\renderer_main.cc` . Lo habrás adivinado, el punto de partida es RendererMain. Empecemos analizando cómo está definido y un poco de su interior  . Podemos ver que acepta un parámetro de tipo MainFunctionParams, lo que significa que esta función aceptará los argumentos pasados al binario. A continuación añadimos un trace point para poder saber que hemos llegado a llamar a RendererMain, y luego desreferenciamos el valor de parameters y lo guardamos en command_line. Luego tenemos unas macros que comprueban la arquitectura específica de la plataforma, que podemos ignorar , comprobamos el valor pasado a kTimeZoneForTesting, que es una zona horaria para usar en las pruebas, y entonces llegamos a conocer una nueva clase y tipo de datos llamado icu.

Ahora, buscando alguna fuente de definición para eso, llegamos a https://unicode-org.github.io/icu-docs . Si miramos el principio del archivo donde está la directiva include, podemos ver que es una librería de terceros. Así que hasta ahora sabemos que es una librería de terceros que se ocupa de los componentes internacionales para unicode, es decir, la usamos para el soporte de unicode. Vale, pero ¿qué hace esa función? Revisando https://unicode-org.github.io/icu-docs vemos , o sea, establece la zona horaria predeterminada según lo que hayamos puesto como parámetros. Luego inicializamos la librería skia y manejamos --renderer-startup-dialog
. Luego nos encontramos con una nueva clase, RendererMainPlatformDelegate.
¿Qué hace esto? Bueno, primero tenemos que especificar que es una clase abstracta específica de la plataforma. Para nuestro caso, estará ubicada en /content/renderer dentro del archivo llamado renderer_main_platform_delegate_win.cc . Se ve así
. Por lo que podemos concluir que todo eso es una función auxiliar que habilita el sandbox en caso de que pasemos --no-sandbox, hace las acciones necesarias y eso es todo. Luego establecemos el nombre de nuestro hilo a CrRendererMain. Luego nos encontramos con otra clase nueva llamada RenderThread. 
Una vez más, como básicamente no sabemos qué hace, exploremos un poco. Nuestra búsqueda para entender cómo se ve RendererThread comienza en content/public/renderer/render_thread.h. Mirando dentro del archivo render_thread.h vemos que está definido como . De todo ese archivo, nos interesa IsMainThread, que está definido en el archivo rendere_thread.cc y se ve así  (añade una descripción larga sobre este mecanismo). Ahora, continuando con el análisis del código del renderer, podemos ver el momento más esperado y es que finalmente llegamos a ver algo de código de blink. El primero de su tipo y lo que hace es inicializar la librería.. Detrás de las cortinas, la función se ve así . Ahora la pregunta natural que surge es: ¿qué demonios son esas clases? ¿Qué son WTF y Platform? Por suerte, la documentación de blink nos dice qué son.
https://docs.google.com/document/d/1aitSOucL0VHZa9Z2vbRJSyAIsAz24kX8LFByQ5xQnUg/edit . Mirando "Directory structure and dependencies" podemos deducir que platform es una clase que ayuda con la geometría y los gráficos. Ahora, sobre las clases WTF y Partitions. La documentación de blink también dice sobre WTF: viene de Web Template Framework y es más o menos un "wrapper" de la librería stl, como en "es una librería base para Blink que proporciona una variedad de funcionalidades básicas, como contenedores, librerías de cadenas, mecanismos de conteo de referencias, functores, primitivas de hilos, etc." (citas de la documentación de blink(https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/platform/wtf/README.md)).Añade más detalles sobre blink
Ok, ahora continuando con el análisis de renderer_main.cc vemos algo de lo que de nuevo no sabemos qué hace y es  Añade detalles mañana.Luego llamamos a .Luego comprobamos si hemos habilitado el plugin al compilar y, en caso de que lo hayamos hecho, los cargamos.
 Luego hacemos algunas comprobaciones más que decidí omitir explicar porque se está volviendo una explicación bastante larga y no son necesarias en esta etapa. Pero en versión TL;DR, es para decidir si habilitar o no el sandbox antes de la inicialización de RenderProcess. Y entonces finalmente llegamos a .(añade el resto de los detalles sobre el resto de las funciones). Si bien este no es el final del análisis del renderer, podemos considerarlo el punto de partida del análisis real del renderer, porque como veremos más tarde, aquí ocurre la mayoría de las cosas interesantes, así que podemos considerarlo como el código del renderer. Nos interesa RenderThreadImpl, que se ve así . De todo ese código nos interesa la función Init(), que se ve así:
, pero es mucho más larga. :) desafortunadamente no podemos capturarla en una sola imagen, y por eso capturamos su comienzo. Aparte de eso, todo lo demás que ocurre después de InitializeWebKit() no es realmente de nuestro interés, ya que en su mayoría es solo comunicación ipc con el proceso gpu, que no está en nuestro radar por ahora. La fortuna nos sonríe, porque en esa función también capturamos una de las funciones que nos interesan, precisamente InitializeWebKit(), que de nuevo se ve así .Nos desviamos un poco de nuestra búsqueda de explicar renderer_main.cc para entender mejor qué ocurre dentro de InitializeWebKit. Ahora sé que es mucho para entender, pero tened paciencia mientras intentamos darle algo de sentido a este desastre. Así que vemos que InitializeWebKit() comienza tomando cualquier argumento que se le haya pasado a este proceso, luego comprobamos si hemos habilitado al compilar -dENABLE_VTUNE_JIT_INTERFACE, y en caso de que lo hayamos hecho, comprobamos la opción pasada a cmdline que es enable-vtune-support. ¿Qué demonios es eso? Buscamos en Google y encontramos un enlace a https://www.intel.com/content/www/us/en/develop/documentation/vtune-help/top.html y en esa página dice que es: "una herramienta de análisis de rendimiento para aplicaciones seriales y multihilo". Así que en resumen, algo que mejora el rendimiento de tu chrome.Luego inicializamos blink . Ahora, para entender el proceso de inicialización de blink, expliquemos brevemente, ya que entraremos en detalle en el próximo capítulo, que profundizará en blink. Nos encontramos con otra clase que nos resulta desconocida, y esa es RendererBlinkPlatformImpl, que se ve así
 y se puede encontrar en
content/renderer/renderer_blink_platform_impl.cc . Basándonos en el nombre, podemos concluir que es una clase abstracta que se implementa según la plataforma y también podemos ver que hereda de  Repasando lo que RendererBlinkPlatformImpl hace realmente, vemos que comprueba la plataforma y luego, según los resultados de la comprobación, marca un flag, y luego comprueba si el hilo actual es un RendererThread obteniendo un puntero a tls
bla bla bla, mira a ver si añades más contenido sobre las clases. Luego llegamos a algo que todos podríamos haber estado esperando y es el primer fragmento de código de v8. Que es . ¿Qué hace esto? Antes que nada, sabemos por la documentación que un v8::isolate es una instancia del motor V8. Es decir, una copia independiente del runtime de V8, incluido un administrador de heap, un recolector de basura, etc. No es suficiente para ejecutar scripts. vemos blink::MainThreadIsolate() definido como
 que está en el archivo blink/renderer/platform/bindings/v8_per_isolate_data.cc, que a su vez V8PerIsolateData::MainThreadIsolate() se ve así
 , V8PerIsolateData que, como habrás adivinado, también es una
clase que se encuentra en bindings/core/v8/V8PerIsolateData.h y se ve más o menos así bla bla bla, añade detalles. Luego comprobamos si pasamos kDisableThreadedCompositing(añade mañana cómo se ve realmente el flag) a la línea de comandos. En caso de que no lo hayamos hecho, iniciamos un hilo compositor. ¿Qué demonios es eso? Citando de https://frontendmasters.com/courses/web-performance/the-compositor-thread/ , es un hilo cuyo "único trabajo es dibujar bitmaps, tomar los bitmaps, enviarlos a la GPU y ponerlos en la pantalla". Luego registramos lo que se llama un scheme. Básicamente, ¿recuerdas cuando ves el código fuente que tienes algo delante de la url como: "view-source:website"? Sí, eso en realidad lo maneja el renderer. Y hay más de estos schemes. ¿Qué quieren decir con registrar? Aún no estoy seguro, pero creo que significan algo como: oye, esto es algo que quiero que manejes cuando el usuario venga y haga esto. De todos modos, así es como se ve. añade más detalles. bien, ahora cuando añada, detalla también la última pieza de código 
. bla, bla, bla y finalmente llegamos a la parte final de renderer_main actualiza con detalles

=====================================================================
Parte de análisis dinámicoAhora, para poder depurarlo dinámicamente, en caso de que seas tan novato como yo, querrás ejecutar windbg desde cmd.exe de la siguiente manera: 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", y establecer .childdbg 1 para poder depurar el proceso hijo generado. Desde ahí, querrás usar bp content!content::RendererMain y dejarlo correr unas 5 o 6 veces. (modifica para hacer referencia al 4 en el video, que así queda más claro) después de eso llegamos a .





MainDllLoader

--no-sandbox
chrome_mainAhora veamos qué hace. Abrimos chromium.dll en binja
Basándonos en esta imagen de https://blogs.igalia.com/jaragunde/files/2019/03/chrome-init-sequence.png tenemos una idea aproximada de qué deberíamos buscar en binja
Así es como se ve el gráfico en binja
.
Para poder rastrearlo mejor, busca una función llamada ChromeMain. Esta es la lógica principal (también conocida como núcleo) de chrome. Dentro de ella podemos ver las fases antes mencionadas de cómo se ejecuta chrome.dll al inicio:
Aquí podemos ver que primero llama a algunas funciones como sub_180001420, sub_180017020, sub_180017020 que hacen algunas comprobaciones para ver detalles sobre cómo se instaló/compiló chrome. Usando el código fuente podemos rastrear sus atributos. La primera función, sub_180001420, corresponde a UmaHistogramEnumeration; luego tenemos sub_180017020, que es InitializeFromPrimaryModule; y la tercera y última, sub_180017020, es chrome_main_delegate.
Seguimos repitiendo chrome_main_delegate pero nunca definimos cuál es su propósito. ChromeMainDelegate es una clase que hereda de ContentMainDelegate y principalmente proporciona funciones relacionadas con el inicio y el procesamiento de llamadas de procesos. !En caso de que quieras, puedes implementar una interfaz ContentMainDelegate personalizada para cambiar el comportamiento predeterminado del módulo Content, y usar una clase ChromeMainDelegate personalizada en Chromium para personalizar el comportamiento del proceso de inicio.
Dentro de él hay algunas llamadas a funciones que hacen algo de xor sobre algunas regiones de datos y lo concatenan con algún registro para obtener algunas opciones sobre el inicio de chrome.
.
Ten en cuenta también que la función sub_180001510 está creando una referencia de puntero scoped con dos callbacks. No es algo realmente importante lo que hace, pero es interesante aprender qué es un BindStateBase. Lo encontraremos mucho en el código base de chrome.
Ahora te estarás preguntando qué hacen las últimas tres líneas, precisamente
hacen. Así que la primera línea básicamente crea un std::unique_ptr<> para Closures. ¡¿Qué demonios es un closure!? Bueno, si tuviéramos que citar a Mozilla Dev: "closure es la combinación de una función empaquetada (encerrada) con referencias a su estado circundante (el entorno léxico). En otras palabras, un closure te da acceso al ámbito de una función externa desde una función interna. En JavaScript, los closures se crean cada vez que se crea una función, en el momento de creación de la función." Si tuviéramos que usar lenguaje humano, es una función dentro de otra función que accede a una variable dentro de la función externa. Ej:
.
Así que, en esencia, se asegura de que el closure se ejecute. Y las otras dos líneas simplemente establecen un comportamiento específico para los volcados cuando chrome crashes.InstallDetails::Get().VersionMismatch() patchuie aici
Continuando, comprueba su versión en tiempo de ejecución y en caso de que no coincida, se bloquea y llegamos al análisis de la línea de comandos
Entrando en sub_184171800 podemos ver que no es tan grande
Primero toma los argumentos pasados al proceso actual, luego llama a una función que toma un StringPiece, básicamente una clase envoltorio para std::string pero un poco más moderna, y esa función básicamente comprueba si el binario fue llamado como headless, compara si el nombre del binario que se ejecutó es chrome, comprueba si USE_HEADLESS_CHROME está establecido y luego continúa al siguiente paso, que es content main.
Basándose en los argumentos proporcionados, el trabajo de content main es iniciar el shell respectivo.
Ahora, en caso de que no proporcionemos ningún argumento al shell, el flujo de ejecución va a
.
Para entender lo que hace, necesitamos verificar su fuente. Podemos encontrarla en /src/content/app/content_main.cc
Tendremos que desplazarnos hasta el final y allí encontraremos la definición de la función ContentMain. Allí vemos que llama a dos funciones. Una que inicializa un ContentMainRunner, que es la clase que maneja toda la "creación de procesos" en el contexto de crear el navegador ipc sqli net y el resto. Y la otra que básicamente comprueba el tipo de subprocesos y lo alimenta a la clase ContentMainRunner.
Ahora demos un paso atrás para entender el flujo de código. Primero examinemos RunContentProcess, ya que ContentMainRunner es bastante complejo. El primer paso que hace es crear un GlobalActivityTracker. ¡Oh Dios! Ya lo adivinaste, Google te rastrea :))) No, solo bromeo, por favor no me demandes, Google. Pero hablando en serio, esto rastrea cada hilo, que es un rastreador de hilos, y hace un par de cosas útiles para un mejor depurado, como:
lo que en esencia significa que habrá un entero único utilizado como identificador para cada hilo para comprender mejor qué causó que el proceso se rompiera.
ej:```
kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4
kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3
kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1
kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100,