使用 GDB + Qemu 調試 Linux 內核

2021-12-25 Linux雲計算網絡
1. 概述

在某些情況下,我們需要對於內核中的流程進行分析,雖然通過 BPF 的技術可以對於函數傳入的參數和返回結果進行展示,但是在流程的調試上還是不如直接 GDB 單步調試來的直接。本文採用的編譯方式如下,在一臺 16 核 CentOS 7.7 的機器上進行內核源碼相關的編譯(主要是考慮編譯效率),調試則是基於 VirtualBox 的 Ubuntu 20.04 系統中,採用 Qemu + GDB 進行單步調試,網上查看了很多文章,在最終進行單步跟蹤的時候,始終不能夠在斷點處停止,進行過多次嘗試和查詢文檔,最終發現需要在內核啟動參數上添加 nokaslr ,本文是對整個搭建過程的總結。

2. Linux 內核編譯和文件系統製作2.1 Linux 內核編譯

編譯內核和製作文件系統在 CentOS 7.7 的機器上。源碼從國內清華的源下載:http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/, 此處選擇 linux-4.19.172.tar.gz 版本。詳細編譯步驟如下:

$ sudo yum group install "Development Tools"
$ yum install ncurses-devel bison flex elfutils-libelf-devel openssl-devel
 
$ wget http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/v4.x/linux-4.19.172.tar.gz
$ tar xzvf linux-4.19.172.tar.gz
$ cd linux-4.19.172/
$ make menuconfig

在內核編譯選項中,開啟如下 「Compile the kernel with debug info」, 4.19.172 中默認已經選中:

Kernel hacking —> Compile-time checks and compiler options —> [ ] Compile the kernel with debug info

以上配置完成後會在當前目錄生成 .config 文件,我們可以使用 grep 進行驗證:

# grep CONFIG_DEBUG_INFO .config
CONFIG_DEBUG_INFO=y

接著我們進行內核編譯:

 $ nproc       # 查看當前的系統核數
 $ make -j 12  # 或者採用 make bzImage 進行編譯, -j N,表示使用多少核並行編譯
 
 # 未壓縮的內核文件,這個在 gdb 的時候需要加載,用於讀取 symbol 符號信息,由於包含調試信息所以比較大
 $ ls -hl vmlinux 
-rwxr-xr-x 1 root root 449M Feb  3 14:46 vmlinux

# 壓縮後的鏡像文件 
$ ls -hl ./arch/x86_64/boot/bzImage
lrwxrwxrwx 1 root root 22 Feb  3 14:47 ./arch/x86_64/boot/bzImage -> ../../x86/boot/bzImage

$ ls -hl ./arch/x86/boot/bzImage
-rw-r--r-- 1 root root 7.6M Feb  3 14:47 ./arch/x86/boot/bzImage

不同發行版本下的內核的詳細編譯文檔可以參考這裡。

2.2 啟動內存文件系統製作

# 首先安裝靜態依賴,否則會有報錯,參見後續的排錯章節
$ yum install -y glibc-static.x86_64 -y

$ wget https://busybox.net/downloads/busybox-1.32.1.tar.bz2
$ tar -xvf busybox-1.32.1.tar.bz2
$ cd busybox-1.32.1/

$ make menuconfig

# 安裝完成後生成的相關文件會在 _install 目錄下
$ make && make install   

$ cd _install
$ mkdir proc
$ mkdir sys
$ touch init  

#  init 內容見後續章節,為內核啟動的初始化程序
$ vim init   

# 必須設置成可執行文件
$ chmod +x init  

$ find . | cpio -o --format=newc > ./rootfs.img
cpio: File ./rootfs.img grew, 2758144 new bytes not copied
10777 blocks

$ ls -hl rootfs.img
-rw-r--r-- 1 root root 5.3M Feb  2 11:23 rootfs.img

其中上述的 init 文件內容如下,列印啟動日誌和系統的整個啟動過程花費的時間:

#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp

mdev -s 
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"

# normal user
setsid /bin/cttyhack setuidgid 1000 /bin/sh

到此為止我們已經編譯了好了 Linux 內核(vmlinux 和 bzImage)和啟動的內存文件系統(rootfs.img)。

2.3 錯誤排查

