本篇內(nèi)容介紹了“Pwn In Kernel基礎(chǔ)知識(shí)有哪些”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、富拉爾基網(wǎng)絡(luò)推廣、成都微信小程序、富拉爾基網(wǎng)絡(luò)營(yíng)銷、富拉爾基企業(yè)策劃、富拉爾基品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供富拉爾基建站搭建服務(wù),24小時(shí)服務(wù)熱線:18982081108,官方網(wǎng)址:www.cdcxhl.com
簡(jiǎn)單分析一下 CTF Kernel Pwn 題目的形式,以 2017 CISCN babydrive 為例。
先對(duì)文件包解壓
? 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 三個(gè)文件
? 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 文件是用來啟動(dòng)這個(gè)程序的,調(diào)用 qemu 來加載 rootfs.cpio 與 bzImage 運(yùn)行起來
上面的參數(shù)都是 qemu 的參數(shù)
-initrd rootfs.cpio,使用 rootfs.cpio 作為內(nèi)核啟動(dòng)的文件系統(tǒng) -kernel bzImage,使用 bzImage 作為 kernel 映像 -cpu kvm64,+smep,設(shè)置 CPU 的安全選項(xiàng),這里開啟了 smep -m 64M,設(shè)置虛擬 RAM 為 64M,默認(rèn)為 128M
? 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)核文件
? example file rootfs.cpio rootfs.cpio: gzip compressed data, last modified: Tue Jul 4 08:39:15 2017, max compression, from Unix
這是一個(gè) linux 內(nèi)核文件系統(tǒng)壓縮包,我們可以對(duì)其解壓并重新壓縮,從而修改這個(gè)系統(tǒng)的文件
新建一個(gè)文件夾來解壓
? 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
這些就是運(yùn)行起來后這個(gè)系統(tǒng)擁有的文件,查看這個(gè) 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 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)試這個(gè) ko 文件,使用 IDA 對(duì)其進(jìn)行分析,利用漏洞
對(duì)此文件系統(tǒng)進(jìn)行打包也是要在這個(gè)目錄下進(jìn)行
? fs find . | cpio -o --format=newc > rootfs.cpio cpio: File ./rootfs.cpio grew, 43008 new bytes not copied 5640 blocks
有些題目會(huì)給 vmlinux 這個(gè)文件,這是編譯出來的最原始的內(nèi)核文件,未壓縮的,是個(gè) ELF 形式,方便找 gadget
可以使用一個(gè)工具來從 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
Kernel Pwn 就是找出內(nèi)核模塊中的漏洞,然后寫一個(gè) C 語言程序,放入文件系統(tǒng)中打包,重新運(yùn)行取來,此時(shí)用戶一般都是普通用戶,運(yùn)行程序調(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
比賽時(shí)一般是上傳 C 語言程序的 base64 編碼到服務(wù)器,然后運(yùn)行
要對(duì)內(nèi)核模塊進(jìn)行調(diào)試,在啟動(dòng)腳本中加入
-gdb tcp::1234
然后使用 gdb 連接
gdb -q -ex "target remote localhost:1234"
如果顯示 Remote 'g' packet reply is too long 一長(zhǎng)串?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/中是加載的各個(gè)模塊的信息
/ $ 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 中載入符號(hào)信息,就可以對(duì)內(nèi)核模塊進(jìn)行下斷調(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.
Kernel 是一個(gè)程序,是操作系統(tǒng)底層用來管理上層軟件發(fā)出的各種請(qǐng)求的程序,Kernel 將各種請(qǐng)求轉(zhuǎn)換為指令,交給硬件去處理,簡(jiǎn)而言之,Kernel 是連接軟件與硬件的中間層
Kernel 主要提供兩個(gè)功能,與硬件交互,提供應(yīng)用運(yùn)行環(huán)境
在 intel 的 CPU 中,會(huì)將 CPU 的權(quán)限分為 Ring 0,Ring 1,Ring 2,Ring 3,四個(gè)等級(jí),權(quán)限依次遞減,高權(quán)限等級(jí)可以調(diào)用低權(quán)限等級(jí)的資源
在常見的系統(tǒng)(Windows,Linux,MacOS)中,內(nèi)核處于 Ring 0 級(jí)別,應(yīng)用程序處于 Ring 3 級(jí)別
內(nèi)核模塊是 Linux Kernel 向外部提供的一個(gè)插口,叫做動(dòng)態(tài)可加載內(nèi)核模塊(Loadable Kernel Module,LKM),LKM 彌補(bǔ)了 Linux Kernel 的可拓展性與可維護(hù)性,類似搭積木一樣,可以往 Kernel 中接入各種 LKM,也可以卸載,常見的外設(shè)驅(qū)動(dòng)就是一個(gè) LKM
LKM 文件與用戶態(tài)的可執(zhí)行文件一樣,在 Linux 中就是 ELF 文件,可以利用 IDA 進(jìn)行分析
LKM 是單獨(dú)編譯的,但是不能單獨(dú)運(yùn)行,他只能作為 OS Kernel 的一部分
與 LKM 相關(guān)的指令有如下幾個(gè)
insmod:接入指定模塊
rmmod:移除指定模塊
lsmod:列出已加載模塊
這些都是 shell 指令,可以在 shell 中運(yùn)行查看
? ~ lsmod Module Size Used by rfcomm 77824 2 vmw_vsock_vmci_transport 32768 2 vsock 36864 3 vmw_vsock_vmci_transport ......
ioctl 是設(shè)備驅(qū)動(dòng)程序中對(duì)設(shè)備的 I/O 通道進(jìn)行管理的函數(shù)
所謂對(duì) I/O 通道進(jìn)行管理,就是對(duì)設(shè)備的一些特性進(jìn)行控制,例如串口的傳輸波特率、馬達(dá)的轉(zhuǎn)速等等。它的調(diào)用個(gè)數(shù)如下: int ioctl(int fd, ind cmd, …);
其中 fd 是用戶程序打開設(shè)備時(shí)使用 open 函數(shù)返回的文件標(biāo)示符,cmd 是用戶程序?qū)υO(shè)備的控制命令,至于后面的省略號(hào),那是一些補(bǔ)充參數(shù),一般最多一個(gè),這個(gè)參數(shù)的有無和 cmd 的意義相關(guān)
ioctl 函數(shù)是文件結(jié)構(gòu)中的一個(gè)屬性分量,就是說如果你的驅(qū)動(dòng)程序提供了對(duì) ioctl 的支持,用戶就可以在用戶程序中使用 ioctl 函數(shù)來控制設(shè)備的 I/O 通道。
意思就是說如果一個(gè) LKM 中提供了 iotcl 功能,并且實(shí)現(xiàn)了對(duì)應(yīng)指令的操作,那么在用戶態(tài)中,通過這個(gè)驅(qū)動(dòng)程序,我們可以調(diào)用 ioctl 來直接調(diào)用模塊中的操作
在程序運(yùn)行時(shí),總是會(huì)經(jīng)歷 user space 與 kernel space 之前的切換,因?yàn)橛脩魬B(tài)應(yīng)用程序在執(zhí)行某些功能時(shí),是由 Kernel 來執(zhí)行的,這就涉及到兩個(gè) space 之前的切換
user land -> kernel land
當(dāng)用戶態(tài)程序執(zhí)行系統(tǒng)調(diào)用,異常處理,外設(shè)終端時(shí),會(huì)從用戶態(tài)切換到內(nèi)核態(tài),切換過程如下:
1.swapgs 指令修改 GS 寄存器切換到內(nèi)核態(tài)
2.將當(dāng)前棧頂(sp)記錄在 CPU 獨(dú)占變量區(qū)域,然后將此區(qū)域里的內(nèi)核棧頂賦給 sp
3.push 各寄存器的值
4.通過匯編指令判斷是否為 32 位
5.通過系統(tǒng)調(diào)用號(hào),利用函數(shù)表 sys_call_table 執(zhí)行響應(yīng)操作
ENTRY(entry_SYSCALL_64) /* SWAPGS_UNSAFE_STACK 是一個(gè)宏,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 保存寄存器值,形成一個(gè) 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ù)到用戶空間
內(nèi)核態(tài)與用戶態(tài)的函數(shù)有一些區(qū)別
printk:類似與 printf,但是內(nèi)容不一定會(huì)在終端顯示起來,但是會(huì)在內(nèi)核緩沖區(qū)里,可以用 dmsg 命令查看
copy_from_user:實(shí)現(xiàn)了將用戶空間的數(shù)據(jù)傳送到內(nèi)核空間
copy_to_user:實(shí)現(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)限
內(nèi)核態(tài)與用戶態(tài)的保護(hù)方式有所區(qū)別
相同的保護(hù)措施:DEP,Canary,ASLR,PIE,RELRO
不同的保護(hù)措施:MMAP_MIN_ADDR,KALLSYMS,RANDSTACK,STACKLEAK,SMEP,SMAP
MMAP_MIN_ADDR
MMAP_MIN_ADDR 保護(hù)機(jī)制不允許程序分配低內(nèi)存地址,可以用來防御 null pointer dereferences
如果沒有這個(gè)保護(hù),可以進(jìn)行如下的攻擊行為:
1.函數(shù)指針指針為 0,程序可以分配內(nèi)存到 0x000000 處。
2.程序在內(nèi)存 0x000000 寫入惡意代碼。
3.程序觸發(fā) kernel BUG()。這里說的 BUG() 其實(shí)是 linux kernel 中用于攔截內(nèi)核程序超出預(yù)期的行為,屬于軟件主動(dòng)匯報(bào)異常的一種機(jī)制。
4.內(nèi)核執(zhí)行惡意代碼。
KALLSYMS
/proc/kallsyms 給出內(nèi)核中所有 symbol 的地址,通過 grep /proc/kallsyms 就可以得到對(duì)應(yīng)函數(shù)的地址,我們需要這個(gè)信息來寫可靠的 exploit,否則需要自己去泄露這個(gè)信息。在低版本的內(nèi)核中所有用戶都可讀取其中的內(nèi)容,高版本的內(nèi)核中缺少權(quán)限的用戶讀取時(shí)會(huì)返回 0。
SMEP
管理模式執(zhí)行保護(hù),保護(hù)內(nèi)核是其不允許執(zhí)行用戶空間代碼。在 SMEP 保護(hù)關(guān)閉的情況下,若存在 kernel stack overfolw,可以將內(nèi)核棧的返回地址覆蓋為用戶空間的代碼片段執(zhí)行。在開啟了 SMEP 保護(hù)下,當(dāng)前 cpu 處于 ring 0 模式,當(dāng)返回到用戶態(tài)執(zhí)行時(shí)會(huì)觸發(fā)頁錯(cuò)誤。
操作系統(tǒng)是通過 CR4 寄存器的第 20 位的值來判斷 SMEP 是否開啟,1 開啟,0 關(guān)閉,檢查 SMEP 是否開啟
cat /proc/cpuinfo | grep smep
可通過 mov 指令給 CR4 寄存器賦值從而達(dá)到關(guān)閉 SMEP 的目的,相關(guān)的 mov 指令可以通過 ropper,ROPgadget 等工具查找
SMAP
管理模式訪問保護(hù),禁止內(nèi)核訪問用戶空間的數(shù)據(jù)
KASLR
內(nèi)核地址空間布局隨機(jī)化,并不默認(rèn)開啟,需要在內(nèi)核命令行中添加指定指令。
qemu 增加啟動(dòng)參數(shù) -append "kaslr" 即可開啟
提取,越獄,就是要以 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)存時(shí),就會(huì)觸發(fā)一個(gè)頁錯(cuò)誤。
也可以修改 cred 結(jié)構(gòu)體,cred 結(jié)構(gòu)體記錄了進(jìn)程的權(quán)限,每個(gè)進(jìn)程都有一個(gè) cred 結(jié)構(gòu)體,保存了進(jìn)程的權(quán)限等信息(uid,gid),如果修改某個(gè)進(jìn)程的 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;
先下載一份 Kernel 源碼,我用的是 2.6.32,由于我的機(jī)子是 ubuntu 16.04,預(yù)裝的 make 與 gcc 版本過高,編譯 2.6 的 kernel 會(huì)失敗,所以需要降級(jí)
# 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 生成在源碼目錄里,稍后需要用這個(gè) 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 保護(hù)需要編輯源碼中的.config 文件 349 行,注釋掉 CONFIG_CC_STACKPROTECTOR=y 這一項(xiàng)
安裝必備依賴
sudo apt-get install build-essential libncurses5-dev
解壓后進(jìn)入源碼目錄,使用剛安裝的 make
~/MAKE/make-3.80/make menuconfig
進(jìn)入 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)這個(gè)信息就說明編譯成功了
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/里
編譯 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
編譯完成后源碼目錄下會(huì)有一個(gè)_install 文件夾,進(jìn)入
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
得到三個(gè)文件后,可以利用 qemu 運(yùn)行起來,啟動(dòng)腳本 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
簡(jiǎn)單寫一個(gè) hello 的程序,hello.c 內(nèi)容如下
#include#include #include #include 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)
再寫一個(gè)調(diào)用程序 call.c
#include#include #include #include #include #include #include int main() { int fd = open("/proc/hello", O_WRONLY); write(fd, "Mask", 4); return 0; }
將 helloc.ko 文件與 call 文件復(fù)制.
進(jìn)文件系統(tǒng),也就是 busybox 目錄里的_install 文件夾,重新打包 rootfs.cpio,運(yùn)行起來即可看見模塊
/ # insmod hello.ko [ 11.743066] hello driver init! / # ./call [ 25.860294] You write something.
“Pwn In Kernel基礎(chǔ)知識(shí)有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!