使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)

文章目录

    • 0x01 前言:
    • 0x02 解决思路:(有兴趣可了解)
    • 0x03 具体操作:
    • 0x04 提醒:

0x01 前言:

最近在复现&分析 Java 领域历史相关漏洞,学习学习 Java 框架漏洞代码审计思路。分析原理少不了调试,然而每次都得搭建漏洞环境,又是构建maven, 又是引入依赖,挺繁琐的。Github 有很多其他同行利用 Docker 搭建好的漏洞环境镜像,一键拉取运行即可, 但是问题来了, 运行在 docker 容器里的程序我怎么断点调试呢?手上没有构建镜像时的源代码,该怎么办呢?


0x02 解决思路:(有兴趣可了解)

Docker 容器里面一般运行的是 java 打包的 jar 包,那么就等于要解决怎么远程调试正在运行的 jar 包?我们先看看本地项目市如何 Debug 的,仔细的你是否察觉到平时IDEA上Debug本地项目的时候都会出现的一行信息?
在这里插入图片描述
为什么 Debug 会出现这么一行信息呢?由于博主学过一些 JVM 虚拟机相关知识 (Java 开发必须了解 JVM),IDEA Debug 大致过程如下:
使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第1张图片

以上过程被称为 JPDA调用体系。

JPDA(Java Platform Debugger Architecture)是 sun 公司开发的 java平台调试体系, 它主要有三个层次组成,即 Java 虚拟机工具接口 (JVMTI) ,Java 调试线协议(JDWP)以及 Java 调试接口(JDI)

  • JVMTI (JVMDI): jdk1.4 之前称为JVMDI,之后改为了JVMTI,它是虚拟机的本地接口,其相当于 Thread 的 sleep、yield native 方法
  • JDWP(Java Debug Wire Protocol):java调试网络协议,其描述了调试信息的格式,以及在被调试的进程(server)和调试器(client)之间传输的请求
  • JDI:java调试接口,虚拟机的高级接口,调试器(client)自己实现 JDI 接口,比如 idea 等其他编译器。

综上我们知道了 IDEA 调试的原理大致如下:

1、先建立起了 socket 连接
2、将断点位置创建了断点事件通过 JDI 接口传给了 服务端(程序端)的 VM,VM 调用 suspend 将 VM 挂起
3、VM 挂起之后将客户端需要获取的 VM 信息返回给客户端,返回之后 VM resume 恢复其运行状态
4、客户端获取到 VM 返回的信息之后可以通过不同的方式展示给客户端


好了,讲了这么多,现在我们知道了本地调试其实也可以认为是远程调试,IDEA 通过 127.0.0.1:20256(端口随机)与 JVM 进行 socket 通信。那么这个端口到底怎么设置的?这就要搬出咱们的 jdk-8xxx-docs-all 官方完整文档 来查阅了。端口是由 JVM 创建的这毋庸置疑,所以直接点击最下层的 Java HotSpot Client and Server VM 进行查阅:
使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第2张图片

进入以后选择对应的操作系统 (当前需要运行的 Java 程序在什么操作系统上,以 Windows 为例):
在这里插入图片描述 跳转以后 Ctrl + f 搜索 Debug 关键字如下:
使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第3张图片

文档中详细描述了,启动 Java 程序之前,如果需要 Debug,需要添加参数:-agentlib:jdwp=transport=dt_socket,server=y,address=xxxx 。adderss 填写自定义端口。

现在咱们知道 JVM 如何手动开通 Debug 端口了,但是又一个问题来了,IDEA 如何自定义连接 JVM 的呢 ?只要不会咱们就翻官方文档,官方文档往往会带来惊喜。具体如何连接,请自行前往查阅: Tutorial: Remote debug

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第4张图片使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第5张图片

由于咱们是引入的别人的docker镜像,咱们手上又没有构建docker镜像时的源代码,咱们最多只能提取 docker 容器中的 jar 包。突然想到我们平时 IDEA 引入第三方 jar 包,只要 Add as Library 操作,jar 包就被打开了,可以看到 “源代码”,并且 jar 包内的ClassName就可以被我们实例化调用,还可以在 jar 包的.class 文件里打断点进行调试。咱们不妨试试这样行不行得通? 事实上是可行的!

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第6张图片


OK ,经过这一路上的思考过程,远程调试 Docker 中运行的 Java 程序也就 so easy 啦!

实现步骤:

1、在 docker-compose.yml 配置映射端口让 jvm debug 端口能外部访问。
2、在 docker-compose.yml 中使用 command 字段添加自定义启动命令:java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=xxxx -jar jar包名称.jar
3、容器启动后,从容器中把运行的 jar 包复制出来,新建一个文件夹 ,IDEA 点击 Open 打开这个文件夹, 复制粘贴 jar 包, 右键jar包,选择 Add as Lirary 添加到项目依赖库中,并在代码上打上几个断点。
4、 IDEA 配置 Remote Debug,点击 Debug 运行即可。


0x03 具体操作:

以 fastjson 反序列化漏洞docker镜像为例进行远程调试。

1、在 docker-compose.yml 配置映射端口让 jvm debug 端口能外部访问。

在这里插入图片描述

2、在 docker-compose.yml 中使用 command 字段添加自定义启动命令:java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=xxxx -jar jar包名称.jar

是不是突然发现自己不知道 jar 包名字叫啥? 咱们可以先启动容器,然后执行 docker ps --no-trunc 不截断输出完整的容器描述,就可以看到容器名称以及容器中的路径。

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第7张图片

3、容器启动后,从容器中把运行的 jar 包复制出来,新建一个文件夹 (例:CVE-2017-18349),IDEA 点击 Open 打开 CVE-2017-18349 文件夹, 复制粘贴 fastjsondemo.jar, 右键jar包,选择 Add as Lirary 添加到项目依赖库中,并在代码上打上几个断点。

启动容器后,执行命令:docker cp 容器id:jar包路径 目标路径, 将 jar 复制出来。
在这里插入图片描述

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第8张图片
4、 IDEA 配置 Remote Debug,点击 Debug 运行即可。

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第9张图片
测试发现远程 debug 失败… 到底是什么原因呢?

问题解决: 打开一个自己原先开发的项目,瞅一瞅引入的 jar 包结构是啥样的, 比较区别

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第10张图片Project Structure,Modules->Dependencies 添加要调试的class文件的目录 BOOT-INF

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第11张图片
再次测试远程断点调试是否生效:

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第12张图片
但是还是有问题,断点调试无法进入第三方依赖包中,我们需要把lib文件夹复制到根目录,并右键 Add as Library 。

使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第13张图片
使用 IDEA 快速远程调试 Docker 中运行的 Java 应用程序 (附解决思考过程)_第14张图片

最终远程断点调试的前期准备全部完成,可以快快乐乐的打断点了~


0x04 提醒:

❗️❗️❗️如果你是一名Java开发人员,请注意如果生产环境下迫不得已需要远程调试,调试完一定要关闭JDWP服务,或者JDWP服务监听的端口不对公网开放。❗️❗️❗️

附一篇优秀的 JDWP 服务漏洞利用文章 ===> JDWP调试接口远程命令执行漏洞原理分析

你可能感兴趣的:(Docker,java,intellij-idea,docker)