开启内核地址随机化KASLR后, qemu 调试 kernel 不能设置断点

#1 问题: gdb 断点异常

这几天更新了 qemu, 然后在进行 gdb 调试的时候, 发现断点断不住了.
问题: gdb 断点异常

之前都是正常的, 从来没有出现过这种情况啊. 继续分析下看看是哪里出现的异常.

#2 原因分析

难道是 gdb 或者 QEMU 出现 BUG 了, 我们先看下断点的位置是否正确.

  • vmlinux 中符号的地址(gdb插入断点的位置)

gdb 是直接读取 vmlinux 中的符号的加载地址去添加断点的, 那么 0xffffffff81aa1800 应该就是 vmlinux 中 schedule 的函数地址

#objdump -d ./vmlinux| grep "<schedule>:"
ffffffff81aa1800 <schedule>:

问题1

可以看到没有问题, 但是为什么没有断到呢, 难道当前内核镜像中的地址不是这个么?

  • 当前内核镜像的符号地址
# cat /proc/kallsyms | grep -E " schedule$"
ffffffffb0ca1800 T schedule

图片3

  • 原因分析

schedule 在 vmlinux 镜像中的符号地址(ffffffff81aa1800)与 qemu 启动的内核中虚拟地址(ffffffffb0ca1800)不一样, 貌似发现问题所在了. 所以 gdb 根据 vmlinux 中的地址插入断点, 其实插入的位置并不是我们想要的, 这也就解释了为什么断点断不到.

可以看到两个地址刚好差了一个偏移 0x2f200000L.

图片4

这让我们想到了什么? 地址随机化?

内核启用 kaslr 这项特性之后, 内核启动时会随机化内核的各个 section 的虚拟地址(VA), 导致 gdb 断点设置在错误的虚拟地址上, 内核执行时就不会触发这些断点。

新版本的 qemu 竟然已经支持了地址随机化, 好事情.

#3 问题解决

##3.1 方法1 disable KASLR

最直接了当的方法(也是官方提供的方法) 是关闭地址随机化,

  • 重新编译内核关闭地址随机化
  • 或者在cmdline 里面直接添加 nokaslr

图片5

现在可以看到我们断点成功了, 内核的符号也没有偏移.

图片6

##3.2 方法2 重新加载内核镜像

###3.2.1 重新加载内核 text 段

我们找到内核加载的起始地址, 这个一般是 _text 符号的地址.

# echo 0x$(cat /proc/kallsyms | egrep -e "T _text$" | awk '{print $1}')
# cat /proc/kallsyms | grep -E " _text$"
ffffffff9ee00000 T _text

# cat /proc/kallsyms | grep -E " schedule$"
ffffffff9f8a1800 T schedule

此时 KASLR 的偏移量为 0xffffffff9ee00000 - 0xffffffff81000000 = 0x1de00000L

然后通过 add-symbol-file 将 vmlinux 的起始地址指定到 0xffffffff9ee00000 位置处.

add-symbol-file ./vmlinux 0xffffffff9ee00000

图片7

可以看到断点断到了实际位置.

但是由于开启 KASLR 之后内核各个段的地址都是分别映射的, 这段我们只重新指定了内核代码段的位置.

###3.2.2 进阶用法

objdump -h ./vmlinux | grep -E " .text| .data "
  0 .text 00e010f1 ffffffff81000000 0000000001000000 00200000 2**12
 11 .data 0014c540 ffffffff82400000 0000000002400000 01600000 2**13

之前算出来 KASLR 的偏移是 0x1de00000L, .data 在 vmlinux 中的地址为 0xffffffff82400000, 加上偏移后实际的加载地址就是 0xffffffffa0200000. 这个地址就是 init_stack 的地址

使用 add-symbol-file 指定其他段(比如 data) 的地址.

# add-symbol-file ./vmlinux 0xffffffff9ee00000 -s .data 0xffffffffa0200000

add symbol table from file "./vmlinux" at
 .text_addr = 0xffffffff9ee00000
 .data_addr = 0xffffffffa0200000
(y or n) y
Reading symbols from ./vmlinux...done.

但是如此读取后, 再去读取数据段的值, 依旧失败, gdb 还是寻找错误的地址.
我们在 StackOverFlow 中找到了类似的例子, GDB can’t load kernel symbol correctly with KASLR enabled

图片8

这个感觉应该是 gdb 的问题, 记录在此, 等待问题解决后, 更新

#4 参考资料

gdb-qemu-cant-put-break-point-on-kernel-function-kernel

gdb-kernel-debugging

Using kgdb, kdb and the kernel debugger internals

Commands to specify files

发布了440 篇原创文章 · 获赞 1316 · 访问量 571万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览