壹佰 发表于 2024-4-2 13:19

【重磅】Aspose全家桶永久去限制全攻略(支持.NET 4x/6/8)

Aspose.Total全家桶包含我们日常经常要用到的Aspose.Cells/Aspose.Words/Aspose.Slides/Aspose.PDF/...等一系列库文件。
我们通过Visual Studio 2022中NuGet安装最新版到本地试用时,基本都有水印限制。


今天我们就用.Net HOOK技术来去掉所有限制,体验全功能的强大魅力。


首先,讲解下实现思路。先从网上去找一个过期的License(我找到一个20200827过期的License),调用SetLicense方法来设置这个License。
这个时候库会去判断这个License是否过期,然后我们就可以通过Hook技术拦截它的这个判断,让它永久返回不过期,从而达到预期结果。


说起来简单,实现起来真的太难了。目前有很多开源库,都实现了.net4.8及以下版本Framework的Hook技术,比如Harmony/HarmonyX等等。
但是.net6/7/8却一个都没有,在这方面研究的人太少,也没什么成果出来。当然也可能是我没发现,有知道的评论区告诉我。


没办法,只有自己去摸索了。在加班加点辛苦了快两年,终于让我整理出一个支持所有.net 4x/6/8的Hook技术库,我把它命名为Crane MethodHook。
这个Hook库的实现原理太复杂,有时间我专门开贴详细介绍。今天主要介绍一下使用它来破解Aspose全家桶。


我们先建个控制台应用(框架选择.Net 4x/6/8都是可以的),使用NuGet安装一下这个Crane.MethodHook库,最新版是我今天刚发布的V1.0.6版。


然后我们设置一下License,不同的Aspose库设置方法不一样,但是基本都是new Aspose.<product>.License().SetLicense()方法。
设置License之前我们还必须进行方法Hook绑定,注意顺序先hook再设置License。


其中有三个重要的方法:MethodBase.Invoke(),string.Compare,XmlElement.get_InnerText
我们分别对这三个方法进行Hook,先建立这三个方法的新版本:


public static object NewMethodInvoke(MethodBase method, object obj, object[] parameters)
{
    if (Assembly.GetCallingAssembly() != null && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.") && method.Name == "ParseExact" && parameters.Length > 0 && parameters.ToString().Contains("0827"))
    {
      var ret = DateTime.ParseExact(DATE_CHANGED_TO, "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture);
      ShowLog(method, ret, obj, parameters, true);
      return ret;
    }
    else if (Assembly.GetCallingAssembly() != null && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.") && method.Name == "ParseExact" && parameters.Length > 0 && System.Text.RegularExpressions.Regex.Match(parameters.ToString(), @"^\d{4}\.\d{2}\.\d{2}$").Success)
    {
      var ret = DateTime.ParseExact("20200501", "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture);
      ShowLog(method, ret, obj, parameters, true);
      return ret;
    }
    else if (Assembly.GetCallingAssembly() != null && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.") && method.DeclaringType != null && method.DeclaringType.Name == "String" && method.Name == "Compare")
    {
      if (parameters.Length == 2)
      {
            if (parameters.ToString() == "20200827")
            {
                var ret = 1;
                ShowLog(method, ret, obj, parameters, true);
                return ret;
            }
            else if (parameters.ToString() == "20200827")
            {
                var ret = -1;
                ShowLog(method, ret, obj, parameters, true);
                return ret;
            }
      }
    }
    else if (Assembly.GetCallingAssembly() != null && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.") && method.Name == "op_GreaterThan" && parameters.Length == 2 && parameters is DateTime && ((DateTime)parameters).ToString("MMdd") == "0827")
    {
      ShowLog(method, false, obj, parameters, true);
      return false;
    }
    else if (Assembly.GetCallingAssembly() != null && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.") && method.Name == "Split" && System.Text.RegularExpressions.Regex.Match(obj.ToString(), @"^\d{4}\.\d{2}\.\d{2}$").Success
         && obj != null && obj.ToString().Substring(0, 4) == DateTime.Now.Year.ToString())
    {
      var ret = new string[] { "2019", "08", "27" };
      ShowLog(method, ret, obj, parameters, true);
      return ret;
    }

    var hook = MethodHookManager.Instance.GetHook(System.Reflection.MethodBase.GetCurrentMethod());
    var result = hook.InvokeOriginal<object>(method, obj, parameters?.ToArray());
    ShowLog(method, result, obj, parameters);
    return result;

}

public static int NewCompare(string s1, string s2)
{
        if (Assembly.GetCallingAssembly() != null && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.") && s2 == "20200827")
        {
                Utils.LogWriteLine($"HOOK SUCCESS: From {Assembly.GetCallingAssembly().GetName().Name} String.Compare({s1},{s2}) return -1;", ConsoleColor.Green);
                return -1;
        }
        else
        {
                var hook = MethodHookManager.Instance.GetHook(MethodBase.GetCurrentMethod());
                var ret = hook.InvokeOriginal<int>(null, s1, s2);
                Utils.LogWriteLine($"NOT Aspose Call: From {Assembly.GetCallingAssembly().GetName().Name} String.Compare({s1},{s2}) return {ret};", ConsoleColor.DarkRed);
                return ret;
        }
}

private static readonly string DATE_CHANGED_TO = (DateTime.Today.Year + 1).ToString() + "0827";

public static string NewInnerText(XmlElement element)
{
        if (Assembly.GetCallingAssembly() != null && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.") && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.Words") == false && Assembly.GetCallingAssembly().FullName.StartsWith("Aspose.Hook") == false && element.Name == "SubscriptionExpiry")
        {
                Utils.LogWriteLine($"HOOK SUCCESS: From {Assembly.GetCallingAssembly().GetName().Name} XmlElement.InnerText ({element.Name},{element.InnerXml}) return {DATE_CHANGED_TO};", ConsoleColor.Green);
                return DATE_CHANGED_TO;
        }
        else
        {
                var hook = MethodHookManager.Instance.GetHook(System.Reflection.MethodBase.GetCurrentMethod());
                return hook.InvokeOriginal<string>(element);
        }
}

然后执行Hook绑定,这很简单,先调用AddHook方法。

Crane.MethodHook.MethodHookManager.Instance.AddHook(new MethodHook(
                        typeof(MethodBase).GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(object), typeof(object[]) }, null),
                        typeof(NewHookMethods).GetMethod(nameof(NewHookMethods.NewMethodInvoke), BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(MethodBase), typeof(object), typeof(object[]) }, null)
                  ));

Crane.MethodHook.MethodHookManager.Instance.AddHook(new MethodHook(
                        typeof(string).GetMethod("Compare", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(string), typeof(string) }, null),
                        typeof(NewHookMethods).GetMethod(nameof(NewHookMethods.NewCompare), BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(string), typeof(string) }, null)
                  ));

注意一下XmlElement.get_InnerText方法在.net4x和.net6/8下的获取稍有不同:

#if NET40
                  Hook = new MethodHook(
                        typeof(XmlElement).GetProperty("InnerText", BindingFlags.Public | BindingFlags.Instance).GetGetMethod(true),
                        typeof(NewHookMethods).GetMethod(nameof(NewHookMethods.NewInnerText), BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(XmlElement) }, null)
                  ),
