概述
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<CNewDataNoThrow>::`vftable'
回到文章最初的ICorJitInfo,那个表中第一个值是0x34,最终定位到getMethodName,第二个0x3C又指向什么方法呢?你自己跟一下吧,呵呵。
下次,会真正跟一些CLR内部的东西,那些被标记上for internal use的玩意儿。以上分析基于xp sp2 +.net framework 2.0 简体中文版。
最后
以上就是震动天空为你收集整理的CLR内核随记(1)的全部内容,希望文章能够帮你解决CLR内核随记(1)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复