一:背景

1. 讲故事

这篇文章起源于和一家 .NET公司 开线上会议时,提出的一个场景问题,程序出现了非托管内存暴涨,这些非托管内存关联的对象都囤积在 终结器队列 中,很显然这是代码中没用 using 及时释放引发的,而这块代码又是第三方组件,你想加也加不了,所以提出了一个设想:能不能设法干预 终结器队列的 freachable 节段,让里面的对象提前释放,而不是等待不稳定的终结器线程来兜底。。。

这个问题我最近也在考虑,毕竟我写过如何用 harmony 拦截 .net sdk ,用 minhook 拦截 win32api,唯独这一块没有跟大家聊,虽然 eventpipe 给 coreclr 开了很多的日志口子,但怎么说呢? eventpipe 是一种君子之法,和黑客性质的minhook无法相提并论,所以这一篇就详细的和大家聊一聊。

二:如何拦截 coreclr

1. 一个小案例

为了方便演示,就以拦截 GC.Collect() 方法为例吧,参考代码如下:

static void Main(){for (int i = 0; i < 3; i++){Console.WriteLine($"Triggering GC #{i}...");GC.Collect();Thread.Sleep(1000);}}

熟悉GC的朋友应该知道 GC.Collect() 下游方法是coreclr!WKS::GCHeap::GarbageCollect(),我想在命中这个方法的时候执行一点我的自定义逻辑,这里有一点注意的是,钩子的回调不要回调到 C#,最好采用 SlideCar 的方式,这里使用静态链接,C代码参考如下:


#include <windows.h>
#include <stdio.h>
#include <MinHook.h>// 1. 使用 extern "C" 防止名称修饰
#ifdef __cplusplus
extern "C" {
#endif// 2. 定义原始函数类型typedef int(__fastcall* Real_GarbageCollect)(void* pThis, int generation, bool lowMemory, int mode);// 3. 导出函数声明__declspec(dllexport) BOOL WINAPI InstallGCHook();__declspec(dllexport) void WINAPI UninstallGCHook();#ifdef __cplusplus
}
#endif// 4. 全局变量
static Real_GarbageCollect fpOriginalGarbageCollect = NULL;
static void* pTargetFunction = NULL;// 5. 获取 coreclr.dll 中的函数地址(关键修改点)
static void* GetGCDunctionAddress() {HMODULE hCoreCLR = GetModuleHandleW(L"coreclr.dll");if (!hCoreCLR) {printf("[ERROR] coreclr.dll not loaded\n");return NULL;}// 计算目标地址return (BYTE*)hCoreCLR + 0x30E670; // 替换为你的实际偏移量
}// 6. Detour 函数(保持不变)
int __fastcall Hook_GarbageCollect(void* pThis, int generation, bool lowMemory, int mode) {printf("[GC Hook] this=0x%p, gen=%d, lowMem=%d, mode=%d\n",pThis, generation, lowMemory, mode);if (fpOriginalGarbageCollect) {MH_DisableHook(pTargetFunction);int result = fpOriginalGarbageCollect(pThis, generation, lowMemory, mode);MH_EnableHook(pTargetFunction);return result;}return 0;
}// 7. 安装Hook(改为自动计算地址)
__declspec(dllexport) BOOL WINAPI InstallGCHook() {pTargetFunction = GetGCDunctionAddress();if (!pTargetFunction) return FALSE;if (MH_Initialize() != MH_OK) {printf("[ERROR] MinHook init failed\n");return FALSE;}MH_STATUS status = MH_CreateHook(pTargetFunction,&Hook_GarbageCollect,(void**)&fpOriginalGarbageCollect);if (status != MH_OK) {printf("[ERROR] CreateHook failed (status=0x%X)\n", status);MH_Uninitialize();return FALSE;}if (MH_EnableHook(pTargetFunction) != MH_OK) {printf("[ERROR] EnableHook failed\n");MH_Uninitialize();return FALSE;}printf("[SUCCESS] Hook installed at 0x%p\n", pTargetFunction);return TRUE;
}// 8. 卸载Hook(保持不变)
__declspec(dllexport) void WINAPI UninstallGCHook() {if (pTargetFunction) {MH_DisableHook(pTargetFunction);MH_RemoveHook(pTargetFunction);}MH_Uninitialize();printf("[INFO] Hook uninstalled\n");
}

然后指定 头文件,链接文件,截图如下:


上面的 Hook_GarbageCollect 函数就是回调的地方,我用 printf 输出当前 GarbageCollect 参数信息, 接下来就是 C# 侧了,把生成好的 ConsoleApplication2.dll 丢到 C# 的 bin 目录下,参考代码如下:


