首页/文章列表/文章详情

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

编程知识622025-05-19评论

一:背景

1. 讲故事

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

二:harmony 注解特性

1. HarmonyPatch 解读

在harmony支持的众多特性中,HarmonyPatch算是最基础的一个,注解特性简单来说就是harmony目标类 沟通的桥梁,为了让沟通更加简洁,harmony 提供了 20 个重载,参考如下:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate, AllowMultiple = true)]public class HarmonyPatch : HarmonyAttribute{ public HarmonyPatch(); public HarmonyPatch(Type declaringType); public HarmonyPatch(Type declaringType, Type[] argumentTypes); public HarmonyPatch(Type declaringType, string methodName); public HarmonyPatch(Type declaringType, string methodName, params Type[] argumentTypes); public HarmonyPatch(Type declaringType, string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations); public HarmonyPatch(Type declaringType, MethodType methodType); public HarmonyPatch(Type declaringType, MethodType methodType, params Type[] argumentTypes); public HarmonyPatch(Type declaringType, MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations); public HarmonyPatch(Type declaringType, string methodName, MethodType methodType); public HarmonyPatch(string methodName); public HarmonyPatch(string methodName, params Type[] argumentTypes); public HarmonyPatch(string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations); public HarmonyPatch(string methodName, MethodType methodType); public HarmonyPatch(MethodType methodType); public HarmonyPatch(MethodType methodType, params Type[] argumentTypes); public HarmonyPatch(MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations); public HarmonyPatch(Type[] argumentTypes); public HarmonyPatch(Type[] argumentTypes, ArgumentType[] argumentVariations); public HarmonyPatch(string typeName, string methodName, MethodType methodType = MethodType.Normal);}

上面的20个重载方法都是从各种角度灵活定位到目标方法,基本上能覆盖95%的场景,非常的强大,接下来我们使用这些特性优化上一篇的案例,一个洞察为什么突然的线程暴涨,参考代码如下:

 internal class Program { static void Main(string[] args) { // 创建 Harmony 实例 var harmony = new Harmony("com.example.threadhook"); // 应用补丁 harmony.PatchAll(); Task.Factory.StartNew(() => { Test(); }); Console.ReadLine(); } static void Test() { // 测试线程 var thread = new Thread(() => Console.WriteLine("线程正在运行")); thread.Start(); } } [HarmonyPatch(typeof(Thread),"Start", new Type[] { })] public class ThreadStartHook { // 前缀补丁 - 在原始方法执行前运行 public static void Prefix(Thread __instance) { Console.WriteLine("----------------------------"); Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}"); Console.WriteLine(Environment.StackTrace); Console.WriteLine("----------------------------"); } }

三:案例优化

1. 如何实现多Start监控

上面的例子只是对无参Start进行的监控,可能会漏掉那些有参Start的情况,所以保守起见新增一个监控,代码如下:

 [HarmonyPatch(typeof(Thread),"Start", new Type[] { })] public class ThreadStartHook { // 前缀补丁 - 在原始方法执行前运行 public static void Prefix(Thread __instance) { Console.WriteLine("----------------------------"); Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}"); Console.WriteLine(Environment.StackTrace); Console.WriteLine("----------------------------"); } } [HarmonyPatch(typeof(Thread),"Start", new Type[] { typeof(object) })] public class ThreadStartWithParamHook { // 前缀补丁 - 在原始方法执行前运行 public static void Prefix(Thread __instance) { Console.WriteLine("----------------------------"); Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}"); Console.WriteLine(Environment.StackTrace); Console.WriteLine("----------------------------"); } }

为了对Start(object)监控,我新增了一个ThreadStartWithParamHook类,虽然可以实现,但代码显的有些凌乱和累赘,那如何优化呢?这时候就可以使用新的HarmonyPrefix特性以及利用注解的层级特点来解决问题,优化之后的代码如下:

 [HarmonyPatch(typeof(Thread),"Start")] public class ThreadStartHook { [HarmonyPrefix] [HarmonyPatch(new Type[] { })] public static void Start(Thread __instance) { Console.WriteLine("----------------------------"); Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}"); Console.WriteLine(Environment.StackTrace); Console.WriteLine("----------------------------"); } [HarmonyPrefix] [HarmonyPatch(new Type[] { typeof(object) })] public static void StartWithParam(Thread __instance) { Console.WriteLine("----------------------------"); Console.WriteLine($"即将启动线程: {__instance.ManagedThreadId}"); Console.WriteLine(Environment.StackTrace); Console.WriteLine("----------------------------"); } }

大家可以对比下代码,是不是好很多,这里稍微解释下:

  1. 我们将 [HarmonyPatch(typeof(Thread),"Start", new Type[] { typeof(object) })]拆成了[HarmonyPatch(typeof(Thread),"Start")] + [HarmonyPatch(new Type[] { })]的模式,这就是层次性。

  2. 由于 Prefix 是 harmony 默认的前缀方法,如果不想用这个方法名,就必须使用 HarmonyPrefix标注到你自定义的方法名上。

到这里可能有些人要反驳我,如果我的 Start 有10个重载,是不是也要对应的写10个hook方法?这样搞的话还是太麻烦了,有没有更加简洁的方式? 我要告诉你的是,肯定是可以的,你所焦虑的事情别人早就考虑到了,答案就是 harmony 在底层开了一个口子,让你通过自定义代码返回要 patch 的方法,参考代码如下:

 [HarmonyPatch] public class ThreadStartHook { //harmony 开的口子,返回要注入的方法。 static IEnumerable<MethodBase> TargetMethods() { var methodlist = typeof(Thread).GetMethods() .Where(method => method.Name.StartsWith("Start")) .Cast<MethodBase>(); return methodlist; } public static void Prefix(Thread __instance, MethodBase __originalMethod) { var parameters = __originalMethod.GetParameters().Select(i => i.ParameterType.Name); Console.WriteLine("----------------------------"); Console.WriteLine($"{__originalMethod.Name} ({string.Join(",", parameters)})"); Console.WriteLine(Environment.StackTrace); Console.WriteLine("----------------------------"); } }

代码中的 TargetMethods 方法就像一把利剑一样,批量的注入Start方法,你也可以根据你的需要灵活筛选,最后上一张图,

四:总结

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

神弓

一线码农

这个人很懒...

用户评论 (0)

发表评论

captcha