开启内核地址随机化KASLR后, qemu 调试 kernel 不能设置断点
Posted CHENG Jian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了开启内核地址随机化KASLR后, qemu 调试 kernel 不能设置断点相关的知识,希望对你有一定的参考价值。
#1 问题: gdb 断点异常
这几天更新了 qemu, 然后在进行 gdb 调试的时候, 发现断点断不住了.
之前都是正常的, 从来没有出现过这种情况啊. 继续分析下看看是哪里出现的异常.
#2 原因分析
难道是 gdb 或者 QEMU 出现 BUG 了, 我们先看下断点的位置是否正确.
- vmlinux 中符号的地址(gdb插入断点的位置)
gdb 是直接读取 vmlinux 中的符号的加载地址去添加断点的, 那么 0xffffffff81aa1800
应该就是 vmlinux
中 schedule 的函数地址
#objdump -d ./vmlinux| grep "<schedule>:"
ffffffff81aa1800 <schedule>:
可以看到没有问题, 但是为什么没有断到呢, 难道当前内核镜像中的地址不是这个么?
- 当前内核镜像的符号地址
# cat /proc/kallsyms | grep -E " schedule$"
ffffffffb0ca1800 T schedule
- 原因分析
schedule 在 vmlinux 镜像中的符号地址(ffffffff81aa1800)与 qemu 启动的内核中虚拟地址(ffffffffb0ca1800)不一样, 貌似发现问题所在了. 所以 gdb 根据 vmlinux 中的地址插入断点, 其实插入的位置并不是我们想要的, 这也就解释了为什么断点断不到.
可以看到两个地址刚好差了一个偏移 0x2f200000L.
这让我们想到了什么? 地址随机化?
内核启用 kaslr 这项特性之后, 内核启动时会随机化内核的各个 section 的虚拟地址(VA), 导致 gdb 断点设置在错误的虚拟地址上, 内核执行时就不会触发这些断点。
新版本的 qemu 竟然已经支持了地址随机化, 好事情.
#3 问题解决
##3.1 方法1 disable KASLR
最直接了当的方法(也是官方提供的方法) 是关闭地址随机化,
- 重新编译内核关闭地址随机化
- 或者在cmdline 里面直接添加
nokaslr
现在可以看到我们断点成功了, 内核的符号也没有偏移.
##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
可以看到断点断到了实际位置.
但是由于开启 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
这个感觉应该是 gdb 的问题, 记录在此, 等待问题解决后, 更新
#4 参考资料
gdb-qemu-cant-put-break-point-on-kernel-function-kernel
Using kgdb, kdb and the kernel debugger internals
以上是关于开启内核地址随机化KASLR后, qemu 调试 kernel 不能设置断点的主要内容,如果未能解决你的问题,请参考以下文章