.NET外挂系列:5. harmony 中补丁参数的有趣玩法(下)
一:背景
1. 讲故事
开局一张表,故事全靠编,为了能够承上启下,先把参数列表放出来。
| 参数名 | 说明 |
|---|---|
__instance |
访问非静态方法的实例(类似 this)。 |
__result |
获取/修改返回值,要想修改用 ref。 |
__resultRef |
修改返回引用(方法返回是 ref 返回 )。 |
__state |
在前缀和后缀间传递自定义数据 。 |
___fields |
读写私有字段(三下划线开头,修改需加 ref)。 |
__args |
以 object[] 形式访问所有参数(修改数组即修改参数)。 |
方法参数同名 |
直接映射原参数。 |
__n |
__n 表示直接访问第 n 个参数,从 0 开始)。 |
__originalMethod |
获取原方法的 MethodBase。 |
__runOriginal |
判断原方法是否被执行。 |
如果说上一篇聊到的参数是无害的,那这篇所聊到的参数就具有破坏性了,会让一些底层方法产生匪夷所思的输出结果。
二:补丁参数解读
1. __result
这个参数可以获取被注入方法的返回值,你可以对他进行查看和修改,为了让例子更有趣一点,我们对 DateTime.Now 进行注入,让它永远的丢失时分秒,是不是有点像黑客? 哈哈,参考代码如下:
internal class Program
{
static void Main(string[] args)
{
var harmony = new Harmony("com.example.patch");
harmony.PatchAll();
var time = DateTime.Now;
Console.WriteLine($"当前时间:{time}");
Console.ReadLine();
}
}
[HarmonyPatch(typeof(DateTime), "Now", MethodType.Getter)]
public class DateTimeHook
{
public static void Postfix(ref DateTime __result)
{
__result = __result.Date;
}
}

是不是让人很恼火,明明调的是 DateTime.Now ,怎么时分秒不见了。。。
2. __args
在 harmony 中有三种方式可以获取原方法的参数,分别为:
- object[] __args 获取,支持读写。
- __n 下标获取,支持读写。
- parameter 同名法,默认只读,写的话要加 ref。
为了让例子更加有趣和黑客,我们对 HttpClient 的底层方法 SendAsync 进行拦截,然后纂改url,指向一个来历不明的网址,参考代码如下:
internal class Program
{
static async Task Main(string[] args)
{
// 应用Harmony补丁
var harmony = new Harmony("com.example.httpclient");
harmony.PatchAll();
var url = "https://www.cnblogs.com";
var httpClient = new HttpClient();
Console.WriteLine($"1.request:{url}");
var response = await httpClient.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"2.response:\n{content.Substring(0, 500)}");
Console.ReadKey();
}
}
[HarmonyPatch(typeof(HttpClient), "SendAsync", new Type[] { typeof(HttpRequestMessage), typeof(HttpCompletionOption), typeof(CancellationToken) })]
class HttpClientPatch
{
static void Prefix(object[] __args)
{
HttpRequestMessage request = (HttpRequestMessage)__args[0];
request.RequestUri = new Uri("http://www.baidu.com");
}
}

从卦中看,我明明请求的是 博客园,怎么给我返回 百度 的内容,是不是非常诡异。。。
可能有朋友看到了,这里有一个 (HttpRequestMessage)__args[0]; 强转的逻辑,能不能在 Prefix(object[] __args) 中直接接收 HttpRequestMessage 参数呢?可以的,这这就 harmony 的另外一种同名参数法,也就是参数名一定要和底层的 SendAsync 方法签名保持一致,截图如下:

修改后的代码如下,是不是非常的清爽。
[HarmonyPatch(typeof(HttpClient), "SendAsync", new Type[] { typeof(HttpRequestMessage), typeof(HttpCompletionOption), typeof(CancellationToken) })]
class HttpClientPatch
{
static void Prefix(HttpRequestMessage request)
{
request.RequestUri = new Uri("http://www.baidu.com");
}
}
可能有些人会遇到这样的情况,比如 SendAsync 方法的第一个参数是 internel 类型,由于是程序集可访问,所以你无法在另一个程序集的 Prefix 中声明此类型,这时候怎么办呢?可以借助 harmony 提供的 __n 索引法,下标是从0开始的。修改代码如下:
[HarmonyPatch(typeof(HttpClient), "SendAsync", new Type[] { typeof(HttpRequestMessage), typeof(HttpCompletionOption), typeof(CancellationToken) })]
class HttpClientPatch
{
static void Prefix(object __0)
{
Type requestType = __0.GetType();
PropertyInfo requestUriProperty = requestType.GetProperty("RequestUri");
Uri newUri = new Uri("http://www.baidu.com");
requestUriProperty.SetValue(__0, newUri);
}
}
3. ___fields
这个参数也是一个非常简单粗暴的特性,它可以用三下划线___引出当前 this 实例上的私有字段,使用场景可以是这样的,我们知道 new Thread 默认是没有 ThreadName 的,这在高级调试中往往有所不便,所以可加这样的一段逻辑:一旦发现无名的 ThreadName 就给它赋一个默认的名字,参考代码如下:
internal class Program
{
static void Main(string[] args)
{
var harmony = new Harmony("com.example.patch");
harmony.PatchAll();
var thread = new Thread(() => { });
thread.Start();
Console.WriteLine($"1.查看线程名:{thread.Name?.ToString()}");
Console.ReadLine();
}
}
[HarmonyPatch(typeof(Thread), "Name", MethodType.Getter)]
public class ThreadStartHook
{
public static void Prefix(Thread __instance, ref string ____name)
{
if (string.IsNullOrEmpty(____name))
{
____name = $"Default Threadid:{__instance.ManagedThreadId}";
}
}
}

