
Chrome pwning 및 V8 pwning을 시작하기 위한 잘 구조화된 문서
chrome pwning & v8 pwning을 시작하기 위한 잘 구조화된 문서
이 문서가 어떻게 구성되어 있는지
브라우저는 오늘날 가장 많이 사용되는 기술 중 하나입니다. 모든 기성 컴퓨터에서 플러그 앤 플레이만 하면 브라우저가 설치되어 있는 것을 볼 수 있습니다. 그렇기 때문에 공격자 관점과 위협 모델 관점에서 공격자가 악성 페이지를 통해 브라우저를 손상시킬 수 있다면 매우 보람 있는 일입니다. 이러한 근거로 저는 Google의 Javascript 엔진, 특히 v8을 연구하기로 선택했습니다.
v8 프로젝트의 방대한 규모를 고려하여 인터프리터, 즉 d8을 시작점으로 선택했습니다. d8에 대한 광범위한 연구가 이미 수행되었지만, 우리는 적어도 하나의 버그를 찾기를 희망하며, 찾지 못하더라도 v8이 브라우저 공격에 사용되는 기본 익스플로잇 개발 전략의 진입점을 제공하므로 브라우저 공격 연구를 계속 진행할 수 있기를 바랍니다.
v8을 대상으로 선택한 또 다른 이유는 여러 브라우저에서 사용되기 때문입니다.
내부를 살펴보면 이 엔진이 MicrosoftEdge에서도 사용되는 것을 볼 수 있으므로 여러 버그 바운티를 받을 기회가 있습니다. 기본 운영 체제에 관해서는 연구자가 Windows와 Linux를 혼합하여 사용할 것입니다. v8 버그는 wasm 페이지를 통한 코드 실행을 허용하고 특정 플랫폼에 국한되지 않기 때문에 셸을 얻는 데 제한이 없습니다.
불행히도 v8에서 악용된 버그는 코드 실행으로 이어지지만, 샌드박스로 인해 코드를 실행할 수는 없습니다. 따라서 렌더러 컨텍스트에서 코드 실행을 얻을 수 있지만 컴퓨터에서 코드를 실행할 수는 없습니다. 이를 위해서는 샌드박스에 대한 또 다른 익스플로잇이 필요하며, 시스템을 공격하려면 전체 체인(full chain)이 필요합니다.
그리고 브라우저 해킹을 시작할 수 있도록 다음과 같은 목표를 정의합니다.
프로젝트의 첫 번째 단계에서는 Chrome 아키텍처와 각 구성 요소가 서로 어떻게 상호 작용하는지에 대한 지식을 최대한 많이 수집해야 합니다. 이를 더 잘 이해하려면 Chromium 프로젝트를 여러 하위 구성 요소로 나누어 모든 것을 분리하고 제대로 분석할 수 있어야 합니다. 보다 정확하게는 다음 각 구성 요소가 몇 개의 하위 구성 요소로 나뉘는지입니다.
첫 단계로 가장 논리적인 단계는 Chromium 아키텍처를 이해하는 것입니다.
좋아요, 우리는 브라우저를 공격하려고 합니다. 그런데 브라우저를 처음 시작하면 무슨 일이 일어날까요? chromium 실행 파일을 클릭하면 실행 파일이 몇 가지 프로세스를 시작합니다.
그 순서와 이름은 다음과 같습니다.
첫 번째는 content process라고 합니다. 이 프로세스는 무엇을 하나요?
이제 그것이 무엇을 하는지 간단히 알았으니 더 자세히 살펴볼 차례입니다.
Chromium이 적어도 Windows에서 작동하는 방식은 파일을 dll로 컴파일한 다음 메모리에 로드하는 것입니다. 따라서 Chromium 브라우저의 핵심 로직은 chromium.dll에 있습니다.
이것은 코드로도 확인됩니다.
.
이것은 chrome_exe_main_win.cc에서 가져온 것입니다. 전체 코드를 읽고 싶다면 chromium/src/chrome/app에 있습니다. 좋아요, 실행을 따라가면 MakeMainDllLoader()를 호출하여 dll 로더 클래스를 호출한 다음 "loader"를 실행합니다. 즉, chrome.dll을 로드하고 필요한 경우 필요한 명령줄과 함께 다시 시작합니다. Loader를 더 분석하려면 같은 디렉토리에 있는 mail_dll_loader_win.cc 파일의 코드를 이해해야 합니다. 파일 끝까지 스크롤하면 MakeMainDllLoader 호출을 볼 수 있으며, 사용 중인 버전에 따라 ChromeDllLoader 또는 ChromiumDllLoader를 호출합니다.