在編譯過程中出現以下報錯:

/bin/ld: cannot find -lcrypt
/bin/ld: cannot find -lm
/bin/ld: cannot find -lresolv
/bin/ld: cannot find -lrt
collect2: error: ld returned 1 exit status
Note: if build needs additional libraries, put them in CONFIG_EXTRA_LDLIBS.
Example: CONFIG_EXTRA_LDLIBS="pthread dl tirpc audit pam"

出錯的原因是因為我們採用靜態編譯依賴的底層庫沒有安裝,如果不清楚這些庫有哪些 rpm 安裝包提供,則可以通過 yum provides 命令查看,然後安裝相關依賴包重新編譯即可。

$ yum provides */libm.a
// ...
glibc-static-2.17-317.el7.x86_64 : C library static libraries for -static linking.
Repo        : base
Matched from:
Filename    : /usr/lib64/libm.a

3. Qemu 啟動內核

在上述步驟準備好以後,我們需要在調試的 Ubuntu 20.04 的系統中安裝 Qemu 工具,其中調測的 Ubuntu 系統使用 VirtualBox 安裝。

$ apt install qemu qemu-utils qemu-kvm virt-manager libvirt-daemon-system libvirt-clients bridge-utils

把上述編譯好的 vmlinux、bzImage、rootfs.img 和編譯的源碼拷貝到我們當前 Unbuntu 機器中。

拷貝 Linux 編譯的源碼主要是在 gdb 的調試過程中查看源碼,其中 vmlinux 和 linux 源碼處於相同的目錄,本例中 vmlinux 位於 linux-4.19.172 源目錄中。

$ qemu-system-x86_64 -kernel ./bzImage -initrd  ./rootfs.img -append "nokaslr console=ttyS0" -s -S -nographic

使用上述命令啟動調試,啟動後會停止在界面處,並等待遠程 gdb 進行調試,在使用 GDB 調試之前,可以先使用以下命令進程測試內核啟動是否正常。

qemu-system-x86_64 -kernel ./bzImage -initrd  ./rootfs.img -append "nokaslr console=ttyS0" -nographic

其中命令行中各參數如下:

-kernel ./bzImage:指定啟用的內核鏡像;

-initrd ./rootfs.img:指定啟動的內存文件系統;

-append "nokaslr console=ttyS0" :附加參數,其中 nokaslr 參數必須添加進來,防止內核起始地址隨機化,這樣會導致 gdb 斷點不能命中;參數說明可以參見這裡。

-s :監聽在 gdb 1234 埠;

-S :表示啟動後就掛起,等待 gdb 連接;

-nographic:不啟動圖形界面,調試信息輸出到終端與參數 console=ttyS0 組合使用;

4. GDB 調試

在使用 qemu-system-x86_64 命令啟動內核以後,進入到我們從編譯機器上拷貝過來的 Linux 內核原始碼目錄中,在另外一個終端我們來啟動 gdb 命令:

[linux-4.19.172]$ gdb 
(gdb) file vmlinux           # vmlinux 位於目錄 linux-4.19.172 中
(gdb) target remote :1234
(gdb) break start_kernel     # 有些文檔建議使用 hb 硬體斷點,我在本地測試使用 break 也是 ok 的
(gdb) c              # 啟動調試,則內核會停止在 start_kernel 函數處

整體運行界面如下:

5. Eclipse 圖像化調試

我們可以通過 eclipse-cdt 進行可視化項目調試。

」File「 -> 「New」 -> 「Project」 ,然後選擇 」Makefile Project with Existing Code「 選項,後續按照嚮導導入代碼。

在 「Run」 -> 「Debug Configurations」 選項中,創建一個 」C/C++ Attach to Application「 的調試選項。

Project:選擇我們剛才創建的項目名字;

C/C++ Application:選擇編譯 Linux 內核帶符號信息表的 vmlinux;

Build before launching:選擇 」Disable auto build「;

Debugger:選擇 gdbserver,具體設置如下圖;

在 Debugger 中的 Connection 信息中選擇 」TCP「,並填寫埠為 」1234「;

啟動 Debug 調試,即可看到與 gdb 類似的窗口。

啟動 」Debug「 調試以後的窗口如下,在 Debug 窗口欄中,設置與 gdb 調試相同的步驟即可。

