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
的实现原理,可以参考这个系列的文章:
- How debuggers work: Part 1 - Basics
- How debuggers work: Part 2 - Breakpoints
- How debuggers work: Part 3 - Debugging information
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
,并且让container
以privileged mode
运行。第一步是关掉正在运行的这个容器:
$ docker stop sleepy_wu
这个sleepy_wu
是正在运行的,普通模式运行的容器的name
。stop
命令关掉容器有一定时间,耐心等待即可。这个容器里面已经安装好了包,做好了配置,使用docker commit
命令把它保存成image
:
$ docker commit sleepy_wu
因为这个容器里面里面已经安装了不少软件包,因此保存过程也要花些时间,等待commit
完成。保存完成后,会生成一个新的image
,并给出新保存的image
的id
编号:
上面可以看到新保存的image
的id
为fcf2e1efd4f1...
,我们后续可以通过image
命令查看:
可以看到上面的image
列表里面,IMAGE ID
为fcf2e1efd4f1...
(完整的id
没有全部显示,前面这几位一般够标记一个container
了)的就是我们刚刚保存的,尺寸为183mb
。相比之下,另一个初始的alpine
这个image
只有5.53mb
。接下来我们用这个image
重建一个privileged mode
的container
:
$ docker run -it --privileged fcf2e1efd4fa sh
通过上面的命令,我们就创建了一个privileged mode
的container
,此时可以使用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文档:
- https://cs.brown.edu/courses/csci0330/docs/guides/gdb.pdf
- https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf
以上就是在容器当中运行gdb
的方法介绍。