吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4829|回复: 57
收起左侧

[.NET逆向] 用Mod的思路去给第三方库打补丁

[复制链接]
pjy612 发表于 2023-1-18 11:43
本帖最后由 pjy612 于 2023-2-1 20:05 编辑

一般去使用一些第三方库的时候,如果想去水印很多的时候都是硬编码反编译DLL。
这样一旦更新什么的就要重新操作一遍,费时费力。
如果是反射和注入的话,又不一定能很好的定位静态对象。
那么有没有类似Aop的Hook 能帮忙我们对运行中的库动态打补丁和加Hook呢?

还真有。。。HarmonyLib 和 HarmonyX
一般这两个常用的是 基于 BepInEx 给 Unity 游戏做Mod。但我们也能把他用在.Net应用上。

下面就拿一个 有水印的 office 文档处理的库 做个示范演示一下。
111.png
坛子里有大佬发过对应的逻辑,都是基于过期的license处理。这里不过多的说明

但是 license 都是有 RSA 验签的。
那我们能不能不改动整个 license,但是 在 dll 读取 过期时间的时候 我们去hook它呢?
license 的格式是 XML的 所以肯定会用到 XML相关的读取函数。那么我们对它进行Hook。

222.png
Hook  XmlElement 属性的 InnerText 的 Getter 函数,如果 当前是在读取  SubscriptionExpiry 我们直接将 返回值 "20200827" 修改成 "20991231"。

这样不影响 校验部分对整个 xml 做 校验。但是单独变更了 过期时间。

同理 有的DLL 是用的 XmlReader 的 ReadString ,我们也处理一下。
333.png

那么问题来了,有些DLL 在 读取完时间之后 还会又重新验证下 这个内容是否是真的在 xml 里面。
444.png
我们也Hook 一下,如果检查的时候我们自己修改的新过期时间,就给改成一个 大于 -1 的就行。


然后问题来了,那个 StackInAspose 函数是什么?
因为我们Hook 的都是 底层函数,虽然加了一些前置判断 为了避免影响到其他 逻辑,判断一下调用堆栈。
555.png
只 Hook 对 SetLicense 之下的调用链做处理。

然后 既然是针对 系统函数的Hook ,那么我们 对这种仅一次授权验证的 需要在Patch完后及时释放掉。
所以 Patch类用 using 包裹比较好。
666.png

最后 使用上就是
777.png
new 对象的时候 激活 Harmony 的 Patch。
析构时 解除 Patch。

测试结果
888.png

水印都成去除。

总结,
使用 HarmonyLib 的好处就是,不用每次针对性 修改DLL,也不需要处理 DLL之前的强命名关联等。全部在运行时中打补丁。
当然 补丁前 该怎么Hook 的分析还是要的。

PS:
用好了这个大杀器 对一些 RSA 验签也可以无脑 return true. 可以单独只逆向算法,不用去想公钥私钥的问题了。


PS2:
cell 库 里面有个 随机数控制的 暗桩 所以需要 针对性静态Patch一下(见回复)
其他库不清楚什么时候也会实装类似的校验。

免费评分

参与人数 9威望 +1 吾爱币 +28 热心值 +7 收起 理由
coffee0818 + 1 谢谢@Thanks!
frozleaf + 1 + 1 我很赞同!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
chyuyuguo2004 + 1 + 1 我很赞同!
decai + 1 + 1 谢谢@Thanks!
笙若 + 1 + 1 谢谢@Thanks!
光之优雅 + 1 + 1 用心讨论,共获提升!
1MajorTom1 + 1 热心回复!
sdieedu + 2 鼓励转贴优秀软件安全工具和文档!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| pjy612 发表于 2023-2-1 17:56
本帖最后由 pjy612 于 2023-2-10 15:47 编辑
dplxin 发表于 2023-2-1 14:08
那就这个帖子回复,
我这边还有个今天能用的 license  你可以试一下

Cell 里面有个随机数暗桩  = =。 看了下 没虚拟化 有三处调用,保存 或者 用了图表 可能 概率性触发。
其他的库里面还没看到。。。
c1.png

c2.png

c3.png

AsposeLicensePatch 里面针对 Cell 加个静态Patch吧。。。

RSA Patch 的话 控制 日期 小于 20600101 即可。

[C#] 纯文本查看 复制代码
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[0].ParameterType == typeof(string) && parameterInfos[1].ParameterType == t)
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }).ToArray();
    if (array.Length == 1)
    {
        _cellCheckMethod = AccessTools.FirstMethod(array[0], 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
[C#] 纯文本查看 复制代码
~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
[C#] 纯文本查看 复制代码
[HarmonyPatch(typeof(XmlCharacterData), "Data", MethodType.Getter)]
[HarmonyPostfix]
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打上补丁了。
下面是代码。
[C#] 纯文本查看 复制代码
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
    [HarmonyPatch(typeof(XmlElement), "InnerText", MethodType.Getter)]
    [HarmonyPostfix]
    private static void Postfix_XmlElement_InnerText_Getter(XmlElement __instance, ref String __result, MethodInfo __originalMethod)
    {
        if (StackInAspose())
        {
            if (__instance.Name == "SubscriptionExpiry")
            {
                __result = NEW_EXP;
            }
        }
    }

    [HarmonyPatch(typeof(XmlReader), "ReadString")]
    [HarmonyPostfix]
    private static void Postfix_XmlReader_ReadString(XmlReader __instance, ref String __result, MethodInfo __originalMethod)
    {
        if (StackInAspose())
        {
            if (__instance.LocalName == "SubscriptionExpiry")
            {
                __result = NEW_EXP;
            }
        }
    }

    [HarmonyPatch(typeof(String), "IndexOf", new Type[] { typeof(String) })]
    [HarmonyPostfix]
    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;
            }
        }
    }

    [HarmonyPatch(typeof(XmlCharacterData), "Data", MethodType.Getter)]
    [HarmonyPostfix]
    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;
            }
        }
    }
}

[C#] 纯文本查看 复制代码
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
虽然没有看懂 我觉得依然牛


这种教程 算是 头一次写。。。
主要算是介绍一个 可以动态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>
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-25 00:36

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表