Linux編譯模塊及通過模塊修改系統(tǒng)調(diào)用

理解內(nèi)核模塊原理及正確編寫源代碼

原理:內(nèi)核模塊可以作為獨立程序來編譯的函數(shù)和數(shù)據(jù)類型的集合。之所以提供模塊機制势木,是因為Linux本身是一個單內(nèi)核。單內(nèi)核由于所有內(nèi)容都集成在一起稠腊,效率很高篓跛,但可擴展性和可維護性相對較差,模塊機制可以彌補這一缺陷瀑凝。

Linux模塊可以通過靜態(tài)或動態(tài)的方法加載到內(nèi)核空間序芦,靜態(tài)加載是指在內(nèi)核啟動過程中加載;動態(tài)加載是指在內(nèi)核運行的過程中隨時加載粤咪。

一個模塊被加載到內(nèi)核中時谚中,就成為內(nèi)核代碼的一部分。模塊加載入系統(tǒng)時,系統(tǒng)修改內(nèi)核中的符號表宪塔,將新加載的模塊提供的資源和符號添加到內(nèi)核符號表中磁奖,以便模塊間通信比搭。

編寫模塊代碼

  • 模塊構(gòu)造函數(shù):
  • 執(zhí)行insmod或modprobe指令加載內(nèi)核模塊時會調(diào)用的初始化函數(shù)南誊。函數(shù)原型必須是module_init(),內(nèi)是函數(shù)指針戚长。
  • 模塊析構(gòu)函數(shù)(釋放模塊對象所占用的內(nèi)存空間前所必須執(zhí)行的一個函數(shù)):
  • 執(zhí)行rmmod指令卸載模塊時調(diào)用的函數(shù)。函數(shù)原型是module_exit();
  • 模塊模塊許可聲明:
  • 函數(shù)原型是MODULE_LICENSE()怠苔,告訴內(nèi)核程序使用的許可證柑司,不然在加載時它會提示該模塊污染內(nèi)核蟆湖。一般會寫GPL隅津。

向內(nèi)核輸出簡單信息的代碼:

test.c

#include<linux/init.h>
#include<linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static char *name="tomorrow";
static void __init name_init(void)
{
        printk("Hello World~\n");
        printk("Hello %s\n",name);
}

static void __exit name_exit(void)
{
        printk(KERN_INFO"Name module exit\n");
}

module_init(name_init);
module_exit(name_exit);

module_param(name,charp,S_IRUGO);

Makefile文件

obj-m:=test.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-4.4.0-21-generic
all:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

make運行成功后會在原目錄下生成以下文件:


將模塊加載進內(nèi)核:

sudo insmod test.ko

查看模塊向內(nèi)核輸入的信息:

dmesg

卸載模塊:

sudo rmmod test

實現(xiàn)輸出當前進程信息的功能

module2.c

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/sched.h>

static struct task_struct *pcurrent;

int print_current_task_info(void);
static int __init print_init(void)
{
        printk(KERN_INFO "print current task info\n");
        print_current_task_info();
        return 0;
}

static void __exit print_exit(void)
{
        printk(KERN_INFO "Finished\n");
}

int print_current_task_info(void)
{
        pcurrent=get_current();
        printk(KERN_INFO "Task state: %ld\n",current->state);
        printk(KERN_INFO "pid: %d\n",current->pid);
        printk(KERN_INFO "tgid: %d\n",current->tgid);
        printk(KERN_INFO "prio: %d\n",current->prio);
        return 0;
}
module_init(print_init);
module_exit(print_exit);

Makefile

obj-m:=module2.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-4.4.0-21-generic
all:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

運行后在內(nèi)核中將輸出以下信息:

卸載后效果如下:

module3.c

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/sched.h>

static struct task_struct *pcurrent;

static int __init print_init(void)
{
        printk(KERN_INFO "print current task info\n");
        printk("pid\ttgid\tprio\tstate\n");
        for_each_process(pcurrent){
                printk("%d\t",pcurrent->pid);
                printk("%d\t",pcurrent->tgid);
                printk("%d\t",pcurrent->prio);
                printk("%ld\n",pcurrent->state);
        }
        return 0;
}
static void __exit print_exit(void)
{
        printk(KERN_INFO "Finished\n");
}

module_init(print_init);
module_exit(print_exit);

Makefile

obj-m:=module3.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-4.4.0-21-generic
all:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

加載后效果如下:

卸載后效果如下:

修改系統(tǒng)調(diào)用的模塊

在/usr/include/i386-linux-gnu/asm/unistd_32.h文件中查看系統(tǒng)調(diào)用序號

發(fā)現(xiàn)222號和223號系統(tǒng)調(diào)用是空的谓苟,因此選取223作為新的系統(tǒng)調(diào)用號。

在/boot/System.map-3.16.0-30-generic查看系統(tǒng)調(diào)用表的內(nèi)存地址:

為0xc1697140

編寫模塊文件:

module5.c

#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  //系統(tǒng)調(diào)用號為223
int orig_cr0;  //用來存儲cr0寄存器原來的值
unsigned long *sys_call_table_my=0;

static int(*anything_saved)(void);  //定義一個函數(shù)指針湾趾,用來保存一個系統(tǒng)調(diào)用

