天天看點

linux核心實驗環境搭建

文章目錄

    • 前言
    • 編譯linux核心
      • 下載下傳核心源碼
      • 編譯核心
    • 制作initramfs檔案系統
      • initrd介紹
      • initramfs檔案系統制作
      • qemu啟動
    • 使用Debootstrap制作檔案系統
      • 使用Debootstrap制作檔案系統
      • 修改檔案系統
      • 添加共享檔案夾
    • builtroot
    • 參考文章

前言

本部落格要求:可以根據需要,自行搭建基礎的linux實驗環境。

編譯核心要求:可以通過checkout,編譯任何指定版本的核心。

制作disk image要求:可以自行填充busybox等資訊制作一個簡易的initrd;可以借助debootstrap制作并修改成一個可以正常使用的disk image。

qemu要求:啟動的時候可以指定bzImage和disk image;可以設定通路外網;可以設定主機和虛拟機之間共享檔案件。

builtroot:了解即可。

ps:在閱讀這篇blog之前,建議先閱讀:syzkaller安裝

編譯linux核心

我fork一份linux源碼到自己的倉庫。主分支定期拉取上遊倉庫,使得可以看到最新的linux核心源碼。

當需要特定版本的核心操作實驗的時候,checkout過去就好。實驗後作為分支上傳到自己的倉庫。

編譯的時候全部,全部編譯的外面的目錄。

這樣便一直有一份最新的源碼,随時可以切換到特定的版本。非常友善。

下載下傳核心源碼

git檔案名大小寫問題:我電腦的磁盤空間不夠,外接一個U盤。U盤開始采用fat格式,clone linux之後,出現檔案大小寫問題。是以我将U盤部分格式化為ext4,重新clone。

Github進行fork後如何與原倉庫同步:重新fork很省事,但不如反複練習版本合并:為了更好的檢視和修改代碼,我frok了linux到我的倉庫。

# 我fork了linux到我的倉庫
git clone [email protected]:da1234cao/linux.git

# [該commit](https://github.com/torvalds/linux/commit/581738a681b6faae5725c2555439189ca81c0f1f)
# 在5.5版本中引入,也影響到5.6版本
git show 581738a681b6faae57 # 檢視commit資訊
git branch --contains 581738a681b6faae57 # 檢視該commit對應的分支
git tag --contains 581738a681b6faae57 # 從目前的commit往後查找tag

# 切換到v5.6
git checkout v5.6
           
##### 為了和上遊保持同步
# 添加上遊倉庫
git remote add upstream [email protected]:torvalds/linux.git
git remote -v # 檢視你的遠端倉庫的路徑

##### 漏洞自建一個分支,以後所有關于該漏洞的内容都送出到該分支

# 我checkout 5.6之後,沒有先建立分支,直接修改了5.6的版本
git stash # 在目前分支上的修改暫存起來
git branch CVE-2020-8835
git checkout CVE-2020-8835
git stash pop # 将暫存的修改放到建立分支中
git add .
git commit 
git push origin CVE-2020-8835 

# 下次記得修改之前,建立自己的分支
git branch test
git checkout test

##### 為了和上遊保持同步
git checkout master
# 因為我主分支一直沒動,是以fetch之後,下面merger不會沖突。這步很慢,可以在晚上的時候運作
git fetch upstream  # 抓取上遊原倉庫的更新;
git merge upstream/master # 合并upstream/master到目前分支
git push origin master

#### 這裡再補充一下标簽同步
git fetch upstream --tags  #擷取源項目的tag
git push origin --tags  #将新的tag推送到fork項目
           

編譯核心

基本編譯如下。Kernel Build System還是不要看了,待萬不得已的時候再看。

# 編譯核心:O=...,指定kbuild的output;
make mrproper # 保持幹淨源碼
make defconfig O=../linux_image/5.6 # 生成預設配置到./config中
make kvmconfig O=../linux_image/5.6 # Enable additional options for kvm guest kernel support
make O=../linux_image/5.6
           

很多核心參數,我不知道。需要的時候,去搜特定的參數功能吧。這裡開始積攢我用到過的參數。

# 修改幾個核心配置選項,編輯./config
# Enable kernel config options required for syzkaller 
# Coverage collection.
CONFIG_KCOV=y

# Debug info for symbolization.
CONFIG_DEBUG_INFO=y

# Memory bug detector
CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y

# Required for Debian Stretch
CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y
           

制作initramfs檔案系統

我是看到0x04-pwn2own-ebpf-jmp32-cve-2020-8835。這樣隻要有一個人搭建漏洞環境,其他人直接運作就好。感覺很友善。

對于上面倉庫中initramfs制作過程的疑惑,我送出了一個issue:initramfs.cpio中的init可以為空嗎

initrd介紹

參考: boot loader 與 kernel 加載

當啟動時無法挂載根目錄的情況下,一定需要 initrd。

如果你的 Linux 是安裝在 IDE 介面的磁碟上,并且使用預設的 ext2/ext3 檔案系統, 那麼不需要 initrd 也能夠順利的啟動進入 Linux 的!

