大家还记得上一篇的测试代码吗?我们用了:

Console.WriteLine("Function Pointer: 0x{0:x16}", Marshal.GetFunctionPointerForDelegate(addDelegate).ToInt64());

来获得 委托函数指针 地址,通过这个突破口最终实现了 动态代码 的调试,这种方式可以是可以,但很显然这是侵入式的,那有没有办法实现 非侵入 调试动态代码呢?在 .NET高级调试 这本书上还真给找到了,方法就是在  JIT 编译动态方法时进行拦截,获取其中的 方法描述符

为了方便讲解,先上测试代码:

class Program{private delegate int AddDelegate(int a, int b);static void Main(string[] args){var dynamicAdd = new DynamicMethod("Add", typeof(int), new[] { typeof(int), typeof(int) }, true);var il = dynamicAdd.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);il.Emit(OpCodes.Add);il.Emit(OpCodes.Ret);var addDelegate = (AddDelegate)dynamicAdd.CreateDelegate(typeof(AddDelegate));//Debugger.Break();//Console.WriteLine("Function Pointer: 0x{0:x16}", Marshal.GetFunctionPointerForDelegate(addDelegate).ToInt64());Console.WriteLine(addDelegate(10, 20));}}

可以看到,我把上面两行侵入式的代码给屏蔽掉了,接下来在 il.Emit(OpCodes.Ret); 处下断点,目的是为了在 clr 加载后寻找 JIT的 compileMethod 方法。

0:000> !mbp Program.cs 28
The CLR has not yet been initialized in the process.
Breakpoint resolution will be attempted when the CLR is initialized.
0:000> g
ModLoad: 76910000 7698a000   C:\Windows\SysWOW64\ADVAPI32.dll
...
ModLoad: 77190000 77226000   C:\Windows\SysWOW64\OLEAUT32.dll
Breakpoint: JIT notification received for method ConsoleApp1.Program.Main(System.String[]) in AppDomain 00783758.
Breakpoint set at ConsoleApp1.Program.Main(System.String[]) in AppDomain 00783758.
Breakpoint 1 hit
eax=00000001 ebx=0019f5ac ecx=023c3684 edx=ffffffff esi=023c230c edi=0019f4fc
eip=048a0a02 esp=0019f4ac ebp=0019f508 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
048a0a02 b901000000      mov     ecx,1

接下来可以用 x 命令模糊搜索 compileMethod 签名,找出签名是为了更好的下断点。

0:000> x *!*compileMethod*
...
61413700          clrjit!CILJit::compileMethod (class ICorJitInfo *, struct CORINFO_METHOD_INFO *, unsigned int, unsigned char **, unsigned long *)

可以看到 compileMethod 的完整签名是 clrjit!CILJit::compileMethod, 并且它的方法入口点地址是 61413700,有了它就可以对其下断点啦!

0:000> bp 61413700
0:000> g
Breakpoint 0 hit
eax=61494698 ebx=80000004 ecx=61413700 edx=00005c10 esi=6148b3fc edi=0019efa4
eip=61413700 esp=0019ede0 ebp=0019ee38 iopl=0         nv up ei ng nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000282
clrjit!CILJit::compileMethod:
61413700 55              push    ebp
0:000> kb# ChildEBP RetAddr      Args to Child
00 0019ee38 62a4ccc3     61494698 0019efa4 0019ef1c clrjit!CILJit::compileMethod [f:\dd\ndp\clr\src\jit32\ee_il_dll.cpp @ 151]
01 0019ee38 62a4cd9b     0019ef1c 0019f06c 0019f024 clr!invokeCompileMethodHelper+0x10b

很开心,成功命中,接下来提取 compileMethod 方法的第三个参数,它就是需要编译方法所指向的 方法描述符 地址,可以用 dp 给提取出来。

0:000> dp 0019ef1c L1
0019ef1c  0071537c
0:000> !dumpmd 0071537c
Method Name:  DynamicClass.Add(Int32, Int32)
Class:        007152e8
MethodTable:  0071533c
mdToken:      06000000
Module:       00714ea8
IsJitted:     no
CodeAddr:     ffffffff
Transparency: Transparent

方法描述符果然给调出来了,但这里的方法字节码是 CodeAddr: ffffffff ,说明此时动态方法还没有开始编译,为了能够使其编译,我们在 Console.WriteLine(addDelegate(10, 20)); 处再下一个断点,因为代码到此处时, JIT 肯定编译了该办法,自然就能看到编译后的 CodeAddr 地址。

0:000> !mbp Program.cs 35
Breakpoint set at ConsoleApp1.Program.Main(System.String[]) in AppDomain 00783758.
0:000> g
Breakpoint 3 hit
eax=023c5f88 ebx=0019f5ac ecx=023c5f3c edx=00008f17 esi=023c230c edi=0019f4fc
eip=048a0a9b esp=0019f4ac ebp=0019f508 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
048a0a9b 6a14            push    14h
0:000> !dumpmd 0071537c
Method Name:  DynamicClass.Add(Int32, Int32)
Class:        007152e8
MethodTable:  0071533c
mdToken:      06000000
Module:       00714ea8
IsJitted:     yes
CodeAddr:     04a00050
Transparency: Transparent

可以看到,此时的 CodeAddr: 04a00050 ,也就表明已经编译完成了,接下来继续 bp 。

0:000> bp 04a00050
0:000> g
Breakpoint 4 hit
eax=023c5f98 ebx=0019f5ac ecx=0000000a edx=00000014 esi=023c230c edi=0019f4fc
eip=04a00050 esp=0019f4a8 ebp=0019f508 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
04a00050 8bc1            mov     eax,ecx

可以看到,全部搞定,非侵入式,

.NET高级调试 | 通过JIT拦截无侵入调试 C# Emit 生成的动态代码相关推荐

