Fedora 增加系統調用

←手機掃碼閱讀     火星人 @ 2014-03-25 , reply:0

系統:32位x86系統
OS:FC6
內核版本:2.6.18
目的:新增一個系統調用,將當前進程的UID和EUID都設置成0(超級用戶)
步驟:
1 內核源碼樹位置:SRC=/usr/src/redhat/BUILD/kernel2.6.18/linux-2.6.18.i386

2 增加新系統調用號,修改頭文件
$SRC/include/arm-i386/unistd.h



#define __NR_splice 313
#define __NR_sync_file_range 314
#define __NR_tee 315
#define __NR_vmsplice 316
#define __NR_move_pages 317
#define __NR_mysyscall 318 /*新增加的一行,其中318是系統調用號,
根據系統不同自己定義與已經有的系統
調用號不相同即可*/
3 修改系統調用表。修改文件
$SRC/arch/i386/kernel/syscall-table.s(注意,以前版本的內核應該修改和前面文件同

級目錄下的entry.s)
ENTRY(sys_call_table)
.long sys_restart_syscall /* 0 - old "setup()" system call, used

for restarting */
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open /* 5 */
.long sys_close
.long sys_waitpid
.long sys_creat
.long sys_link
.long sys_unlink /* 10 */
.
.
.
.long sys_mysyscall /*新增加一行*/


4 定義新系統調用的具體內容,一般可以直接在$SRC/kernel/sys.c中添加,
當然也可以加入與其功能緊密聯繫的代碼中去。這裡我加在$SRC/kernel/sys.c中,
在該文件末尾加入:
asmlinkage int sys_mysyscall(void)
{
current->uid = current->euid = 0;
return 0;
}

5 通常我們在用戶層使用系統調用都是通過C庫支持來使用的,也就是說C庫中對系統調用

進行了一次封裝
,可以通過查看glibc的源碼可以看到具體封裝過程。由於glibc十分龐大,如果我們自己

新定義對上面新增
系統調用的C庫函數的話,編譯時間太長,所以我想直接利用LINUX提供的宏_syscallN來完

成。
其中N是系統調用的參數個數。舉例說明該宏的用法:
對於系統調用open()的定義本來是:
long open(const char* filename,int flags,int mode)
如果不靠c庫支持使用該系統調用方法如下:
#define __NR_open 5 /*5是open的系統調用號*/
_syscall3(long,open,const char* filename,flags,mode)
有了這些定義後下面對open可以直接調用了
(一般書上都會講到這種方法),但是在實際操作過程中,會發現在2.6.X中,已經取消了

宏_syscallN的定義,所以
你在編譯的時候是通不過的。當然2.6取消了這些宏定義,我們可以自己在版本的代碼中找

出這些宏定義加上即
在2.4.18內核的源代碼中,可以在其內核源碼樹下include/asm-i386/unistd.h中找到這些

宏定義:
#define __syscall_return(type, res) \
do { \
if ((unsigned long)(res) >= (unsigned long)(-125)) { \
errno = -(res); \
res = -1; \
} \
return (type) (res); \
} while (0)

#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name)); \
__syscall_return(type,__res); \
}

#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(arg1))); \
__syscall_return(type,__res); \
}

#define _syscall2(type,name,type1,arg1,type2,arg2) \
type name(type1 arg1,type2 arg2) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
__syscall_return(type,__res); \
}



將這些代碼copy到你自己的$SRC/include/asm-i386/unistd.h(我在實際中去掉了所有宏


包含變數errno的一行,因為在我們的代碼中沒有這個變數的定義,即使你在其中加入
extern int errno;
在編譯沒問題,鏈接也會有問題的,試試就知道了,如果非要使用errno,那就自己專研吧

,呵呵!)

6 所有準備工作完成,現在就是編譯新內核了(可能有人會提到將這個系統調用以模塊方

式載入就不用重新
編譯內核了,想法是好的,但是考慮到安全問題,不知道是從什麼版本開始,已經取消了

對系統調用表
sys_call_table的導出,所以你在模塊中是沒法訪問系統調用表的,當然也可以通過其他

途徑來完成這個任務
,不過那是hacker們的辦法了,這裡不加討論)
在$SRC中
make menuconfig
make
make install
即可,在2.6中已經對編譯內核的方法簡化了許多,不需要以前對依賴關係的檢查了,新的

Makefile幫助完成這些工作了。
在make install中已經幫你完成了
(1)將新內核鏡像bzImage拷貝到/boot/vmlinuz-verison
(2)構建新的initrd-version.img文件
(3)創建新的內核符號表System.map(這個文件不是內核啟動必須的,一般調試用)
(4)修改你的grub或者lilo啟動文件
所以你只需要reboot就可以看到你新編譯的內核了。至於內核的配置和vmlinuz和initrd2

個文件各自的用處,大家可以上網查詢,這裡
不詳細介紹了。如果你的新內核無法啟動,多半是initrd出了問題(簡單的說下initrd文

件包含了你的內核在載入文件系統前的一些驅動程序
比如硬碟驅動,ext3文件系統的驅動等等,如果這些有問題,內核是無法啟動的,網上說

可以把這些一起編譯到內核vmlinuz中而不需要initrd,
大家可以去試試)
7 現在新內核啟動起來了,我們寫一個用戶測試程序mysyscall.c使用我們新的系統調用吧


#include
#include
#include
#include
#include
#define __NR_mysyscall 318
_syscall0(int,mysyscall)
int main()
{
printf("before, my euid is: %d!\n",geteuid());
if(open("/etc/shadow",O_RDONLY)<0)
printf("open failed!\n");

mysyscall();
printf("after, my euid is %d!\n",geteuid());
if(open("/etc/shadow",O_RDONLY)>0)
printf("open succeed!\n");

return 0;
}
在普通用戶下運行該程序
before, my euid is: 500!
open failed!
after, my euid is 0!
open succeed!

發現居然可以訪問本來只有root能訪問的shadown文件了 o(∩_∩)o!




[火星人 ] Fedora 增加系統調用已經有199次圍觀

http://www.coctec.com/docs/linux/show-post-169627.html