CLR引擎初始化分析
在以前的一篇叫做<深入追蹤Exe加載過(guò)程>的文章里面,從clix的launch函數(shù)開(kāi)始,再到CorExeMain2函數(shù),在CorExeMain2里面有一個(gè)叫做CoInitializeEE的函數(shù):
result = CoInitializeEE(COINITEE_DEFAULT | COINITEE_MAIN)
一路找到EnsureEEStarted,這個(gè)是確保EE啟動(dòng)的方法。在這個(gè)方法里面,首先做一系列的狀態(tài)判斷,然后判斷ee (Execute Engine)是否已經(jīng)啟動(dòng)。如果沒(méi)有啟動(dòng),就啟動(dòng)EE:
if (!g_fEEStarted && !g_fEEInit && SUCCEEDED (g_EEStartupStatus))
{
EEStartup(flags);
bStarted=g_fEEStarted;
hr = g_EEStartupStatus;
}
else
{
hr = g_EEStartupStatus;
if (SUCCEEDED(g_EEStartupStatus))
{
hr = S_FALSE;
}
}
這里首先做了幾個(gè)狀態(tài)檢查:
if (!g_fEEStarted && !g_fEEInit && SUCCEEDED (g_EEStartupStatus))
需要三個(gè)條件同時(shí)滿足的時(shí)候,才會(huì)執(zhí)行EEStartup函數(shù)來(lái)啟動(dòng)EE:
1. EE沒(méi)有啟動(dòng)。
2. EE沒(méi)有被正在啟動(dòng),當(dāng)然,在執(zhí)行這個(gè)函數(shù)之前是設(shè)置了Lock的。
3. !EEStartup在上一次啟動(dòng)的時(shí)候失敗。
殺入EEStartup,找到最關(guān)鍵的幾行代碼:
PAL_TRY
{
EEStartupHelper(fFlags);
}
PAL_EXCEPT_FILTER (FilterStartupException, NULL)
{
// The filter should have set g_EEStartupStatus to a failure HRESULT.
_ASSERTE(FAILED(g_EEStartupStatus));
}
PAL_ENDTRY
這里使用了PAL_TRY來(lái)確保這個(gè)這個(gè)過(guò)程的acid性質(zhì)。現(xiàn)在這篇文章的主角登場(chǎng)了:
EEStartupHelper(fFlags);
首先說(shuō)一下這個(gè)fFlags類型,這個(gè)是在framework的cor.h里面的一個(gè)枚舉類型函數(shù):
typedef enum tagCOINITEE
{
// Default initialization mode.
COINITEE_DEFAULT = 0x0,
// Initialization mode for loading DLL.
COINITEE_DLL = 0x1,
// Initialize prior to entering the main routine
COINITEE_MAIN = 0x2
} COINITIEE;
這里的fFlags=COINITEE_DEFAULT | COINITEE_MAIN
下面,來(lái)具體敘說(shuō)下CLR的EE在啟動(dòng)的時(shí)候所做的事情,就一段一段的說(shuō)吧,比較長(zhǎng),基本上對(duì)于每句都加上了注釋:
g_fEEInit = true;
//set Ctrl event Control handler
::SetConsoleCtrlHandler(DbgCtrlCHandler, TRUE/*add*/);
// Initialize event tracing early so we can trace CLR startup time events.
InitializeEventTracing();
ETWTraceStartup trace(ETW_TYPE_STARTUP_EESTARTUP);
TIMELINE_AUTO(STARTUP, "EEStartup");
首先置g_fEEInit為true,這個(gè)是標(biāo)識(shí)EE是否在啟動(dòng)的全局變量,每次只運(yùn)行一個(gè)EE的啟動(dòng),每次EE啟動(dòng)的時(shí)候,會(huì)lock上相關(guān)的資源并且檢查g_fEEInit的值。
SetConsoleCtrlHandler方法設(shè)置了響應(yīng)Ctrl鍵的方法。InitializeEventTracing方法調(diào)用的挺早,這個(gè)是為了盡早啟動(dòng)事件跟蹤機(jī)制,這樣就能夠跟蹤CLR在啟動(dòng)的時(shí)候的相關(guān)的事件。所以在設(shè)置了對(duì)Ctrl的響應(yīng)模式以后立馬就調(diào)用了這個(gè)方法。
而對(duì)于ETWTraceStartup trace(ETW_TYPE_STARTUP_EESTARTUP),這記錄了CLR這個(gè)啟動(dòng)的這個(gè)事件。ETW_TYPE_STARTUP_EESTARTUP是eventtrace.h中記錄的一系列不同的事件類型。在每次運(yùn)行到某個(gè)特定的事件的時(shí)候,就trace下來(lái)。Eventtrace,實(shí)現(xiàn)了windows操作系統(tǒng)的事件記錄功能。如果你打開(kāi)響應(yīng)的事件記錄級(jí)別的話。在eventtrace中,還定義了許多關(guān)于GC,ThreadPool,Exceptions,Monitor,Start Up Event,Fusion building log響應(yīng)的事件。有興趣的可以去看看。最后的一行,是一個(gè)簡(jiǎn)單的定時(shí)的手動(dòng)的事件日志記錄,這兩行放在了一起。這個(gè)主要是用于profiling的用途。
接下來(lái)的第二段啟動(dòng)代碼:
IPCFuncCallSource::DoThreadSafeCall();
//GSCookies (guard-stack cookies) for detecting buffer overruns
InitGSCookie();
Frame::Init();
// Initialize EEConfig
if (!g_pConfig)
{
IfFailGo(EEConfig::Setup());
initEEConfig = TRUE;
}
GetStartupInformation();
// get any configuration information from the registry
if (initEEConfig)
IfFailGo(g_pConfig->SetupConfiguration());
首先說(shuō)下IPCFuncCallSource是干嘛的,他的主要用途,是為了支持一個(gè)跨越進(jìn)程的函數(shù)之間的相互調(diào)用。如果在EE啟動(dòng)的時(shí)候,有一部分代碼需要被別的進(jìn)程hook到,就需要在這段代碼的前后都調(diào)用這樣的一個(gè)方法來(lái)制定可以safeCall的范圍。因此,在代碼的下面也就可以再看到這樣的相同的一行。這里調(diào)用這樣的一個(gè)方法,主要是為了給性能監(jiān)視程序,PerMon.exe一個(gè)機(jī)會(huì)來(lái)hook到ee的啟動(dòng)內(nèi)部來(lái)監(jiān)視相關(guān)的活動(dòng)
GSCookies是Guard-stack cookies的縮寫,啟動(dòng)檢測(cè)buffer overrun的功能。
然后是Frame的初始化。這里的初始化主要是基礎(chǔ)類的初始化,而EE需要的Frame主要是由JIT和Stub Manager生成放到Stack上面的。
再frame.h文件中,有一個(gè)關(guān)于Frame很清晰的架構(gòu)圖,可以參考一下。
接下來(lái),就是初始化EE的Config文件。主要是從注冊(cè)表里面獲取相關(guān)的一些配置信息,譬如是不是該Jit啊,是不是需要檢查一下GC啊,等等。
下面的幾行,也是和讀取和配置配置信息相關(guān)的,不多說(shuō)了。
Go on 第三波,還有很多呢,對(duì)了,聲明下,clr instense和一些調(diào)試信息,還有一些記錄到log文件里面的信息我都沒(méi)列出來(lái):
// SString
SString::Startup();
// Fusion
{
ETWTraceStartup traceFusion(ETW_TYPE_STARTUP_FUSIONINIT);
IfFailGoLog(InitializeFusion());
}
// A class to maintain a pool of available events.
//static EventStore s_EventStore;
InitEventStore();
if (g_pConfig != NULL)
{
IfFailGoLog(g_pConfig->sync());
}
// Initialize all our InterProcess Communications with COM+
IfFailGoLog(InitializeIPCManager());
首先是SString的初始化。String地球人都知道,SString是嘛玩意呢?
SString是EE中一個(gè)非常重要的東西。它的存在,主要是基于兩個(gè)目的的:首先是提供一個(gè)相對(duì)來(lái)說(shuō)針對(duì)所有的API來(lái)說(shuō)比較標(biāo)準(zhǔn)的string class,和一個(gè)易用的,性能相對(duì)來(lái)說(shuō)的比System.String更加底層的string實(shí)現(xiàn)類。另外一個(gè)功能,就是把string操作里面的所有不安全的功能都封裝了起來(lái)。
SString里面包含的數(shù)據(jù)的編碼方式,都是用的是Unicode。這種編碼方式可以在需要的時(shí)候轉(zhuǎn)換成為ASCII,UTF-8,或者是ANSI。
接下來(lái),就是啟動(dòng)Fusion,fusion中主要是包含了配件相關(guān)的綁定,策略相關(guān)檢查和全局配件緩存的實(shí)現(xiàn)代碼。
InitEventStore是一個(gè)實(shí)現(xiàn)了把現(xiàn)有的Event都存儲(chǔ)到一個(gè)pool里面的class。在來(lái)不及都處理的時(shí)候,形成一個(gè)隊(duì)列。
InitializeIPCManager實(shí)現(xiàn)了對(duì)進(jìn)程間通信的初始化,主要是COM+的使用。主要分配一個(gè)IPCManager的實(shí)現(xiàn),然后和相關(guān)的接口hook到一起,同時(shí)調(diào)用相關(guān)的函數(shù)來(lái)激活IPC相關(guān)的模塊。
Then,第四段:
// Set up the cor handle map. This map is used to load assemblies in
// memory instead of using the normal system load
PEImage::Startup();
// Implementation of the ICodeInfo interface
EECodeInfo::Init();
HardCodedMetaSig::Init();
Stub::Init();
// StubLinker with extensions for generating X86 code.
StubLinkerCPU::Init();
// weak_short, weak_long, strong; no pin
//Wraps handle table to implement various handle types (Strong, Weak, etc.)
Ref_Initialize();
// Initialize remoting
CRemotingServices::Initialize();
// Initialize contexts
Context::Initialize();
// Initialize ThreadManager,One-time initialization. Called during Dll initialization.
InitThreadManager();
// Event to synchronize EE shutdown.
g_pEEShutDownEvent = new CLREvent();
// If TRUE, initial state is signalled
g_pEEShutDownEvent->CreateManualEvent(FALSE);
// Initialize RWLocks(Read Write Lock)
CRWLock::ProcessInit();
// Initialize debugger manager
CCLRDebugManager::ProcessInit();
在CLR的啟動(dòng)過(guò)程中,每個(gè)地方都是重要的部分..
首先調(diào)用一個(gè)靜態(tài)方法,來(lái)set cor的handle map,這個(gè)map,是在需要把完成的PE文件格式整到成一個(gè)內(nèi)存image的時(shí)候用的,而不是system的load方法。
特別聲明一下:
PEImage是一個(gè)PE文件,它由“simulated loadlibrary”機(jī)制load,一個(gè)PEImage可以以兩種方式被加載,一種是FLAT方式,這種加載方式加載以后,其布局和在磁盤上面的布局相同。另外一種加載方式是MAPPED方式。這種方式對(duì)PE Sections的各個(gè)部分都map到了不同的虛擬地址上面。
也可以參考rick的最新的一篇博文也是講的這個(gè)問(wèn)題:
http://www.rzrgm.cn/rick/archive/2008/05/15/1199197.html
昨天剛發(fā)的,呵呵。
EECodeInfo::Init初始化了一個(gè)實(shí)現(xiàn)了ICodeInfo的類,這個(gè)interface的主要作用,是來(lái)讓ICodeManager來(lái)獲取正在執(zhí)行的Method的GCinfo,說(shuō)是比較有用的東西,不過(guò)我沒(méi)體會(huì)到…
接下來(lái)是和load的moudle相關(guān)的簽名的初始化,然后是stub的初始化。然后把stub鏈接到特定CPU的架構(gòu)。
然后是一些引用類型的初始化。不同的handle類型,強(qiáng)連接弱連接等,不包括pin。
接著初始化Remoting,以及Context上下文。
然后啟動(dòng)ThreadManager,啟動(dòng)線程管理。
然后是同步pEEShutDownEvent事件。
接著初始化Read Write Lock。Then,啟動(dòng)調(diào)試器響應(yīng)處理邏輯,啟動(dòng)位于后臺(tái)的Debugger Thread。
到這里,EE的啟動(dòng),就完成了一半的工作。恩,僅僅是一半。
對(duì)EE啟動(dòng)的過(guò)程中所做的事情認(rèn)識(shí)的越深刻了解的越詳細(xì),對(duì)了解EE的各個(gè)部分的功能就越容易,條理就越清晰,架構(gòu)就越熟悉。
只有完全熟悉了EE的啟動(dòng)過(guò)程中所做的事情,才能說(shuō):welcome to the EE World.
這個(gè)對(duì)以后通過(guò)編寫代碼來(lái)調(diào)用CLR的特定的功能也是非常有幫助的。同時(shí),對(duì)于Crack和加密解密的研究也是非常有幫助的。
To be continued..
5/16/2008 10:53:19 AM 首發(fā) http://sscli.cnblogs.com
posted on 2008-05-16 10:59 lbq1221119 閱讀(3491) 評(píng)論(10) 收藏 舉報(bào)
浙公網(wǎng)安備 33010602011771號(hào)