萬一遇到根檔案系統在一個硬碟上。而核心沒有讀取這個硬碟的驅動。而硬碟的驅動存儲在硬碟中的根檔案系統中。這樣便産生死鎖。

boot loader 可以加載kernel 與initrd ,然後在記憶體中讓initrd 解壓縮成為根目錄, kernel 就能夠借此加載适當的驅動程式,并挂載實際的根目錄檔案系統, 就能夠開始後續的正常啟動流程。

我們也可以使用cpio檢視目前系統中/boot/initrd.img。

initramfs檔案系統制作

我們借助busybox制作。但是busybox是一些程式的集合,沒法填充成完整的檔案系統。是以我們還需要手動複制其他檔案。

我不知道咋整,從網上東看看,西瞅瞅,如下設定。

整體内容填充。

# 下載下傳busybox
curl https://busybox.net/downloads/busybox-1.33.1.tar.bz2 | tar xjf -
cd busybox-1.33.1

# 編譯安裝busybox
mkdir -pv obj/busybox-x86
make O=obj/busybox-x86 defconfig
cd obj/busybox-x86
ls -alh .config
vim .config # 修改添加靜态連結: CONFIG_STATIC=y #
make -j4
make install # install在_install目錄

# 檢視_install裡面内容:有三個目錄,一個檔案
# 裡面的内容全部是busybox的軟連接配接。