ChromiumDllLoader가 MainDllLoader에서 상속되는 클래스임을 알 수 있습니다. 정의에서 이 클래스가 cmdline과 process type에 전달된 인수에 따라 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을 로드하는 문제입니다. 둘째, 명령줄에 전달된 인수를 가져와 샌드박스 서비스를 초기화합니다.
.
먼저 샌드박스 초기화를 호출하는 쪽이 브라우저인지 확인한 다음, 샌드박스 초기화를 호출한 프로세스가 클라우드 프린트 서비스로 호출되었는지 확인합니다. 또한 바이너리에 가 전달되었는지 확인하는데, 이는 기본적으로 바이너리에 샌드박스에서 실행하지 말라고 지시합니다. 이러한 옵션 중 하나라도 설정되었는지 확인하고, 둘 중 하나라도 true이면 해당 옵션과 함께 샌드박스를 호출합니다. 마지막으로 우리가 관심 있는 부분에 도달합니다.
이것이 우리가 관심 있는 부분이며, 호출을 래핑하는 것을 나타냅니다.
우리는 chrome_main_delegate를 계속 반복하지만 그 목적이 무엇인지 정의한 적은 없습니다. ChromeMainDelegate는 ContentMainDelegate에서 상속되는 클래스로, 주로 시작 관련 함수와 프로세스 호출 처리를 제공합니다. 원하는 경우 사용자 지정 ContentMainDelegate 인터페이스를 구현하여 Content 모듈의 기본 동작을 변경하고 Chromium에서 사용자 지정 ChromeMainDelegate 클래스를 사용하여 시작 프로세스의 동작을 사용자 지정할 수 있습니다.

그 안에는 일부 데이터 영역에 xor을 수행하고 이를 일부 레지스트리와 연결하여 chrome 시작에 대한 일부 옵션을 얻는 함수 호출이 있습니다.
.
또한 함수 sub_180001510이 두 개의 콜백을 사용하여 scoped pointer 참조를 생성한다는 점에 유의하세요. 실제로 중요한 것은 아니지만 BindStateBase가 무엇인지 배우는 것은 흥미롭습니다. 우리는 chrome의 basecode에서 그것을 많이 만나게 될 것입니다.
이제 마지막 세 줄이 정확히 무엇을 하는지 궁금할 수 있습니다.

첫 번째 줄은 기본적으로 Closures에 대한 std::unique_ptr<>를 생성합니다. 도대체 closure가 뭐냐고!? 음, mozilla dev에서 인용하자면 "클로저는 함수가 주변 상태(렉시컬 환경)에 대한 참조와 함께 묶여(둘러싸여) 있는 결합입니다. 즉, 클로저는 내부 함수에서 외부 함수의 스코프에 접근할 수 있게 해줍니다. JavaScript에서 클로저는 함수가 생성될 때마다 생성됩니다." 인간의 언어로 말하자면, 외부 함수 내부의 변수에 접근하는 함수 안의 함수입니다. 예:
.
즉, 기본적으로 클로저가 실행되도록 보장합니다. 그리고 나머지 두 줄은 chrome이 crashes.InstallDetails::Get().VersionMismatch()에 도달할 때 덤프에 대한 특정 동작을 설정합니다. 여기에 붙여넣기
계속 진행하면 런타임에 버전을 확인하고, 일치하지 않으면 충돌이 발생하며 명령줄 파싱에 도달합니다.

sub_184171800 안으로 들어가면 그렇게 크지 않은 것을 볼 수 있습니다.

