JDK.attach 包解析
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 代码结构
-
VirtualMachine
是核心,通过操作它,来进行attach
等 -
AttachPermission
来负责权限控制功能 -
VirtualMachineDescriptor
vm的模型,一个数据对象,包含pid或者vmname信息 -
AttachProvider
各个JVM平台,自己负责对接实现。利用SPI
给VirtualMachine
的接口提供实现,截图中给出了OPENJDK14
中默认对Hotspot
的实现
3.2 模型类
AttachPermisson
用来做权限控制
权限目标名称 | 权限允许什么 | 允许此权限的风险 |
---|---|---|
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
。
AttachProvider
用来attach到jvm的接口。真正被使用的是各个厂家提供的jvm实现,是该类的子类。
2.3 核心类VirtualMacine
有两个功能
- loadagent
加载JPLTS agent
,这个agent
会加载agent.jar
包 并执行agentMain
或者premain
方法 - attach(attach)
依附到目标jvm - detach 离开
示例
在此示例中,我们附加到由进程标识符标识的Java虚拟机2177。然后,使用提供的参数在目标进程中启动JMX管理代理。最后,客户端从目标VM分离。
VirtualMachine可安全地供多个并发线程使用。
一个Jvm的实现
loadAgent() 方法
加载制作好的agent.jar
包,并执行premain domain
方法。
加载 JPLTS agent
,这个agent
会加载agent.jar
包 并执行 agentMain
或者premain
方法
源码就不贴了,这里整个类的执行过程。可以总结为和jvm建立连接后,向jvm
发送命令,传输agent.jar
的path
参数,并触发其中的premain
或者domain
方法
loadAgentPath()
用来加载 native
方法库,即用他语言写的库。可以看出 load cmd
会有字段来标识是不是本地方法
attach()方法
这个方法的实现最终是在
provider
中实现,最终的结果是返回一个VirtualMachine
对象。可以和jvm
通信,让jvm
执行指令。
流程
- 检查权限
- 测试是否可以attach
- 成功attach
- 以上检查都没问题,返回
new VirtualMachineImpl(this, vmid)
;
detach()
hotspot的实现比较直接,直接将fd文件设置为null
监控JVM
如果对JMXBean不陌生的就可以知道,这些bean是用来获取JVM运行信息:堆内存,线程状态,gc次数等。VirtualMacine提供这样的函数,向jvm发送指令,暴露端口。通过这个端口就可以获取JVM运行信息。
startManagementAgent(Properties agentProperties)
startLocalManagementAgent()
仅暴露本地端口,最终是从本地fd读取jvm信息。