找回密码
 立即注册
首页 业界区 业界 .NET外挂系列:2. 了解强大的 harmony 注解特性 ...

.NET外挂系列:2. 了解强大的 harmony 注解特性

公新蕾 前天 10:15
一:背景

1. 讲故事

上一篇我们简单的聊了下harmony外挂的基本玩法,让大家宏观上感受到了外挂在 .NET高级调试 领域的威力,这一篇我们从 注解特性 这个角度继续展开。
二:harmony 注解特性

1. HarmonyPatch 解读

在harmony支持的众多特性中,HarmonyPatch算是最基础的一个,注解特性简单来说就是harmony和 目标类 沟通的桥梁,为了让沟通更加简洁,harmony 提供了 20 个重载,参考如下:
  1. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate, AllowMultiple = true)]
  2. public class HarmonyPatch : HarmonyAttribute
  3. {
  4.     public HarmonyPatch();
  5.     public HarmonyPatch(Type declaringType);
  6.     public HarmonyPatch(Type declaringType, Type[] argumentTypes);
  7.     public HarmonyPatch(Type declaringType, string methodName);
  8.     public HarmonyPatch(Type declaringType, string methodName, params Type[] argumentTypes);
  9.     public HarmonyPatch(Type declaringType, string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations);
  10.     public HarmonyPatch(Type declaringType, MethodType methodType);
  11.     public HarmonyPatch(Type declaringType, MethodType methodType, params Type[] argumentTypes);
  12.     public HarmonyPatch(Type declaringType, MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations);
  13.     public HarmonyPatch(Type declaringType, string methodName, MethodType methodType);
  14.     public HarmonyPatch(string methodName);
  15.     public HarmonyPatch(string methodName, params Type[] argumentTypes);
  16.     public HarmonyPatch(string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations);
  17.     public HarmonyPatch(string methodName, MethodType methodType);
  18.     public HarmonyPatch(MethodType methodType);
  19.     public HarmonyPatch(MethodType methodType, params Type[] argumentTypes);
  20.     public HarmonyPatch(MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations);
  21.     public HarmonyPatch(Type[] argumentTypes);
  22.     public HarmonyPatch(Type[] argumentTypes, ArgumentType[] argumentVariations);
  23.     public HarmonyPatch(string typeName, string methodName, MethodType methodType = MethodType.Normal);
  24. }
复制代码
上面的20个重载方法都是从各种角度灵活定位到 目标方法,基本上能覆盖95%的场景,非常的强大,接下来我们使用这些特性优化上一篇的案例,一个洞察为什么突然的线程暴涨,参考代码如下:
  1.     internal class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             // 创建 Harmony 实例
  6.             var harmony = new Harmony("com.example.threadhook");
  7.             // 应用补丁
  8.             harmony.PatchAll();
  9.             Task.Factory.StartNew(() => { Test(); });
  10.             Console.ReadLine();
  11.         }
  12.         static void Test()
  13.         {
  14.             // 测试线程
  15.             var thread = new Thread(() => Console.WriteLine("线程正在运行"));
  16.             thread.Start();
  17.         }
  18.     }
  19.     [HarmonyPatch(typeof(Thread), "Start", new Type[] { })]
  20.     public class ThreadStartHook
  21.     {
  22.         // 前缀补丁 - 在原始方法执行前运行
  23.         public static void Prefix(Thread __instance)
  24.         {
  25.             Console.WriteLine("----------------------------");
  26.             Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}");
  27.             Console.WriteLine(Environment.StackTrace);
  28.             Console.WriteLine("----------------------------");
  29.         }
  30.     }
复制代码
三:案例优化

1. 如何实现多Start监控

