目錄
- 前言
- 子產品與系統調用
- 用子產品列印Hello, world!
- 用子產品添加自定義系統調用
- top指令
- 關閉Linux圖形界面
- 重編核心添加系統調用
- 解壓系統源代碼
- 撰寫自定義系統調用
- 編譯核心
- 測試新核心
- 最後
要自定義系統調用, 正常的兩個方法是子產品和重編核心, 一起來看看吧.
更新:
在64位ubuntu12.04.5上也成功運作.
解決了14.04, 16.04, 18.04上的問題.
首先看下系統版本和核心版本. 我用的是32位的ubuntu12.04.5LTS, 我很不喜歡用這麼舊的版本, 但是, 其它版本都出現各種問題, 之後我會給大家展示我掉過的坑, 如果你能幫助我解決, 請評論區, 提前感謝~~
uname -a
cat /proc/version
uname -r
我是在mac端用ssh通路Linux的, 這樣是有很多好處的, 比如直接複制粘貼, 不需要改鍵盤映射等等. 先來寫一個test.c.
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void)
{
printk("Hello, world! Written by Sorrower\n");
return 0;
}
static void __exit hello_exit(void)
{
printk("Exit, world! Written by Sorrower\n");
}
module_init(hello_init);
module_exit(hello_exit);
然後寫Makefile. 注意看, 如果你用的vim, make前面如果是空格不是TAB, vim是會提示你的. 我這裡就是TAB, 是以沒有提示.
obj-m:=test.o
CURRENT_PATH :=$(shell pwd)
VERSION_NUM :=$(shell uname -r)
LINUX_PATH :=/usr/src/linux-headers-$(VERSION_NUM)
all :
make -C $(LINUX_PATH) M=$(CURRENT_PATH) modules
clean :
make -C $(LINUX_PATH) M=$(CURRENT_PATH) clean
輸入make指令. 會生成一些檔案, 要用的是.ko檔案.
你可以用lsmod指令看下有什麼子產品. 然後插入剛才生成的test.ko.
sudo insmod test.ko
然後看下列印了消息沒.
dmesg | grep "sorrower"
可以再用lsmod看一下. 然後解除安裝子產品. 用dmesg | grep "Sorrower"檢視.
sudo rmmod test
注意, 題目是用系統調用列印Hello, world!, 之前的隻是熟悉一下子產品的使用, 還不是系統調用列印出來的.
來到/usr/include/i386-linux-gnu/asm, 檢視unistd_32.h, 注意這是32位ubutnu12.04.5中的位置, 不代表其他版本其他位數的.
看到223了嗎, 這很明顯就是拿來自定義的.
然後來到/boot, 要檢視sys_call_table的記憶體位置, 注意, 要管理者權限.
然後用vim搜尋sys_call_table. 我特意把行号标出來了, 你要是想手動找到, 祝你好運了.
開始寫syscall.c. 這段代碼不是我寫的, 來自 這篇文章 , 寫得很棒. 然後請原諒我不要臉地在自定義系統調用裡面加了自己的Hello, world!(手動滑稽)
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/sched.h>
MODULE_LICENSE("Dual BSD/GPL");
#define SYS_CALL_TABLE_ADDRESS 0xc1697140 //sys_call_table對應的位址
#define NUM 223 //系統調用号為223
int orig_cr0; //用來存儲cr0寄存器原來的值
unsigned long *sys_call_table_my=0;
static int(*anything_saved)(void); //定義一個函數指針,用來儲存一個系統調用
static int clear_cr0(void) //使cr0寄存器的第17位設定為0(核心空間可寫)
{
unsigned int cr0=0;
unsigned int ret;
asm volatile("movl %%cr0,%%eax":"=a"(cr0));//将cr0寄存器的值移動到eax寄存器中,同時輸出到cr0變量中
ret=cr0;
cr0&=0xfffeffff;//将cr0變量值中的第17位清0,将修改後的值寫入cr0寄存器
asm volatile("movl %%eax,%%cr0"::"a"(cr0));//将cr0變量的值作為輸入,輸入到寄存器eax中,同時移動到寄存器cr0中
return ret;
}
static void setback_cr0(int val) //使cr0寄存器設定為核心不可寫
{
asm volatile("movl %%eax,%%cr0"::"a"(val));
}
asmlinkage long sys_mycall(void) //定義自己的系統調用
{
printk("Hello, world! Written by Sorrower\n");
printk("子產品系統調用-目前pid:%d,目前comm:%s\n",current->pid,current->comm);
return current->pid;
}
static int __init call_init(void)
{
sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS);
printk("call_init......\n");
anything_saved=(int(*)(void))(sys_call_table_my[NUM]);//儲存系統調用表中的NUM位置上的系統調用
orig_cr0=clear_cr0();//使核心位址空間可寫
sys_call_table_my[NUM]=(unsigned long) &sys_mycall;//用自己的系統調用替換NUM位置上的系統調用
setback_cr0(orig_cr0);//使核心位址空間不可寫
return 0;
}
static void __exit call_exit(void)
{
printk("call_exit......\n");
orig_cr0=clear_cr0();
sys_call_table_my[NUM]=(unsigned long)anything_saved;//将系統調用恢複
setback_cr0(orig_cr0);
}
module_init(call_init);
module_exit(call_exit);
MODULE_AUTHOR("25");
MODULE_VERSION("BETA 1.0");
MODULE_DESCRIPTION("a module for replace a syscall");
Makefile檔案和之前差不多, 改下生成的.o檔案名字就好.
然後要寫一個使用者态的程式來測試了.
什麼是使用者态, 來快速解釋一下. cpu有使用者态和核心态, 系統調用以及中斷和異常都會由使用者态變成核心态. 上一張程序轉換圖(或者叫狀态機?), 圖檔來自網絡, 我覺得畫得一般, 但是我不想再手動畫一張了.

