使用VS-Code结合源码进行内核及内核模块远程调试的方法

本文来源自平安银河实验室

作者:朱文哲



>>>0x01 前言


事情的起因是我们在编写更新HIDS相关的内核模块和eBPF模块代码中有时会遇到一些奇怪的问题需要进行分析,为了分析这些问题就需要对Linux内核及内核模块进行调试,于是就有了这篇文章。


>>>0x02 准备内核调试分析环境


我们首先需要准备对应版本的内核调试环境。

我们考虑选用vmware自带的debugStub功能进行内核调试,首先需要手工编辑虚拟机的vmx文件并追加如下代码即可开启该功能:
# 启用64位的CPU调试功能,默认监听端口localhost:8864 debugStub.listen.guest64="TRUE"# 如遇到断点原因不明的失效,可以试试追加下面的设置。debugStub.hideBreakpoints="FALSE"

在完成vmware的配置后,我们就需要下载对应的内核代码进行重新编译从而加入debug符号信息。3.10.0-1160最新版本的内核源码可以在 https://vault.centos.org/7.9.2009/updates/Source/SPackages/kernel-3.10.0-1160.62.1.el7.centos.plus.src.rpm 下载。


具体的rpm包解压等命令如下:

# RPM解压命令rpm2cpio kernel-3.10.0-1160.62.1.el7.centos.plus.src.rpm | cpio -div# 解压源码tar -xvf linux-3.10.0-1160.62.1.el7.tar.xz# 复制当前内核编译参数cp -v /boot/config-$(uname -r) ./linux-3.10.0-1160.62.1.el7/.config # 此时还需要修改.config追加 CONFIG_DEBUG_INFO_BTF=y 参数,编译DEBUG符号信息

完成上述准备工作后就可以开始编译内核了。
# 编译模块, 这个会很耗时且会占用大量的磁盘和内存make modules_installmake install # 检查系统上可用的内核awk -F' '$1=="menuentry " {print $2}' /etc/grub2.cfg # 设置开机从新编译的内核启动grub2-set-default 0

此时生成的600M的vmlinux文件就是我们后续需要调试的带符号的内核文件了。

使用VS-Code结合源码进行内核及内核模块远程调试的方法

此外为了方便调试,我们还需要通过修改grub的启动参数禁用内核的kaslr功能。
# 打开/etc/default/grub, 找到GRUB_CMDLINE_LINUX_DEFAULT这行配置然后在最后追加nokaslr# 完成修改后执行如下命令生效grub2-mkconfig -o /boot/grub2/grub.cfg

由于我们已经拥有了内核源码且编译了带有符号的内核文件,接下来就可以使用vscode的远程GDB调试功能进行调试了。

如下图所示,新建一个GDB调试配置文件。

使用VS-Code结合源码进行内核及内核模块远程调试的方法

