-
-
[原创]Code Compare破解心得
-
发表于:
2013-7-25 01:04
22264
-
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直播授课