溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Pwn In Kernel基礎(chǔ)知識有哪些

發(fā)布時間:2021-11-03 17:54:43 來源:億速云 閱讀:186 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“Pwn In Kernel基礎(chǔ)知識有哪些”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

簡單分析一下 CTF Kernel Pwn 題目的形式,以 2017 CISCN babydrive 為例。

先對文件包解壓

 ?  example ls
 babydriver.tar
 ?  example file babydriver.tar 
 babydriver.tar: POSIX tar archive
 ?  example tar -xvf babydriver.tar 
 boot.sh
 bzImage
 rootfs.cpio
 ?  example ls
 babydriver.tar  boot.sh  bzImage  rootfs.cpio

得到 boot.sh,bzImage,rootfs.cpio 三個文件

boot.sh

 ?  example cat -n boot.sh
      1  #!/bin/bash
      2  qemu-system-x86_64 \
      3  -initrd rootfs.cpio \
      4  -kernel bzImage \
      5  -append 'console=ttyS0 root=/dev/ram oops=panic panic=1' \
      6  -enable-kvm \
      7  -monitor /dev/null \
      8  -m 64M \
      9  --nographic  \
     10  -smp cores=1,threads=1 \
     11  -cpu kvm64,+smep

boot.sh 文件是用來啟動這個程序的,調(diào)用 qemu 來加載 rootfs.cpio 與 bzImage 運行起來

上面的參數(shù)都是 qemu 的參數(shù)

 -initrd rootfs.cpio,使用 rootfs.cpio 作為內(nèi)核啟動的文件系統(tǒng)
 -kernel bzImage,使用 bzImage 作為 kernel 映像
 -cpu kvm64,+smep,設(shè)置 CPU 的安全選項,這里開啟了 smep
 -m 64M,設(shè)置虛擬 RAM 為 64M,默認為 128M

bzImage

 ?  example file bzImage       
 bzImage: Linux kernel x86 boot executable bzImage, version 4.4.72 (atum@ubuntu) #1 SMP Thu Jun 15 19:52:50 PDT 2017, RO-rootFS, swap_dev 0x6, Normal VGA

bzImage 是經(jīng)壓縮過的 linux 內(nèi)核文件

rootfs.cpio

 ?  example file rootfs.cpio
 rootfs.cpio: gzip compressed data, last modified: Tue Jul  4 08:39:15 2017, max compression, from Unix

這是一個 linux 內(nèi)核文件系統(tǒng)壓縮包,我們可以對其解壓并重新壓縮,從而修改這個系統(tǒng)的文件

新建一個文件夾來解壓

 ?  example mkdir fs && cd fs
 ?  fs cp ../rootfs.cpio ./rootfs.cpio.gz
 ?  fs gunzip ./rootfs.cpio.gz
 ?  fs cpio -idmv < rootfs.cpio 
 .
 etc
 etc/init.d
 etc/passwd
 etc/group
 bin
 ......
 linuxrc
 home
 home/ctf
 5556 blocks
 ?  fs ll                  
 total 2.8M
 drwxrwxr-x 2 mask mask 4.0K 1 月  20 12:16 bin
 drwxrwxr-x 3 mask mask 4.0K 1 月  20 12:16 etc
 drwxrwxr-x 3 mask mask 4.0K 1 月  20 12:16 home
 -rwxrwxr-x 1 mask mask  396 6 月  16  2017 init
 drwxr-xr-x 3 mask mask 4.0K 1 月  20 12:16 lib
 lrwxrwxrwx 1 mask mask   11 1 月  20 12:16 linuxrc -> bin/busybox
 drwxrwxr-x 2 mask mask 4.0K 6 月  15  2017 proc
 -rwxrwxr-x 1 mask mask 2.8M 1 月  20 12:15 rootfs.cpio
 drwxrwxr-x 2 mask mask 4.0K 1 月  20 12:16 sbin
 drwxrwxr-x 2 mask mask 4.0K 6 月  15  2017 sys
 drwxrwxr-x 2 mask mask 4.0K 6 月  15  2017 tmp
 drwxrwxr-x 4 mask mask 4.0K 1 月  20 12:16 usr

這些就是運行起來后這個系統(tǒng)擁有的文件,查看這個 init 文件

 ?  fs cat -n ./init           
      1  #!/bin/sh
      2   
      3  mount -t proc none /proc
      4  mount -t sysfs none /sys
      5  mount -t devtmpfs devtmpfs /dev
      6  chown root:root flag
      7  chmod 400 flag
      8  exec 0</dev/console
      9  exec 1>/dev/console
     10  exec 2>/dev/console
     11
     12  insmod /lib/modules/4.4.72/babydriver.ko
     13  chmod 777 /dev/babydev
     14  echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
     15  setsid cttyhack setuidgid 1000 sh
     16
     17  umount /proc
     18  umount /sys
     19  poweroff -d 0  -f

看到第 12 行的 insmod /lib/modules/4.4.72/babydriver.ko,意味著要調(diào)試這個 ko 文件,使用 IDA 對其進行分析,利用漏洞

對此文件系統(tǒng)進行打包也是要在這個目錄下進行

 ?  fs find . | cpio -o --format=newc > rootfs.cpio
 cpio: File ./rootfs.cpio grew, 43008 new bytes not copied
 5640 blocks

vmlinux

有些題目會給 vmlinux 這個文件,這是編譯出來的最原始的內(nèi)核文件,未壓縮的,是個 ELF 形式,方便找 gadget

可以使用一個工具來從 bzImage 中導(dǎo)出 vmlinux,extract-vmlinux

?  example ./extarct-vmlinux ./bzImage > vmlinux
?  example file bzImage                                
bzImage: Linux kernel x86 boot executable bzImage, version 4.4.72 (atum@ubuntu) #1 SMP Thu Jun 15 19:52:50 PDT 2017, RO-rootFS, swap_dev 0x6, Normal VGA
?  example file vmlinux 
vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=e993ea9809ee28d059537a0d5e866794f27e33b4, stripped

exploit

Kernel Pwn 就是找出內(nèi)核模塊中的漏洞,然后寫一個 C 語言程序,放入文件系統(tǒng)中打包,重新運行取來,此時用戶一般都是普通用戶,運行程序調(diào)用此模塊的功能利用漏洞,從而提升權(quán)限到 root 用戶,讀取 flag

/ $ ls
bin          exp          lib          root         sys
dev          home         linuxrc      rootfs.cpio  tmp
etc          init         proc         sbin         usr
/ $ whoami
ctf
/ $ ./exp 
[   18.277799] device open
[   18.278768] device open
[   18.279760] alloc done
[   18.280706] device release
/ # whoami
root

比賽時一般是上傳 C 語言程序的 base64 編碼到服務(wù)器,然后運行

Kernel Pwn Debug

要對內(nèi)核模塊進行調(diào)試,在啟動腳本中加入

-gdb tcp::1234

然后使用 gdb 連接

gdb -q -ex "target remote localhost:1234"

如果顯示 Remote 'g' packet reply is too long 一長串?dāng)?shù)字,要設(shè)置一下架構(gòu)

gdb -q -ex "set architecture i386:x86-64:intel" -ex "target remote localhost:1234"

要調(diào)試內(nèi)核模塊,可以先查看內(nèi)核加載地址,在/sys/module/中是加載的各個模塊的信息

/ $ cd sys/module/                                         
/sys/module $ ls                                           
8250                ipv6                scsi_mod           
acpi                kdb                 sg                 
acpi_cpufreq        kernel              spurious           
acpiphp             keyboard            sr_mod             
apparmor            kgdb_nmi            suspend            
ata_generic         kgdboc              sysrq              
ata_piix            libata              tcp_cubic          
babydriver          loop                thermal            
battery             md_mod              tpm                
block               module              tpm_tis            
core                mousedev            uhci_hcd           
cpuidle             netpoll             uinput             
debug_core          pata_sis            usbcore            
dm_mod              pcc_cpufreq         virtio_balloon     
dns_resolver        pci_hotplug         virtio_blk         
dynamic_debug       pci_slot            virtio_mmio        
edd                 pcie_aspm           virtio_net         
efivars             pciehp              virtio_pci         
ehci_hcd            ppp_generic         vt                 
elants_i2c          printk              workqueue          
ext4                processor           xen_acpi_processor 
firmware_class      pstore              xen_blkfront       
fuse                rcupdate            xen_netfront       
i8042               rcutree             xhci_hcd           
ima                 rfkill              xz_dec             
intel_idle          rng_core            zswap

獲取 babydrive 模塊的加載地址

/sys/module $ cd babydriver/                            
/sys/module/babydriver $ ls                             
coresize    initsize    notes       sections    taint   
holders     initstate   refcnt      srcversion  uevent  
/sys/module/babydriver $ cd sections/                   
/sys/module/babydriver/sections $ grep 0 .text          
0xffffffffc0000000

在 gdb 中載入符號信息,就可以對內(nèi)核模塊進行下斷調(diào)試

pwndbg> add-symbol-file ./fs/lib/modules/4.4.72/babydriver.ko 0xffffffffc00000
00
add symbol table from file "./fs/lib/modules/4.4.72/babydriver.ko" at
        .text_addr = 0xffffffffc0000000
Reading symbols from ./fs/lib/modules/4.4.72/babydriver.ko...done.
pwndbg> b*babyopen 
Breakpoint 1 at 0xffffffffc0000030: file /home/atum/PWN/my/babydriver/kernelmo
dule/babydriver.c, line 28.

Basic Knowledge

Kernel

Kernel 是一個程序,是操作系統(tǒng)底層用來管理上層軟件發(fā)出的各種請求的程序,Kernel 將各種請求轉(zhuǎn)換為指令,交給硬件去處理,簡而言之,Kernel 是連接軟件與硬件的中間層

Kernel 主要提供兩個功能,與硬件交互,提供應(yīng)用運行環(huán)境

在 intel 的 CPU 中,會將 CPU 的權(quán)限分為 Ring 0,Ring 1,Ring 2,Ring 3,四個等級,權(quán)限依次遞減,高權(quán)限等級可以調(diào)用低權(quán)限等級的資源

在常見的系統(tǒng)(Windows,Linux,MacOS)中,內(nèi)核處于 Ring 0 級別,應(yīng)用程序處于 Ring 3 級別

LKM

內(nèi)核模塊是 Linux Kernel 向外部提供的一個插口,叫做動態(tài)可加載內(nèi)核模塊(Loadable Kernel Module,LKM),LKM 彌補了 Linux Kernel 的可拓展性與可維護性,類似搭積木一樣,可以往 Kernel 中接入各種 LKM,也可以卸載,常見的外設(shè)驅(qū)動就是一個 LKM

LKM 文件與用戶態(tài)的可執(zhí)行文件一樣,在 Linux 中就是 ELF 文件,可以利用 IDA 進行分析

LKM 是單獨編譯的,但是不能單獨運行,他只能作為 OS Kernel 的一部分

與 LKM 相關(guān)的指令有如下幾個

insmod:接入指定模塊

rmmod:移除指定模塊

lsmod:列出已加載模塊

這些都是 shell 指令,可以在 shell 中運行查看

?  ~ lsmod 
Module                  Size  Used by
rfcomm                 77824  2
vmw_vsock_vmci_transport    32768  2
vsock                  36864  3 vmw_vsock_vmci_transport
......

ioctl

ioctl 是設(shè)備驅(qū)動程序中對設(shè)備的 I/O 通道進行管理的函數(shù)

所謂對 I/O 通道進行管理,就是對設(shè)備的一些特性進行控制,例如串口的傳輸波特率、馬達的轉(zhuǎn)速等等。它的調(diào)用個數(shù)如下: int ioctl(int fd, ind cmd, …);

其中 fd 是用戶程序打開設(shè)備時使用 open 函數(shù)返回的文件標(biāo)示符,cmd 是用戶程序?qū)υO(shè)備的控制命令,至于后面的省略號,那是一些補充參數(shù),一般最多一個,這個參數(shù)的有無和 cmd 的意義相關(guān)

ioctl 函數(shù)是文件結(jié)構(gòu)中的一個屬性分量,就是說如果你的驅(qū)動程序提供了對 ioctl 的支持,用戶就可以在用戶程序中使用 ioctl 函數(shù)來控制設(shè)備的 I/O 通道。

意思就是說如果一個 LKM 中提供了 iotcl 功能,并且實現(xiàn)了對應(yīng)指令的操作,那么在用戶態(tài)中,通過這個驅(qū)動程序,我們可以調(diào)用 ioctl 來直接調(diào)用模塊中的操作

Land Switch

在程序運行時,總是會經(jīng)歷 user space 與 kernel space 之前的切換,因為用戶態(tài)應(yīng)用程序在執(zhí)行某些功能時,是由 Kernel 來執(zhí)行的,這就涉及到兩個 space 之前的切換

user land -> kernel land

當(dāng)用戶態(tài)程序執(zhí)行系統(tǒng)調(diào)用,異常處理,外設(shè)終端時,會從用戶態(tài)切換到內(nèi)核態(tài),切換過程如下:

1.swapgs 指令修改 GS 寄存器切換到內(nèi)核態(tài)

2.將當(dāng)前棧頂(sp)記錄在 CPU 獨占變量區(qū)域,然后將此區(qū)域里的內(nèi)核棧頂賦給 sp

3.push 各寄存器的值

4.通過匯編指令判斷是否為 32 位

5.通過系統(tǒng)調(diào)用號,利用函數(shù)表 sys_call_table 執(zhí)行響應(yīng)操作

ENTRY(entry_SYSCALL_64)
 /* SWAPGS_UNSAFE_STACK 是一個宏,x86 直接定義為 swapgs 指令 */
 SWAPGS_UNSAFE_STACK

 /* 保存棧值,并設(shè)置內(nèi)核棧 */
 movq %rsp, PER_CPU_VAR(rsp_scratch)
 movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp

/* 通過 push 保存寄存器值,形成一個 pt_regs 結(jié)構(gòu) */
/* Construct struct pt_regs on stack */
pushq  $__USER_DS      /* pt_regs->ss */
pushq  PER_CPU_VAR(rsp_scratch)  /* pt_regs->sp */
pushq  %r11             /* pt_regs->flags */
pushq  $__USER_CS      /* pt_regs->cs */
pushq  %rcx             /* pt_regs->ip */
pushq  %rax             /* pt_regs->orig_ax */
pushq  %rdi             /* pt_regs->di */
pushq  %rsi             /* pt_regs->si */
pushq  %rdx             /* pt_regs->dx */
pushq  %rcx tuichu    /* pt_regs->cx */
pushq  $-ENOSYS        /* pt_regs->ax */
pushq  %r8              /* pt_regs->r8 */
pushq  %r9              /* pt_regs->r9 */
pushq  %r10             /* pt_regs->r10 */
pushq  %r11             /* pt_regs->r11 */
sub $(6*8), %rsp      /* pt_regs->bp, bx, r12-15 not saved */

kernel land -> user land

內(nèi)核態(tài)返回用戶態(tài)流程:

1.swapgs 指令恢復(fù)用戶態(tài) GS 寄存器

2.sysretq 或者 iretq 恢復(fù)到用戶空間

Kernel Functions

內(nèi)核態(tài)與用戶態(tài)的函數(shù)有一些區(qū)別

printk:類似與 printf,但是內(nèi)容不一定會在終端顯示起來,但是會在內(nèi)核緩沖區(qū)里,可以用 dmsg 命令查看

copy_from_user:實現(xiàn)了將用戶空間的數(shù)據(jù)傳送到內(nèi)核空間

copy_to_user:實現(xiàn)了將內(nèi)核空間的數(shù)據(jù)傳送到用戶空間

kmalloc:內(nèi)核態(tài)內(nèi)存分配函數(shù)

kfree:內(nèi)核態(tài)內(nèi)存釋放函數(shù)

用來改變權(quán)限的函數(shù):

int commit_creds(struct cred *new)

struct cred prepare_kernel_cred(struct task_struct daemon)

執(zhí)行 commit_creds(prepare_kernel_cred(0)) 即可獲得 root 權(quán)限

Expoit Mitigations

內(nèi)核態(tài)與用戶態(tài)的保護方式有所區(qū)別

相同的保護措施:DEP,Canary,ASLR,PIE,RELRO

不同的保護措施:MMAP_MIN_ADDR,KALLSYMS,RANDSTACK,STACKLEAK,SMEP,SMAP

MMAP_MIN_ADDR

MMAP_MIN_ADDR 保護機制不允許程序分配低內(nèi)存地址,可以用來防御 null pointer dereferences

如果沒有這個保護,可以進行如下的攻擊行為:

1.函數(shù)指針指針為 0,程序可以分配內(nèi)存到 0x000000 處。

2.程序在內(nèi)存 0x000000 寫入惡意代碼。

3.程序觸發(fā) kernel BUG()。這里說的 BUG() 其實是 linux kernel 中用于攔截內(nèi)核程序超出預(yù)期的行為,屬于軟件主動匯報異常的一種機制。

4.內(nèi)核執(zhí)行惡意代碼。

KALLSYMS

/proc/kallsyms 給出內(nèi)核中所有 symbol 的地址,通過 grep  /proc/kallsyms 就可以得到對應(yīng)函數(shù)的地址,我們需要這個信息來寫可靠的 exploit,否則需要自己去泄露這個信息。在低版本的內(nèi)核中所有用戶都可讀取其中的內(nèi)容,高版本的內(nèi)核中缺少權(quán)限的用戶讀取時會返回 0。

SMEP

管理模式執(zhí)行保護,保護內(nèi)核是其不允許執(zhí)行用戶空間代碼。在 SMEP 保護關(guān)閉的情況下,若存在 kernel stack overfolw,可以將內(nèi)核棧的返回地址覆蓋為用戶空間的代碼片段執(zhí)行。在開啟了 SMEP 保護下,當(dāng)前 cpu 處于 ring 0 模式,當(dāng)返回到用戶態(tài)執(zhí)行時會觸發(fā)頁錯誤。

操作系統(tǒng)是通過 CR4 寄存器的第 20 位的值來判斷 SMEP 是否開啟,1 開啟,0 關(guān)閉,檢查 SMEP 是否開啟

cat /proc/cpuinfo | grep smep

可通過 mov 指令給 CR4 寄存器賦值從而達到關(guān)閉 SMEP 的目的,相關(guān)的 mov 指令可以通過 ropper,ROPgadget 等工具查找

SMAP

管理模式訪問保護,禁止內(nèi)核訪問用戶空間的數(shù)據(jù)

KASLR

內(nèi)核地址空間布局隨機化,并不默認開啟,需要在內(nèi)核命令行中添加指定指令。

qemu 增加啟動參數(shù) -append "kaslr" 即可開啟

Privilege Escalation

提取,越獄,就是要以 root 用戶拿到 shell,獲取 root 的方式有幾種

在內(nèi)核態(tài)調(diào)用 commit_creds(prepare_kernel_cred(0)),返回用戶態(tài)執(zhí)行起 shell

void get_r00t() {
    commit_creds(prepare_kernel_cred(0));
}
int main(int argc, char *argv) {
    ...
    trigger_fp_overwrite(&get_r00t);
    ...
    // trigger fp use
    trigger_vuln_fp();
    // Kernel Executes get_r00t()
    ...
    // Now we have root
    system("/bin/sh");
}

SMEP 防預(yù)這種類型的攻擊的方法是:如果處理器處于 ring0 模式,并試圖執(zhí)行有 user 數(shù)據(jù)的內(nèi)存時,就會觸發(fā)一個頁錯誤。

也可以修改 cred 結(jié)構(gòu)體,cred 結(jié)構(gòu)體記錄了進程的權(quán)限,每個進程都有一個 cred 結(jié)構(gòu)體,保存了進程的權(quán)限等信息(uid,gid),如果修改某個進程的 cred 結(jié)構(gòu)體(uid = gid = 0),就得到了 root 權(quán)限

struct cred {
    atomic_t    usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t    subscribers;    /* number of processes subscribed */
    void        *put_addr;
    unsigned    magic;
#define CRED_MAGIC    0x43736564
#define CRED_MAGIC_DEAD    0x44656144
#endif
    kuid_t        uid;        /* real UID of the task */
    kgid_t        gid;        /* real GID of the task */
    kuid_t        suid;        /* saved UID of the task */
    kgid_t        sgid;        /* saved GID of the task */
    kuid_t        euid;        /* effective UID of the task */
    kgid_t        egid;        /* effective GID of the task */
    kuid_t        fsuid;        /* UID for VFS ops */
    kgid_t        fsgid;        /* GID for VFS ops */
    unsigned    securebits;    /* SUID-less security management */
    kernel_cap_t    cap_inheritable; /* caps our children can inherit */
    kernel_cap_t    cap_permitted;    /* caps we're permitted */
    kernel_cap_t    cap_effective;    /* caps we can actually use */
    kernel_cap_t    cap_bset;    /* capability bounding set */
    kernel_cap_t    cap_ambient;    /* Ambient capability set */
#ifdef CONFIG_KEYS
    unsigned char    jit_keyring;    /* default keyring to attach requested
                     * keys to */
    struct key __rcu *session_keyring; /* keyring inherited over fork */
    struct key    *process_keyring; /* keyring private to this process */
    struct key    *thread_keyring; /* keyring private to this thread */
    struct key    *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
    void        *security;    /* subjective LSM security */
#endif
    struct user_struct *user;    /* real user ID subscription */
    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
    struct group_info *group_info;    /* supplementary groups for euid/fsgid */
    struct rcu_head    rcu;        /* RCU deletion hook */
} __randomize_layout;

Build Linux Kernel

Source Code

先下載一份 Kernel 源碼,我用的是 2.6.32,由于我的機子是 ubuntu 16.04,預(yù)裝的 make 與 gcc 版本過高,編譯 2.6 的 kernel 會失敗,所以需要降級

# 4.7 gcc
sudo apt install gcc-4.7 g++-4.7
sudo rm /usr/bin/gcc /usr/bin/g++
sudo ln -s /usr/bin/gcc-4.7 /usr/bin/gcc
sudo ln -s /usr/bin/g++-4.7 /usr/bin/g++
# 3.80 make
wget https://mirrors.tuna.tsinghua.edu.cn/gnu/make/make-3.80.tar.gz
tar -xvf make-3.80.tar.gz
cd make-3.80/
./configure
make
sudo make install

3.80 的 make 生成在源碼目錄里,稍后需要用這個 make 文件

修改三處 2.6 源碼文件

1.arch/x86/vdso/Makefile 中第 28 行的 -m elf_x86_64 改成 -m64,第 72 行的-m elf_i386 改成-m32

2.drivers/net/igbvf/igbvf.h 中注釋第 128 行

3.kernel/timeconst.pl 中第 373 行 defined(@val) 改成 @val

4.(可選)關(guān)閉 canary 保護需要編輯源碼中的.config 文件 349 行,注釋掉 CONFIG_CC_STACKPROTECTOR=y 這一項

bzImage

安裝必備依賴

sudo apt-get install build-essential libncurses5-dev

解壓后進入源碼目錄,使用剛安裝的 make

~/MAKE/make-3.80/make menuconfig

進入 kernel hacking,勾選 Kernel debugging,Compile-time checks and compiler options-->Compile the kernel with debug info,Compile the kernel with frame pointers 和 KGDB,然后開始編譯

~/MAKE/make-3.80/make bzImage

