|
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,先建立这三个方法的新版本:
[C#] 纯文本查看 复制代码 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[0].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[0].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[0].ToString() == "20200827")
{
var ret = 1;
ShowLog(method, ret, obj, parameters, true);
return ret;
}
else if (parameters[1].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[1] is DateTime && ((DateTime)parameters[1]).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方法。
[C#] 纯文本查看 复制代码 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下的获取稍有不同:
[C#] 纯文本查看 复制代码 #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即可。
[C#] 纯文本查看 复制代码 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技术,把它应用到未来所有的挑战中。
也欢迎大家和我一起交流技术,共同进步。
|
评分
-
参与人数 11 | HB +14 |
THX +8 |
收起
理由
|
松岛枫
| + 1 |
+ 1 |
|
布鲁斯李
| + 2 |
+ 1 |
[吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩! |
身心自在
| + 1 |
|
|
花盗睡鼠
| + 2 |
+ 1 |
[吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少! |
汇编基础
| + 1 |
|
|
消逝的过去
| |
+ 1 |
|
白丁老师
| + 1 |
|
[吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少! |
jyjjf
| + 1 |
+ 1 |
|
美好映像
| + 2 |
+ 1 |
|
chinamsu
| + 2 |
+ 1 |
[吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩! |
CrackLy
| + 1 |
+ 1 |
感谢分享 |
查看全部评分
|