星期一, 九月 15, 2008

FlyTreeView V4.3.1.43破解手记


   官方网址:http://www.9rays.net/
    未破解前,会有45天的试用期限制。超其以后运行时会有异常:“9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation period has

expired.”
    破解工具:ildasm,ilasm,StrongNameRemove20, UEdit, Reflector(个人习惯使用,这个随便了) [注] ildasm[vs2003版]可以到看雪下载

修改版,原版的有限制
    这个DLL的破解的关键是让它永不过期或者修改一个足够大的时间值让我们使用就可以了,网上有破解的就是直接修改它的过期期限,使用

用户可以使用的期限加长,达到破解。在本例中,我使用的是直接把过期异常干掉,使得永不过期。

  1. 用ildasm打开NineRays.WebControls.FlyTreeView.dll,转存为aaa.il;

  2. 找到用UEdit打开aaa.il,并找到"has expired",来到如下的代码处理
// 为了便于分析,我把reflactor的反编译代码贴出来
public FlyTreeView()
{
    this.NodeEvents = new List();
    DateTime maxValue = DateTime.MaxValue;
   
    // 当然这个异常我们也可以去掉,但在本次过程中,我们主要去掉下面那个过期的异常部分
    try
    {
        maxValue = File.GetLastWriteTime(base.GetType().Assembly.Location);
    }
    catch
    {
        throw new Exception("Unknown TRIAL version error has occurred.";
    }

    // 我们最关心的是过期的异常,所以这个异常是关键部分
    if (maxValue < DateTime.Now.AddDays(-45))
    {
        throw new Exception("9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation period has expired.";
    }
    this._flyControlCommon = new FlyControlCommon(this, this.ViewState, this.Context);
    this._dataBindings = new FlyNodeBindingCollection();
    this._nodes = new FlyTreeNodeCollection(this);
    this._nodeTypes = new FlyNodeTypeCollection();
    this.ShadowNodes = new FlyTreeNodeCollection(this);
}

看了反编译的代码,简单一点儿,我们只要把
    if (maxValue < DateTime.Now.AddDays(-45))
    {
        throw new Exception("9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation period has expired.";
    }
这段代码干掉就可以了啊?当然,我们也可以把
    try
    {
        maxValue = File.GetLastWriteTime(base.GetType().Assembly.Location);
    }
    catch
    {
        throw new Exception("Unknown TRIAL version error has occurred.";
    }
这段代码也干掉,呵呵。
那我们就开始吧,
下面我们结合反编译代码对下面的程序进行分析,并去掉关键的异常代码部分

// 源代码如下
  .method public hidebysig specialname rtspecialname
          instance void  .ctor() cil managed
  {
    // 代码大小       173 (0xad)
    .maxstack  5
    .locals init (valuetype [mscorlib]System.DateTime V_0,
             string V_1,
             valuetype [mscorlib]System.DateTime V_2)

    //***     this.NodeEvents = new List(); 对应的IL代码开始   ***//
    IL_0000:  ldarg.0
    IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1
NineRays.WebControls.FlyTreeNodeEventArgs>::.ctor()
    IL_0006:  stfld      class [mscorlib]System.Collections.Generic.List`1

NineRays.WebControls.FlyTreeView::NodeEvents
    //***     this.NodeEvents = new List(); 对应的IL代码结束   ***//

//**************** 获取最大的时间值,并保存到本地变量maxValue里对应IL就是V_0
    //***     DateTime maxValue = DateTime.MaxValue; 对应的IL代码开始   ***//
    IL_000b:  ldarg.0
    IL_000c:  call       instance void [System.Web]System.Web.UI.WebControls.HierarchicalDataBoundControl::.ctor()
    IL_0011:  ldsfld     valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::MaxValue
    IL_0016:  stloc.0
    //***     DateTime maxValue = DateTime.MaxValue; 对应的IL代码结束   ***//




// ************************************   第一个异常的代码对应 开始  ***********************************//
//************ 取文件的最后修改时间值,正常的话保存到本地变量maxValue里对应IL就是V_0,如果错误,则异常
*  reflector反编译代码
*    try
*    {
*        maxValue = File.GetLastWriteTime(base.GetType().Assembly.Location);
*    }
*    catch
*    {
*        throw new Exception("Unknown TRIAL version error has occurred.";
*    }

* 对应的IL 代码
    .try
    {
      IL_0017:  ldarg.0
      IL_0018:  call       instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
      IL_001d:  callvirt   instance class [mscorlib]System.Reflection.Assembly [mscorlib]System.Type::get_Assembly()
      IL_0022:  callvirt   instance string [mscorlib]System.Reflection.Assembly::get_Location()
      IL_0027:  stloc.1
      IL_0028:  ldloc.1
      IL_0029:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.IO.File::GetLastWriteTime(string)
      IL_002e:  stloc.0
      IL_002f:  leave.s    IL_003d

    }  // end .try
    catch [mscorlib]System.Object
    {
      IL_0031:  pop
      IL_0032:  ldstr      "Unknown TRIAL version error has occurred."
      IL_0037:  newobj     instance void [mscorlib]System.Exception::.ctor(string)
      IL_003c:  throw

    }  // end handler
// ************************************   第一个异常的代码对应 结束  ***********************************//



// *************************************  第二个异常的代码对应 开始  ***********************************//
//************ maxValue跟当前时间-45天相比较,如果在试用期内,则正常,不在试用期内,则异常
*  reflector反编译代码
*    if (maxValue < DateTime.Now.AddDays(-45))
*    {
*        throw new Exception("9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation period has expired.";
*    }

* IL 代码开始
    // ******** 这里开始是判断当前时间与文件创建时间的比较,如果在试用期内,则正常试用,否则则抛出过期的异常
    IL_003d:  ldloc.0
    IL_003e:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
    IL_0043:  stloc.2
    IL_0044:  ldloca.s   V_2
    IL_0046:  ldc.r8     -45.
    IL_004f:  call       instance valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::AddDays(float64)
    IL_0054:  call       bool [mscorlib]System.DateTime:p_LessThan(valuetype [mscorlib]System.DateTime,
                                                                     valuetype [mscorlib]System.DateTime)
    IL_0059:  brfalse.s  IL_0066        // 如果没超出试用期,则跳转,否则抛异常
// 我们的思路,不管是否超出试用期都不让它抛出异常,那么我们最简单的方法,就是把抛异常的代码段干掉
// 那我们还等什么呀,直接把下面的关键部分代码注释掉不就行了吗,呵呵:) 是不是很Easy的啊
//*************************   关键位置开始  ^_^  *******************************************************//
    //IL_005b:  ldstr      "9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation p"
    //+ "eriod has expired."                // 查找到的位置***************************
    //IL_0060:  newobj     instance void [mscorlib]System.Exception::.ctor(string)
    //IL_0065:  throw
//*************************   关键位置结束 ^_^   *******************************************************//



// *************************************  其他类域的初始化代码 开始  ***********************************//
*  reflector反编译代码
*    this._flyControlCommon = new FlyControlCommon(this, this.ViewState, this.Context);
*    this._dataBindings = new FlyNodeBindingCollection();
*    this._nodes = new FlyTreeNodeCollection(this);
*    this._nodeTypes = new FlyNodeTypeCollection();
*    this.ShadowNodes = new FlyTreeNodeCollection(this);

* IL 代码开始
    IL_0066:  ldarg.0
    IL_0067:  ldarg.0
    IL_0068:  ldarg.0
    IL_0069:  callvirt   instance class [System.Web]System.Web.UI.StateBag [System.Web]System.Web.UI.Control::get_ViewState()
    IL_006e:  ldarg.0
    IL_006f:  callvirt   instance class [System.Web]System.Web.HttpContext [System.Web]System.Web.UI.Control::get_Context()
    IL_0074:  newobj     instance void class NineRays.WebControls.FlyControlCommon`1
NineRays.WebControls.FlyTreeView>::.ctor(!0,
                                                                                                                              

      class [System.Web]System.Web.UI.StateBag,
                                                                                                                              

      class [System.Web]System.Web.HttpContext)
    IL_0079:  stfld      class NineRays.WebControls.FlyControlCommon`1

NineRays.WebControls.FlyTreeView::_flyControlCommon
    IL_007e:  ldarg.0
    IL_007f:  newobj     instance void NineRays.WebControls.FlyNodeBindingCollection::.ctor()
    IL_0084:  stfld      class NineRays.WebControls.FlyNodeBindingCollection NineRays.WebControls.FlyTreeView::_dataBindings
    IL_0089:  ldarg.0
    IL_008a:  ldarg.0
    IL_008b:  newobj     instance void NineRays.WebControls.FlyTreeNodeCollection::.ctor(object)
    IL_0090:  stfld      class NineRays.WebControls.FlyTreeNodeCollection NineRays.WebControls.FlyTreeView::_nodes
    IL_0095:  ldarg.0
    IL_0096:  newobj     instance void NineRays.WebControls.FlyNodeTypeCollection::.ctor()
    IL_009b:  stfld      class NineRays.WebControls.FlyNodeTypeCollection NineRays.WebControls.FlyTreeView::_nodeTypes
    IL_00a0:  ldarg.0
    IL_00a1:  ldarg.0
    IL_00a2:  newobj     instance void NineRays.WebControls.FlyTreeNodeCollection::.ctor(object)
    IL_00a7:  stfld      class NineRays.WebControls.FlyTreeNodeCollection NineRays.WebControls.FlyTreeView::ShadowNodes

// *************************************  其他类域的初始化代码 结束  ***********************************//

    IL_00ac:  ret
  } // end of method FlyTreeView::.ctor

  3. 别忘了看一下文件开头有没有加publickey,还真的有呀,我这里找到如下的代码
  .publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00   // .$..............
                00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00   // .$..RSA1........
                EB FD B1 7F 49 35 9E C0 95 39 EE 11 CD D2 30 A3   // ....I5...9....0.
                72 D3 A0 72 DD 10 42 86 EA 59 60 AB 5F C4 3F 7F   // r..r..B..Y`._.?.
                09 A7 6C 5E FA D0 54 FF B9 B7 12 C6 13 DD 6D C3   // ..l^..T.......m.
                D3 5B E4 90 76 EC CC 92 CD B0 7E 9B 22 A5 A4 71   // .[..v.....~."..q
                D0 EA 1A EE 0D 6B BE 82 55 D6 7E B2 7F B1 32 DB   // .....k..U.~...2.
                50 5B F7 50 07 19 91 59 22 BF FB 82 A9 0B F7 DE   // P[.P...Y".......
                87 36 F9 6C 19 CA D9 63 55 63 78 44 C6 33 6C 55   // .6.l...cUcxD.3lU
                39 00 7B 0A 89 8E C2 C5 8E 4A 52 C2 8E 23 37 B3 ) // 9.{......JR..#7.
  .hash algorithm 0x00008004

  还在想什么呢?直接删除呀,嘿嘿

  4. 到现在破解就完工了,可以编译新的程序了。ilasm /dll /resource=aaa.res aaa.il,得到的aaa.dll即为完美破解版了。

  顺便说一句,使用的时候,最好先安装原版,再把破解版覆盖,就好了
  如果感觉好用,请支持正版

hook compileMethod的几种常见形式


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 */                )
     一般的用法是通过该函数的第二个参数COFINFO_METHOD_INFO取得代码的IL和大小。但其实还可以更加深入,注意第一个传入参数很有意思,指向了ICorJitInfo接口。该接口定义如下:
代码:
/*********************************************************************************  * a ICorJitInfo is the main interface that the JIT uses to call back to the EE and  *      get information  *********************************************************************************/ class ICorJitInfo : public virtual ICorDynamicInfo {//省略}
     这说明ICorJitInfo继承了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 {//省略}
     这说明ICorDynamicInfo又继承了ICorStaticInfo。接着来,继续看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
     这一次牛了,ICorStaticInfo继承了8个接口的方法,其中第一个为ICorMethodInfo。该接口定义了许多与方法相关的函数,如果能调用它,在hook中是非常爽的。那可不可以调用呢?当然可以,compileMethod的第一个参数就是我们需要的。
     比如我们需要取得当前hook方法的方法名,可以调用ICorMethodInfo中的如下方法:
代码:
       virtual const char* __stdcall getMethodName (                CORINFO_METHOD_HANDLE          ftn,           /* IN */                const char                   **moduleName     /* OUT */                ) = 0;
     这里需要传入CORINFO_METHOD_HANDLE这个参数,该参数是.NET内核中表示方法的核心结构,在sscli中也未公开。不过它的值是可以取得的,从compileMethod第二个参数CORINFO_METHOD_INFO结构的第一项中便可以取得:
代码:
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; };
     同样,在调用ICorModuleInfor接口中的许多方法时,也需要传入CORINFO_MODULE_HANDLE作为参数,同样可以从CORINFO_METHOD_INFO结构中获得该值。比如调用findClass函数,以取得CORINFO_CLASS_HANDLE,定义如下:
代码:
       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;
     其中出现了CORINFO_CONTEXT_HANDLE。该结构也非常容易取得,具体参考sscli,就不详述了。
     具体在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' ...//下略
     这里又涉及到VC编译器对类的vftable和vbtable在内存中的布局问题了,最先两项是类自身定义的虚方法表和虚基址表,相关资料请自己查阅。我们跟进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
       这些地址在静态编译时就已经确定了,因此如果用IDA反编译mscorwks.dll,同样会得到这些值。这便是最新的.NET框架内核中ICorJitInfo定义的方法。和sscli对比下(在corjit.h文件中),完全一样!(也可以再对比其它关键接口的方法,应该也是一样,我没有详细对比了。)
     这样,我们就得到如下的结论:.NET内核框架从2.0开始,内核变化不大,包括sscli的内核代码,这些可以从内部函数的定义看出来;通过hook compileMethod,可以得到ICorJitInfo等关键接口,并调用其中的许多内部方法;具体编写时,可以在VS中引入sscli的corinfo.h、corjit.h和corhdr.h等文件,之后便可以直接调用。因此,通过compileMethod,我们可以做的事很多很多,远不限于仅获得某个方法的IL。

Net 2.0 的泛型小结

微软在.Net FrameWork 2.0中,引入了范型,相比.Net FrameWork 1.1中的三个集合类。
范型具有类型安全、无需GC的优点,对值类型无需进行性能损失很大的装箱与拆箱操作。
主要看 System.Collections.Generic 命名空间,略作总结:
ArrayList --> List
Hashtable --> Dictionary
SortedList --> SortedDictionary
Stack --> Stack
Queue --> Queue
LinkedList 无对应类

使用Reflactor反编译.Net程序的经验

相信大多数.Net程序员都有使用Reflactor的经历。无论出于什么目的,当用Reflactor反编译托管程序后,还想对其代码加以修改,那么本文所列举的可能是一份有用的参考。

用Reflactor的FileGenerator插件反编译代码后可以得到包括项目文件的源代码,但代码中存在各种问题,一般无法一次编译通过,以下将详谈这些问题:
枚举问题

为了代码可读,可能需要花点时间查阅metadata把int值修改回枚举值,尤其是想利用窗体设计器的,VS2008可能还不理解int值。
属性问题

比如一个叫Names的属性被反编译后,可能还原为的set_Names(names),get_Names()方法,逐个替换可能很慢,可采用正则表达式整体替换。对于set_Xxx(xxx)方法,可替换set_{[a-z]*}\(为\1 = (对于get_Xxx()方法,可替换get_{[a-z]*}\(\)为\1然后,再修复个别被误换的方法。
委托和回调函数问题

一般会被还原为add_Xxx(MethodsName)方法,需要改为 += MethodsName
资源问题

需要使用.Net Framework SDK 下的 resgen.exe 工具,反编译嵌入资源文件*.resources为*.resx文件,语法为:ResGen.exe *.resources *.resx,然后将*.resx包含入项目,就会自动和同名的窗体文件*.cs关联,如果没有关联可采用先排除再添加大法,一一搞定。
命名空间问题

如果需要切换到IDE的窗体设计器,而不出错,则还需要在*.cs中添加比如System.Windows.Forms的命名空间前缀。
窗体设计器识别问题

需要把以下代码ComponentResourceManager manager = new ComponentResourceManager(typeof(ClassName));替换为System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ClassName));窗体设计器才能正常识别。