開啓內核地址隨機化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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章