먼저 현재 프로세스에 전달된 인수를 가져온 다음 StringPiece(기본적으로 std::string에 대한 클래스 래퍼이지만 조금 더 멋진)를 사용하는 함수를 호출합니다. 해당 함수는 기본적으로 바이너리가 headless로 호출되었는지 확인하고, 실행된 바이너리의 이름이 chrome인지 비교하며, USE_HEADLESS_CHROME이 설정되었는지 확인한 후 다음 단계인 content main으로 진행합니다.
제공된 인수를 기반으로 content main의 작업은 해당 셸을 시작하는 것입니다.
이제 셸에 인수를 제공하지 않으면 실행 흐름은 다음으로 이동합니다.
.
그것이 무엇을 하는지 이해하려면 해당 소스를 확인해야 합니다. 에서 찾을 수 있습니다.
맨 아래까지 스크롤하면 ContentMain 함수의 정의를 찾을 수 있습니다. 거기서 두 개의 함수를 호출하는 것을 볼 수 있습니다. 하나는 ContentMainRunner를 초기화하는데, 이 클래스는 브라우저 ipc sqli net 생성 및 나머지 작업의 맥락에서 모든 "프로세스 생성"을 처리합니다. 다른 하나는 기본적으로 하위 프로세스의 유형을 확인하고 이를 ContentMainRunner 클래스에 전달합니다.
이제 코드 흐름을 이해하기 위해 한 발 물러서 봅시다. ContentMainRunner는 상당히 복잡하므로 먼저 RunContentProcess를 살펴보겠습니다. 가장 먼저 하는 일은 GlobalActivityTracker를 만드는 것입니다. 세상에 !!! 짐작하셨겠지만 Google이 당신을 추적합니다 :))) 농담입니다. 제발 고소하지 마세요 Google. 진지하게 말하면, 각 스레드를 추적하는 스레드 추적기이며 디버깅을 위해 몇 가지 유용한 기능을 수행합니다. 예를 들어:
본질적으로 각 스레드에 대한 식별자로 사용되는 고유한 정수가 있어 무엇이 프로세스를 중단시켰는지 더 잘 이해할 수 있습니다.
예:```
kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4
kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3
kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1
kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100,
다음과 같이 정의되는 프로세스 수명 단계를 추적하는 것은```
// 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,
프로세스가 종료될 때 즉시(ASAP) 로그를 남기고, 모듈(즉, 크로미움의 구성 요소)이 로드될 때 모듈에 대한 정보를 저장합니다. 기본적으로 동일한 기능이 반복되지만 스레드와 클래스에 따라 다릅니다. 관심이 있다면 chromium/src/base/debug/activity_tracker.h에서 확인할 수 있습니다.
그런 다음 "Main()"이 이전에 호출되었는지 확인합니다. 여기서는 ChromeMain()이 이전에 호출되었는지를 의미한다고 생각합니다. 기본적으로 콘텐츠 프로세스에 전달된 명령이 인수와 함께 호출되었는지 확인합니다. 만약 그렇다면 브라우저 프로세스도 동일한 인수를 받도록 합니다. 그런 다음 플랫폼별 사항을 확인합니다. Windows가 발견되면 CreateATLModuleIfNeeded를 수행하여 자체 핸들러를 초기화합니다. 그 다음에는 플랫폼별 사항을 확인하는 동일한 과정이 이어지고, SetupCRT를 사용하여 인수를 전달합니다. 그리고 프로세스를 생성한 후 다른 프로세스와 통신할 수 있도록 IPC를 초기화하는 부분에 도달합니다. 여기에 그 작업을 담당하는 코드가 있습니다. 이에 대한 자세한 내용은 나중에 크롬의 IPC 메커니즘에 대해 더 깊이 논의할 때 다시 다루겠습니다. 지금은 이 메커니즘이 크롬의 다중 아키텍처 프로세스 간 대화를 가능하게 한다는 것만 알아두세요. IPC가 무엇의 약자인지 모른다면, 이는 Inter-Process Communication(프로세스 간 통신)을 뜻합니다.
.
그런 다음 하는 일은 ui::RegisterPathProvider를 사용하여 UI 인수를 설정하는 대신 오히려 "전달"하는 것입니다. 추적기를 호출하고 content_main_runner->Initialize(std::move(params))를 사용하여 결과를 얻은 다음, 부모 콘솔을 생성합니다. 제 생각에 이것은 부모 프로세스를 생성한다는 의미인 것 같습니다. 몇 가지 확인을 더 수행한 후 중요한 부분에 도달하는데, 바로
입니다.
여기서는 IsSubprocess라는 함수 호출을 볼 수 있습니다.
기본적으로 이 함수는 기존의 상용구(boilerplate) 코드를 피하기 위해, 명령줄에서 받은 프로세스 유형을 하나의 함수에서 확인합니다. 그리고 옵션이 전달된 경우, 다음 중에서 선택하여 해당 프로세스를 생성합니다.```
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()`를 호출한다는 것을 알 수 있습니다. 즉, ContentMainRunner가 아닌 ContentMainRunnerImpl의 정의를 찾아야 하며, 그것은 앞서 언급한 동일한 폴더의 `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를 사용해 메시지로 게시합니다.
 .
다음으로는 UI에 대한 몇 가지 항목을 설정합니다.
다시 명령줄로 전달된 내용을 기반으로 RegisterMainThreadFactories를 호출합니다. 이 함수는 RegisterUtilityMainThreadFactory의 래퍼이며, RegisterUtilityMainThreadFactory는 g_utility_main_thread_factory만 설정합니다. 이름으로 보아 이 팩토리는 나머지 스레드들을 감시하는 역할을 하는 메인 스레드를 생성하고, 마지막으로 브라우저 프로세스를 실행하는 것 같습니다. 
제 말을 믿지 못하겠다면, 여기 binja에서 디스어셈블한 Chromium이 있으며, 이에 대한 인메모리 분석도 일부 살펴볼 것입니다.
운 좋게도 우리에게는 chome.exe.pdb가 있어서 디버그 심볼을 확보할 수 있고, 바이너리를 리버싱하는 동안 고생하지 않아도 됩니다.
Windows에서 Chrome을 다루기 때문에 프로그램의 주요 진입점은 wWinMain입니다:
그래프가 어떻게 생겼는지는 다음과 같습니다.
.
이 함수 자체는 거대하기 때문에 필요한 부분만 보여드리겠습니다. 여러 초기화를 거친 후 `MakeMainDllLoader()`를 호출하는 지점에 도달합니다.
, 이 함수가 무엇을 하는지는 이미 설명했습니다. 그럼 이제 Chrome을 동적으로 디버깅하고 앞서 언급한 모든 것을 증명하려면 어떻게 해야 할까요? 먼저 windbg로 로드한 다음 lm을 실행하고 실행 파일의 이름을 찾습니다. 그 후 chrome!MakeMainDllLoader라는 함수를 찾아 중단점(bp)을 설정하고 실행을 진행합니다.. 그 함수 안으로 들어가서  ret 명령에 도달할 때까지 실행한 다음 함수 밖으로 나오면 chrome!wWinMain+0x764에 도달합니다. . 그런 다음 chrome!MainDllLoader::Launch에 도달할 때까지 실행하고 그 안으로 스텝 인합니다. 거기서 chrome!MainDllLoader::Load에 중단점을 설정하여 chrome.dll이 메모리에 어떻게 로드되는지 확인합니다. 그리고 볼 수 있듯이 가장 먼저 로드된 것 중 하나가 chrome.dll이었습니다.  여기부터 분석 흐름은 동일하며, 전체 인메모리 분석은 독자의 연습 과제로 남겨두겠습니다. 특별한 목적을 위해 content 프로세스가 거의 완료되고 브라우저 프로세스가 시작되기 직전 지점까지만 분석을 수행하고 거기서 멈추겠습니다. 이 목적은 단지 IPC가 서로 간에 어떻게 통신을 시작하는지 보여주기 위한 것입니다. 이제 dll 로드 후에 call rax 명령을 찾아야 합니다. 제가 한 방법은 dll 로드 후 현재 eip에서 0x40개의 명령을 나열하는 것이었습니다.. 실제로 그 주소에 있는 것이 우리가 찾는 바로 그 함수, chrome!ChromeMain입니다.
거기서 에 중단점을 설정하고 싶습니다. 이 지점은 본질적으로 content!content::ContentMain으로 점프하기 전의 큰 검사입니다. 거기서 몇 개의 명령을 건너뛰면 content!content::ContentMain에 도달합니다. 그 안으로 스텝 인하여 content!content::RunContentProcess에서 중단점을 설정하고 싶습니다.  다시 스텝 인한 다음 content!content::ContentMainRunnerImpl::Run+0x430에 중단점을 설정합니다. 그리고 스텝 오버하자마자  mojo ipc가 시작되는 것을 볼 수 있으며, 다음 프로세스, 즉 브라우저 프로세스가 실제 브라우저 구현과 다른 모든 프로세스 처리를 담당한다는 결론을 내릴 수 있습니다.
===============================================================================================
2.
지난번에는 content 프로세스를 이해한 뒤 마무리했습니다. 오늘은 browser 프로세스에 대해 알아보겠습니다.
browser 프로세스는 content 프로세스 이후에 시작되는 프로세스로, Chrome이 시작하는 프로세스 중 두 번째입니다.
간단히 설명해 보겠습니다:
* content 프로세스는 초기화 루틴과 일부 의존성 검사에 가깝고 browser 프로세스는 content 프로세스에 의해 시작되므로, browser 프로세스를 메인 프로세스로 간주할 수 있습니다.
* browser의 전체 수명 동안 계속 살아 있습니다.
* 모든 프로세스의 중앙 조정자 역할을 하며, browser가 사용할 수 있는 최고 권한 수준에서 작동합니다.
* 최고 권한 수준에서 실행되므로, 다른 프로세스가 더 높은 수준의 작업을 수행해야 하는 경우 해당 요청은 browser 프로세스가 처리합니다.
* 주소 표시줄, 북마크, 뒤로/앞으로/새로고침 버튼과 같은 기능을 제어합니다. 가장 높은 권한을 가진 프로세스이므로 다른 프로세스가 전달하는 데이터를 신뢰하지 않습니다.
* 필요할 때 다른 프로세스를 대신해 UI, 네트워킹, 파일시스템 저장소와 같은 권한이 필요한 작업도 처리합니다.
이제 그것이 무엇을 하는지 간략히 알았으니, 더 깊이 들어가 볼 차례입니다:
우리의 여정은 `src\content\app` 디렉터리의 content_main_runner_impl.cc 파일에 있는 int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params,bool start_minimal_browser) 함수에서 시작합니다. 이 함수가 실행하는 첫 번째 작업은 TRACE_EVENT_INSTANT0입니다. .
그게 뭘까요? 그 전에, trace 함수란 대체 무엇일까요? https://lwn.net/Articles/379903/ 에 가보면 그들은 이를 이렇게 정의합니다. , 약간 추상화해 보면 trace 함수는 스택 프레임의 특정 지점에서 데이터를 기록하는 함수라는 결론에 도달할 수 있습니다. 단순히 실행 함수의 스택을 기록하는 것 이상으로, 마지막으로 실행된 함수의 지역 변수도 기록할 수 있습니다. 이제 trace 함수가 무엇인지 알았으니 우리가 사용하는 것이 무엇을 하는지 살펴봅시다.
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))"로, 값에 기반해 볼 때 해당 인스턴트 이벤트의 ID를 나타내는 것 같습니다. 따라서 본질적으로 이것은 우리가 RunBrowser 함수 안으로 진입했다는 사실을 단순히 추적(로그)하는 것입니다. 그런 다음 브라우저 메인 루프가 이미 시작되었는지 확인하며, 이미 시작된 경우 함수를 종료합니다.  . 그런 다음 플래그를 설정하고, 그 후 꽤 흥미로운 코드 부분에 도달합니다. mojo_ipc 메커니즘 지원 여부를 확인하고, 지원되는 경우 ShouldCreateFeatureList를 사용합니다. 이 함수는 여러 프로세스로 구성된 feature list를 생성하고, 프로세스들을 초기화하려 시도한 다음 마지막으로 mojo 기능을 초기화하려 시도합니다.. 그런 다음 threadpool을 생성합니다. threadpool이 도대체 뭐죠?!?! 위키피디아에서 인용하자면: '컴퓨터 프로그램에서 실행 동시성을 달성하기 위한 소프트웨어 디자인 패턴'입니다. 더 정확히 설명하면 '스레드 풀은 감독 프로그램이 동시 실행을 위해 할당할 작업을 기다리는 여러 스레드를 유지합니다' (이 인용문도 위키피디아에서 가져왔습니다). 더 쉽게 설명하면, 공장에서 일하는 두 줄의 사람들을 상상해 보세요. 그들을 line a와 line b라고 부릅시다. 그들은 모두 상사인 line c의 감독을 받습니다. 이제 line b는 일을 시작하려면 line a가 작업을 마치고 line c로부터 알림을 받아야 합니다. line a도 마찬가지입니다. 이것이 스레드 풀입니다. 여기 작은 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를 이해해야 합니다. BaseBrowserTaskExecutor는 TaskExecutor에서 상속받는 것을 볼 수 있습니다. 운 좋게도 TaskExecutor의 메서드들을 재정의 가능한 속성(overwriteable)으로 재정의하고 있는데, 이는 나중에 다른 메서드 호출에 의해 다시 재정의될 수 있음을 의미합니다. 하지만 궁금한 사람들을 위해 말하자면, 이 클래스는 base/task/task_executor.h에서 찾을 수 있으며, 살펴보면 TaskExecutor는 '특정 TaskTraits 확장 ID를 가진 Tasks를 실행할 수 있는' 클래스임을 알 수 있습니다.  .
task가 무엇이고 tasktraits가 무엇일까요? 우리는 이미 task가 무엇인지 언급했지만, 다시 상기시키자면 task는 Chrome 프로세스의 작업 중 하나입니다. 그렇다면 TaskTraits란 무엇일까요? TaskTraits는 base/task/task_traits.h에 있으며, '스레드 풀이 더 나은 스케줄링 결정을 내리는 데 도움이 되는 task에 대한 정보를 캡슐화'하는 것으로 정의되어 있습니다. 다시 우리의 BrowserTaskExecutor로 돌아가 보겠습니다. 우리가 호출하는 메서드는 Create()이며, 그 분석은 다음과 같습니다. 
먼저 현재 task가 SingleThreadTaskRunner에서 실행되어야 하는지 확인합니다. 즉, 이 task를 독립적인 스레드로 실행해야 하는지 여부입니다. 이를 위해 tls에 대한 포인터를 얻습니다. 그런 다음 ui와 스레드 스케줄러를 초기화합니다. 기본적으로 여기서는 향후 ui 관련 이벤트를 위한 스케줄러를 초기화합니다..
이 기능에 대해서는 그 정도가 전부입니다. content_main_runner_impl.cc 분석을 계속 진행하면 여기에 도달합니다.
variations ids provider 클래스 파일을 찾아보면 `components/variations/variations_ids_provider.h`에 있으며, 여기서 꽤 흥미로운 것을 발견할 수 있습니다. 그것은 `.mojom.h` 파일을 포함합니다. 해당 정의는 `Debug/gen/components/variations/`의 variations.mojom.h에서 찾을 수 있는데, 이는 ipc 메커니즘과 관련이 있음을 의미합니다. 이제 클래스의 실제 정의를 살펴보면 '사용자 지정 HTTP 요청 헤더로 전송되는 클라이언트 실험 및 메트릭 상태를 유지하기 위한 도우미 클래스'라는 주석이 있습니다. 그 동작과 소스 정의를 살펴보면, 이것은 단순히 함수의 마커로 사용되며 'GetClientDataHeaders()에 제공되는 signed-in 매개변수'를 표시한다는 결론을 내릴 수 있습니다. 즉, 스택 실행 어딘가에서 GetClientDataHeaders 호출이 있고 나중에 매개변수와 함께 호출될 것임을 의미합니다. 그런 다음 delegate_->PostEarlyInitialization(!!main_params.ui_task);를 수행합니다. 이것은 단순히 mojo의 ipc에 ui 작업을 시작해야 한다고 게시합니다. 초기화를 더 수행한 다음  RunBrowserProcessMain를 호출하게 됩니다.  . 저처럼 여기서 GUI가 시작되는 모습을 보기를 기대했다면, 당신은 틀렸습니다. 마스터 우그웨이의 명언입니다.  . 그런 다음 몇 가지 확인을 더 수행하고 BrowserMain에 도달합니다. 그것을 살펴보면 몇 가지 추적을 수행한 다음 에 도달하는데, 이곳에서 실제로 GUI가 시작됩니다. 이제 제 말을 믿지 못하겠다면, 동적 분석에 도달할 때까지 조금 더 인내심을 가져야 할 것입니다. 그런 다음 실제로 우리가 관심을 가졌던 실제 내용인 Run 메서드에 도달합니다. 그 뒤를 따라 브라우저 프로세스 이해를 위한 다음 지점으로 이동하게 됩니다. 하지만 지금은 잠시 다른 길로 빠져서 Initialize 메서드가 어떻게 생성되는지 조사해 봅시다. 바로 시작해 보면, init 메서드의 실행을 추적하고 그 전에 히스토그램을 생성하는 것을 볼 수 있습니다.. 그런 다음 initialization_started_ 플래그를 확인하여 초기화 단계에 도달했는지 확인합니다. 도달하지 않은 경우, Chrome에서 사용하는 그래픽 라이브러리인 skia를 초기화하고, 이 메서드 실행에 걸린 시간을 측정하여 나중에 히스토그램에서 사용하기 위한 '타이머'를 시작하며, 디버거가 프로세스에 연결될 때까지 기다리라는 매개변수를 바이너리에 전달했는지 확인하고, 마지막으로 notification_service_를 시작합니다. 추측컨대 이 서비스는 threadpool의 메인 감시자에게 언제 서비스를 시작할지 알려줄 것입니다. . 그런 다음 Chrome에 필요한 글꼴을 초기화하고, 모든 프로세스의 조정자 역할을 하는 mainbrowserloop를 생성합니다. 그리고 우리에게 관심 없는 세 개의 메서드를 건너뜁니다. main_loop_->CreateStartupTasks();
int result_code = main_loop_->GetResultCode(); . 여기서 우리가 관심 있는 것은 CreateStartupTasks로 들어가는 것입니다. 거기서 content\browser\browser_main_loop.cc를 보면, 우리는 createstartuptask 메서드 내부에 있는 startup_task_runner_->RunAllTasksNow();에 관심이 있습니다. starup_task_runner_는 같은 디렉터리의 startup_task_runner.cc 파일에 있는 StartupTaskRunner입니다. 이제 RunAllTasksNow 메서드를 살펴보면, 그것이 하는 일은 단순히 모든 task를 반복하면서 실행하는 것임을 알 수 있습니다.
==========================================================================================
이제 동적 분석을 할 시간입니다.이제 렌더러를 잡으려면 windbg에서 바이너리를 시작해야 합니다. File->Open Executable을 선택하고 인수로 --renderer-startup-dialog --no-sandbox --wait-for-debugger-children=renderer --renderer-process-limit=1을 전달하면 됩니다.. 그런 다음 content!content::StartupTaskRunner::RunAllTasksNow+0x88에 bp를 설정하여 IPC 메시지와 이후의 렌더러를 잡을 수 있습니다. 참고로, 한 번 실행한 후 디버거가 어떻게 보이는지 보여드리면 다음과 같습니다. . "chrome is running in full browser mode" 메시지가 보일 것입니다. 거기서 1부터 5까지 세면서 네 번 더 실행하면 다음과 같은 화면이 나타나야 합니다. . 렌더러 프로세스가 시작될 때 모니터링하려면 procmon을 사용하는 것이 좋습니다. 그런 다음 이를 다른 디버거에 연결하고, 위의 인수를 사용한 경우 렌더러 PID를 알려주는 메시지 상자가 나타날 것입니다.. 그런 다음 base!base::RunLoop::Run에 bp를 걸고 싶을 것입니다. 불행히도 어떤 이유에서인지 content!content::RendererMain에는 중단점을 걸 수 없습니다. 아마도 renderemain 함수를 종료한 직후에 프로세스에 연결하고 있고, 그것이 스레드에 의해 실행되기 때문일 것입니다. 어쨌든 네 번 실행한 후의 IPC 상태는 다음과 같습니다.. 이것은 GUI가 시작되었음을 나타냅니다. 그리고 렌더러 프로세스가 시작된 후의 IPC 상태는 다음과 같습니다.. 그런 다음 다음 위치에 bp를 설정합니다.
content!content::StartupTaskRunner::RunAllTasksNow+0x88 및 content!content::RunOtherNamedProcessTypeMain. 실행하게 두세요.
============================================================================================================
3. 이제 브라우저 프로세스 분석의 두 번째 부분으로, 렌더러를 이해하고 디버깅할 수 있는 지점까지 왔지만 아직 거기로 가지는 않겠습니다. RunBrowserProcessMain 이후에 분석할 함수 하나를 일부러 남겨 두었습니다. 엄밀히 말하면 RunBrowser 다음이지만, RunBrowser는 RunBrowserProcessMain의 래퍼라는 점을 기억하실 겁니다. 남겨 둔 것은 src/content/app 폴더의 content_main_runner_impl.cc 파일에 있는 함수로, 렌더러를 생성한 후 호출되며 이름은 RunOtherNamedProcessTypeMain입니다. 이 프로세스는 다른 모든 프로세스를 실행하는 역할을 담당합니다.. 이제 코드에서 무슨 일이 일어나는지 살펴보겠습니다. 프로토타입은 와 같으며, 이는 cmdline에 전달된 인수, 기대하는 프로세스 유형, 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 관련 작업을 처리하는 새 프로세스가 생성되며, content!content::RunContentProcess에 도달할 때까지 계속 bp를 설정해야 합니다. 이것이 제가 교육받은 시행착오(educated trial and error)라고 부르는 것입니다:)) .
=====================================================================
4. 렌더러 분석
!면책 고지
렌더러 코드를 분석하는 동안 우리는 렌더러에서 무슨 일이 제대로 일어나는지 이해하기 위해 blink 코드도 조금 살펴볼 것입니다.
이 강의의 이 부분을 작성하면서 나는 이 프로세스가 무엇을 하는지, 왜 우리가 그것을 살펴보는지에 대해 간략히 설명하는 것을 잊었다는 것을 깨달았습니다. 우리가 알다시피 이것은 chromium이 시작하는 세 번째 프로세스이며 renderer라고 불립니다. 그런데 왜 "renderer"라고 불릴까요? 웹사이트에서 우리가 보는 모든 것을 렌더링(그리는)하는 일을 하기 때문입니다. 기본적으로 웹사이트를 방문할 때 테이블이 테이블처럼 보이는 이유, CSS가 텍스트 조각을 사용자 정의할 수 있게 해 주는 이유, JS가 블랙 매직을 할 수 있는 이유가 바로 이것입니다. 이 프로세스를 분석하는 또 다른 중요한 이유는 대부분의 버그가 여기서 발생하기 때문입니다. html, css, js 또는 다른 구성 요소를 이야기하든, chrome bugs.chromium.org에서 blink 아래 모두 볼 수 있습니다.
* 렌더러 프로세스는 더 많습니다. 브라우저가 현재 열어 둔 각 탭마다 완전히 별도의 프로세스가 하나씩 있습니다.
* 이 프로세스는 실제 웹사이트 탭 내부의 모든 것을 제어합니다.
* 2018년부터 iframe도 모두 탭을 가질 수 있도록 업그레이드되었습니다. 따라서 iframe의 각 탭에는 개별 렌더러 프로세스가 있습니다. 이를 Site-Isolation이라고 합니다.
* 웹사이트를 구문 분석하고, 테이블, 이미지 등 웹사이트 내부에 있는 내용을 화면에 그리며, javascript를 실행하는 책임이 있습니다.
* 샌드박스 처리되어 있습니다.
* 핵심에는 Blink라는 렌더링 엔진을 사용합니다.
* chrome://, devtools://, chrome-error:// 같은 URL 스킴을 생성하고 처리합니다.
* 실제로 모든 구문 분석과 무거운 작업을 수행할 Blink 엔진을 초기화합니다.
지난번에 우리는 브라우저 프로세스 분석을 마쳤고, 이제 모두가 기다려 온 렌더러 분석 부분으로 갈 시간입니다. 바로 그때, chromium을 가지고 놀다가 크래시를 일으킬 수 있었고 다음과 같은 스택 추적을 얻었습니다.  이를 통해 우리의 여정이 `\content\renderer\renderer_main.cc`에 있는 content::RendererMain에서 시작된다는 것을 알 수 있습니다. 짐작하셨겠지만 시작점은 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 파일에 위치합니다. 다음과 같이 생겼습니다.
. 따라서 이것은 --no-sandbox를 전달할 경우 샌드박스를 활성화하고 필요한 작업을 수행하는 헬퍼 함수일 뿐이라는 결론을 내릴 수 있습니다. 그게 전부입니다. 그런 다음 스레드 이름을 CrRendererMain으로 설정합니다.. 그런 다음 RenderThread라는 또 다른 새로운 클래스를 만나게 됩니다. 
다시 말하지만 우리는 이것이 무엇을 하는지 전혀 모르기 때문에 조금 탐구해 봅시다. RendererThread가 어떻게 생겼는지 이해하려는 우리의 탐구는 content/public/renderer/render_thread.h에서 시작됩니다. render_thread.h 파일 내부를 보면 다음과 같이 정의되어 있습니다. . 그 파일 전체에서 우리가 관심을 가지는 것은 rendere_thread.cc 파일에 정의된 IsMainThread이며, 다음과 같이 생겼습니다.  (이 메커니즘에 대한 긴 설명을 추가할 것). 이제 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의 기본 라이브러리"입니다."(blink 문서 인용(https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/platform/wtf/README.md)).blink에 대한 세부 사항을 더 추가할 것.
좋아요, 이제 renderer_main.cc 분석을 계속하면 또 무엇을 하는지 모르는 것을 보게 됩니다.  내일 세부 사항을 추가할 것. 그런 다음 를 호출합니다. 그런 다음 컴파일할 때 플러그인을 활성화했는지 확인하고, 활성화했다면 플러그인을 로드합니다.
. 그런 다음 추가로 확인하는 작업이 몇 가지 더 있는데, 설명이 매우 길어지고 이 단계에서는 필요하지 않기 때문에 설명을 건너뛰기로 했습니다. TL;DR 버전으로 말하면 RenderProcess 초기화 전에 샌드박스를 활성화할지 여부를 확인하는 것입니다. 그리고 마지막으로 에 도달합니다.(나머지 함수들에 대한 나머지 세부 사항을 추가할 것). 이것이 렌더러 분석의 끝은 아니지만, 실제 렌더러 분석의 시작 지점이라고 볼 수 있습니다. 나중에 보겠지만 여기서 가장 흥미로운 일들이 일어나기 때문입니다. 그래서 이것을 렌더러의 코드라고 생각할 수 있습니다. 우리가 관심을 가지는 것은 RenderThreadImpl이며, 다음과 같이 생겼습니다. . 그 모든 코드에서 우리는 Init() 함수에 관심이 있으며, 다음과 같이 생겼습니다:
, 하지만 훨씬 더 깁니다. :) 불행히도 한 장의 사진으로는 캡처할 수 없어서 시작 부분만 캡처했습니다. 그 외에 InitializeWebKit() 이후에 일어나는 다른 모든 것은 실제로 우리 관심사가 아닙니다. 대부분 GPU 프로세스와의 IPC 통신뿐이고, 그것은 현재 우리 레이더에 없기 때문입니다. 운이 좋게도 그 함수 안에서 우리가 관심을 가지는 함수 중 하나인 InitializeWebKit()을 정확히 잡을 수 있었습니다. 이것 역시 처럼 생겼습니다. InitializeWebKit 내부에서 무슨 일이 일어나는지 더 잘 이해하기 위해 renderer_main.cc 설명이라는 우리의 탐구에서 잠시 벗어나겠습니다. 이해하기에 많은 내용이라는 것을 알지만, 이 난잡한 상황을 정리하려고 하니 저를 따라와 주세요. InitializeWebKit()은 이 프로세스에 전달된 모든 인수를 가져오는 것으로 시작한 다음, 컴파일할 때 -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이 실제로 무엇을 하는지 살펴보면 플랫폼을 확인한 다음 그 결과에 따라 플래그를 표시하고, TLS에 대한 포인터를 가져와 현재 스레드가 RendererThread인지 확인합니다.
어쩌구저쩌구, 클래스에 대한 내용을 더 추가할지 보십시오. 그런 다음 우리 모두가 기다렸을 수도 있는 것, 즉 v8 코드의 첫 조각에 도달합니다. 바로 입니다. 이것은 무엇을 할까요? 우선 문서에서 v8::isolate가 V8 엔진의 인스턴스라는 것을 알고 있습니다. 즉(힙 관리자, 가비지 컬렉터 등을 포함하는 V8 런타임의 독립적인 복사본) 스크립트를 실행하기에는 충분하지 않습니다. blink::MainThreadIsolate()는 다음과 같이 정의되어 있으며,
 이는 blink/renderer/platform/bindings/v8_per_isolate_data.cc 파일에 있고, V8PerIsolateData::MainThreadIsolate()는 다음과 같이 생겼습니다.
. , V8PerIsolateData는 짐작하셨겠지만 bindings/core/v8/V8PerIsolateData.h에 있는 클래스이며 대략 다음과 같이 생겼습니다. blah blah blah 세부 사항을 추가할 것. 그런 다음 kDisableThreadedCompositing(내일 플래그가 실제로 어떻게 생겼는지 추가할 것)을 명령줄에 전달했는지 확인합니다.. 전달하지 않은 경우 컴포지터 스레드를 시작합니다. 그게 뭘까요? https://frontendmasters.com/courses/web-performance/the-compositor-thread/ 에서 인용하자면, "유일한 작업은 비트맵을 그리고, 비트맵을 GPU로 보내고, 화면에 표시하는 것"인 스레드입니다. 그런 다음 스킴(scheme)이라고 부르는 것을 등록합니다.. 기본적으로 소스 보기(view source)를 할 때 "view-source:website"처럼 URL 앞에 무언가가 붙는 것을 기억하시나요? 네, 그것은 실제로 렌더러가 처리합니다. 그리고 이런 스킴이 더 많이 있습니다. 등록한다는 것이 무엇을 의미하는지요? 아직 확실하지 않지만, "사용자가 이런 일을 하면 처리해 주길 원한다"는 것을 알리는 것이라고 생각합니다. 어쨌든 다음과 같이 보입니다.. 더 많은 세부 사항을 추가할 것. 좋아, 이제 마지막 코드 조각도 자세히 설명할 때입니다. 
. blah blah 그리고 마침내 renderer_main의 마지막 부분에 도달했습니다. 세부 사항으로 업데이트할 것

=====================================================================
동적 분석 파트이제 동적으로 디버그하려면, 저처럼 초보라면 cmd.exe에서 windbg를 다음과 같이 실행하고 싶을 것입니다: 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을 설정하세요. 그런 다음 content!content::RendererMain에 bp를 걸고 5~6번 정도 실행되도록 두세요. (비디오에서 4를 참조하도록 수정하세요. 그게 더 명확하니까요.) 그 후에는 에 도달하게 됩니다.







--no-sandbox
chrome_main이제 그것이 무엇을 하는지 봅시다. binja에서 chromium.dll을 엽니다.
https://blogs.igalia.com/jaragunde/files/2019/03/chrome-init-sequence.png 의 이 그림을 기반으로 binja에서 무엇을 찾아야 하는지 대략적인 아이디어가 있습니다.
이것이 binja에서 그래프가 보이는 모습입니다.
.
더 잘 추적하려면 ChromeMain이라는 함수를 찾으세요. 이것이 chrome의 메인(일명 핵심) 로직입니다. 그 안에서 chrome.dll이 시작 시 실행되는 앞서 언급한 단계들을 볼 수 있습니다.
여기서 먼저 sub_180001420, sub_180017020, sub_180017020과 같은 일부 함수를 호출하여 chrome이 어떻게 설치/컴파일되었는지에 대한 세부 사항을 확인하는 것을 볼 수 있습니다. 소스 코드를 사용하여 그 속성을 추적할 수 있습니다. 첫 번째 함수 sub_180001420는 UmaHistogramEnumeration에 해당하고, 다음 sub_180017020은 InitializeFromPrimaryModule이며, 세 번째이자 마지막 sub_180017020은 chrome_main_delegate입니다.

/src/content/app/content_main.cc