static int clear_cr0(void) //使cr0寄存器的第17位設(shè)置為0(內(nèi)核空間可寫)
{
    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寄存器設(shè)置為內(nèi)核不可寫
{
    asm volatile("movl %%eax,%%cr0"::"a"(val));
}

asmlinkage long sys_mycall(void) //定義自己的系統(tǒng)調(diào)用
{   
    printk("模塊系統(tǒng)調(diào)用-當前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]);//保存系統(tǒng)調(diào)用表中的NUM位置上的系統(tǒng)調(diào)用
    orig_cr0=clear_cr0();//使內(nèi)核地址空間可寫
    sys_call_table_my[NUM]=(unsigned long) &sys_mycall;//用自己的系統(tǒng)調(diào)用替換NUM位置上的系統(tǒng)調(diào)用
    setback_cr0(orig_cr0);//使內(nèi)核地址空間不可寫
    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;//將系統(tǒng)調(diào)用恢復
    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

obj-m:=module5.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-3.16.0-30-generic
all:
        make -C  $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
        make -C  $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
~                                                               

測試程序:
test.c

#include<stdio.h>
#include<stdlib.h>
int main()
{
        unsigned long x = 0;
        x = syscall(223);        //測試223號系統(tǒng)調(diào)用
        printf("Hello, %ld\n", x);
        return 0;
}

測試結(jié)果:

將2號系統(tǒng)調(diào)用fork替換為獲取當前進程的pid:

編寫模塊文件module.c

#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 2  //系統(tǒng)調(diào)用號為2
int orig_cr0;  //用來存儲cr0寄存器原來的值
unsigned long *sys_call_table_my=0;

static int(*anything_saved)(void);  //定義一個函數(shù)指針袁滥,用來保存一個系統(tǒng)調(diào)用

static int clear_cr0(void) //使cr0寄存器的第17位設(shè)置為0(內(nèi)核空間可寫)
{
    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寄存器設(shè)置為內(nèi)核不可寫
{
    asm volatile("movl %%eax,%%cr0"::"a"(val));
}

asmlinkage long sys_mycall(void) //定義自己的系統(tǒng)調(diào)用
{   
    printk("模塊系統(tǒng)調(diào)用-當前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]);//保存系統(tǒng)調(diào)用表中的NUM位置上的系統(tǒng)調(diào)用
    orig_cr0=clear_cr0();//使內(nèi)核地址空間可寫
    sys_call_table_my[NUM]=(unsigned long) &sys_mycall;//用自己的系統(tǒng)調(diào)用替換NUM位置上的系統(tǒng)調(diào)用
    setback_cr0(orig_cr0);//使內(nèi)核地址空間不可寫
    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;//將系統(tǒng)調(diào)用恢復
    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");

測試程序:

test.c

#include<stdio.h>
#include<stdlib.h>
int main()
{
        unsigned long x = 0;
        x = syscall(2);        //測試2號系統(tǒng)調(diào)用
        printf("Hello, %ld\n", x);
        return 0;
}

測試結(jié)果:

插入模塊前輸出子進程的pid和子進程中的返回值0,為fork調(diào)用:

插入模塊后,將2號fork進程替換為getpid赏参,輸出當前進程的pid:

卸載模塊后纫溃,將2號進程還原為fork進程:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市坊谁,隨后出現(xiàn)的幾起案子口芍,更是在濱河造成了極大的恐慌鬓椭,老刑警劉巖膘融,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異岛都,居然都是意外死亡臼疫,警方通過查閱死者的電腦和手機烫堤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來富蓄,“玉大人灭红,你說我怎么就攤上這事变擒×尴睿” “怎么了悠菜?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長芬骄。 經(jīng)常有香客問我账阻,道長淘太,這世上最難降的妖魔是什么蒲牧? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮挎扰,結(jié)果婚禮上遵倦,老公的妹妹穿的比我還像新娘橙弱。我一直安慰自己,他們只是感情好龙致,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著榛了,像睡著了一般霜大。 火紅的嫁衣襯著肌膚如雪战坤。 梳的紋絲不亂的頭發(fā)上途茫,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音边败,去河邊找鬼笑窜。 笑死,一個胖子當著我的面吹牛断傲,可吹牛的內(nèi)容都是我干的认罩。 我是一名探鬼主播垦垂,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼间校,長吁一口氣:“原來是場噩夢啊……” “哼憔足!你這毒婦竟也來了滓彰?” 一聲冷哼從身側(cè)響起找蜜,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诚纸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體井辆,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了塘揣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亲铡。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出檀头,到底是詐尸還是另有隱情暑始,我是刑警寧澤廊镜,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站雹姊,受9級特大地震影響吱雏,放射性物質(zhì)發(fā)生泄漏歧杏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冕房。 院中可真熱鬧,春花似錦行瑞、人聲如沸血久。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蛮粮,卻和暖如春蝉揍,著一層夾襖步出監(jiān)牢的瞬間又沾,已是汗流浹背杖刷。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工颓鲜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留甜滨,地道東北人衣摩。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓既琴,卻偏偏與公主長得像甫恩,于是被迫代替她去往敵國和親纹腌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容