上面的例子只是对 无参Start 进行的监控,可能会漏掉那些 有参Start 的情况,所以保守起见新增一个监控,代码如下:
  1.     [HarmonyPatch(typeof(Thread), "Start", new Type[] { })]
  2.     public class ThreadStartHook
  3.     {
  4.         // 前缀补丁 - 在原始方法执行前运行
  5.         public static void Prefix(Thread __instance)
  6.         {
  7.             Console.WriteLine("----------------------------");
  8.             Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}");
  9.             Console.WriteLine(Environment.StackTrace);
  10.             Console.WriteLine("----------------------------");
  11.         }
  12.     }
  13.     [HarmonyPatch(typeof(Thread), "Start", new Type[] { typeof(object) })]
  14.     public class ThreadStartWithParamHook
  15.     {
  16.         // 前缀补丁 - 在原始方法执行前运行
  17.         public static void Prefix(Thread __instance)
  18.         {
  19.             Console.WriteLine("----------------------------");
  20.             Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}");
  21.             Console.WriteLine(Environment.StackTrace);
  22.             Console.WriteLine("----------------------------");
  23.         }
  24.     }
复制代码
为了对 Start(object) 监控,我新增了一个 ThreadStartWithParamHook 类,虽然可以实现,但代码显的有些凌乱和累赘,那如何优化呢?这时候就可以使用新的 HarmonyPrefix 特性以及利用 注解的层级特点 来解决问题,优化之后的代码如下:
  1.     [HarmonyPatch(typeof(Thread), "Start")]
  2.     public class ThreadStartHook
  3.     {
  4.         [HarmonyPrefix]
  5.         [HarmonyPatch(new Type[] { })]
  6.         public static void Start(Thread __instance)
  7.         {
  8.             Console.WriteLine("----------------------------");
  9.             Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}");
  10.             Console.WriteLine(Environment.StackTrace);
  11.             Console.WriteLine("----------------------------");
  12.         }
  13.         [HarmonyPrefix]
  14.         [HarmonyPatch(new Type[] { typeof(object) })]
  15.         public static void StartWithParam(Thread __instance)
  16.         {
  17.             Console.WriteLine("----------------------------");
  18.             Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}");
  19.             Console.WriteLine(Environment.StackTrace);
  20.             Console.WriteLine("----------------------------");
  21.         }
  22.     }
复制代码
大家可以对比下代码,是不是好很多,这里稍微解释下:

  • 我们将  [HarmonyPatch(typeof(Thread), "Start", new Type[] { typeof(object) })] 拆成了 [HarmonyPatch(typeof(Thread), "Start")] + [HarmonyPatch(new Type[] { })] 的模式,这就是层次性。
  • 由于 Prefix 是 harmony 默认的前缀方法,如果不想用这个方法名,就必须使用 HarmonyPrefix 标注到你自定义的方法名上。
到这里可能有些人要反驳我,如果我的 Start 有10个重载,是不是也要对应的写10个hook方法?这样搞的话还是太麻烦了,有没有更加简洁的方式? 我要告诉你的是,肯定是可以的,你所焦虑的事情别人早就考虑到了,答案就是 harmony 在底层开了一个口子,让你通过自定义代码返回要 patch 的方法,参考代码如下:
  1.     [HarmonyPatch]
  2.     public class ThreadStartHook
  3.     {
  4.         //harmony 开的口子,返回要注入的方法。
  5.         static IEnumerable<MethodBase> TargetMethods()
  6.         {
  7.             var methodlist = typeof(Thread).GetMethods()
  8.                                            .Where(method => method.Name.StartsWith("Start"))
  9.                                            .Cast<MethodBase>();
  10.             return methodlist;
  11.         }
  12.         public static void Prefix(Thread __instance, MethodBase __originalMethod)
  13.         {
  14.             var parameters = __originalMethod.GetParameters().Select(i => i.ParameterType.Name);
  15.             Console.WriteLine("----------------------------");
  16.             Console.WriteLine($"{__originalMethod.Name} ({string.Join(",", parameters)})");
  17.             Console.WriteLine(Environment.StackTrace);
  18.             Console.WriteLine("----------------------------");
  19.         }
  20.     }
复制代码
代码中的 TargetMethods 方法就像一把利剑一样,批量的注入Start方法,你也可以根据你的需要灵活筛选,最后上一张图,
1.png

四:总结

通过不断的对 Thread.Start 方法进行注入优化,相信大家也感受到了harmony的异常强大,最后就是希望给训练营里的朋友一些思考和资料参考吧。
2.jpg


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册