6. 參考

How to compile and install Linux Kernel 5.6.9 from source code

用qemu + gdb調試linux內核 ***

QEMU+busybox 搭建Linux內核運行環境 ***

QEMU+gdb調試Linux內核全過程 *

linux內核編譯與調試方法

How to Build A Custom Linux Kernel For Qemu (2015 Edition)

qemu與qemu-kvm到底什麼區別

在qemu環境中用gdb調試Linux內核 *

原文:https://www.ebpf.top/post/qemu_gdb_busybox_debug_kernel/

相關焦點

  • 使用QEMU chroot進行固件本地調試
    QEMU是我們在調試一些不同架構的程序時經常使用的虛擬機軟體。
  • 技術分享 | 探索QEMU-KVM中PIO處理的奧秘
    我們都知道在kvm/qemu的虛擬機中向埠讀寫輸入會陷入kvm中(絕大部分埠)。
  • 一步步教你:在x86平臺,如何用Qemu來模擬ARM系統
    因為Qemu是使用純軟體模擬的,它的強項是模擬那些不涉及到外部的具體硬體設備的場景,比如:想學習如何定製bootloader;想在Arm系統中進行文件系統的裁剪,學習文件系統的掛載過程;想體驗一下如何配置、裁剪linux kernel;想學習Linux系統中的設備樹;...
  • 使用qemu安裝虛擬機
    必要的系統檢查創建虛擬機的虛擬磁碟下載guest系統的iso鏡像必要的系統檢查檢查分為兩種,硬體和軟體,軟體上又可以分為內核模塊和應用程式。首先晶片上要支持kvm,比如x86的架構需要有vmx。這個在linux系統上通過命令「cat /proc/cpuinfo | grep vmx」來判斷。如果有這個標誌,說明晶片是支持的。
  • 內核調試神器SystemTap — 簡介與使用(一)
    安裝依賴包gcc:C語言編譯器elfutils:提供分析調試信息的庫函數linux-headers-generic:編譯內核模塊所需的內核頭文件以及模塊配置信息3.安裝內核調試信息(kernel-debuginfo)kernel-debuginfo提供了調試內核所需的符號表,如果沒有安裝的話SystemTap的威力就會大打折扣,只能提供kprobes系列的功能。
  • CUDA-GDB安裝+環境配置
    CUDA-GDB提供了無縫的調試體驗,可以同時調試應用程式的CPU和GPU部分。就像GDB一樣,CUDA-GDB提供了基於控制臺的調試界面,可以從本地系統或具有Telnet或SSH訪問權限的任何遠程系統的命令行中使用。如果您更喜歡使用GUI前端進行調試,則CUDA-GDB還支持與DDD,EMACS或Nsight Eclipse Edition集成 。
  • qemu-pwn-基礎知識
    內核ctf,第二是qemu逃逸ctf以及cve。公眾號預期是一周兩更,所以接下來也會直接以專題的形式更新,周三更新qemu逃逸pwn相關專題,周日更新linux內核pwn相關專題。前言最近開始研究qemu,想看看qemu逃逸相關的知識,看了一些資料,學習pwn qemu首先要對qemu的一些基礎知識有一定的掌握。qemu 是純軟體實現的虛擬化模擬器,幾乎可以模擬任何硬體設備。
  • GDB 調試實戰之 Redis 通信協議
    接下來的實戰演習以 Redis 為例來講解實際項目中的伺服器結構是怎樣的。我會先假設預先不清楚 Redis 網絡通信層的結構,結合 GDB 調試,以探究的方式逐步搞清楚 Redis 的網絡通信模塊結構。
  • GDB catch命令:建立捕捉斷點
    建立捕捉斷點的方式很簡單,就是使用 catch 命令,其基本格式為:(gdb) catch event其中,event 參數表示要監控的具體事件。對於使用 GDB 調試 C、C++ 程序,常用的 event 事件類型如表 1 所示。
  • 【Linux內核漏洞利用】強網杯2018-solid_core-任意讀寫
    (2)利用ret2dir技術繞過PXN該技術由哥倫比亞大學在2014年提出,其利用原理是,linux內核在設計的時候,為了提高內存的操作效率,在用戶空間映射內存的時候,內核也相應地在內核的低端內存區地址映射一段影子內存。
  • 如何在 Ubuntu/Linux Mint 中安裝最新 Linux內核
    它提供了一個帶有「主線mainline」內核更新列表的簡單界面,允許在基於 Ubuntu 的發行版中一鍵安裝、刪除或清除內核。如何在Ubuntu中安裝Mainline:注意:主線內核由Ubuntu內核團隊提供,用於測試和調試。它們不受支持,也不適合生產使用。您應該只在它們可能修復當前內核的一個嚴重問題時才安裝它們。
  • 使用qemu對IOT設備進行模擬執行研究
    可以使用以下命令之一完成此操作: qemu-mipsel -L  ` `qemu-arm -L  ` `qemu- -L當二進位連結到外部依賴性(例如uCLibc或加密庫)時,-L選項很重要。它告訴動態連結器查找具有所提供前綴的依賴項,以下是imgdecrypt為D-Link DIR-882路由器運行二進位文件的示例。
  • 本地內核調試神器 —— livekd 使用總結
    雙機內核調試(需要另外一臺機器來做雙機調試)2. 讓系統崩潰(可以使用 sysinternals中的 notmyfault 或者 使用快捷鍵讓系統崩潰,並設置 系統崩潰的時候自動保存轉儲文件)(有點小題大作了)。3. 使用 sysinternals 中的 livekd,不需要特殊設置,綠色環保。以上幾種方案中,使用 livekd 最方便快捷。
  • 一文匯總Linux 系統動態追蹤(高級調試)技術
    追蹤機制說明kprobes/kretprobes/uprobeskprobes 主要用來對內核進行調試追蹤, 屬於比較輕量級的機制, 本質上是在指定的探測點(比如函數的某行, 函數的入口地址和出口地址, 或者內核的指定地址處)插入一組處理程序.
  • Linux 系統動態追蹤(高級調試)技術
    perf_eventperf_event 隨內核的主版本進行發布, 一直是 linux 用戶的主要追蹤工具, 通常由 perf 命令提供服務. 可以支持對 tracepoint, kprobes 和 uprobes 機制的處理, 另外 perf 也是可以對 cpu 性能進行計數的強大工具之一.
  • 編寫屬於你的第一個Linux內核模塊
    內核堅定地支持GPL兼容代碼,因此如果你把許可證設置為其它非GPL兼容的(如,「Proprietary」[專利]),某些特定的內核功能將在你的模塊中不可用。什麼時候不該寫內核模塊內核編程很有趣,但是在現實項目中寫(尤其是調試)內核代碼要求特定的技巧。通常來講,在沒有其它方式可以解決你的問題時,你才應該在內核級別解決它。
  • GDB調試器原來那麼簡單
    當然,在Windows下也可以直接使用gcc、gdb來做編譯調試我們的C程序,如MinGW( 一個可自由使用和自由發布的Windows特定頭文件和使用GNU工具集導入庫的集合 )中就同時包含有gcc與gdb工具:
  • linux內核調優詳解
    conf.default.rp_filter = 1                            開啟反向路徑過濾net.ipv4.conf.default.accept_source_route = 0      處理無源路由的包kernel.sysrq = 0                                                      控制系統調試內核的功能要求
  • Manjaro的Linux內核管理
    Manjaro不僅支持使用多個內核(可從開機畫面處選擇),也可以方便地安裝最新的內核。這需要藉助Manjaro的硬體檢測命令來完成。該命令的語法如下:sudo mhwd-kernel [-i] [新內核:linux(version)] [可選-刪除當前內核:rmc]當要用命令安裝的新內核時,不必寫完整的版本號。
  • 理解Linux內核搶佔模型(最透徹一篇)
    /#.XrKLcfnx05k作者:Liran B.H譯者:宋寶華當配置Linux內核的時候,我們可以選擇一些參數,這些參數能影響系統的行為。當你使用 make menuconfig配置內核的時候,你能看到這樣的菜單:為了深入理解這三個搶佔模型的區別,我們將寫一個案例:如果低優先級的線程陷入系統調用,高優先級的線程睡眠到期,究竟會發生什麼?下面我們來一種模型一種模型地看。