首页
社区
课程
招聘
[原创]Code Compare破解心得
发表于: 2013-7-25 01:04 22263

[原创]Code Compare破解心得

2013-7-25 01:04
22263

Code Compare是一个能集成在visual studio中的代码比较工具,目前最新版本是3.0.44,网址为:
http://www.devart.com/codecompare/

它与我上一周破解的review assistant(见http://bbs.pediy.com/showthread.php?t=175747&highlight=,那个是我第一次破解.NET程序的心得)是同一家公司出品。

由于破解review assistant只花了两个晚上,这两个软件又是同一家软件出的,我猜测它们的加密处理应该是类似的,以为破解它也就一两个小时的事,没想到软件的破解花了我足足4个痛苦的晚上。

还是将破解心得汇报一下吧。

1、仍选用Reflector和reflexil进行破解,它的代码混淆工具也是Eaz,也不能完全还原;

2、以为它的加密与review assistant类似,于是以“license”为关键字搜,没有搜到任何内容;

3、它的about对话框上有试用天数的提示,于是运行它,在弹出about对话框后,使用Visual studio附加进程,中断,再查看,呵呵,原来它的注册相关的管理类的名称不叫“...license...”,而是叫“...laycense...”;

4、以“laycense”为关键字搜索,找出接口ILaysenseInfo:
public interface ILaysenseInfo
{
    int LaysenseStatus { get; }  
    void ValidateLaysense(int timeout);
}

5、找到它的实现类,经过分析,LaysenseStatus是取得“总试用天数+剩余天数”,ValidateLaysense是弹出购买提示对话框。

6、 直接将这两个函数的实现短路。

7、再搜new thread,找出是否有干坏事的线程,找出三个线程,简单分析后现这三个线程确定会干坏事,于是也将其实现短路,整个过程也就只花了一个多小时。

8、Code Compare可以在Visual studio中运行,也可独立运行,独立运行之,一切正常,以为大功告成。

9、没想到Visual studio却启动不了,也没无特殊提示信息,只是提示装载扩展包不成功。

10、我对如何调试Visual studio扩展包没任何经验,上网查找,也无任何有用信息,一下子措手无策(有牛人能指点一下吗?)。经过一晚上折腾,仍不能找到如何分析破解Visual studio扩展包装载不成功的方法。

11、第二天,我就想,能不能另寻破解思路,不改它的执行程序,从它的试用时间记录文件入手呢。它肯定会在一个文件或注册表中中记录试用结束时间的,我如果能仿造一个时间记录文件,每隔一个月造一个新的时间记录文件,不也是间接破解了?

12、分析LaysenseStatus的实现,它的读文件的函数如下:
    private int method_25()
    {
        byte[] bytes = null;
        string path = null;
        try
        {
            path = (string) GKeys[3];
            bytes = File.ReadAllBytes(path);
        }
        catch (Exception exception)
        {
            if ((((!(exception is ArgumentException) && !(exception is NotSupportedException)) && (!(exception is FileNotFoundException) && !(exception is IOException))) && ((!(exception is SecurityException) && !(exception is DirectoryNotFoundException)) && (!(exception is UnauthorizedAccessException) && !(exception is PathTooLongException)))) && !(exception is ArgumentOutOfRangeException))
            {
                throw;
            }
        }
        bool flag = false;
        int index = 4;
        while (((int) GKeys[0]) == 0)
        {
            long num8;
            TimeSpan span;
            if ((bytes != null) && (index < (bytes.Length - 8)))
            {
                long num7 = (((bytes[index++] + bytes[index++]) << ((8 + bytes[index++]) & 0x1f)) << ((0x10 + bytes[index++]) & 0x1f)) << 0x18;
                for (int i = 4; i < (bytes.Length - 8); i++)
                {
                    if (((i + 8) < index) || (i > (index + 8)))
                    {
                        num8 = ((((((bytes[i] + (bytes[i + 1] << 8)) + (bytes[i + 2] << 0x10)) + (bytes[i + 3] << 0x18)) + (bytes[i + 4] << 0x20)) + (bytes[i + 5] << 40)) + (bytes[i + 6] << 0x30)) + (bytes[i + 7] << 0x38);
                        num7 ^= num8;
                        if (num8.ToString().GetHashCode() == num7)
                        {
                            goto Label_0142;
                        }
                    }
                }
            }
            continue;
        Label_0142:
            span = new TimeSpan(DateTime.Now.ToUniversalTime().Ticks - num8);
            GKeys[0] = span.Days | 0x1e0000;
            flag = span.Minutes >= 5;
        }
        if (flag)
        {
            Random random = new Random();
            bytes = new byte[random.Next(700, 900)];
            for (int j = 0; j < bytes.Length; j++)
            {
                bytes[j] = (byte) random.Next(0, 0x100);
            }
            long ticks = DateTime.Now.ToUniversalTime().Ticks;
            int hashCode = ticks.ToString().GetHashCode();
            ticks ^= hashCode;
            index = random.Next(4, bytes.Length - 8);
            bytes[index] = (byte) (ticks & 0xffL);
            bytes[index + 1] = (byte) ((ticks >> 8) & 0xffL);
            bytes[index + 2] = (byte) ((ticks >> 0x10) & 0xffL);
            bytes[index + 3] = (byte) ((ticks >> 0x18) & 0xffL);
            bytes[index + 4] = (byte) ((ticks >> 0x20) & 0xffL);
            bytes[index + 5] = (byte) ((ticks >> 40) & 0xffL);
            bytes[index + 6] = (byte) ((ticks >> 0x30) & 0xffL);
            bytes[index + 7] = (byte) ((ticks >> 0x38) & 0xffL);
            int num4 = 0;
            while (true)
            {
                num4 = random.Next(4, bytes.Length - 4);
                if (((num4 + 4) >= index) && (num4 <= (index + 4)))
                {
                    bytes[num4] = (byte) (hashCode & 0xff);
                    bytes[num4 + 1] = (byte) ((hashCode >> 8) & 0xff);
                    bytes[num4 + 2] = (byte) ((hashCode >> 0x10) & 0xff);
                    bytes[num4 + 3] = (byte) ((hashCode >> 0x18) & 0xff);
                    if (File.Exists(path))
                    {
                        FileAttributes fileAttributes = File.GetAttributes(path);
                        if ((fileAttributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                        {
                            fileAttributes &= ~FileAttributes.ReadOnly;
                            File.SetAttributes(path, fileAttributes);
                        }
                    }
                    File.WriteAllBytes(path, bytes);
                    break;
                }
            }
        }
        return (int) GKeys[0];
    }

13、这个函数前半段是读文件,后半段是写文件,我以为一个是读试用时间,一个是写试用时间,经过提取和试验,发现它们不等价,写的内容不能被读出,会导致读文件死循环。

14、这个结论让我迷惑好久,最后终于想明白:
    A、这个写文件的操作就是为了破坏读试用时间,使得超期之后,即便再将系统时间改回来也再能试用;
    B、有另一线程在检查,到一定时间之后设置GKeys[0],能让读循环退出;
    C、这段写操作具有很大的迷惑性,写了一段与读操作逻辑差不多但却是错误的代码,就是为了迷惑破解者。

15、第三天,我就想,它的写试用时间文件的操作应该是在安装时进行的,能不能分析一下它的安装过程?

16、用资源查看器打开它的安装程序,想看看它是用什么软件制作的安装包,找到一段描述“此软件是使用Inno Setup制作的”,于是上网搜索,看是否能有什么软件能分析Inno Setup安装包,于是找到了innounp,这个软件能将Inno Setup制造的安装包还原出来。

17、运行innounp,哈哈,果然还原出来了,除了被安装的目录和文件,还将它的安装描述文件也还原出来。

18、Inno Setup安装描述文件中【RUN】段说明安装之后需执行的程序,它的第一条是:
[Run]
Filename: "{commonappdata}\Devart\Code Compare\Uninstall\EndSetup.exe"; Parameters: "-i none codecompare ""3.0.44"" ""Release"""; StatusMsg: "{cm:StatusMsg_UpdatingSystem}"; Check: "Is32BitInstallMode"; MinVersion: 0.0,5.0; Flags: nowait

19、这个EndSetup太可疑了,我怀疑它就是在安装完之后检查注册表看是否已安装过,并产生试用时间文件的。

20、用IDA PRO打开,看它使用了哪些系统函数,果然,我猜想的三类函数都有:注册表读写、文件读写、读系统当前时间。当看到读系统当前时间这个函数存在时,我已确认它就是我要找的文件。

21、Inno Setup中都已经将调用参数列出来了:“-i none codecompare  3.0.44 Release”,直接将这个参数传入,进行调试。

22、果然,它从注册表中检查是否有键"Software\microsoft\windows\currentversion\MachineState",根据读出的内容决定是否写文件。

23、我试着将键"Software\microsoft\windows\currentversion\MachineState"删除,果然,它产生了一个试用时间文件。

24、我以为到此已经成功,结果证明我又错了。

25、只能仔细分析EndSetup的文件生成过程,分析一段时间之后,我头都大了,它为了防止被静态或动态跟踪,作了很多复杂的迷宫似的处理,而我,又不是专业的破解人员,我哪有这么多时间去分析呀。

26、今天,在分析了一晚上之后,我终于决定放弃。

27、我安慰自己,算了吧,至少独立运行的程序已破解,结束这段自由自在的破解生活,再投入到正常的工作中。

28、我将程序还原到四天前已破解的状态,准备收工,却意外发现:它在Visual studio中已能正常运行!!!

29、我猜测,它的代码中有检查键"Software\microsoft\windows\currentversion\MachineState",删除该键的内容,它就不干坏事了。

30、算一个完美的结果吧,我实在没精力再继续分析下去。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 5
支持
分享
最新回复 (6)
雪    币: 878
活跃值: (737)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
支持下,感谢分享...
2013-7-25 11:56
0
雪    币: 81
活跃值: (110)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
逆向能力不足就是硬伤啊
2013-7-25 18:36
0
雪    币: 70
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
是呀,确实需要提高。另外,比不了年轻人,没那么多时间去做这些别人眼中算不务正业的事情。
2013-7-25 21:50
0
雪    币: 233
活跃值: (59)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
感谢楼主分享!
另外请问:你修改的是OpCode还是Replace all with Code?
2013-7-29 21:53
0
雪    币: 70
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
OpCode,有汇编基础,其实理解这个不太难。

由于Eaz不能完全破解,不能使用Replace all with Code。
2013-7-30 22:05
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
感谢楼主分享!
2014-3-5 11:25
0
游客
登录 | 注册 方可回帖
返回
//