  1. 微服务应用性能分析实战11 资源节点树:通过 Sentinel 无侵入实现流量链生成规则

    前两个课时,我们重点围绕 SkyWalking 进行了原理解析.讲完了 SkyWalking,接下来我们就进入 Sentinel. 这一讲开始,我将用两个课时围绕 Sentinel 的技术骨架展开,来 ...

  2. Spring Boot 无侵入式 实现 API 接口统一 JSON 格式返回

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 无侵入式 统一返回JSON格式 其实本没有没打算写这篇博客 ...

  3. JAVA服务治理实践之无侵入的应用服务监控--转

    原文地址:http://chuansong.me/n/603660351655 之前在分享微智能的话题中提到了应用服务监控,本文将会着重介绍Java环境下如何实现无侵入的监控,以及无侵入模式对实现各种 ...

  4. 基于WASM的无侵入式全链路A/B Test实践

    简介:我们都知道,服务网格(ServiceMesh)可以为运行其上的微服务提供无侵入式的流量治理能力.通过配置VirtualService和DestinationRule,即可实现流量管理.超时重试. ...

  5. Spring Boot 无侵入式 实现API接口统一JSON格式返回

    作者 | 小魏小魏我们去那里呀 来源 | blog.csdn.net/qq_34347620/article/details/102239179 无侵入式 统一返回JSON格式 其实本没有没打算写这篇 ...

  6. spring学习笔记(14)引介增强详解:定时器实例:无侵入式动态增强类功能

    引介增强实例需求 在前面我们已经提到了前置.后置.环绕.最终.异常等增强形式,它们的增强对象都是针对方法级别的,而引介增强,则是对类级别的增强,我们可以通过引介增强为目标类添加新的属性和方法,更为诱人 ...

  7. Dexposed:Android平台免Root无侵入AOP框架

    本文来自阿里巴巴技术协会(ATA) 本文首发于 http://www.infoq.com/cn/news/2015/07/dexposed 近日,阿里巴巴无线事业部推出首个重量级Android开源项目 ...

  8. Android新技术学习——阿里巴巴免Root无侵入AOP框架Dexposed

    阿里巴巴无线事业部近期开源的Android平台下的无侵入运行期AOP框架Dexposed,该框架基于AOP思想,支持经典的AOP使用场景.可应用于日志记录,性能统计,安全控制.事务处理.异常处理等方面 ...

  9. delphi ui编辑工具源码_一种无侵入比swagger-ui兼容性更好更简单的API文档生成方案

    在后端项目中,难免遇到需要写接口文档方便第三方调用的场景,一般业界最常用的方案是使用swagger.Java项目中,一般采用springfox项目,它集成了swagger和swagger-ui,不需要 ...

最新文章

  1. 网游运营基础知识与专业术语
  2. input border IE6 bug
  3. 「JOISC 2020 Day4」治疗计划(线段树+dijkstra最短路)
  4. RuntimeError: DataLoader worker (pid(s) 13512, 280, 21040) exited unexpectedly
  5. 在res/values下创建attrs.xml
  6. spi app理解和编写测试
  7. 从C语言到C++的进阶之C到C++的转变(篇一)
  8. 基于visual Studio2013解决C语言竞赛题之1071打印工资
  9. Linux系统服务及其创建详解(service/chkconfig)
  10. 【缺陷识别】基于matlab GUI SVM金属表面缺陷分类与测量(带面板)【含Matlab源码 1652期】
  11. Mac UE各版本破解方法
  12. 欧氏空间距离和内积_希尔伯特空间(Hilbert Space)
  13. 如何使用IconFont?——矢量图标
  14. 北京计算机专业考研录取分数线,2018北京航空航天大学计算机考研复试分数线_计算机考研分数线...
  15. python-opencv第四期:threshold函数详解
  16. app 显示未验证应用解决
  17. linux 运行有道词典,Ubuntu中使用有道词典
  18. PLC-Recorder常用授权功能详解
  19. Java实现蓝桥杯二项式的系数规律
  20. 继续BT的研究-第二部份关于BT中的tracker

热门文章

  1. spring mvc 入门配置
  2. 在Centos中yum安装和卸载软件的使用方法
  3. 标准MD5 .Net,实现!的对与错!
  4. qemu+linux+x86+64,kvm 内部错误:无法找到适合 x86_64 的模拟器
  5. 基于.NET2.0的System.Net.Mail发送邮件Demo
  6. 用了30天整理的一些GO语言学习资料,2019请你加油
  7. [Swift]LeetCode1013. 将数组分成和相等的三个部分 | Partition Array Into Three Parts With Equal Sum...
  8. Visual Studio 2019 preview中体验C# 8.0新语法
  9. PHP 多维数组转json对象
  10. Windows下怎样安装Tomcat