这几天看了一下linux内核提权的一个漏洞,里面涉及到了驱动程序漏洞及驱动调试内容,由于各类linux操作系统版本的不同,如果不能在自己机器上亲自调试驱动程序,可以说即使给了漏洞利用的POC源码也根本无法成功利用。因为内核漏洞的利用涉及到指令集的POC构造,不同内核版本模块加载指令地址不同,导致即使有POC也根本无法利用,只有在自己系统中亲自调试,才能做出相应的修改,达到内核漏洞利用的效果。这样就要求我们对linux内核驱动的调试过程,调试方法有个深入了解。经过两天的各处查找,配置,调试,终于弄清楚了内核调试的基本方法,为之后内核调试,漏洞分析提供技术支持。
目标
在驱动程序开发或是内核漏洞分析过程中经常需要对内核模块进行调试。在通常情下对于驱动程序的调试是利用最直接的方式即打印调试的方式,在驱动程序中通过printk,加入调试信息。同时通过动态加载模块的方式,即可实现对驱动的动态调试,这也是最简单的调试方式。而对于内核漏洞的分析,由于linux系统是开源项目,所有不管对于应用程序的调试还是对内核驱动程序的调试都可以通过查看源码找到漏洞的触发点。
那如果想像调试用户态应用程序一样对内核驱动做动态的源码即调试或是更进一步的对驱动程序进行汇编级调试或是开发内核漏洞利用程序那又该怎么办呢?也许有人会说一般没有必要进行汇编级调试。但是在对内核漏洞利用过程中经常需要调试内核驱动程序,并且需要对内核驱动进行汇编指令级单步跟踪,这样才能确定程序的走向。或是我们需要构造特殊的指令块来完成某项功能。这样就对我们调试内核带来的新的挑战。
那内核里面又是怎么实现的呢,又该如果能够去跟进内核内部去调试呢?
本文就是要解决这个问题:在动态汇编调试用户态应用程序的同时,能够跟进应用程序的的系统调用接口,直接源码级或是汇编级的调试(如果没有符号表)调试驱动程序。
本文演示的程序是通过一个应用程序demo,调用自己写的一个驱动程序接口,通过在调试应用程序的是时候能够跟进调试驱动程序。搭建这样的环境我们使用了vmware虚拟机,该虚拟机使用普遍,安装简单。为了能够调试程序,需要一个目标机和一个客户机。
目标机是用来安装驱动程序,同时运行应用程序,应用程序会调用驱动程序中的接口。同时目标机自己调试应用程序(用户态使用GDB调试)。
客户机是用来连接客户机,同时在客户机中调试目标机中的驱动程序(使用GDB调试)。
需要注意几点:
- 为了能够能够调试目标机的驱动程序,要求目标机需要支持KGDB调试。
- 为了能让客户机与目标机通信,我们在vmware中配置这两台机器通过串口通信调试。
- 客户机如果要支持驱动的源码级调试需要将驱动程序的符号表加载的客户机的调试器中。
- 本文用的vmware虚拟机需要将目标机和客户机同时安装在虚拟机中。
- 由于在文章中会包含客户机和目标机的操作过程和调试过程,本文中会使用绿框表示客户机相关操作,使用红框表示目标机上的相关操作。
基础环境搭建
下图是一个调试应该程序与驱动程序的一个框图,两个操作系统都安装在虚拟机中,一个客户机,一个目标机,客户机通过串口调试目标机中的驱动程序。
1.1资源环境
Vmware虚拟机 |
Ubuntu操作系统 |
Ubuntu源码 |
1.2安装vmware虚拟机及ubuntu操作系统
这里使用的宿主机win7操作系统,所以直接在网上下载了VMWARE WORKSTATION 10.0.4版本的虚拟机。
安装虚拟机过程就省略。。。。
在安装完虚拟机后需要在虚拟机中安装好ubuntu操作系统,在虚拟机中安装ubuntu操作系统也省略。。。。
上面说到目标机和客户机两个操作系统,而这里我们在虚拟机里只安装了一个操作系统,先别急,后面就知道了。接下来从官网下载一个内核源码,如下图:
这里下载的是X86-64版本的3.2.86版本(上面忘了说了,之所以用X86-64版本是因为上面装的ubuntu操作系统也是这个架构)。
下载完成后将该源码拷贝到ubuntu虚拟机中,并解压,如下图所示:
编译内核
为了能够调试驱动程序需要让目标机的操作系统支持调试模式,这样就需要从新编译内核,让目标机支持调试模式。
2.1配置内核参数
首先进入目录linux-3.2.86,之后执行命令make menconfig,如下图所示:
就会出现如下图形界面:
这里每一项就是在编译内核之前需要选择的条目,可以根据需要来编译不同的条目。为了能够让操作系统支持内核级调试,需要打开KGDB调试开关参数,根据ubuntu版本不同该调试开关的位置有所不同。如下图所示: