システムコールの追加

参考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)関数を使ってカーネル内のバッファにコピーすること。

a

スポンサーリンク







シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク