pjy612 发表于 2023-1-18 11:43

用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-1 17:56

本帖最后由 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:01

本帖最后由 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;
                }
      }
}

      

peikesmart 发表于 2023-11-9 20:07

感谢,我用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);
}

sdieedu 发表于 2023-1-18 20:09

虽然没有看懂 我觉得依然牛

pjy612 发表于 2023-1-18 20:20

本帖最后由 pjy612 于 2023-1-18 20:21 编辑

sdieedu 发表于 2023-1-18 20:09
虽然没有看懂 我觉得依然牛
{:1_896:}
这种教程 算是 头一次写。。。
主要算是介绍一个 可以动态Hook .NET 的库...

毕竟在写应用的时候,那些第三方库 过于依赖nuget 不适合去反编译破解。

当然 破解过程部分 先去坛子里看看 其他大佬 破解 Aspose 的 帖子 再看可能 更明白一点。。。

Blanke 发表于 2023-1-19 10:11

思路不错
代码可以分享下吗

yaoguen 发表于 2023-1-19 14:45

能分享一下代码吗?,谢谢

q705031 发表于 2023-1-26 13:03

虽然没有看懂 我觉得依然牛

billpeace 发表于 2023-1-31 14:48

谢谢分享

dplxin 发表于 2023-2-1 14:07

那就这个帖子回复,
我这边还有个今天能用的 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>

dplxin 发表于 2023-2-1 14:08

那就这个帖子回复,
我这边还有个今天能用的 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>
页: [1] 2 3 4 5 6 7
查看完整版本: 用Mod的思路去给第三方库打补丁