逆向分析某office插件

注:本文仅用于技术研究与探讨,不提供任何成品和补丁程序

收集信息

今天要逆向的是XX格子这款部分功能付费的Office插件。先安装好后打开看看,看它都有啥可以调教的功能,发现了一个VIP窗口,那这就有意思了,狠狠的开破。

随后我们点击加入会员,点击离线登录,弹出如下窗口:

关键信息都已经取得了,那就开始进入分析环节。

因为现在微软加载项都是以vsto进行部署的,所以我们先打开目录下的vsto文件,看看它调用了哪些DLL库,vsto文件关键节选如下:

  <dependency>
    <dependentAssembly dependencyType="install" codebase="FFCell.dll.manifest" size="25452">
      <assemblyIdentity name="FFCell.dll" version="2.0.0.0" publicKeyToken="b0c5e50cded54578" language="neutral" processorArchitecture="msil" type="win32" />
      <hash>
        <dsig:Transforms>
          <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
        </dsig:Transforms>
        <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <dsig:DigestValue>HswFQTT/AEBRt6Esc2Qlgpkx1Nc=</dsig:DigestValue>
      </hash>
    </dependentAssembly>
  </dependency>

不难发现其首先带调用的是FFCell.dll这个库,那我们直接开干,拖入exeinfope查壳,发现其为.NET程序并且加了混淆,如下图所示:

可以发现其为IntelliLock加密,无所谓,我会出手,but技术水平有限,就不手工脱壳了,直接拖入NET Reactor Slayer脱壳机,可以直接一键脱掉。

脱壳机

开始分析

基本代码分析

脱壳完毕后直接拖入dnSpy开始分析,我们直接搜索“绑定”,发现找不到,于是把函数名看看,发现关于软件注册的函数并没有在这个模块里面,那我们重新猜想,把目录里面的DLL都拿来分析了下,发现除了本体FFCell.dll外,还有若干个DLL也同样加了壳,猜测这几个DLL可能也是作者原创的DLL,再次执行上述的脱壳大法后拖入dnspy分析,可以在Newmem.dll这个文件中找到关于软件注册的函数,函数名也是如此,那我们就不难推断其实这个DLL专门就是管软件注册的事情的

关于软件注册的函数

随后我们再次搜索关键字符串“绑定”,可以发现出现了结果

在多个函数中均出现了关键字符串,那无所谓我们一个个看,经过分析,发现这几个函数分别对应不同的注册方式,分别有离线和在线两种。

首先先进行离线注册的分析,可在代码中发现确定绑定的按钮如下:

this.Button1.TabIndex = 0;
this.Button1.Text = "确定绑定";
this.Button1.UseVisualStyleBackColor = true;
this.LinkLabel1.AutoSize = true;

那我们就Button1对应的事件,长这样子:

internal virtual Button Button1
{
    get
    {
        return this.button_0;
    }
    set
    {
        EventHandler value2 = new EventHandler(this.method_2);
        if (this.button_0 != null)
        {
            this.button_0.Click -= value2;
        }
        this.button_0 = value;
        if (this.button_0 != null)
        {
            this.button_0.Click += value2;
        }
    }
}

可以看出好像method_2方法是点击这个按钮后的,无所谓是不是总之我们先跟进去看看就是,节选如下:

private void method_2(object sender, EventArgs e)
    {
        int num;
        int num4;
        object obj;
        try
        {
            IL_01:
            ProjectData.ClearProjectError();
            num = -2;
            IL_0A:
            int num2 = 2;
            string machineName = Environment.MachineName;
            IL_13:
            num2 = 3;
            string computerNo = VipRegV2Helper.GetComputerNo(machineName);
            IL_1E:
            num2 = 4;
            string text = this.TextBox1.Text;
            IL_2D:
            num2 = 5;
            if (Operators.CompareString(text, "授权码", false) != 0)
            {
                goto IL_4B;
            }
            IL_42:
            num2 = 6;
            text = "";
            IL_4B:
            num2 = 8;
            if (Operators.CompareString(text, "", false) != 0)
            {
                goto IL_7B;
            }
            IL_5F:
            num2 = 9;
            Interaction.MsgBox("授权码不能为空", MsgBoxStyle.OkOnly, "提示");
            IL_76:
            goto IL_2E3;
            IL_7B:
            num2 = 12;
            string left = this.TextBox3.Text.Trim();
            IL_91:
            num2 = 13;
            if (Operators.CompareString(left, this.VerfyImage1.code, false) == 0)
            {
                goto IL_CC;
            }
            IL_B0:
            num2 = 14;
            Interaction.MsgBox("验证码错误", MsgBoxStyle.OkOnly, "提示");
            IL_C7:
            goto IL_2E3;
            IL_CC:
            num2 = 17;
            string str = "";
            IL_D6:
            num2 = 18;
            if (VipRegV2Helper.CheckAccessCode(text, computerNo, ref str))
            {
                goto IL_11A;
            }
            IL_E9:
            num2 = 19;
            Interaction.MsgBox("授权码错误,原因:" + str, MsgBoxStyle.OkOnly, "提示");
            IL_106:
            num2 = 20;
            this.VerfyImage1.Gen();
            IL_115:
            goto IL_2E3;
            IL_11A:
            num2 = 23;
            DateTime expireDateFromAccessCode = VipRegV2Helper.GetExpireDateFromAccessCode(text);
            IL_125:
            num2 = 24;
            if (DateTime.Compare(expireDateFromAccessCode, DateTime.Now) < 0)
            {
                goto IL_1BF;
            }
            IL_13F:
            num2 = 31;
            string userNameFromAccessCode = VipRegV2Helper.GetUserNameFromAccessCode(text);
            IL_14B:
            num2 = 32;
            LoginHelper.SetLocalLogin(userNameFromAccessCode, "----");
            IL_15C:
            num2 = 33;
            VipOfflineHelper.SetLocalExpireDate(expireDateFromAccessCode);
            IL_166:
            num2 = 34;
            VipRegHelper.g_isReg = true;
            IL_170:
            num2 = 35;
            VipRegHelper.GetVipUility().SetVipValid(true);
            IL_180:
            num2 = 36;
            Interaction.MsgBox("离线绑定成功!绑定至" + expireDateFromAccessCode.ToString("yyyy年MM月dd日") + "\r\n\r\n到期后可继续申请绑定。", MsgBoxStyle.OkOnly, "离线绑定成功");
            IL_1AF:
            num2 = 37;
            this.DialogResult = DialogResult.OK;
            IL_1BA:
            goto IL_2E3;
            IL_1BF:
            num2 = 26;
            IL_1C3:
            num2 = 27;
            Interaction.MsgBox("授权码中绑定的日期已经过期", MsgBoxStyle.OkOnly, "提示");
            IL_1DA:
            num2 = 28;
            this.VerfyImage1.Gen();
            IL_1E9:
            goto IL_2E3;
            IL_1EE:
            int num3 = num4 + 1;
            num4 = 0;
            @switch(ICSharpCode.Decompiler.ILAst.ILLabel[], num3);
            IL_296:
            goto IL_2D8;
            IL_298:
            num4 = num2;
            if (num <= -2)
            {
                goto IL_1EE;
            }
            @switch(ICSharpCode.Decompiler.ILAst.ILLabel[], num);
            IL_2B4:;
        }

不难看出这个函数其实就是判断看用户输入的注册码是否正确,但是还存在部分没有脱壳脱干净的代码,不过无伤大雅,已经不影响我们对代码的判读了。其实就是先根据电脑名称算一个机器码出来,然后与用户输入的注册码进行对比,对比上了就说明输入正确,予以注册,否则就弹错误窗口,期间还有注册码格式的检查过程等。其中,关键的函数是

if (VipRegV2Helper.CheckAccessCode(text, computerNo, ref str))
{
    goto IL_11A;
}

这个CheckAccessCode函数就是我们要找的关键了,跟进看看:

不难发现就是判断注册码输对了没,然后返回一个bool类型,如果是true就说明对了,可以注册,是false就说明不对,不让注册,那我们思路就非常清楚了,可以在函数的返回处下断进行调试。

此处下断直接强行改变量为true,这下不得不给我狠狠注册了

随后程序带着true跳出CheckAccessCode函数,返回到method_2函数,因为改成true了所以现在要判断注册多长时间,因为我刚才的注册码是胡乱敲的,怎么可能包含注册有效时间在里面,所以我们还要在我们再改一处,当程序来到这里的时候我们可以修改expireDateFromAccessCode变量的值,如下图所示:

直接把到期日期设置为2099年1月1日,随后我们继续运行程序,可以看到我们梦寐以求的窗口弹出来了:

至此,我们就成功拿到了会员身份,但是还是有局限。

  1. 全程需要断网运行,否则软件一旦联网就发现你这个会员身份是假的,就给你覆盖掉了,又成为了非会员身份。

  1. 每次用这个插件都要先用dnspy调试下断改变量,老麻烦了

那首先我们先解决第二个问题,那既然每次都要用,那我们为什么不直接输入正确的注册码呢,那我们就对注册码的算法来一波狠狠的扒皮。

授权码算法分析

通过对多个函数的分析,为了方便读者阅读,特画出超强理解的流程图,保证你一看就懂。

注册表分析

如果觉得上面注册码算法过于困难,还是有点看不懂,没有关系,其实我们还可以利用注册表法来直接把到期时间改了,不难发现SetLocalExpireDate函数就是拿来把到期日期写进电脑系统的注册表的,其代码如下:

public static void SetLocalExpireDate(DateTime dt)
{
    Simple3Des simple3Des = new Simple3Des("为防止泄露密钥已隐藏");
    string v = simple3Des.EncryptData(dt.ToString());
    RegistryHelper.SetValue("FFCELL_VIPOFFLINE_SETTING", "OfflineExpireFlag", v);
}

可以很清楚的发现这是三轮的DES加密,密钥也直接写死在代码中了,为防止泄露,在本文中就隐藏掉了,我们拿到密钥后自己把授权到期日期一加密手动写进注册表就直接达到了授权到2099年的目的了。

再提一嘴,除了加密授权到期的日期外,如果用的是联网登录账号,那么你的账号和密码也是用这个算法来加密存储在注册表里面的。

通信协议分析

联网验证也没关系,我们只需要分析一下通信内容即可,先找到判断是否为VIP的联网通信函数

public static bool GetVipInfo(ref string info, ref VipUser vip1)
    {
        string localUserName = LoginHelper.GetLocalUserName();
        string localEnpwd = LoginHelper.GetLocalEnpwd();
        bool result;
        if (Operators.CompareString(localUserName, "", false) == 0 | Operators.CompareString(localEnpwd, "", false) == 0)
        {
            result = false;
        }
        else
        {
            try
            {
                Login login = new Login();
                string vipInfo = login.GetVipInfo(localUserName);
                if (vipInfo == null)
                {
                    info = "无法连接服务器,请检查网络或联系我们!\r\n" + Information.Err().Description;
                    result = false;
                }
                else if (vipInfo.IndexOf("RegOK") > 0)
                {
                    string text = "#@x#";
                    info = "";
                    int num = vipInfo.LastIndexOf(")");
                    string text2 = vipInfo.Substring(checked(num + 1));
                    string[] array = Regex.Split(text2, text);
                    if (array.Length != 5)
                    {
                        result = false;
                    }
                    else
                    {
                        if (Operators.CompareString(array[0].ToLower(), "t", false) == 0)
                        {
                            vip1.isValid = true;
                        }
                        else
                        {
                            vip1.isValid = false;
                        }
                        vip1.userName = array[1];
                        vip1.expireDate = array[2];
                        vip1.lastPc = array[3];
                        vip1.vipscore = array[4];
                        if (Operators.CompareString(vip1.vipscore, "", false) == 0)
                        {
                            vip1.vipscore = "0";
                        }
                        result = true;
                    }
                }
                else
                {
                    info = vipInfo;
                    result = false;
                }
            }
            catch (Exception ex)
            {
                info = "执行过程出现异常:" + ex.Message;
                result = false;
            }
        }
        return result;
    }

注意上述代码的第34行,如果从服务器接收的值是t就是会员,是其他值就不是会员,那我们使用Fiddler抓包看看

请求:

POST /vip/VipRegV2/VipRegVer2.aspx HTTP/1.0

接收:

(RegOK:1)f#@x#UserName#@x#2023/2/18 23:51:32#@x##@x#0

可以看到接收的内容中有f,我们猜测这可能就是和是否为会员相关的值,我们使用fiddler的拦截功能将数据包拦下,修改成t后再放行此数据包 ,可以看见已经成为会员了。

接收内容的最后那个0猜测就是会员等级,也可以随意修改的。

总结

至此,这个软件就分析的差不多了,还有其他的一些小功能在此就不再赘述了,本文起到一个抛砖引玉的作用,读者可以参考本文自行分析其余的功能模块,若想获得VIP权限,有如下几种方法。

  1. 每次使用软件时用dnSpy调试,修改局部变量, 在内存中改成VIP;

  1. 分析授权码算法,自行编写注册机进行授权;

  1. 直接拿密钥写注册表;

  1. 拦截通讯数据包,抓包改包,欺骗软件自己已经是VIP了。

上述1~3条方法建议配合断网使用,否则可能会失效,不过也可以通过hook方法屏蔽掉它的联网功能,此处不再赘述。

NET程序员的开发利器.NET Reflector 一、 简介   程序集,作为.NET时代的动态链接库,蕴藏了太多的软件秘密。为此,Visual Studio内置的ILDASM成为最初挖掘程序集的上佳工具。但自从Reflector出现后,ILDASM相形见拙。因为,Reflector能提供更多的程序集信息,而且是免费的工具。   如今,在Visual Studio.NET中编译源代码(无论是VB还是C#)时,编译器都会将高级源代码翻译成MSIL,即“微软中间语言”,而不是特定的机器指令。具有更多安全性、版本控制、共享能力与其它相关元数据的中间语言(IL),是包在一个或多个DLL或可执行文件中的。   基于ILDASM检查程序集中的IL有时很有用,但它要求我们熟悉MSIL。通常,比起IL,大多数开发人员对像C#或Visual Basic这样的高级程序设计语言更为熟悉。   Reflector的出现使.NET程序员眼前豁然开朗,因为这个免费工具可以将.NET程序集中的中间语言反编译成C#或者Visual Basic代码。除了能将IL转换为C#或Visual Basic以外,Reflector还能够提供程序集中类及其成员的概要信息、提供查看程序集中IL的能力以及提供对第三方插件的支持。   二、 下载与运行Reflector   Reflector是由微软员工Lutz Roeder编写的免费程序。这个软件经常更新,你可以在http://www.aisto.com/roeder/dotnet下载最新的版本。在本文成文之时,Reflector的最新版本为5.0.35.0,仅有1M大小。只要双击下载后的Reflector.exe文件即可运行Reflector。   默认情况下,Reflector会打开一组公共程序集(mscorlib、System、System.Data、System.Drawing等等)。每个打开的程序集都列在Reflector的主窗口中。单击程序集旁边的+图标可以展开树结构并展示程序集的命名空间。每个命名空间旁边都有一个相关的+图标,单击这个图标将显示这个命名空间内的类。除此以外,还可以展开每个类,显示类的成员:事件、字段、方法与属性。   要想查看其它程序集(包括我们自己创建的程序集)的细节,你可以使用菜单命令“File→Open”。然后,浏览到想要查看的程序集。只要选择了有效的.NET程序集,这个程序集就可以在Reflector的主窗口中与其它默认程序集共同显示。要想从Reflector的主窗口中删除程序集,只需右击程序集并选择“Close”。   三、 使用Reflector反汇编程序集   提供基本的程序集是Reflector唾手可得的功能,然而,Reflector真正的威力体现在它的反汇编能力。只要浏览到类级的成员,就可以通过Tools菜单中的Disassembler项(或在该项上单击右键)反汇编此成员。这将打开第二个窗格,以C#、Visual Basic、Delphi或者IL显示反汇编后的内容。图2以C#语言展示了对SmtpClient类中Abort方法的反汇编结果。   有了Reflector的反编译功能,要研究.NET框架基类库就容易多了。我们完全可以在没有源代码的情况下研究我们所创建或者正在使用的程序集相应源代码。   不用太担心,你自己的.NET应用程序还有其它受保护措施。但是,想阻止别人查看.NET程序集的IL(继而反编译成C#或者Visual Basic)是不可能的,但你可以使用“混淆”技术使IL变得混乱。目前,市场上有许多.NET混淆产品,比如:PreEmptive Solution的Dotfuscator、WiseOwl的Dmeanor以及Remotesoft的.NET Obfuscator等。   四、 Reflecator的其它功能   除了作为对象浏览器与反汇编器之外,Reflector还可以显示类与其成员的调用与被调用图、提供单键访问Google或MSDN搜索的能力并提供了允许第三方开发人员为Reflector创建插件的框架。   要查看调用或被调用图,只需要在树视图中选择一个成员,访问Tools菜单,选择Call Graph或Callee Graph选项即可。Call Graph会列出所选项所调用的成员,而Callee Graph列出调用所选项的成员。   通过使用插件,Reflector的功能可以得到进一步扩展。目前有能显示程序集依赖图、自动加载当前运行中的程序集、输出整个程序集的反汇编内容以及在Visual Studio中作为Reflector的宿主等的插件。还有更多插件都列在http://www.codeplex.com/reflectoraddins中。在此,我极力推荐有兴趣的读者前去一看。   不出乎所料,的确存在一个Reflector.VisualStudio插件。有了这个插件,就可以让Reflector集成在Visual Studio环境中,进而取代以前那个有些“拙漏”的对象浏览器。   首先,我们需要在机器上准备Reflector的最新版,然后从http://www.testdriven.NET/reflector下载最新版本的Reflector.VisualStudio插件。据说安装了此插件之后,Reflector能够与Visual Studio完美融合。但遗憾的是,本人试下载此软件并安装(仅能得到TestDriven.NET-2.8.2130 RTM个人测试版本,时间7/30/2007,大小3.3M)后,没有大发现—仅是在VS2005中添加“Go to Reflector”和一个帮助菜单项。点击“Go to Reflector”菜单项仅是从VS2005内部启动Reflector而已。   有了Reflector.VisualStudio插件(正版,下面是根据在线资料介绍),Reflector可以寄在Visual Studio中Reflector不仅是一个对象浏览器、反汇编器,包裹在同一个程序中的所有功能还可以寄于Visual Studio中。   五、 小结   还记得Spy++吗?在Win32时代,这个软件对于我们分析一个陌生软件的设计思路可谓立下汗马功劳。在如今的.NET时代,Reflector又成为每个严肃的.NET开发人员工具箱中必不可少的精髓工具之一。怎么样,请赶紧试锋吧。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值