三:总结
这篇文章我们聊到的一些参数多多少少都带点黑客性质,建议大家不要乱用,这里声明一下,我所说的一切都是为.NET高级调试训练营服务的,也是给学员们提供的拓展资料。
.NET外挂系列:5. harmony 中补丁参数的有趣玩法(下)的更多相关文章
- 【转载】总结一下Android中主题(Theme)的正确玩法
http://www.cnblogs.com/zhouyou96/p/5323138.html 总结一下Android中主题(Theme)的正确玩法 在AndroidManifest.xml文件中有& ...
- 聊聊 C# 和 C++ 中的 泛型模板 底层玩法
最近在看 C++ 的方法和类模板,我就在想 C# 中也是有这个概念的,不过叫法不一样,人家叫模板,我们叫泛型,哈哈,有点意思,这一篇我们来聊聊它们底层是怎么玩的? 一:C++ 中的模板玩法 毕竟 C+ ...
- [ES6系列-03]ES6中关于参数相关特性详解(参数默认值与参数解构赋值与剩余参数)
[原创] 码路工人 大家好,这里是码路工人有力量,我是码路工人,你们是力量. 今天总结一下 ES6 中跟参数相关的内容. 欢迎补充斧正.留言交流. 让我们互相学习一起进步. 1. ES6 参数默认值( ...
- 总结一下Android中主题(Theme)的正确玩法
在AndroidManifest.xml文件中有<application android:theme="@style/AppTheme">,其中的@style/AppT ...
- Flask系列03--Flask的路由 app.route中的参数, 动态参数路由
Flask–路由 添加路由的两种方式 第一种 @app.route("/my_de") def detail() 第二种(了解即可) app.add_url_rule(" ...
- 给Source Insight做个外挂系列之三--构建外挂软件的定制代码框架
上一篇文章介绍了“TabSiPlus”是如何进行代码注入的,本篇将介绍如何构建一个外挂软件最重要的部分,也就是为其扩展功能的定制代码.本文前面提到过,由于windows进程管理的限制,扩展代码必须以动 ...
- 给Source Insight做个外挂系列之一--发现Source Insight
一提到外挂程序,大家肯定都不陌生,QQ就有很多个版本的去广告外挂,很多游戏也有用于扩展功能或者作弊的工具,其中很多也是以外挂的形式提供的.外挂和插件的区别在于插件通常依赖于程序的支持,如果程序不支持插 ...
- solr与.net系列课程(四)solr查询参数的讲解与.net如何获取solr数据
solr与.net系列课程(四)solr查询参数的讲解与.net如何获取solr数据 上一节我们完成了solr连接数据库,细心的朋友会发现一个问题,就是solr其实和语言没有任何关系,配置完成后任何语 ...
- [转载]linux下编译php中configure参数具体含义
编译N次了 原来这么回事 原文地址:linux下编译php中configure参数具体含义作者:捷心特 php编译参数的含义 ./configure –prefix=/usr/local/php ...
- 梯度优化算法总结以及solver及train.prototxt中相关参数解释
参考链接:http://sebastianruder.com/optimizing-gradient-descent/ 如果熟悉英文的话,强烈推荐阅读原文,毕竟翻译过程中因为个人理解有限,可能会有谬误 ...
随机推荐
- K8s - 容器编排引擎Kubernetes
什么是Kubernetes? 背景 Kubernetes 是开源的容器集群管理项目,诞生于2014年,由Google公司发起 前身Borg系统在Google内部应用了十几年,积累了大量来自生产环境的实 ...
- 深度科普 - 大名鼎鼎的bun.js到底是什么? 它能否替代node.js? 是否能成为前端生态的未来?
什么是bun? 聪明的小伙伴们,你们在接触bun时是否有过这样的疑问呢? bun.js是什么? 它是如何诞生的? 跟node.js的区别是什么? 有什么优势? 目前的发展情况如何了? 他是否是前端的未 ...
- JS中数组的操作方法大全
常见的一些数组操作push . pop.unshift. shift push 语法: array.push(item1, item2, -, itemX) push( )方法:可以将一个或者更多的参 ...
- windows下测试TCP/UDP端口连通性
一.简介 最近调试项目的时候有需要测试UDP连接,所以顺便研究了一下. MaQaQ:省流,不介意下载工具的话就直接拉到最后看Advanced Port Scanner. 二.TCP 1.telnet( ...
- 3.14 + 1e10 - 1e10 = 0 ? ——浮点数的本质
3.14 + 1e10 - 1e10 = 0 ? --浮点数的本质 我们先看这样一个例子: #include <iostream> int main(int argc, char **ar ...
- 【踩坑系列】使用httpclient调用第三方接口返回javax.net.ssl.SSLHandshakeException异常
1. 踩坑经历 最近做了个需求,需要调用第三方接口获取数据,在联调时一直失败,代码抛出javax.net.ssl.SSLHandshakeException异常, 具体错误信息如下所示: javax. ...
- 《HelloGitHub》第 108 期
兴趣是最好的老师,HelloGitHub 让你对开源感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. github.com/521xueweihan/HelloG ...
- 【JVM之内存与垃圾回收篇】本地方法接口
本地方法接口 什么是本地方法 简单地讲,一个 Native Method 是一个 Java 调用非 Java 代码的接囗.一个 Native Method 是这样一个 Java 方法:该方法的实现由非 ...
- Java 与 LLM 大模型融合的技术革命:JBoltAI 如何重构企业级 AI 开发范式
Java 与 LLM 大模型融合的技术革命:JBoltAI 如何重构企业级 AI 开发范式 一.Java 技术栈的智能化转型挑战 随着 LLM(大语言模型)技术的突破,全球超过 900 万家 Java ...
- nodejs读写redis和mongo
nodejs读写redis https://redis.io/commands https://www.npmjs.com/package/redis var redis = require('red ...