using System;
using System.Runtime.InteropServices;class Program
{[DllImport("ConsoleApplication2.dll", CallingConvention = CallingConvention.StdCall)]public static extern bool InstallGCHook();[DllImport("ConsoleApplication2.dll", CallingConvention = CallingConvention.StdCall)]public static extern void UninstallGCHook();static void Main(){try{if (InstallGCHook()){Console.WriteLine("Hook installed. Press any key to exit...");for (int i = 0; i < 3; i++){Console.WriteLine($"Triggering GC #{i}...");GC.Collect();Thread.Sleep(1000);}}}finally{UninstallGCHook();}}
}

最后运行程序,可以清楚的看到每次 GC.Collect() 都被成功拦截,截图如下:

如果你很想知道汇编层到底发生了什么变化,可以用 windbg 观察便知,截图如下,真的太完美了,经典的 jmp 跳转。

2. 相对偏移 0x30E670 的疑问

相信有不少人阅读代码之后,会对 return (BYTE*)hCoreCLR + 0x30E670; 中的 0x30E670 感兴趣,其实这条语句表示函数coreclr!WKS::GCHeap::GarbageCollect 的入口地址,其中的 0x30E670 偏移是怎么知道的呢? 我是用 windbg 观测的,计算如下:


0:000> lmvm coreclr
Browse full module list
start             end                 module name
00007ff8`508c0000 00007ff8`50d9d000   coreclr    (private pdb symbols)  
...0:000> x coreclr!WKS::GCHeap::GarbageCollect
00007ff8`50bce670 coreclr!WKS::GCHeap::GarbageCollect (int, bool, int)0:000> ? 00007ff8`50bce670 - 00007ff8`508c0000
Evaluate expression: 3204720 = 00000000`0030e670

卦中的 000000000030e670 便是,相信此时又会有人提一个疑问,不同版本不同环境下的 coreclr 都可以用这个 0x30e670 吗?很显然这是不对的, 0x30e670 本质上是相对 模块 的偏移地址,同版本的coreclr是没有问题的,不同版本因为代码结构不一样,自然相对地址就不一样,所以大家需要根据生产环境的coreclr版本提前计算一下偏移值即可。

三:总结

借助 harmony,minhook 两大工具可以黑进三大代码领域 .netsdk,win32,coreclr,这在.NET高级调试体系下是一枚核武的存在,相信这篇文章也给这家 .NET公司 解决场景问题提供了一个思考点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87392.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87392.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87392.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DPI深度检索原理和架构

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; DPI&#xff08;深度包检测&#xff09;技术通过**透视网络载荷内容**实现精细化流量管控与威胁检测&#xff0c;其核心在于突破传统防火墙仅检查IP/端口等表层信息的局限&#xff0c;对**应用层数据**进…

QT Creator的返回到上一步、下一步的快捷键是什么?

在 Qt Creator 中&#xff0c;用于导航的 返回上一步 (Back) 和 前进下一步 (Forward) 的快捷键如下&#xff1a; 默认快捷键&#xff1a; 功能Windows/LinuxmacOS返回上一步Alt ←Command [前进下一步Alt →Command ]

UI前端大数据处理策略优化:基于云计算的数据存储与计算

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 一、引言&#xff1a;大数据时代前端处理的挑战与云计算破局 在数字化转型的浪潮中&#xff…

机器学习基础 多层感知机

机器学习基础 多层感知机 文章目录 机器学习基础 多层感知机1. 多层感知机1.1 线性模型的失效1.2 感知机1.3 感知机的收敛定理1.4 从线性到非线性1.5 多层感知机的定义和实现 参考 1. 多层感知机 1.1 线性模型的失效 ​ 在李沐《动手学深度学习》中有这样的描述&#xff1a; …

关于安装Ollama大语言模型本地部署工具

一、Ollama 安装方法概述 Ollama 是一个开源的大型语言模型(LLM)本地部署工具&#xff0c;支持在 Windows、macOS 和 Linux 系统上运行。它简化了在本地计算机上运行和管理大语言模型的流程&#xff0c;让开发者能够轻松部署各种开源模型。 Windows 系统安装步骤 访问 Ollam…

html配置rem实现页面自适应

1.在js文件使用&#xff0c;建议放到全局js中 // 全局js文件 $(function () {// 设置根目录字体大小var baseSize 16; // 设计稿的基准字体大小&#xff0c;通常是16pxvar baseWidth 750; // 设计稿的基准宽度&#xff0c;通常是750pxfunction adjustFontSize() {const widt…

POI实现文档的图片的提取和替换

1. 简介 在日常办公自动化开发中&#xff0c;常常需要对 Word 文档中的图片进行批量提取、保存&#xff0c;甚至将图片替换为自定义的文本或链接。Apache POI 是一款强大的 Java 开源库&#xff0c;支持对 Microsoft Office 文档&#xff08;包括 Word、Excel、PowerPoint 等&…

毫米波雷达 – 深度学习

目录 数据表示 公开数据库 未来发展方向 稀疏点云 + 深度学习 直接处理点云 (1/2) 候选生成+特征提取+候选分类(DL* ) 候选生成+特征提取+候选分类(DL) 直接处理点云 (2/2) 候选生成+特征提取(DL)+候选分类(DL) 网格数据+端对端检测(DL) 稠密数据块 + 深度学习 直接…

