システムコールの追加

参考URL:http://www.csg.is.titech.ac.jp/~kourai/memo/linux_kernel.html
※リンク先は切れている。
 
システムコールの追加
各システムコールは arch/i386/kernel/entry.S の system_callという関数から呼ばれる。ユーザプログラムからレジスタを使って渡されたシステムコールへの引数はスタックに積まれ、システムコールを実現する関数の引数としてアクセスできる。
新しいシステムコールを追加するには以下のようにする。
arch/i386/kernel/entry.Sに新しいエントリを追加する。
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_exit)
:
.long SYMBOL_NAME(sys_new_syscall) /* 新しいエントリ */
.rept NR_syscalls-191 /* ‘191’の値は追加したエントリ数だけ増やす */
.endr
sys_new_syscallという関数をどこかに追加する。(kernel/sys.cの最後 に追加するとよい)
書式は
asmlinkage int sys_new_syscall(…)
{

}
include/asm/unistd.hで__NR_new_syscallというマクロを定義する。
#define __NR_exit 1
#define __NR_fork 2
:
#define __NR_new_syscall 191 /* 追加 */
include/bits/syscall.hでSYS_new_syscallというマクロを定義する。
#define SYS_new_syscall __NR_new_syscall
新しいシステムコールを使うには以下のようなユーザプログラムを書く。
#include <linux/unistd.h>
_syscall1(int, new_syscall, long, x);
main()
{
int ret;
ret = new_syscall(1);
}
_syscall1はシステムコールの引数が一つ(long x)という意味で、
int new_syscall(long x)
というシステムコールを呼ぶ関数を定義する。引数が2つあれば、
_syscall2(int, new_syscall, long, x, char *, y)
のようになる。
練習:
コンソールに文字列を出力するシステムコールを作ってみよ。
ヒント:
コンソールに文字列を表示するためのカーネル関数としてprintkが用意されている。使い方はprintfとほぼ同じである。(X Window Systemを立ち上げているとコンソールに出力された文字が見えないことがあるので注意すること。)
——————————————————————————–
LKMによるシステムコールのフック
LKM(loadable kernel module)は/sbin/insmodと/sbin/rmmodによって動的にカーネルに機能を追加したり削除したりすることを可能にする。
LKMは以下のようなプログラムになる。
/* lkm.c */
#define MODULE
#define __KERNEL__
#include <linux/module.h>
/* insmodの時に呼ばれる */
int init_module(void)
{
printk(“init\n”);
return 0; /* 成功 */
}
/* rmmodの時に呼ばれる */
void cleanup_module(void)
{
printk(“cleanup\n”);
}
テストは以下のように行う。
gcc -c lkm.cでLKMをコンパイルし、lkm.oを作る。
rootになる。
/sbin/insmod lkm.oを実行すると”init”がコンソールに表示される。
/sbin/lsmodでlkmというLKMが組み込まれているのを確かめる。
/sbin/rmmod lkmを実行すると”cleanup”が表示される。
次にシステムコールのフック(横取り)を行えるようにする。そのためにはシステムコールの追加の時に変更したsys_call_tableのエントリをLKMの init_moduleの中で変更してやればよい。
例えば、unameシステムコールをフックするLKMは以下のようになる。
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/utsname.h>
#include <sys/syscall.h>
extern void *sys_call_table[];
int (*orig_uname)(struct old_utsname *buf);
/* 独自のunameシステムコール */
static int my_uname(struct old_utsname *buf)
{
/* オリジナルのunameシステムコールはorig_unameを使って呼べる */
return orig_uname(buf);
}
int init_module(void)
{
/* エントリを変更する */
orig_uname = sys_call_table[SYS_uname];
sys_call_table[SYS_uname] = my_uname;
return 0;
}
void cleanup_module(void)
{
/* エントリを元に戻す */
sys_call_table[SYS_uname] = orig_uname;
}
LKMをロードする前と後とで/bin/unameコマンドを実行結果を変えることができる。
練習1:
前回作った文字列を表示するシステムコールをフックし、固定の文字列を前半部分に表示するようにしてみよ。
例: KERNEL: <表示すべき文字列>
練習2:
さらに、表示すべき文字列に変更を加えてみよ。(小文字を大文字にするなど)
ヒント:
システムコールを使ってカーネルに渡されたバッファの中身はカーネルから直接操作してはいけない。一旦、copy_from_user(to, from, size)関数を使ってカーネル内のバッファにコピーすること。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です