【文章标题】: 静态分析破Xenocode 2006 14天评估版
【文章作者】: dreaman
【作者邮箱】: dreaman_163@163.com
【作者主页】: http://dreaman.haolinju.net
【下载地址】: 看雪论坛《工具下载》有提到下载地址
【加壳方式】: 无壳
【操作平台】: .net 2.0
【软件介绍】: 一个类似Reflector的软件,不过不是免费的。
【作者声明】: 请参照看雪破文的惯例声明
--------------------------------------------------------------------------------
【详细过程】
  今天想起来要用一下Xenocode ,结果运行时被告知14天试用时间已到,非常感谢评估,请付费云云。
  有点不太爽,就研究了一下,结果发现超级简单!
  1、查找程序入口点
  首先用reflector打开看了看,一堆x07168889之类的类名与成员,试了一下动态调试,调试器一装
  载fox.exe就会直接运行,弹出过期信息,点确定后直接退出。
  于是拿出ildasm来,反出的IL文件来,用UltraEdit打开这个文本文件,搜索
  System.Windows.Forms.Application::Run
  (猜想windows form程序应该会用这个函数作主循环[D3D的好象不用这个,代之DoEvents])
  找到的地方上下文如下:
  
  .class private abstract auto ansi sealed beforefieldinit Xenocode.Fox.Gui.x867eb3246b182488
         extends [mscorlib]System.Object
  {
    .field public static int32 x7ee7e0aa39016337
    .method private hidebysig static void  xc447809891322395() cil managed
    {
      .entrypoint
      .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
      // 代码大小       30 (0x1e)
      .maxstack  8
      IL_0000:  call       bool Xenocode.Fox.Gui.x867eb3246b182488::_xc7c43f12e732db09()
      IL_0005:  brfalse.s  IL_0008
  
      IL_0007:  ret
  
      IL_0008:  call       void [System.Windows.Forms]System.Windows.Forms.Application::EnableVisualStyles()
      IL_000d:  ldc.i4.0
      IL_000e:  call       void [System.Windows.Forms]

System.Windows.Forms.Application::SetCompatibleTextRenderingDefault(bool)
      IL_0013:  newobj     instance void Xenocode.Fox.Gui.x3e4e23fadc83a77e::.ctor()
      IL_0018:  call       void [System.Windows.Forms]System.Windows.Forms.Application::Run(class 

[System.Windows.Forms]System.Windows.Forms.Form)
      IL_001d:  ret
    } // end of method x867eb3246b182488::xc447809891322395
  
  由此知此调用出现在类Xenocode.Fox.Gui.x867eb3246b182488中,换reflector继续。
  
  2、回到reflector,在名空间Xenocode.Fox.Gui下查到类x867eb3246b182488(别费劲记这个名啊,就找以2488结尾的,呵呵)
  ,选c#语言,瞧瞧
  
  internal static class x867eb3246b182488
  {
        // Methods
        static x867eb3246b182488();
        public static bool _xc7c43f12e732db09();
        [STAThread]
        private static void xc447809891322395();
  
        // Fields
        public static int x7ee7e0aa39016337;
  }
   
  嗯,很标准的windows form启动类。
  
  3、现在该看看主函数了
  
  [STAThread]
  private static void xc447809891322395()
  {
        if (!x867eb3246b182488._xc7c43f12e732db09())//关键判断
        {
              Application.EnableVisualStyles();
              Application.SetCompatibleTextRenderingDefault(false);
              Application.Run(new x3e4e23fadc83a77e());
        }
  }
  
  [STAThread]这个属性应该也可作为程序启动函数的一个标记(多线程的另说了)!
  可以看到启动循环前有个判断,呵呵,这就是关键了。
  
  4、看看程序正常运行前的这个函数
  
  public static bool _xc7c43f12e732db09()
  {
        if (x71a0073930f50f3f.xb30f5e1eb4806151("XFTRL" + x77fa6322561797a0.x9c1ceef9a932f141, new TimeSpan

(x867eb3246b182488.x7ee7e0aa39016337, 0, 0, 0, 0)))
        {
              MessageBox.Show(MessageTable.TrialExpired, x77fa6322561797a0.x6886d5a1867d55cb);
              return true;
        }
        return false;
  }
  
  还真是暴露的可以啊:)
  看看这个:
  new TimeSpan(x867eb3246b182488.x7ee7e0aa39016337, 0, 0, 0, 0)
  第一个参数是什么呢?点一下来到这里:
  public static int x7ee7e0aa39016337;
  在reflector与上面这个field对应的树叶上右键=>Analyzer,展开右边显示的Analyzer窗口中的
  树:
  Xenocode.Fox.Gui.x867eb3246b182488.x7ee7e0aa39016337 : Int32
    Used By
      Xenocode.Fox.Gui.x867eb3246b182488..cctor()
      Xenocode.Fox.Gui.x867eb3246b182488._xc7c43f12e732db09() : Boolean
  看到在两个地方引用了这个field,一个是构造函数,一个是刚才我们看到的判断函数,看一下构造
  里做了什么:
  
  static x867eb3246b182488()
  {
        x867eb3246b182488.x7ee7e0aa39016337 = 14;
  }
  
  14 ???!!!不是14天评估时间吗?!
  
  5、继续看看怎么判断时间过期的,来到x71a0073930f50f3f.xb30f5e1eb4806151
  
  public static bool xb30f5e1eb4806151(string x2d32ac7a8f1f8f5b, TimeSpan x5906905c888d3d98)
  {
        bool flag1;
        try
        {
              uint num1;
              DateTime time1;
              TimeSpan span1;
              uint num2;
              RegistryKey key1 = Registry.LocalMachine.CreateSubKey(@"Software\Oak Vale Networks\" + 

x2d32ac7a8f1f8f5b);//用到了参数1作注册表键名,从前面知参数1为
  XFTRL开头的串,回去看一下知道这个串是"XFTRL1.0"
              if ((((uint) flag1) - num1) >= 0)//用两个局部变量作减法,迷惑人的代码,一定成立,到0074了
              {
                    goto Label_0074;
  //后面有一段是逆指令流,所以请直接跳到0074处,然后从后往前看。
              }
              goto Label_005D;
        Label_0037:
              flag1 = true;//唉,过期了
              if (num2 < 0)//又来了个迷惑人的,这个东西根本就是0,不会跳的
              {
                    goto Label_007D;
              }
              return flag1;//报告,过期了,告诉他“让他掏钱买吧”
        Label_0052:
              if (span1 >= x5906905c888d3d98)//差 >= TimeSpan(14,0,0,0,0)
              {
                    goto Label_0037;//再往回跳,这便是过期之后的事了
              }
              return false;//报告,还没有过期!
        Label_005D:
              span1 = (TimeSpan) (DateTime.UtcNow - time1);//计算当前时间与记录时间之差
              goto Label_0052;//又往回跳,感觉跟逆指令流似的了!!!
        Label_0074:
              if (key1.GetValue(null) == null)//读注册表中的默认值,没有数据就去00BB
  了
              {
                    goto Label_00BB;
              }
        Label_007D:
              num1 = uint.Parse((string) key1.GetValue(null));//转成整数
              time1 = DateTime.FromOADate(((double) num1) / 10);//除10成OLE日期数据,
  转成.net的日期结构
              if ((num1 - ((uint) flag1)) >= 0)//又减了一次没用过的局部变量,继续迷惑人
              {
                    goto Label_005D;//往回跳了
              }
              goto Label_0074;//跳回去了,如果num1<0可就死循环了,不过从前面知,这是不可能的。
        Label_00BB:
              num2 = (uint) (DateTime.UtcNow.ToOADate() * 10);//当前时间转OLE格式并乘10
              key1.SetValue(null, num2.ToString());//写到默认值,前面是没有默认值时跳来的,
  所以这个分支是初始记录分支,第一次安装时把当时的时间记在注册表
              return false;
              if (num1 <= uint.MaxValue)
              {
                    goto Label_007D;
              }
              goto Label_005D;
        }
        catch (Exception)
        {
              flag1 = true;//如果出现异常,也认为是过期,假设代码被改错了,也算作过期了!
        }
        return flag1;
  }
  
  6、OK,到此已经非常清楚是如何过期的了,我把我的当前日期按上面的计算方法得到的注册表中的值
  后面加了一个0,也就是3887410,这个值表示过期时间为2964-5-1,不知道那时候还是不是国际劳动节
  啊。好了,我们来看一下注册表
  
  HKEY_LOCAL_MACHINE\SOFTWARE\Oak Vale Networks
  其下有两个子键:
  XCTRL4.1
  XFTRL1.0
  后面这个就是fox,前面那个呢,是装Xenocode时一并安装的PostBuild,将它们的默认值都改为
  3887410吧,可以用好多代了啊:)
  
  7、改完后看了一下fox.exe,发现没有签名,说明应该是可以crack的,再次拿出ILDasm来,运行,
  选上“视图”菜单=》显示字节,然后打开主入口函数的反汇编:
  
  .method private hidebysig static void  xc447809891322395() cil managed
  // SIG: 00 00 01
  {
    .entrypoint
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
    // 方法在 RVA 0x112d68 处开始
    // 代码大小       30 (0x1e)
    .maxstack  8
    IL_0000:  /* 28   | (06)001A21       */ call       bool Xenocode.Fox.Gui.x867eb3246b182488::_xc7c43f12e732db09()
    IL_0005:  /* 2C   | 01               */ brfalse.s  IL_0008
    IL_0007:  /* 2A   |                  */ ret
    IL_0008:  /* 28   | (0A)00047E       */ call       void [System.Windows.Forms]

System.Windows.Forms.Application::EnableVisualStyles()
    IL_000d:  /* 16   |                  */ ldc.i4.0
    IL_000e:  /* 28   | (0A)00047F       */ call       void [System.Windows.Forms]

System.Windows.Forms.Application::SetCompatibleTextRenderingDefault(bool)
    IL_0013:  /* 73   | (06)001848       */ newobj     instance void Xenocode.Fox.Gui.x3e4e23fadc83a77e::.ctor()
    IL_0018:  /* 28   | (0A)000480       */ call       void [System.Windows.Forms]

System.Windows.Forms.Application::Run(class [System.Windows.Forms]System.Windows.Forms.Form)
    IL_001d:  /* 2A   |                  */ ret
  } // end of method x867eb3246b182488::xc447809891322395
  
  这里写明了函数开始于RVA 0x112d68 , 我们要做的是把正常运行前的if()判断去掉,也就是
  
    IL_0000:  /* 28   | (06)001A21       */ call       bool Xenocode.Fox.Gui.x867eb3246b182488::_xc7c43f12e732db09()
    IL_0005:  /* 2C   | 01               */ brfalse.s  IL_0008
    IL_0007:  /* 2A   |                  */ ret
  
  这三行,直接nop掉,在.net中nop指令最好记了,它就是 ------- 00
  
  找出一个二进制编辑器,跳到0x112d68-0x1000=0x111d68处,用眼睛扫描一下,28 21 1A 00 06 (ILDasm里显示的操作数居然不是

内存顺序,晕)
  后面接着是 2C 01 2A , 将这些全部改为00 , 另存为UnFox.exe , 运行一下 , 成功!
  再把注册表里的数改成过期数据,比如改为0 , 再运行一下UnFox.exe , 呀 ,又成功了!
  呵呵 ,祝贺一下 (坏了!!!我本来想用Xenocode做什么来着???)
  
  
  
  
  
   
  
  
  
  
--------------------------------------------------------------------------------
【经验总结】
  1、用ILDasm反汇编完整文件,然后搜索System.Windows.Forms.Application::Run来发现程序
  入口点(其实搜索.entrypoint属性应该更准确了,还有STAThread对单线程程序也有效);
  2、reflector就是好啊就是好,别忘了它还有个Analyzer查找引用(Used By)很有效,另外,
  在C#与IL间切换,可以学习IL语言哦(鼠标停在IL指令上能看到指令解释及指令机器码,上次有
  兄弟已经说过了,抄袭一下:)
  3、.net程序没有签名的话,好象很不安全呢。
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪论坛,转载请注明出处。

                                                       2006年06月06日 14:30:41