一:背景

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的异常强大,最后就是希望给训练营里的朋友一些思考和资料参考吧。

.NET外挂系列:2. 了解强大的 harmony 注解特性的更多相关文章

  1. 给Source Insight做个外挂系列之一--发现Source Insight

    一提到外挂程序,大家肯定都不陌生,QQ就有很多个版本的去广告外挂,很多游戏也有用于扩展功能或者作弊的工具,其中很多也是以外挂的形式提供的.外挂和插件的区别在于插件通常依赖于程序的支持,如果程序不支持插 ...

  2. 给Source Insight做个外挂系列之三--构建外挂软件的定制代码框架

    上一篇文章介绍了“TabSiPlus”是如何进行代码注入的,本篇将介绍如何构建一个外挂软件最重要的部分,也就是为其扩展功能的定制代码.本文前面提到过,由于windows进程管理的限制,扩展代码必须以动 ...

  3. 朱晔和你聊Spring系列S1E10:强大且复杂的Spring Security(含OAuth2三角色+三模式完整例子)

    Spring Security功能多,组件抽象程度高,配置方式多样,导致了Spring Security强大且复杂的特性.Spring Security的学习成本几乎是Spring家族中最高的,Spr ...

  4. 单元测试系列之十一:Jmockit之mock特性详解

    本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...

  5. 9.10 翻译系列:EF数据注解特性之StringLength【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/stringlength-dataannotations-attribute-in-co ...

  6. 9.9 翻译系列:数据注解特性之--MaxLength 【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/maxlength-minlength-dataannotations-attribut ...

  7. 9.7 翻译系列:EF数据注解特性之--InverseProperty【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in ...

  8. 9.3 翻译系列:数据注解特性之Key【EF 6 Code-First 系列】

    原文链接:http://www.entityframeworktutorial.net/code-first/key-dataannotations-attribute-in-code-first.a ...

  9. 9.2 翻译系列:数据注解特性之---Column【EF 6 Code First系列】

    原文链接:http://www.entityframeworktutorial.net/code-first/column-dataannotations-attribute-in-code-firs ...

  10. 9.翻译系列:EF 6以及EF Core中的数据注解特性(EF 6 Code-First系列)

    原文地址:http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx EF 6 Code-F ...

随机推荐

  1. 洛谷P4390 [BalkanOI2007] Mokia 摩基亚 题解

    题目传送门. 想必 我的另外一篇题解 已经把这道题的思路说的很清楚了,但是那道题是把所有的修改全部告诉你,然后再一个一个问你矩阵和,但是这道题他是修改中夹着询问,但是没有关系,我们照样可做. 考虑将所 ...

  2. 性能对比实验折线图绘制代码(YOLO系列为例)

    本文用于绘制性能折线图,适用于对比实验,发现很多博文都是收费,欺负哥们懒得学习,一气之下ai了一下再进行代码修改,免费供给大家学习参考,便于大家撰写论文数据时利于绘制图像. import pandas ...

  3. STM32 学习方法

    前言 学习知识要掌握有效的学习方法,学习技术也是一样,本篇分享关于我学习 STM32 后总结的学习方法. 推荐的学习方法 系统学习 在网上购买一款开发板,使用开发板+开发板配套视频教程+开发板配套源码 ...

  4. nginx启动失败 Starting nginx nginx [fail]

    nginx -t :测试配置文件是否有语法错误 看看报什么错误,我的是忘记给权限了 nginx: [alert] could not open error log file: open() " ...

  5. 昨晚接收的俄罗斯Meteor-M2气象卫星云图,接收质量还可以!

    接收设备: 天馈:自制四臂螺旋天线 硬件:SDRsharp 跟踪:Orbitron.SDRSharpDriverDDE 频率:137.1MHZ 解码:SDRSharp.QPSK.M2_LRPT_Dec ...

  6. Proxmox ve(Pve) 安装windows server

    1.安装proxmox ve点击直达 官网地址 下载下来如果下载速度太慢 可以去安装个IDM https://www.52pojie.cn/thread-1013874-1-1.html 然后需要制作 ...

  7. Portainer安装配置

    什么是portainer 官网:https://www.portainer.io/ Portainer(基于 Go) 是一个轻量级的Web管理界面,可让您轻松管理 Docker 主机 或 Swarm ...

  8. Oracle 11G R2 安装图解

    个人学习需要,在Windows Server 2008 R2 上安装 Oracle 11G R2 Tips:需要下载2个文件,file1和file2 解压后需要合并到同一个文件夹下才能正常安装(这里就 ...

  9. Go 应用程序使用 dockerfile multi-stage 的问题

    场景重现 一个简单的go应用,准备通过docker部署,为了减少运行时的镜像和容器体积,使用了multi-stage构建: # dockerfile 大致如下 # 一级构建使用带golang环境的镜像 ...

  10. 学习unigui【27】像pg的jsonb一样编辑json。

    var  I: Integer;  CurrentObject: TJSONObject;  FieldName: string;  Pair: TJSONPair;function CreateJS ...