阿男的小窝

View the Project on GitHub

gdb在docker容器里面的使用

在之前的文章里(配置基于容器的的汇编环境),讲解了如何配置一个基于docker的汇编开发环境,本文继续介绍如何在container里面使用gdb进行代码调试。还是继续使用alpine这个linux发行版本,在之前文章的基础上,继续安装gdb

$ apk add gdb

gdb的依赖包也比较多,耐心等待安装完成:

安装完成,试着加载并调试之前的文章里编译好的代码:

可以看到,gdb能够加载程序,却不能正常运行程序,给出的错误信息是:

warning: Error disabling address space randomization: Operation not permitted

以及:

warning: ptrace: Operation not permitted

关于memory randomization,指的是每次运行程序的时候打乱内存的地址,因此每次加载程序的时候,代码内存的逻辑地址都会变化,和预先设定好的地址不一样,这样的好处是黑客不能依赖程序的内存地址来注入危险代码,这是kernel的安全措施,属于kernel的功能。关于这个话题,可以阅读这篇文档:

关于ptrace,涉及到gdb的实现原理,可以参考这个系列的文章:

gdb 运行程序出错,是因为gdb在请求关掉kernel的ASLR失败,提示没有权限。但是我们在容器里是用root账号运行gdb的,为什么还没有权限关掉kernel的ASLR呢。

因为在container里面,内核是docker提供的,docker掌握着kernel的权限。所以,需要让docker允许容器对kernel进行一些特权操作。docker提供了这个选项,叫做privileged mode

(来源:docker - Privileged containers and capabilities - Stack Overflow

需要注意的是,使用privileged mode来运行容器,是个危险的操作,因为你可以通过容器里面的root账号直接操作host资源,从而破坏了容器与host之间的隔离,可以直接对host造成破坏。关于privileged mode的使用,可以查看这篇文档:

学习了基本概念,我们来对容器进行调整,让gdb正常工作。因为已经运行起来的容器不支持重新以privileged mode运行,所以我们要把当前的装好了各种软件包的容器进行存档,保存成image,然后再从image重新创建container,并且让containerprivileged mode运行。第一步是关掉正在运行的这个容器:

$ docker stop sleepy_wu

这个sleepy_wu是正在运行的,普通模式运行的容器的namestop命令关掉容器有一定时间,耐心等待即可。这个容器里面已经安装好了包,做好了配置,使用docker commit命令把它保存成image

$ docker commit sleepy_wu

因为这个容器里面里面已经安装了不少软件包,因此保存过程也要花些时间,等待commit完成。保存完成后,会生成一个新的image,并给出新保存的imageid编号:

上面可以看到新保存的imageidfcf2e1efd4f1...,我们后续可以通过image命令查看:

可以看到上面的image列表里面,IMAGE IDfcf2e1efd4f1...(完整的id没有全部显示,前面这几位一般够标记一个container了)的就是我们刚刚保存的,尺寸为183mb。相比之下,另一个初始的alpine这个image只有5.53mb。接下来我们用这个image重建一个privileged modecontainer

$ docker run -it --privileged fcf2e1efd4fa sh

通过上面的命令,我们就创建了一个privileged modecontainer,此时可以使用docker ps命令查看容器:

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
71afab756c69        fcf2e1efd4f1        "sh"                12 hours ago        Up 20 seconds                                  suspicious_hawking
c27a1e74e773        alpine              "sh"                43 hours ago        Exited (255) 2 hours ago                       sleepy_wu

可以看到新创建了一个名为suspicious_hawking的容器。此时我们在容器里使用gdb试试看:

可以看到gdb已经可以正常工作了。设个断点,单步执行指令试试看:

试试看查看寄存器的值:

这样我们就拥有一个可以正常使用gdb来调试代码的容器了。关于gdb的日常使用,可以参考这两篇pdf文档:

以上就是在容器当中运行gdb的方法介绍。