compileMethod算是.NET中的万能断点,对付一般的.NET加密壳均可采用hook compileMethod的方式得到IL代码和相关信息,保护层次较深的.NET壳除外。 我们自己在编写程序hook该方法时,一般有以下几种形式。 第一种形式是:my_compileMethod(既需要替换的方法)采用compileMethod的原型。这种形式代码漂亮,结构工整,编程方便,推荐使用。通常代码可以如下定义: int __stdcall my_compileMethod(ULONG_PTR classthis, ICorJitInfo *comp, CORINFO_METHOD_INFO *info, unsigned flags,BYTE **nativeEntry, ULONG *nativeSizeOfCode) { //你的代码 //调用原始的compileMethod int nRet = compileMethod(classthis, comp, info, flags, nativeEntry, nativeSizeOfCode); return nRet; } 上面的代码还有一个优点,就是可直接兼容x64位。 第二种形式:naked。既调用约定不采用__stdcall,而是用naked。代码如下: void __declspec(naked) mycompileMethod() { //你的代码 } 这种形式明显低级一些,许多工作要自己做,比如取CORINFO_METHOD_INFO *时,就要从堆栈中取值: __asm { pushad pushfd mov eax,DWORD PTR[esp+48] push eax pop pmethodinfo } 还有就是,直接使用x86汇编代码则无法兼容x64,需编写两套程序。当然,也不是一点好处没有,比如很多hook程序在开始喜欢保存全部的寄存器和状态,这里就可以使用pushad和pushfd了。 上面两种方法,在具体hook时,通常采取替换CILJit::'vtable'的第一项: dd offset CILJit::compileMethod的地址实现。该值可以通过getJit()函数获得。 第三种方法就更直接了,在JIT的路径中找到合适的位置后,直接Jmp。这种naked+jmp的方式,最底层,也最灵活,在对于某些hook位置较深的壳来说,这也是唯一的方法。不过代码编写时,工作量就比前两种要稍大一些了。 Btw:在编写.NET内核hook程序时,C++/CLI是不二之选。真得很爽! compileMethod方法在各类.NET内核的文章中出现频率相当高,因为它是JIT引擎工作的关键函数。其原型如下(参考sscli代码): 代码: CorJitResult __stdcall FJitCompiler::compileMethod ( ICorJitInfo* compHnd, /* IN */ CORINFO_METHOD_INFO* info, /* IN */ unsigned flags, /* IN */ BYTE ** entryAddress, /* OUT */ ULONG * nativeSizeOfCode /* OUT */ )代码: /********************************************************************************* * a ICorJitInfo is the main interface that the JIT uses to call back to the EE and * get information *********************************************************************************/ class ICorJitInfo : public virtual ICorDynamicInfo {//省略}代码: /***************************************************************************** * ICorDynamicInfo contains EE interface methods which return values that may * change from invocation to invocation. They cannot be embedded in persisted * data; they must be requeried each time the EE is run. *****************************************************************************/ class ICorDynamicInfo : public virtual ICorStaticInfo {//省略}代码: /***************************************************************************** * ICorStaticInfo contains EE interface methods which return values that are * constant from invocation to invocation. Thus they may be embedded in * persisted information like statically generated code. (This is of course * assuming that all code versions are identical each time.) *****************************************************************************/ 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比如我们需要取得当前hook方法的方法名,可以调用ICorMethodInfo中的如下方法: 代码: virtual const char* __stdcall getMethodName ( CORINFO_METHOD_HANDLE ftn, /* IN */ const char **moduleName /* OUT */ ) = 0;代码: struct CORINFO_METHOD_INFO { CORINFO_METHOD_HANDLE ftn; CORINFO_MODULE_HANDLE scope; BYTE * ILCode; unsigned ILCodeSize; unsigned short maxStack; unsigned short EHcount; CorInfoOptions options; CORINFO_SIG_INFO args; CORINFO_SIG_INFO locals; }; 代码: virtual CORINFO_CLASS_HANDLE __stdcall findClass ( CORINFO_MODULE_HANDLE module, /* IN */ unsigned metaTOK, /* IN */ CORINFO_CONTEXT_HANDLE context, /* IN */ CorInfoTokenKind tokenKind = CORINFO_TOKENKIND_Default /* IN */ ) = 0;具体在VS中编程时,可以添加corinfo.h和corjit.h,并在同一目录下添加corhdr.h,便可顺利编译通过。 最后还有一个问题,sscli毕竟是早期框架了,还是精简版,现在还能直接使用吗?不妨分析一下。随便运行一个.NET程序,用WinDbg调试并中断在compileMethod处,查看ICorJitInfo值所指的内存: 代码: 0012ea38 79f10654 mscorwks!CEEJitInfo::`vbtable' 0012ea3c 00174d18 0012ea40 00997850 0012ea44 00107210 0012ea48 00000000 0012ea4c 0018a610 0012ea50 00000000 0012ea54 00000000 0012ea58 00000000 0012ea5c 0012ea40 0012ea60 00000000 0012ea64 00000000 0012ea68 00000000 0012ea6c 79f105b8 mscorwks!CEEJitInfo::`vftable' 0012ea70 00000000 0012ea74 79f10584 mscorwks!CEEJitInfo::`vftable' 0012ea78 00000000 0012ea7c 79f104e0 mscorwks!CEEJitInfo::`vftable' 0012ea80 00000000 0012ea84 79f104bc mscorwks!CEEJitInfo::`vftable' 0012ea88 00000000 0012ea8c 79f104a4 mscorwks!CEEJitInfo::`vftable' 0012ea90 00000000 0012ea94 79f10498 mscorwks!CEEJitInfo::`vftable' 0012ea98 00000000 0012ea9c 79f10494 mscorwks!CEEJitInfo::`vftable' ...//下略代码: 79f10624 79f106ac mscorwks!CEEJitInfo::getMemoryManager 79f10628 79f11d82 mscorwks!CEEJitInfo::allocMem 79f1062c 79f11f39 mscorwks!CEEJitInfo::allocGCInfo 79f10630 7a12b8cb mscorwks!CEEJitInfo::getEHInfo 79f10634 7a12b6cf mscorwks!CEEJitInfo::yieldExecution 79f10638 79f16373 mscorwks!CEEJitInfo::setEHcount 79f1063c 79f16491 mscorwks!CEEJitInfo::setEHinfo 79f10640 7a12ed41 mscorwks!CEEJitInfo::logMsg 79f10644 7a27ffcc mscorwks!ZapperModule::doAssert 79f10648 7a12eeb6 mscorwks!CEEJitInfo::allocBBProfileBuffer 79f1064c 7a2c2fa1 mscorwks!MDInternalRO::ConvertTextSigToComSig 79f10650 79f0efee mscorwks!CEEJitInfo::isVerifyOnly 79f10654 fffffffc这样,我们就得到如下的结论:.NET内核框架从2.0开始,内核变化不大,包括sscli的内核代码,这些可以从内部函数的定义看出来;通过hook compileMethod,可以得到ICorJitInfo等关键接口,并调用其中的许多内部方法;具体编写时,可以在VS中引入sscli的corinfo.h、corjit.h和corhdr.h等文件,之后便可以直接调用。因此,通过compileMethod,我们可以做的事很多很多,远不限于仅获得某个方法的IL。 |
星期一, 九月 15, 2008
hook compileMethod的几种常见形式
订阅:
博文评论 (Atom)

没有评论:
发表评论