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


    0x01 前言:

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


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

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

    以上过程被称为 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 进行查阅:
    在这里插入图片描述

    进入以后选择对应的操作系统 (当前需要运行的 Java 程序在什么操作系统上,以 Windows 为例):
    在这里插入图片描述 跳转以后 Ctrl + f 搜索 Debug 关键字如下:
    在这里插入图片描述

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

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

    在这里插入图片描述在这里插入图片描述

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

    在这里插入图片描述


    👏👏 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 不截断输出完整的容器描述,就可以看到容器名称以及容器中的路径。

    在这里插入图片描述

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

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

    在这里插入图片描述
    4、 IDEA 配置 Remote Debug,点击 Debug 运行即可。

    在这里插入图片描述
    测试发现远程 debug 失败… 到底是什么原因呢?

    请添加图片描述

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

    在这里插入图片描述Project Structure,Modules->Dependencies 添加要调试的class文件的目录 BOOT-INF

    在这里插入图片描述
    再次测试远程断点调试是否生效:

    在这里插入图片描述远程断点调试成功!


    0x04 提醒:

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

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

  • 相关阅读:
    稻草材料优选养牛户制作优质青贮饲料 国稻种芯现代饲料规划
    代码随想录--链表-反转链表
    【2023,学点儿新Java-16】编程语言的学习方法总结 | 编程的本质和架构 | 如何深度理解编程知识和技能 | 如何成为优秀的软件开发工程师 | 附:Java初学者的困惑!
    基本微信小程序的新冠疫苗预约小程序
    Vue入门
    【数据库】04_语法
    GO语言之Goroutine和channel
    获奖喜讯 | 中恒科技连获第十一届“龙图杯”全国BIM大赛三项大奖
    [Linux打怪升级之路]-缓冲区
    计算机毕业设计hadoop+spark知识图谱课程推荐系统 课程预测系统 课程大数据 课程数据分析 课程大屏 mooc慕课推荐系统 大数据毕业设计
  • 原文地址:https://blog.csdn.net/haduwi/article/details/126296308