好了, 不皮了. 來寫test.c吧. 簡單粗暴, 就一個系統223調用.
#include<stdio.h>
#include<stdlib.h>
int main()
{
syscall(223);
return 0;
}
gcc一下, 然後dmesg一下. 這下真的就結束這一部分了.
中途休息一下, 來說些小技巧和指令.
mac下的top指令非常好用. 你輸入top, 然後輸入?, 就顯示全部後續操作了. 比如這裡top下輸入o, 在輸入cpu回車. 就是cpu占有排序.
我沒有很讨厭Linux的圖形界面, 但是用了ssh之後, 你就發現确實用不到了. 我知道大家都會切換到tty的. mac是fn+ctrl+option+f3(當然了, 根據版本不同, fx有效範圍不同, 12.04是f1-f6, f7圖形界面, 測測就知道了)
但是還不夠徹底, 要讓它開機直接字元界面. 關閉/開啟. 當然了, 12.04似乎不吃這個指令. 要再高版本一些.
sudo systemctl set-default multi-user.target
sudo reboot
sudo systemctl set-default graphical.target
sudo reboot
接下來這個就很簡單了, 主要難度在找檔案位置以及cpu. 這裡切換回18.04LTS. cpu不好的, 可能2h+了, 好的cpu編個18.04LTS怎麼20min也要吧. cpu核數兩位數的麻煩關閉頁面, 不在一個頻道了(手動滑稽). 那順帶一提, 之前說的徹底關閉圖形界面在18.04LTS就生效了.
你可以使用指令下載下傳源碼, 也可以 手動下載下傳 . 總之, 下完之後, 解壓檔案. 看圖檔, 我就是用指令下載下傳, 然後再解壓壓縮包, 是以有兩個同名目錄.
sudo apt-get install linux-source
sudo tar -jxvf linux-source-4.15.0.tar.bz2
關鍵是三個檔案sys.c, syscalls.h, syscall_32.tbl. 都在很要命的地方呢.對着調用編号就是666(手動滑稽).
- sys.c在/usr/src/linux-source-4.15.0/linux-source-4.15.0/kernel下
- syscalls.h在/usr/src/linux-source-4.15.0/linux-source-4.15.0/arch/x86/include/asm下
- syscall_32.tbl在/usr/src/linux-source-4.15.0/linux-source-4.15.0/arch/x86/entry/syscalls下
打開sys.c寫自定義函數, 注意函數名對應.
申明函數, 還是注意名稱對應.
需要先補下庫.
sudo apt-get install libncurses5-dev
然後你可以設定編譯參數, 如果你知道自己在幹嘛的話.
sudo make menuconfig
然後就是cpu測試時間了. 編譯好了, 裝下重新開機就完事了. 我就不重做了.
sudo make
sudo make modules
make modules_install
make install
上幾張之前實驗時候截的效果圖, 測試函數還是之前的test.c, 改下調用号就可以了.
先來幾個坑, 求人救救孩子~~
這是14.04.5中的, 說什麼Invalid module format, StackOverFlow說是核心版本不一緻, 但是我Makefile中是用'uname -r'的, 怎麼會不一緻呢.
問題已經解決, 如果出現上述錯誤, 隻需要使用:sudo apt-get install linux-source-(uname -r得到的核心号)即可.
例如:
sudo apt-get install linux-source-4.15.0
之後使用如下指令, 可能會提示補庫:
sudo make bzImage
sudo make modules
sudo make modules_install
reboot之後問題迎刃而解.
然後看一下預設的18.04, 不是我改過核心的那個. 也在google和StackOverFlow看了解決方案, 還是解決不能.
解決方案除了重編核心, 就是重新安裝鏡像, 目前我新裝的18.04.1測試沒問題.
這次也是新開一個篇章, 和以往分享操作不同, 文章更偏向探索, 去學習更深的知識. 喜歡記得點贊, 有意見或者建議評論區見, 暗中關注我也是可以的~