# 跳出到外面,制作initramfs
mkdir initramfs
mkdir -pv {bin,sbin,dev,etc,proc,sys,usr}
mkdir -pv {etc/init.d,usr/{bin,sbin}}
cp -a ../busybox-1.33.1/obj/busybox-x86/_install/* initramfs
sudo cp -a /dev/{null,console,tty1,tty2,tty3,tty4} dev
           

鳥哥中 boot loader 與 kernel 加載 介紹的内容太陳舊了。陳皓也寫過一篇系統啟動的文章:LINUX PID 1 和 SYSTEMD

我當初學習作業系統的時候,學習的是:計算機的啟動過程

現在作業系統的啟動流程,我不清楚。busybox還來這裡攪和一趟。關鍵的是busybox文檔,我也沒看到詳細的描述。

busybox是一個包含很多程式的套件,它也包含

init

程式。

下面我們設定下啟動選項。

方式一:自己寫

touch etc/init.d/rcS

vim etc/init.d/rcS 
# 添加下面内容
#!/bin/sh

mount -t proc proc /proc
mount -t sysfs sysfs /sys
################

# rcS需要可執行權限
chmod 774 etc/init.d/rcS
           

方式二:使用busybox自帶的内容。

cp -a ../../busybox-1.33.1/examples/bootfloppy/etc/* etc
           

或許,我們還應該給檔案系統中添加使用者等等。但是這并不容易。

後面,我們使用工具建立檔案系統,将會容易很好。

制作成initrawfs。

find . -print0 | cpio --null -ov --format=newc | gzip -9 > initramfs-busybox-x86.cpio.gz
           

qemu啟動

關于參數,沒啥辦法,遇到的時候,一個一個到QEMU documentation中搜尋,或直接google。

#!/bin/bash

bzImage_dir=$1
cpio_dir=$2

#run vm
#timeout --foreground 600
qemu-system-x86_64 \
    -m 256M \
    -enable-kvm \
    -nographic -kernel $bzImage_dir \
    -append 'root=/dev/ram rw console=ttyS0' \
    -initrd $cpio_dir \
    -smp cores=2,threads=2  \
    -cpu kvm64,+smep,+smap  \
    -pidfile vm.pid
    2>/dev/null
           
# 檔案結構
➜  linux_image tree -L 1                               
.
├── 5.6               # 編譯生成的核心
├── 5.6_qemu_cmd.sh   # qemu的啟動腳本
└── initramfs         # initramfs

# 啟動
./5.6_qemu_cmd.sh ./5.6/arch/x86/boot/bzImage ./initramfs/initramfs-busybox-x86.cpio.gz

# 關閉
kill `cat vm.pid | grep -v '^$'`
# 或者 ctrl + A ,松手,按x
           

使用Debootstrap制作檔案系統

主要是修改檔案系統+配置共享檔案夾

使用Debootstrap制作檔案系統

自行參考:建構檔案系統

qemu-img create debian.img 1G
Formatting 'debian.img', fmt=raw size=1073741824
# dd if=/dev/zero of=$RELEASE.img bs=1M seek=$SEEK count=1

mkfs.ext4 debian.img

mkdir mnt
sudo mount -o loop debian.img mnt/
sudo debootstrap buster mnt/ # 我們下載下傳debian 10的根檔案系統
sudo umount mnt
rm -rf mnt
           

修改檔案系統

這個檔案系統非常clear,我們得修改修改。

# 比較尴尬的是,待會qemu啟動的時候,我不知道登入密碼
# 是以,我們需要将上面的img挂載起來,chroot進入,修改登入密碼
mkdir mnt
sudo mount debian.img mnt/
sudo chroot mnt
passwd # 密碼随手起[避免以後忘記,這裡記錄一下]:111111
exit
sudo umount mnt
rm -rf mnt
           
# 我們使用qemu登入
#!/bin/bash

bzImage_dir=$1
fs_dir=$2

#run vm
#timeout --foreground 600
qemu-system-x86_64 \
    -m 256M \
    -enable-kvm \
    -nographic -kernel $bzImage_dir \
    -append 'root=/dev/sda rw console=ttyS0' \
    -drive file=$2,format=raw \
    -smp cores=2,threads=2  \
    -cpu kvm64,+smep,+smap  \
    -net nic \
    -net user \
    -pidfile vm.pid \
    2>/dev/null
           

給檔案系統添加網絡配置,以可以連接配接外網。

# 修改網絡配置
ip a
printf '\nauto enp0s3\niface enp0s3 inet dhcp\n' | tee -a /etc/network/interfaces
           
# 添加普通使用者
useradd -d /home/dacao -m dacao
passwd dacao
chsh -s /bin/bash dacao
usermod -G sudo dacao
apt install sudo
           

添加共享檔案夾

還需要Qemu虛拟機與主控端之間檔案傳輸

dd if=/dev/zero of=$PWD/share.img bs=1M count=2000
mkfs.ext4 $PWD/share.img
mkdir $PWD/share
           

但是下面的方法不大靈光。。我不知道為啥。。

#!/bin/bash

bzImage_path="../5.6/arch/x86/boot/bzImage"
rfs_image_path="../debian.img"
share_fs_path="../share.img"
share_mnt_path="../share"

read -e -i "$bzImage_path" -p "enter bzImage path : " bzImage_path 
read -e -i "$rfs_image_path" -p "enter root file system image path : " rfs_image_path
read -e -i "$share_fs_path" -p "enter share file system image path : " share_fs_path
read -e -i "$share_mnt_path" -p  "enter the share image mnt path : " share_mnt_path

sudo mount -o loop $share_fs_path $share_mnt_path

read -s -n1 -p "請将主機的檔案放到共享檔案夾中\n之後\n按任意鍵繼續 ... "

qemu-system-x86_64 \
    -m 256M \
    -enable-kvm \
    -nographic -kernel $bzImage_path \
    -append 'root=/dev/sda rw console=ttyS0' \
    -drive file=$rfs_image_path,format=raw \
    -drive file=$share_fs_path,if=virtio \
    -smp cores=2,threads=2  \
    -cpu kvm64,+smep,+smap  \
    -net nic \
    -net user \
    -pidfile vm.pid \
    2>/dev/null


sudo umount $share_mnt_path
           

是以,我換用Documentation/9psetup | qemu中使用 9p virtio

# 現在的啟動腳本

#!/bin/bash

bzImage_path="../5.6/arch/x86/boot/bzImage"
rfs_image_path="../debian.img"
share_mnt_path="../share"

read -e -i "$bzImage_path" -p "enter bzImage path : " bzImage_path 
read -e -i "$rfs_image_path" -p "enter root file system image path : " rfs_image_path
read -e -i "$share_mnt_path" -p  "enter the share image mnt path : " share_mnt_path


qemu-system-x86_64 \
    -m 256M \
    -enable-kvm \
    -nographic -kernel $bzImage_path \
    -append 'root=/dev/sda rw console=ttyS0' \
    -drive file=$rfs_image_path,format=raw \
    -smp cores=2,threads=2  \
    -cpu kvm64,+smep,+smap  \
    -net nic \
    -net user \
    -fsdev local,security_model=passthrough,id=fsdev0,path=$share_mnt_path \
    -device virtio-9p-pci,fsdev=fsdev0,mount_tag=hostshare \
    -pidfile vm.pid \
    2>/dev/null
           

其中,起到共享目錄的配置是這兩行。

-fsdev local,security_model=passthrough,id=fsdev0,path=$share_mnt_path \
-device virtio-9p-pci,fsdev=fsdev0,mount_tag=hostshare \
           

之後,我們進入虛拟機之後,根據mount_tag,将其挂載到适當位置。

mount -t 9p -o trans=virtio hostshare /home/dacao/host_file/
           

這裡還有另一種共享檔案的方式:virtio-fs – 沒看明白。。

builtroot

builtroot可以根據選擇,同時生成bzImage和根檔案系統。

這裡有個視訊的tutorial:Buildroot Tutorial- Linux Kernel on QEMU Virtual board - Booting Linux and Running Linux Application

  • Target Architecture (x86_64)
  • (111111) Root password | Run a getty (login prompt) after boot
  • Kernel version (Custom version) | (4.6) Kernel version ??
  • Kernel configuration (Using an in-tree defconfig file) | (kvm_guest.config) Additional configuration fragment files ??
  • -*- BusyBox
  • ext2/3/4 variant (ext4)
  • host qemu

我選了這些,不知道對不對。大體是這麼操作的。

參考文章

快速搭建一個Linux核心調試環境 – 這篇文章強調“快”

用QEMU來調試核心 – 親身體驗篇 – 這篇文章的“參考”很好