ManicTime破解小记工具准备 先说一下动手前软件的准备: IDA Pro6,这个没什么好说的,秒杀一切静态反汇编的神器。 Ollydbg,动态调试神器,可惜win7下面的功能很有限。 PEid v0.95,来自飘雪论坛,查壳的神器。 .Net Reflector V7.5,C#程序反汇编神器,最牛的是居然可以反编译成C#的代码。 UltraEdit32,十六进制修改神器,不多解释。 Telerik JustDecompile,现在Reflector已经收费了,试用了一下这个,功能虽然有限但是已经很不错了。 MSDN,查IL汇编必备 前期探查 软件安装下来以后,跑到目录下发现是如下结构: 突然想起来网站上面说至少要.Net3.0+,于是猜想,这孙子不会是用C#写的吧?于是抄起PEID查壳: E:\Program Files\ManicTime\ManicTime.exe ->Microsoft Visual C# / Basic .NET [Overlay] 果不其然这孙子是用C#写的,我们都知道,C#生成的EXE都是所谓的中间代码(IL汇编),这种东西的话,基本动态调试是没希望的,原理同VB,执行的全是伪代码,根本没法查堆栈。于是只能静态反汇编。 抄起.Net Reflector,打开主EXE文件: 看出来问题了吗?主App东西少的可怜,只不过是一个Wrapper而已,真正的东西应该都在那几个DLL上面。 这个作者是个很NB的程序员,文件命名写的很规范:Finkit.ManicTime.Common.dll。就像是花姑娘起了个名字叫侯美丽一样,这个DLL在向大家招手:我这个才是主程序模块~快来破我啊~快来啊~ 于是上PEID: E:\Program Files\ManicTime\Finkit.ManicTime.Common.dll ->Microsoft Visual C# / Basic .NET [Overlay] 没什么问题,高高兴兴抄起反汇编,果不其然找到了关键模块: 但是点进去傻眼了:全是虚函数! .class public interface abstract auto ansi IInstallationIdGenerator { } .class public interface abstract auto ansi ILicenseProvider { } 前面的后缀都是IL汇编的注释符,我们注意到一个关键字:abstract。MSDN如下描述修饰符abstract: abstract 修饰符用于类中不具有实现的方法或属性或者用于包含这些方法的类。 具有抽象成员的类不能使用 new 运算符来实例化。 您可以从抽象基类派生抽象和非抽象的类。 说白了这就是一个纯虚的基类,没有什么深入的价值。但是这里有个很重要的问题:既然是基类,一定会有继承的子类使用这个Lincense。 困难阻碍 左找右找,注意到了Finkit.ManicTime.Common.O.dll,估计到这个说不定就是有加密算法的子类,于是抄起PEID: E:\Program Files\ManicTime\Finkit.ManicTime.Common.O.dll -> Morp hine v1.2 (DLL) [Overlay] * .method public hidebysig specialname newslot virtual final instance int32 get_TrialDaysLeft() cil managed这个DLL居然被加入了保护措施。看来多半是我们要找的目标,继续用Reflector来看: 果不其然,这个便是负责实现许可证密钥的代码! 高兴地点进去看反编译出来的代码,却傻眼了: public class InfralutionTrialProvider : ITrialProvider { // Fields private readonly Dictionary<string, bool> Axn91R2iW; private readonly string AZeiARnCn; private static object EbLJLM27S; private readonly string g2j15LDjS; private EvaluationMonitor o7MreZ115; …. } 大家可以发现,这里所有的名字全是乱码,更有甚者有些函数是Reflector不能跟进去的,一进去就会报错。 哪里出了问题呢?网上查来查去,发现代码作者使用了很多“混淆技术”。 什么是混淆技术呢?顾名思义,就是防止别人,尤其是反编译器读懂自己源代码的技术。这个方面如果谈深了会很深很深,随便找出几个实例来说明一下: 1.名字乱码。把一些内部实现的变量名,函数名用乱码代替,这样子一般人就会花上好多时间去一一对应,例如上文中的 private readonly string g2j15LDjS; 其实是 private readonly string licensedir; 的混淆写法,这个不是我随便说的,而是根据上下文推断出来的。 2. 流程混淆。这个就更牛了,直接放代码: .method assembly hidebysig static void SLV0fFIsptsZtjvFft17() cil managed { .maxstack 8 L_0000: br L_0007 // br是无条件跳转指令 L_0005: pop L_0006: ldc.i4.0 //将0压入堆栈 L_0007: nop L_0008: ret } 明眼人一眼可以看出来,这个函数就是空函数,函数直接跳到L_0007然后就return了。但是这段代码到了Reflector眼里面就要命了:我们可以无视POP指令,但是Reflector不行,这里其实没有任何压栈操作,直接就pop了,于是程序只能无奈的报错了: 类似的牛代码数不胜数,大规模的switch,疯狂跳转等等等,让人不禁感叹,如果作者肯多用些时间来debug就好了,别老想着加密…. 柳暗花明 到了现在,破解思路有三个: a. 认真读代码,找出加密算法,写注册机。这个最安全,但是最费时。 b. 找到加密license,爆破加密逻辑,强行注册。这个不靠谱,作者写了相当详细的逻辑来检查密钥授权文件。 c. 找到试用期的漏洞,制造无限试用期。我的思路就是这个。 从一堆乱码堆里面,终于找到了相关函数: public int TrialDaysLeft { get { if (!this.o7MreZ115.Invalid) // 这个是用来检查许可证是否有效的 { return ((this.5AsnB23p7() - this.o7MreZ115.UsageCount) + 1); // 关键点1 } return 0; // 关键点2 } } 这个函数再好不过了,有好几个地方可以下手,可以直接修改两个关键点,让它返回不为0就行。我选择了从关键点2入手,这段代码的IL汇编如下: { … L_0012: brtrue L_002c L_0017: ldarg.0 … L_0028: sub L_0029: ldc.i4.1 L_002a: add L_002b: ret L_002c: ldc.i4.0 -> ldc.i4.1 L_002d: ret } 修改L_002c的代码为ldc.i4.1,修改之后C#代码变成: public int TrialDaysLeft { get { if (!this.o7MreZ115.Invalid) // 这个是用来检查许可证是否有效的 { return ((this.5AsnB23p7() - this.o7MreZ115.UsageCount) + 1); // 关键点1 } return 1; // 返回1 } } 这种修改的好处是只修改一个字节(ldc.i4.0的机器码是0x16, ldc.i4.1的机器码是0x17)。这样便达到了即使15天试用期过期,也仍然显示试用期剩余一天的效果。 |
|
来自: HDTV > 《Software》