Redis——常用指令汇总指南(一)

目录 1.set & get ①set指令 ②get指令 2.keys 3.del 4.expire & setex & psetex 5.ttl 6.exists 7.setnx 8.flushall 9.object encoding 10. type 1.set & get set & get指令中key和value都是字符串&#xff0c;但是不需要加单引号或双引号。 …

PDF处理控件Aspose.PDF教程:在 Java 中删除 PDF 页面

您是否需要使用 Java 从PDF文档中删除特定页面&#xff1f;无论您是要清理空白页、删除机密部分&#xff0c;还是仅仅在分发前调整内容&#xff0c;以编程方式操作 PDF 页面的能力都将大有裨益。本指南将向您展示如何借助Aspose.PDF仅用几行代码删除不需要的页面。让我们深入了…

RediSearch 字段类型与配置选项

1. 数值字段&#xff08;NUMERIC&#xff09; 用途&#xff1a;存储整数或浮点数&#xff0c;可进行范围查询与排序。 选项&#xff1a; SORTABLE&#xff1a;允许用 SORTBY 排序NOINDEX&#xff1a;不参与索引&#xff0c;仅供返回 定义语法 FT.CREATE idx ON HASH PREFIX…

PHP Yii2 安装SQL Server扩展-MAC M4 Pro芯片

MAC M4 Pro芯片版本&#xff0c;千锤百炼编译十几次终于成功 # 设置基础镜像并强制使用 x86_64 架构&#xff08;适配 M4 芯片&#xff09; FROM --platformlinux/amd64 php:8.1-fpm-alpine3.18WORKDIR /var/www/html# 可选&#xff1a;设置时区 ARG TZAsia/Shanghai ENV TZ${…

HTML初学者第二天

<1>HTML的语法规范 1.1标签 -双标签&#xff1a;如 <html></html> 前面的叫开始标签&#xff0c;后面的叫结束标签。 -单标签&#xff1a;如 <br /> 1.2基本语法概述 -HTML标签是由尖括号包围的关键词&#xff0c;例如<html>。 -HTML标…

【加解密与C】HASH系列(二) SHA

SHA&#xff08;安全散列算法&#xff09;简介 SHA&#xff08;Secure Hash Algorithm&#xff09;是由美国国家安全局&#xff08;NSA&#xff09;设计的一系列密码散列函数&#xff0c;用于将任意长度的数据转换为固定长度的散列值。SHA家族包括SHA-1、SHA-2&#xff08;含S…

【Python】进阶 - 数据结构与算法

系列篇章&#x1f389; No.文章1【Python】基础知识&#xff08;详细&#xff09;&#x1f680;2【Python】基础 - 循环、容器类型&#x1f680;3【Python】基础 - 推导式、函数&#x1f680;4【Python】基础 - 文件、异常、模块&#x1f680;5【Python】进阶 - 面向对象&…

【如何实现分布式压测中间件】

分布式压测中间件的原理及其实现 原理全链路追踪框架&#xff08;Trace&#xff09;MQ中间件数据库分布式缓存中间件&#xff08;Redis&#xff09;分库分表中间件 原理 通过大量阅读中间件源码&#xff0c;开源社区调研&#xff0c;得到设计原理&#xff1a; &#xff08;1&a…

Qt进程间保活方案:详解如何实现进程间通信与自动保活机制

目录 摘要 一、进程间保活的基本原理 二、具体步骤及代码示例 三、常见问题与优化 四、总体方案 摘要 在一些需要长时间运行的应用程序中&#xff0c;确保进程在意外退出时能够自动重启是一项非常重要的任务。尤其是在嵌入式开发、后台服务以及需要高可用性的场景下&#x…

Python-内置数据结构-list-tuple-bubble-字符串-bytes-bytesarray-切片-学习笔记

欠4年前自己的一份笔记&#xff0c;献给今后的自己。 分类 数值型 int、float、complex、bool 序列对象 字符串 str 列表 list tuple 键值对 集合set 字典dict 数值型 int、float、complex、bool都是class&#x…

利用事务钩子函数解决业务异步发送问题

利用事务钩子函数解决业务异步发送问题 一、问题背景二、实现方案1、生产者代码2、消费者代码 三、测试与验证1、未开启事务场景2、开启事务场景 四、项目结构及源码 一、问题背景 在某项业务中&#xff0c;需要在事务完成后&#xff0c;写入日志到某数据库中。需要要么都成功&…

uniapp选择相册

概述 一款针对Android平台下的图片选择器&#xff0c;支持从相册获取图片、视频、音频&拍照&#xff0c;支持裁剪(单图or多图裁剪)、压缩、主题自定义配置等功能&#xff0c;支持动态获取权限&适配Android 5.0系统的开源图片选择框架。 支持Uniapp和Uniapp X下的Vue2、…