用Mod的思路去给第三方库打补丁
本帖最后由 pjy612 于 2023-2-1 20:05 编辑一般去使用一些第三方库的时候,如果想去水印很多的时候都是硬编码反编译DLL。
这样一旦更新什么的就要重新操作一遍,费时费力。
如果是反射和注入的话,又不一定能很好的定位静态对象。
那么有没有类似Aop的Hook 能帮忙我们对运行中的库动态打补丁和加Hook呢?
还真有。。。HarmonyLib 和 HarmonyX
一般这两个常用的是 基于 BepInEx 给 Unity 游戏做Mod。但我们也能把他用在.Net应用上。
下面就拿一个 有水印的 office 文档处理的库 做个示范演示一下。
坛子里有大佬发过对应的逻辑,都是基于过期的license处理。这里不过多的说明
但是 license 都是有 RSA 验签的。
那我们能不能不改动整个 license,但是 在 dll 读取 过期时间的时候 我们去hook它呢?
license 的格式是 XML的 所以肯定会用到 XML相关的读取函数。那么我们对它进行Hook。
HookXmlElement 属性的 InnerText 的 Getter 函数,如果 当前是在读取SubscriptionExpiry 我们直接将 返回值 "20200827" 修改成 "20991231"。
这样不影响 校验部分对整个 xml 做 校验。但是单独变更了 过期时间。
同理 有的DLL 是用的 XmlReader 的 ReadString ,我们也处理一下。
那么问题来了,有些DLL 在 读取完时间之后 还会又重新验证下 这个内容是否是真的在 xml 里面。
我们也Hook 一下,如果检查的时候我们自己修改的新过期时间,就给改成一个 大于 -1 的就行。
然后问题来了,那个 StackInAspose 函数是什么?
因为我们Hook 的都是 底层函数,虽然加了一些前置判断 为了避免影响到其他 逻辑,判断一下调用堆栈。
只 Hook 对 SetLicense 之下的调用链做处理。
然后 既然是针对 系统函数的Hook ,那么我们 对这种仅一次授权验证的 需要在Patch完后及时释放掉。
所以 Patch类用 using 包裹比较好。
最后 使用上就是
new 对象的时候 激活 Harmony 的 Patch。
析构时 解除 Patch。
测试结果
水印都成去除。
总结,
使用 HarmonyLib 的好处就是,不用每次针对性 修改DLL,也不需要处理 DLL之前的强命名关联等。全部在运行时中打补丁。
当然 补丁前 该怎么Hook 的分析还是要的。
PS:
用好了这个大杀器 对一些 RSA 验签也可以无脑 return true. 可以单独只逆向算法,不用去想公钥私钥的问题了。
PS2:
cell 库 里面有个 随机数控制的 暗桩 所以需要 针对性静态Patch一下(见回复)
其他库不清楚什么时候也会实装类似的校验。
本帖最后由 pjy612 于 2023-2-10 15:47 编辑
dplxin 发表于 2023-2-1 14:08
那就这个帖子回复,
我这边还有个今天能用的 license你可以试一下
Cell 里面有个随机数暗桩= =。 看了下 没虚拟化 有三处调用,保存 或者 用了图表 可能 概率性触发。
其他的库里面还没看到。。。
AsposeLicensePatch 里面针对 Cell 加个静态Patch吧。。。
RSA Patch 的话 控制 日期 小于 20600101 即可。
private static Harmony staticHarmony;
static AsposeLicensePatch()
{
staticHarmony = new Harmony($"{nameof(AsposeLicensePatch)}_Static");
}
static MethodBase _cellCheckMethod = null;
public static bool PatchCell(Type cell)
{
if (_cellCheckMethod != null && staticHarmony.GetPatchedMethods().Contains(_cellCheckMethod)) return true;
Type[] types = AccessTools.GetTypesFromAssembly(cell.Assembly);
Type[] array = types.Where(t =>
{
if (t.IsSealed && t.IsClass)
{
List<ConstructorInfo> constructors = AccessTools.GetDeclaredConstructors(t, false);
if (constructors.Count == 1)
{
ConstructorInfo constructorInfo = constructors.First();
ParameterInfo[] parameterInfos = constructorInfo.GetParameters();
if (parameterInfos.Length == 2)
{
if (parameterInfos.ParameterType == typeof(string) && parameterInfos.ParameterType == t)
{
return true;
}
}
}
}
return false;
}).ToArray();
if (array.Length == 1)
{
_cellCheckMethod = AccessTools.FirstMethod(array, m => m.ReturnType == typeof(bool));
staticHarmony.Patch(_cellCheckMethod, new HarmonyMethod(typeof(AsposeLicensePatch), nameof(ForceBoolFalse)));
}
return _cellCheckMethod != null && staticHarmony.GetPatchedMethods().Contains(_cellCheckMethod);
}
static bool ForceBoolFalse()
{
return false;
}
/*
using (new AsposeLicensePatch())
{
new Aspose.Cells.License().SetLicense(new MemoryStream(buffer));
}
//接在 using 处理之后 静态处理
AsposeLicensePatch.PatchCell(typeof(Aspose.Cells.License));
*/
析构函数的代码 最好也改改 主要是 this.Dispose
~AsposeLicensePatch()
{
this.Dispose(false);
}
private bool _isDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this._isDisposed)
{
if (disposing)
{
harmony?.UnpatchSelf();
}
this._isDisposed = true;
}
}
public void Dispose()
{
this.Dispose(true);
System.GC.SuppressFinalize(this);
}
本帖最后由 pjy612 于 2023-8-23 11:24 编辑
最新版的 Words 23.8 里面取值部分有些变动。可以额外Patch个地方处理。
再其他的 就请自行研究了。@dplxin
private static void Postfix_XmlText_Data_Getter(XmlCharacterData __instance, ref string __result, MethodInfo __originalMethod)
{
if (StackInAspose())
{
var stacks = new StackTrace().GetFrames();
if (stacks.Any(s => s.GetMethod().Name.StartsWith("get_OuterXml")))
{
return;
}
var parentNod = __instance.ParentNode;
if (parentNod == null) return;
if (parentNod.Name == "SubscriptionExpiry" || parentNod.Name == "LicenseExpiry")
{
__result = NEW_EXP;
}
}
}
感谢,我用net 6.0和Aspose.PDF最新版本23.10打上补丁了。
下面是代码。
internal class AsposeLicensePatch : IDisposable {
private Harmony harmony;
private const String NEW_EXP = "20591231";
public AsposeLicensePatch()
{
harmony = Harmony.CreateAndPatchAll(this.GetType(), nameof(AsposeLicensePatch));
}
static Boolean StackInAspose()
{
var stacktrace = new StackTrace();
var stackFrames = stacktrace.GetFrames();
if (stackFrames == null) return false;
var isAsposeLicense = stackFrames.Any(r => r.HasMethod() && r.GetMethod().Name == "SetLicense" && r.GetMethod().DeclaringType != null && (r.GetMethod().Module.ScopeName.StartsWith("Aspose.") || r.GetMethod().Module.ScopeName.StartsWith("GroupDocs.")));
return isAsposeLicense;
}
~AsposeLicensePatch()
{
this.Dispose(false);
}
private bool _isDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this._isDisposed)
{
if (disposing)
{
harmony?.UnpatchSelf();
}
this._isDisposed = true;
}
}
public void Dispose()
{
this.Dispose(true);
System.GC.SuppressFinalize(this);
}
// https://www.52pojie.cn/thread-1737340-1-1.html
private static void Postfix_XmlElement_InnerText_Getter(XmlElement __instance, ref String __result, MethodInfo __originalMethod)
{
if (StackInAspose())
{
if (__instance.Name == "SubscriptionExpiry")
{
__result = NEW_EXP;
}
}
}
private static void Postfix_XmlReader_ReadString(XmlReader __instance, ref String __result, MethodInfo __originalMethod)
{
if (StackInAspose())
{
if (__instance.LocalName == "SubscriptionExpiry")
{
__result = NEW_EXP;
}
}
}
{ typeof(String) })]
static void PostFix_String_IndexOf(String __instance, String value, ref Int32 __result, MethodInfo __originalMethod)
{
if (StackInAspose())
{
if (__result != -1) return;
if (value == NEW_EXP)
{
__result = 0;
}
}
}
private static void Postfix_XmlText_Data_Getter(XmlCharacterData __instance, ref string __result, MethodInfo __originalMethod)
{
if (StackInAspose())
{
var stacks = new StackTrace().GetFrames();
if (stacks.Any(s => s.GetMethod().Name.StartsWith("get_OuterXml")))
{
return;
}
var parentNod = __instance.ParentNode;
if (parentNod == null) return;
if (parentNod.Name == "SubscriptionExpiry" || parentNod.Name == "LicenseExpiry")
{
__result = NEW_EXP;
}
}
}
}
using (new AsposeLicensePatch())
{
new Aspose.Pdf.License().SetLicense(new MemoryStream(Convert.FromBase64String("License")));
var document = new Document("用户手册_V1.0.pdf");
document.Save("output.docx", Aspose.Pdf.SaveFormat.DocX);
} 虽然没有看懂 我觉得依然牛 本帖最后由 pjy612 于 2023-1-18 20:21 编辑
sdieedu 发表于 2023-1-18 20:09
虽然没有看懂 我觉得依然牛
{:1_896:}
这种教程 算是 头一次写。。。
主要算是介绍一个 可以动态Hook .NET 的库...
毕竟在写应用的时候,那些第三方库 过于依赖nuget 不适合去反编译破解。
当然 破解过程部分 先去坛子里看看 其他大佬 破解 Aspose 的 帖子 再看可能 更明白一点。。。 思路不错
代码可以分享下吗 能分享一下代码吗?,谢谢 虽然没有看懂 我觉得依然牛 谢谢分享 那就这个帖子回复,
我这边还有个今天能用的 license你可以试一下
<?xml version="1.0"?>
<License>
<Data>
<LicensedTo>CPS</LicensedTo>
<EmailTo>stefan.stachow@cps.gov.uk</EmailTo>
<LicenseType>Site OEM</LicenseType>
<LicenseNote>Up To 10 Developers And Unlimited Deployment Locations</LicenseNote>
<OrderID>220725095952</OrderID>
<UserID>938906</UserID>
<OEM>This is a redistributable license</OEM>
<Products>
<Product>Aspose.Total for .NET</Product>
</Products>
<EditionType>Enterprise</EditionType>
<SerialNumber>28d85812-77fc-4bf1-b0fc-7e0c0be64bac</SerialNumber>
<SubscriptionExpiry>20240109</SubscriptionExpiry>
<LicenseExpiry>20230209</LicenseExpiry>
<ExpiryNote>This is a temporary license for non-commercial use only and it will expire on 2023-02-09</ExpiryNote>
<LicenseVersion>3.0</LicenseVersion>
<LicenseInstructions>https://purchase.aspose.com/policies/use-license</LicenseInstructions>
</Data>
<Signature>gi1yLX1CrlX6JvrPEbJSDMS0dmykurCZTLs7Z7uwe+hncRWFNLNQYgdmDLFwVAhJC85JicsWR+Qr+uncyoerCVtnmjF62RczD4KOOcIvSEMU7AotrA6EXa6M98mYu/GRLqa3gDQmU8s6SBhuKfhFQtNZ79oyf+uxIHWW+XE+YcE=</Signature>
</License> 那就这个帖子回复,
我这边还有个今天能用的 license你可以试一下
<?xml version="1.0"?>
<License>
<Data>
<LicensedTo>CPS</LicensedTo>
<EmailTo>stefan.stachow@cps.gov.uk</EmailTo>
<LicenseType>Site OEM</LicenseType>
<LicenseNote>Up To 10 Developers And Unlimited Deployment Locations</LicenseNote>
<OrderID>220725095952</OrderID>
<UserID>938906</UserID>
<OEM>This is a redistributable license</OEM>
<Products>
<Product>Aspose.Total for .NET</Product>
</Products>
<EditionType>Enterprise</EditionType>
<SerialNumber>28d85812-77fc-4bf1-b0fc-7e0c0be64bac</SerialNumber>
<SubscriptionExpiry>20240109</SubscriptionExpiry>
<LicenseExpiry>20230209</LicenseExpiry>
<ExpiryNote>This is a temporary license for non-commercial use only and it will expire on 2023-02-09</ExpiryNote>
<LicenseVersion>3.0</LicenseVersion>
<LicenseInstructions>https://purchase.aspose.com/policies/use-license</LicenseInstructions>
</Data>
<Signature>gi1yLX1CrlX6JvrPEbJSDMS0dmykurCZTLs7Z7uwe+hncRWFNLNQYgdmDLFwVAhJC85JicsWR+Qr+uncyoerCVtnmjF62RczD4KOOcIvSEMU7AotrA6EXa6M98mYu/GRLqa3gDQmU8s6SBhuKfhFQtNZ79oyf+uxIHWW+XE+YcE=</Signature>
</License>