#else
                  Hook = new MethodHook(
                        typeof(XmlElement).GetProperty("InnerText", BindingFlags.Public | BindingFlags.Instance).GetMethod,
                        typeof(NewHookMethods).GetMethod(nameof(NewHookMethods.NewInnerText), BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(XmlElement) }, null)
                  ),   
#endif

然后启用Hook即可。

Crane.MethodHook.MethodHookManager.Instance.StartHook();

那么现在Hook也设置好了,License也设置了,下面就来测试下成果。
顺便提一句,设置License如果明确知道目标应用要用到哪个Aspose产品,可以直接调用对应产品的SetLicense方法。
而我更喜欢用反射遍历的方式去查找当前应用引用了哪些Aspose产品,然后动态去调用其SetLicense方法,并且已经封装到库中,方便调用,也没有依赖。

我们分别测试Aspose.Cells/Aspose.Words/Aspose.Slides/Aspose.PDF这几款常用产品,检验一下是否达成预期。





通过日志,可以清晰的看到每款产品在SetLicense过程中都有哪些关键方法调用,有助于我们把握其中值得推敲和改善的关键点。
通过测试我们看到每款产品都能全功能完美使用,目标达成。











测试发现,不止是Aspose最新版,就是其历史所有老版本,同样的代码都是能支持的。

这就是Method Hook技术的强大魅力,不用修改库文件,就能改变其功能。值得珍藏。

好了,思路、过程、工具、关键点、结果、总结都有了。你不妨一试,有疑问评论区告诉我。我会给你解惑所有。
同时希望大家都能重视.Net Hook技术,把它应用到未来所有的挑战中。
也欢迎大家和我一起交流技术,共同进步。

壹佰 发表于 2024-5-13 11:41

丶酆麟 发表于 2024-5-11 11:16
我也用碰到这个问题 , 能否发一下你用的许可证 , 我没找到 , 你发的另一篇帖子我还没有权限查看 ...

我发了我用的License,你可以看下下楼的回复。

壹佰 发表于 2024-5-3 16:01

布鲁斯李 发表于 2024-4-25 14:49
用了大佬的方法试了一下,23.12以前的都能用,新的24.开头的版本就不行了,应该是没有用24年的License。会 ...

我测试了最新的V24.4和24.5都没问题。

CrackLy 发表于 2024-4-2 17:45

用深思或者DNG之类的hook了jit的,是否可以用这种方式hook

学编程的闹钟 发表于 2024-4-2 18:06

Aspose是什么软件哦

lies 发表于 2024-4-2 18:07

谢谢分享,大牛啊!

学编程的闹钟 发表于 2024-4-2 21:10

CrackLy 发表于 2024-4-2 17:45
用深思或者DNG之类的hook了jit的,是否可以用这种方式hook

应该是可以的

壹佰 发表于 2024-4-2 21:26

学编程的闹钟 发表于 2024-4-2 18:06
Aspose是什么软件哦

小众软件

学编程的闹钟 发表于 2024-4-2 21:29

壹佰 发表于 2024-4-2 21:26
小众软件

OK,谢谢楼主

jyjjf 发表于 2024-4-3 12:41

学编程的闹钟 发表于 2024-4-2 18:06
Aspose是什么软件哦

好多文档处理类工具都是用这个开发的,简单点就是调用DLL开发,然后套个界面

学编程的闹钟 发表于 2024-4-3 13:48

jyjjf 发表于 2024-4-3 12:41
好多文档处理类工具都是用这个开发的,简单点就是调用DLL开发,然后套个界面 ...

就是xls文档吗

pizazzboy 发表于 2024-4-3 15:54

谢谢大佬的分享。
页: [1] 2 3 4
查看完整版本: 【重磅】Aspose全家桶永久去限制全攻略(支持.NET 4x/6/8)