具体的配置如下:
{    // Use IntelliSense to learn about possible attributes.    // Hover to view descriptions of existing attributes.    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387    "version": "0.2.0",    "configurations": [      {        "name": "(gdb) linux",        "type": "cppdbg",        "request": "launch",        "program": "${workspaceRoot}/vmlinux",        "miDebuggerServerAddress": "localhost:8864",        "args": [],        "stopAtEntry": true,        "cwd": "${workspaceFolder}",        "environment": [],        "externalConsole": false,        "MIMode": "gdb",        "miDebuggerArgs": "-n",        "targetArchitecture": "x64",        "setupCommands": [          {            "text": "set arch i386:x86-64:intel",            "ignoreFailures": false          },          {            "text": "dir .",            "ignoreFailures": false          },          {            "text": "add-auto-load-safe-path ./",            "ignoreFailures": false          },          {            "text": "-enable-pretty-printing",            "ignoreFailures": true          }        ]      }    ]  }

完成上述配置后即可在代码处下断点进行调试了。

使用VS-Code结合源码进行内核及内核模块远程调试的方法



>>>0x03 调试内核模块的方法


在完成Linux内核调试的基础上,有时我们还需要对特定的内核模块进行调试分析。

首先我们需要在编译内核模块时追加-g -DDEBUG CFLAGS使内核模块能够被调试。
以Elkeid的LKM为例,可以追加debug选项。
debug:    @echo "|-----------------------------------|"    @echo "| building HIDS kernel module Debug |"    @echo "|-----------------------------------|"    $(MAKE) -C $(KERNEL_DIR) M=$(MODULE_DIR) modules EXTRA_CFLAGS="-g -DDEBUG"ifneq ($(BATCH), true)    $(MAKE) -C testendif
完成编译后先加载内核模块
使用VS-Code结合源码进行内核及内核模块远程调试的方法

接下来我们需要创建对应的内核模块调试配置, 需要注意的是sourceFileMap字段需要把内核模块源码的路径进行关联映射,不然就无法进行源码级的调试。

{    // Use IntelliSense to learn about possible attributes.    // Hover to view descriptions of existing attributes.    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387    "version": "0.2.0",    "configurations": [      {        "name": "(gdb) linux",        "type": "cppdbg",        "request": "launch",        "program": "${workspaceRoot}/vmlinux",        "miDebuggerServerAddress": "127.0.0.1:8864",        "args": [],        "stopAtEntry": true,        "cwd": "${workspaceFolder}",        "environment": [],        "externalConsole": false,        "MIMode": "gdb",        "miDebuggerArgs": "-n",        "targetArchitecture": "x64",        "sourceFileMap":{          "/root/Elkeid/driver/LKM/": "${workspaceFolder}/LKM"        },        "setupCommands": [          {            "text": "set arch i386:x86-64:intel",            "ignoreFailures": false          },          {            "text": "dir .",            "ignoreFailures": false          },          {            "text": "add-auto-load-safe-path ./",            "ignoreFailures": false          },          {            "text": "-enable-pretty-printing",            "ignoreFailures": true          }        ]      }    ]  }

为了调试,我们需要先查询到内核模块的内存地址,需要注意的是这个地址每次都会变。
cat /sys/module/hids_driver/sections/.textcat /sys/module/hids_driver/sections/.datacat /sys/module/hids_driver/sections/.bss # 或者执行一下命令自动生成echo "-exec add-symbol-file LKM/hids_driver.ko `cat /sys/module/hids_driver/sections/.text`"

执行结果如下图所示:
使用VS-Code结合源码进行内核及内核模块远程调试的方法

完成后我们就可以使用vs-code加载到内核上,并在debug console执行如下命令。

-exec add-symbol-file LKM/hids_driver.ko 0xffffffffc07db000

使用VS-Code结合源码进行内核及内核模块远程调试的方法

尝试在目标代码处下断点
使用VS-Code结合源码进行内核及内核模块远程调试的方法

此时我们就可以使用vscode对内核模块进行源码调试了。

使用VS-Code结合源码进行内核及内核模块远程调试的方法

同样的我们也可以结合gdb命令进行信息查看部分信息。

使用VS-Code结合源码进行内核及内核模块远程调试的方法

例如我们可以使用gdb命令查看recv_data中实际的数据是什么。
-exec x/100bx 0xffff8801753b44e0


使用VS-Code结合源码进行内核及内核模块远程调试的方法

或者查看query的数据是什么。
-exec x/s 0xffff8801753b44e0 + 12

使用VS-Code结合源码进行内核及内核模块远程调试的方法

至此,我们就具备了使用vscode结合源码对内核模块进行调试分析的能力。


该方法相比反复重新编译内核模块并使用printk进行调试的方法有一定的优势,但依然可能遇到优化导致的部分信息无法获取的情况,因此将该方法与printk结合能够更好的辅助我们对Linux内核模块进行调试。


>>>0x04 使用VirtualBox进行内核调试的方法


如果是使用VirtualBox虚拟机的话,在内核调试部分相比VMware会稍微复杂一些。

首先关闭virtualbox虚拟机,并创建用于调试的串口pipe。
使用VS-Code结合源码进行内核及内核模块远程调试的方法
成功设置并打开虚拟机后即可在宿主机的/tmp目录下发现debug-pipe管道。
使用VS-Code结合源码进行内核及内核模块远程调试的方法
使用socat将pipe转换为PTY, 之后就可以直接使用gdb命令remote命令进行连接调试了。
# 在宿主机上执行如下代码socat -d -d -d -d /tmp/debug-pipe PTY,link=/tmp/debug-pipe-pty

至此调试的所有前置工作都已经完成了,由于我们已经拥有了内核源码且编译了带有符号的内核文件,接下来就可以使用vscode的远程GDB调试功能进行调试了。

如下图所示,新建一个GDB调试配置文件。

使用VS-Code结合源码进行内核及内核模块远程调试的方法

具体配置文件如下,主要的区别就是miDebuggerServerAddress处的不同:
{    // Use IntelliSense to learn about possible attributes.    // Hover to view descriptions of existing attributes.    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387    "version": "0.2.0",    "configurations": [      {        "name": "(gdb) linux",        "type": "cppdbg",        "request": "launch",        "program": "${workspaceRoot}/vmlinux",        "miDebuggerServerAddress": "/tmp/debug-pipe-pty",        "args": [],        "stopAtEntry": true,        "cwd": "${workspaceFolder}",        "environment": [],        "externalConsole": false,        "MIMode": "gdb",        "miDebuggerArgs": "-n",        "targetArchitecture": "x64",        "setupCommands": [          {            "text": "set arch i386:x86-64:intel",            "ignoreFailures": false          },          {            "text": "dir .",            "ignoreFailures": false          },          {            "text": "add-auto-load-safe-path ./",            "ignoreFailures": false          },          {            "text": "-enable-pretty-printing",            "ignoreFailures": true          }        ]      }    ]  }

完成上述配置之后即可准备进行远程调试了。

在vscode中点击调试后还需要尽快在guest主机中运行如下命令触发中断。
echo g > /proc/sysrq-trigger

中断后vscode会成功的attach到linux内核中,之后即可正常进行内核调试了。

使用VS-Code结合源码进行内核及内核模块远程调试的方法


>>>0x05 引用


  • Debugging kernel and modules via gdb — The Linux Kernel documentation
  • VMware上进行Linux Kernel调试 | BruceFan’s Blog





银河实验室

使用VS-Code结合源码进行内核及内核模块远程调试的方法

银河实验室(GalaxyLab)是平安集团信息安全部下一个相对独立的安全实验室,主要从事安全技术研究和安全测试工作。团队内现在覆盖逆向、物联网、Web、Android、iOS、云平台区块链安全等多个安全方向。
官网:http://galaxylab.pingan.com.cn/



往期回顾


技术

JSP下的白魔法:JspEncounter

技术

利用Cloudflare 零信任进行C2通信及防护

技术

使用GitHub Actions进行红队自动化构建

技术

CVE-2022-38362:Apache Airflow Docker Provider RCE分析





点赞、分享,感谢你的阅读▼ 


▼ 点击阅读原文,进入官网

原文始发于微信公众号(平安集团安全应急响应中心):使用VS-Code结合源码进行内核及内核模块远程调试的方法

版权声明:admin 发表于 2022年11月23日 下午6:07。
转载请注明:使用VS-Code结合源码进行内核及内核模块远程调试的方法 | CTF导航

相关文章

暂无评论

暂无评论...