JDK.attach 包解析


和JVM通信,发送指令请求

一、描述

JDK6开始引入,除了Solaris平台的Sun JVM支持远程的Attach,在其他平台都只允许Attach到本地的JVM上。

用Java语言编写的工具使用此API附加到目标虚拟机(VM),并将其tool agent 加载到目标VM中。

什么是attach机制?

说简单点就是jvm提供一种jvm进程间通信的能力,能让一个进程传命令给另外一个进程,并让它执行内部的一些操作,比如说我们为了让另外一个jvm进程把线程dump出来,那么我们跑了一个jstack的进程,然后传了个pid的参数,告诉它要哪个进程进行线程dump,既然是两个进程,那肯定涉及到进程间通信,以及传输协议的定义,比如要执行什么操作,传了什么参数等

涉及进程间通信。启动Attach Listener进程和VM进程通信。交换数据的方式使用的是SOCKET
约束
除了 Attach API规定的API功能。还依赖针对JVM的具体实现,如果jvm嗯实现不支持,api也没有用。

功能

  • 内存dump
  • 线程dump
  • 类信息统计(比如加载的类及大小以及实例个数等)
  • 动态加载agent
  • 动态设置vm flag(但是并不是所有的flag都可以设置的,因为有些flag是在jvm启动过程中使用的,是一次性的)
  • 打印vm flag
  • 获取系统属性等

二、实现原理

能够说明ATTCH的作用,也提供更高阶,更细致的分析。

概括性文档

详细流程分析

说简单点就是jvm提供一种jvm进程间通信的能力,能让一个进程传命令给另外一个进程,并让它执行内部的一些操作,比如说我们为了让另外一个jvm进程把线程dump出来,那么我们跑了一个jstack的进程,然后传了个pid的参数,告诉它要哪个进程进行线程dump,既然是两个进程,那肯定涉及到进程间通信,以及传输协议的定义,比如要执行什么操作,传了什么参数等

三、代码分析

3.1 代码结构

JDK.attach 包解析
JDK.attach 包解析

  • VirtualMachine是核心,通过操作它,来进行attach
  • AttachPermission来负责权限控制功能
  • VirtualMachineDescriptor vm的模型,一个数据对象,包含pid或者vmname信息
  • AttachProvider各个JVM平台,自己负责对接实现。利用SPIVirtualMachine的接口提供实现,截图中给出了OPENJDK14中默认对Hotspot的实现

3.2 模型类

AttachPermisson

用来做权限控制

JDK.attach 包解析

权限目标名称 权限允许什么 允许此权限的风险
attachVirtualMachine 能够附加到另一个Java虚拟机并将代理加载到该VM。 这使攻击者可以控制目标VM,这可能会导致目标VM行为异常。
createAttachProvider 能够创建AttachProvider实例。 这使攻击者可以创建AttachProvider,该AttachProvider可以潜在地用于附加到其他Java虚拟机。

程序员通常不会直接创建AttachPermission对象。而是由安全策略代码基于读取安全策略文件来创建它们。

jvm在启动时可以设置一些权限,比如为了安全考虑禁止attach的行为。这个类就是在attach时被调用来检测是否能够attach

VirtualMachineDescriptor

JVM信息的Bean。jvm有很多种,用来保存具体jvm的信息。

VirtualMachineDescriptor是用于描述Java虚拟机的容器类。它封装了标识目标虚拟机的标识符,以及AttachProvider在尝试附加到虚拟机时应使用的对它的引用。该标识符取决于实现,但是通常是每个Java虚拟机都在其自己的操作系统进程中运行的进程标识符(或pid)环境。
VirtualMachineDescriptor也有一个displayName。显示名称通常是工具可能显示给用户的人类可读字符串。例如,显示系统上运行的Java虚拟机列表的工具可能会使用显示名称而不是标识符。阿VirtualMachineDescriptor可以在没有被创建的显示名称。在这种情况下,标识符用作显示名称。
VirtualMachineDescriptor实例通常是通过调用该VirtualMachine.list() 方法来创建的。这将返回描述符的完整列表,以描述所有已安装的Java虚拟机attach providers
JDK.attach 包解析

AttachProvider

用来attach到jvm的接口。真正被使用的是各个厂家提供的jvm实现,是该类的子类。
JDK.attach 包解析

2.3 核心类VirtualMacine

有两个功能

  • loadagent
    加载 JPLTS agent,这个agent会加载agent.jar包 并执行 agentMain或者premain方法
  • attach(attach)
    依附到目标jvm
  • detach 离开

示例
JDK.attach 包解析

在此示例中,我们附加到由进程标识符标识的Java虚拟机2177。然后,使用提供的参数在目标进程中启动JMX管理代理。最后,客户端从目标VM分离。

VirtualMachine可安全地供多个并发线程使用。

一个Jvm的实现
JDK.attach 包解析

loadAgent() 方法

加载制作好的agent.jar包,并执行premain domain方法。

加载 JPLTS agent,这个agent会加载agent.jar包 并执行 agentMain或者premain方法

源码就不贴了,这里整个类的执行过程。可以总结为和jvm建立连接后,向jvm发送命令,传输agent.jarpath参数,并触发其中的premain或者domain方法
JDK.attach 包解析

loadAgentPath()

用来加载 native方法库,即用他语言写的库。可以看出 load cmd 会有字段来标识是不是本地方法JDK.attach 包解析

attach()方法

这个方法的实现最终是在provider中实现,最终的结果是返回一个VirtualMachine对象。可以和jvm通信,让jvm执行指令。

JDK.attach 包解析
流程

  • 检查权限
  • 测试是否可以attach
  • 成功attach
  • 以上检查都没问题,返回new VirtualMachineImpl(this, vmid);

JDK.attach 包解析

detach()

hotspot的实现比较直接,直接将fd文件设置为null
JDK.attach 包解析

监控JVM

如果对JMXBean不陌生的就可以知道,这些bean是用来获取JVM运行信息:堆内存,线程状态,gc次数等。VirtualMacine提供这样的函数,向jvm发送指令,暴露端口。通过这个端口就可以获取JVM运行信息。

startManagementAgent(Properties agentProperties)

JDK.attach 包解析

startLocalManagementAgent()
仅暴露本地端口,最终是从本地fd读取jvm信息。
JDK.attach 包解析