共兩篇,第一篇和殼本身的過程關系不大,主要是涉及了虛方法的內容,第二篇則詳細調試了CLISecure的保護原理。在我的blog上名為CLR內核調試隨記,因為是隨記,所以文章中可能有不全面甚至錯誤的地方。找到錯誤的朋友請與偶聯系。性急的兄弟請直接看第二篇。
本文章所調試殼為CLISecure,下載地址為http://www.secureteam.net/,最新版本為3.0,而我所調試的版本為2.5,所以可能與最新的殼有所不同。
CLR內核隨記(1)
把平時看到的一些東西記下來,沒准以後用的上。
本文利用調試跟蹤CLR中接口virtual方法的定位。為什麼呢?好玩兒而已。
這是在某殼掛鉤JIT的代碼,有些東西還是很有意思(總是覺得這些殼作者知道很多CLR的內部結構,估計微軟對他們部分開源了):
.text:10002A4C mov eax, [ebp+ICorJitInfo]
.text:10002A4F mov ecx, [eax+4]
//此時ecx==.text:79E97C14 const CEEJitInfo::`vbtable’{for `ICorJitInfo’},第一個雙字是什麼?第一個雙字指向
.text:79E97B14 dd offset [thunk]:CEEInfo::getHelperName`vtordisp{4294967292,52}’ (CorInfoHelpFunc)
到這裡,來看看CEEJitInfo::’vbtable’在內存中的表示:
79E97C14 >FC FF FF FF 34 00 00 00 3C 00 00 00 44 00 00 00
79E97C24 4C 00 00 00 54 00 00 00 5C 00 00 00 64 00 00 00
79E97C34 6C 00 00 00 74 00 00 00 80 00 00 00 FC FF FF FF
其實這些值是在編譯時確定的,因為CLR本身部分用C++寫成,也就是C++中的vtable在編譯時確定,mscorwks.dll中的代碼如下:
.text:79E97C14 const CEEJitInfo::`vbtable’{for `ICorJitInfo’} dd 0FFFFFFFCh, 34h, 3Ch, 44h, 4Ch, 54h, 5Ch, 64h, 6Ch
.text:79E97C14 dd 74h, 80h
FCFFFFFF是what?誰知道,也許是一個vtable的開始標志,因為在第三行最後又見到一個同樣的雙字。計算兩個FCFFFFFF之間的雙字值,可以先猜測這個vtable含有10個方法。不過sscli中對應的代碼卻多出了一兩個方法,這裡暫且不管,下面主要看怎麼定位這些vtable的。
.text:10002A52 mov edx, [ecx+4]
.text:10002A55 mov eax, [ebp+ICorJitInfo]
.text:10002A58 mov ecx, [eax+4]
.text:10002A5B mov eax, [ecx+4]
.text:10002A5E mov ecx, [ebp+ICorJitInfo]
.text:10002A61 lea eax, [ecx+eax+4]
.text:10002A65 mov ecx, [ebp+ICorJitInfo]
.text:10002A68 mov edx, [ecx+edx+4]
在lea eax這句執行完後,eax指向內存如下
0013EA18 78 7B E9 79 00 00 00 00 4C 7B E9 79 00 00 00 00
0013EA28 A8 7A E9 79 00 00 00 00 84 7A E9 79 00 00 00 00
總覺得後面那句mov edx,[ecx+edx+4]重復了,直接mov edx,[eax]不就可以了嗎?這時[eax]的值是79E97B78指向什麼?它指向了下面一個方法:
.text:79E97B78 const CEEJitInfo::`vftable’{for `ICorMethodInfo’} dd offset [thunk]:CEEInfo::getMethodName`vtordisp{4294967292,52}’ (CORINFO_METHOD_STRUCT_ *,char const * *)
也就是說,在ICorJitInfo的 vtable表裡有getMethodName方法。可sscli的代碼中並沒有getMethodName方法啊。如果說,vtable是按順序生成的,那麼eax+ecx+4這個指令代表getMethodName方法排名應該還較靠前,而sscli中的代碼為。
class ICorJitInfo : public virtual ICorDynamicInfo
{
public:
// return memory manager that the JIT can use to allocate a regular memory
virtual IEEMemoryManager* __stdcall getMemoryManager() = 0;
// get a block of memory for the code, readonly data, and read-write data
virtual void __stdcall allocMem (
…
}
這時,我們想到了繼承。ICorJitInfo繼承了ICorDynamicInfo,於是來到後者的代碼處:
class ICorDynamicInfo : public virtual ICorStaticInfo
這裡,ICorDynamicInfo中仍然沒有getMethodName的方法,於是再次順著繼承走下去:
class ICorStaticInfo : public virtual ICorMethodInfo, public virtual ICorModuleInfo,
public virtual ICorClassInfo, public virtual ICorFieldInfo,
public virtual ICorDebugInfo, public virtual ICorArgInfo,
public virtual ICorLinkInfo, public virtual ICorErrorInfo
這裡,ICorStaticInfo第一個繼承的就是ICorMethodInfo,終於,我們找到了getMethodName方法:
class ICorMethodInfo
{
public:
// this function is for debugging only. It returns the method name
// and if ‘moduleName’ is non-null, it sets it to something that will
// says which method (a class name, or a module name)
virtual const char* __stdcall getMethodName (
CORINFO_METHOD_HANDLE ftn, /* IN */
const char **moduleName /* OUT */
) = 0;
// this function is for debugging only. It returns a value that
// is will always be the same for a given method. It is used
// to implement the ‘jitRange’ functionality
virtual unsigned __stdcall getMethodHash (
CORINFO_METHOD_HANDLE ftn /* IN */
) = 0;
到這裡,一個簡單的virtual方法定位才算跟完。其實,這和CLR已經沒有多大關系了,更多的是C++編譯器的工作,它是怎麼分配類和虛方法的內存空間,當然,還要結合在運行時的動態填充。
CLR中提供了多少個這種vtable呢?至少有以下這麼多:
.text:79E977EC mov dword ptr [esi+4], offset const CEEJitInfo::`vbtable’{for `CEEInfo’}
.text:79E977F3 mov dword ptr [esi+10h], offset const CEEJitInfo::`vbtable’{for `ICorJitInfo’}
.text:79E977FA mov dword ptr [esi+88h], offset const CEEInfo::`vbtable’{for `ICorStaticInfo’}
.text:79E97804 mov dword ptr [esi+94h], offset const CEEJitInfo::`vbtable’{for `ICorDynamicInfo’}
.text:79E9780E mov dword ptr [esi+44h], offset const ICorStaticInfo::`vftable’{for `ICorMethodInfo’}
.text:79E97815 mov dword ptr [esi+4Ch], offset const ICorModuleInfo::`vftable’
.text:79E9781C mov dword ptr [esi+54h], offset const ICorCompileInfo::`vftable’{for `ICorClassInfo’}
.text:79E97823 mov dword ptr [esi+5Ch], offset const ICorJitInfo::`vftable’{for `ICorFieldInfo’}
.text:79E9782A mov dword ptr [esi+64h], offset const ICorDebugInfo::`vftable’
.text:79E97831 mov dword ptr [esi+6Ch], offset const ICorCompileInfo::`vftable’{for `ICorArgInfo’}
.text:79E97838 mov dword ptr [esi+74h], offset const CHashTableAndData::`vftable’
回到文章最初的ICorJitInfo,那個表中第一個值是0×34,最終定位到getMethodName,第二個0×3C又指向什麼方法呢?你自己跟一下吧,呵呵。
下次,會真正跟一些CLR內部的東西,那些被標記上for internal use的玩意兒。以上分析基於xp sp2 +.net framework 2.0 簡體中文版。