大概 10 分鐘的樣子,出現(xiàn)這個信息就說明編譯成功了

Setup is 15036 bytes (padded to 15360 bytes).
System is 3754 kB
CRC 4505d1c3
Kernel: arch/x86/boot/bzImage is ready  (#1)

vmlinux 在源碼根目錄下,bzImage 在/arch/x86/boot/里

rootfs.cpio

編譯 busybox

wget https://busybox.net/downloads/busybox-1.27.2.tar.bz2
tar -jxvf busybox-1.27.2.tar.bz2
cd busybox-1.27.2
make menuconfig

勾選 Busybox Settings -> Build Options -> Build Busybox as a static binary

make install

編譯完成后源碼目錄下會有一個_install 文件夾,進入

mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
mkdir etc/init.d
touch etc/init.d/init

編輯 etc/inittab 文件,加入以下內(nèi)容(貌似這一步可以省略)

::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init

編輯 etc/init.d/init 文件,加入以下內(nèi)容

#!/bin/sh
mount -t proc none /proc
mount -t sys none /sys
/bin/mount -n -t sysfs none /sys
/bin/mount -t ramfs none /dev
/sbin/mdev -s

接著就可以打包成 rootfs.cpio

chmod +x ./etc/init.d/rcS
find . | cpio -o --format=newc > ../rootfs.cpio

boot

得到三個文件后,可以利用 qemu 運行起來,啟動腳本 boot.sh

#!/bin/sh
qemu-system-x86_64 \
 -initrd rootfs.cpio \
 -kernel bzImage \
 -nographic \
 -append "console=ttyS0 root=/dev/ram rdinit=/sbin/init" \
 -m 64M \
 -monitor /dev/null \
/ # uname -a
Linux (none) 2.6.32 #1 SMP Sun Jan 26 21:51:02 CST 2020 x86_64 GNU/Linux

Run LKM

build

簡單寫一個 hello 的程序,hello.c 內(nèi)容如下

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
int hello_write(struct file *file, const char *buf, unsigned long len)
{
    printk("You write something.");
    return len;
}
static int __init hello_init(void)
{
    printk(KERN_ALERT "hello driver init!\n");
    create_proc_entry("hello", 0666, 0)->write_proc = hello_write;
    return 0;
}
static void __exit hello_exit(void)
{
    printk(KERN_ALERT "hello driver exit\n");
}
module_init(hello_init);
module_exit(hello_exit);

Makefile 內(nèi)容如下,注意 xxx.c 與 xxx.o 文件名一致,KERNELDR 目錄是內(nèi)核源代碼

obj-m := hello.o
KERNELDR := /home/mask/kernel/linux-2.6.32
PWD := $(shell pwd)
modules:
    $(MAKE) -C $(KERNELDR) M=$(PWD) modules
modules_install:
    $(MAKE) -C $(KERNELDR) M=$(PWD) modules_install
clean:
    $(MAKE) -C $(KERNELDR) M=$(PWD) clean

make 出來后得到.ko 文件

?  helloworld ls
helloc.c   helloc.mod.c  helloc.o  modules.order
helloc.ko  helloc.mod.o  Makefile  Module.symvers
?  helloworld file helloc.ko                                                                      
helloc.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=08aaa94df43f8333c14
9073cddf3043e52b28107, not stripped
?  helloworld checksec helloc.ko       
[*] '/home/mask/kernel/test/linux4.4/module/helloworld/helloc.ko'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x0)

再寫一個調(diào)用程序 call.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
int main()
{
    int fd = open("/proc/hello", O_WRONLY);
    write(fd, "Mask", 4);
    return 0;
}

run

將 helloc.ko 文件與 call 文件復(fù)制.

進文件系統(tǒng),也就是 busybox 目錄里的_install 文件夾,重新打包 rootfs.cpio,運行起來即可看見模塊

/ # insmod hello.ko 
[   11.743066] hello driver init!
/ # ./call
[   25.860294] You write something.

“Pwn In Kernel基礎(chǔ)知識有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI