システムコール - みる会図書館


検索対象: UNIX MAGAZINE 2006年2月号
62件見つかりました。

1. UNIX MAGAZINE 2006年2月号

2575 : } 対話的プロセスでは、たとえタイムスライスをデクリメ ントして 0 にならなくても、 CPU 時間を独占しすぎている ときは、カレントプロセスを同優先度のランキューの最後 尾に移動し、再スケジュールを促すフラグをセットします。 スケジューラの呼出し こまでの説明で、プロセスを切り替える処理について は理解できたと思います。次は、プロセス切替えが発生す るタイミングです。 プロセスの切替えは、ユーザープロセスカ噫図的におこ なっ場合と、意図せすにおこなわれる場合があります。 意図的なプロセスの切替えの例としては、指定した時間 カ釜過するのを待つシステムコールや、 I/O デバイスにデ ータ転送を指示するシステムコール、シグナル受信を待つ システムコールなどがあります。この場合は、プロセスが なんらかのイベントを、、待つ " ことを明示しているのがポ イントになります。そして、そのイベントが発生するまで プロセスは停止します。プロセスが停止すると、スケジュ ーラカ剛乎び出されます。 一方、意図せずにおこなわれる例が、割込みハンドラか ら復帰したところです。この場合、プロセスがどのような 命令を実行しているかに関係なく、プロセスからみえない ところでスケジューラカ印乎び出されます。このとき、実行 中のプロセスは何が起こったのか分からないまま、実行可 明示的な呼出し 出される箇所を解説します。 以降では、それぞれの場合についてスケジューラカび でピタっと停止させられるような感じです。 「だるまさんがこ ~ ろんだ 2 」 能状態に戻される場合があります。 82 2 私の出身地では「坊さんカ鯤をこいた」でした。 kernel/timer. c で疋義されています。 ーチン sys-nanosleep() の主要部分です。このコードは 以下は、 nanosleep システムコールを実装するサプル のコードをとりあげます。 明示的な呼出しの例として、 nanosleep システムコール asmlinkage 10 Ⅱ g sys—nanosleep ( ・ current—>state = TASK_INTERRUPTIBLE ; expire = schedule—timeout(expire) ; 1217 : } 1201 : 1200 : 1187 : timer. c で次のように定義されています。 をスリープさせるサプルーチンです。コードは、 kernel/ schedule-timeout() は、自分を呼び出したプロセス かでランキューから削除されることも重要なポイントです。 設疋しているため、あとで呼び出される schedule() のな す。また、 1 , 200 行目で TASK-INTERRUPTIBLE を ウト時間を引数にして schedule-timeout() を呼び出しま sys-nanosleep() は、プロセスから指定されたタイムア 1101 : 1102 : { 1103 : 1104 : 1136 : 1137 : 1138 : 1139 : 1140 : 1141 : 1142 : 1143 : 1144 : 1145 : 1146 : 1147 : 1148 : 1149 : 1150 : 1151 . jiffies; timeout del—singleshot—timer—sync (&timer) ; schedule ( ) ; add—timer (&timer) ; timer. function = process_timeout ; timer. data = (unsigned long) current ; timer. expires = init—timer (&timer) ; expire = timeout 十 jiffies ; unsigned 10 Ⅱ g expire ; struct timer_list timer ; schedule—timeout (signed long timeout) fastcall signed long ——sched out : return timeout く 0 ? 0 timeout ; 1 , 136 ~ 1 , 143 行目では、引数で指定された時間が経過 したときに process-timeout() を呼び出すタイマーエン トリを作成し、タイマーに登録しています。 1 , 144 行目は、スケジューラを呼び出してプロセス切替 えをおこないます。つまり、 schedule() から戻ってきて 1 , 145 行目が実行されるのは、 1 , 143 行目の add-timer( でセットしたタイマーによって、、起こされた " ときです。 1 , 145 行目では、タイマーエントリを削除します。 タイマーから呼び出される process-timeout() は、 ker- nel/timer. c で次のように定義されています。 UNIX MAGAZINE 2006 . 2

2. UNIX MAGAZINE 2006年2月号

ザープロセスは、 CPU の特権レベル 3 の爿た態でテキスト 図 4 カーネ丿戓の内容は同じ 領域のプログラムを実行しています。 プロセス A の 仮想空間 CPU の特権レベルが 3 のとき、ユーザープロセスはユ ーザー領域のアドレスしかアクセスできません。イ反想アド レス空間にはカーネル領域もマップされていますが、カー ネル領域のアドレスにアクセスすると、 CPU の保護回路 によって拒否されてしまいます。 ユーザープロセスは、ファイルの読み書きなど、カーネ ルのサービスを利用するときにシステムコールを発行しま す。すると、 CPU の特権レベルが 3 から 0 へ変更さ楸 カーネル領域にアクセスできるようになります。このとき 同時にカーネルモード・スタックのアドレスが ESP レジ スタにロードされ、 CPU のレジスタの内容がカーネルモ ード・スタックに待避されます ( もうすこしあとで説明す プロセス切替えをおこないます。 る「 switch-to() 」の項に示した図 8 も参照してください ) 。 このコードは kernel/sched. c で定義されています。 そして、システムコールで指定されたサービスを提供す 1542 : static inline るために、カーネルテキスト領域内にあるサプルーチンが context—switch(runqueue—t *rq, 1543 : task_t * 実行されます。 CPU がカーネル領域内のサプルーチンを task—t *prev , task—t *next) 1544 : 実行しているとき、カーネルモード・スタックを使うこと 1545 : struct m.m_struct *mm = next—>mm ・ が重要なポイントです。 1546 : mm_struct *01dmm struct prev—>active_mm ; カーネルモード・スタックとは、プロセス構造体と同じ 1547 : く、カーネル領域に各プロセスにつき 1 つ用意されるスタ 1548 : 1549 : ック領域です。このスタック領域は、システムコールを発 1550 : 1551 : 行したときにレジスタの内容を待避させたり、カーネル領 1552 : 域内のサプルーチンを実行するときに、ローカル変数を格 1553 : 1554 : 納するために使われます。 1555 : システムコールのサプルーチンが終ると、カーネルモー 1556 : 1557 : ド・スタックに待避したレジスタの内容が CPU に戻され 1558 : ます。同様に、 ESP レジスタにもユーザー領域のスタック 1559 : 1560 : のアドレスカ唳されます。最後に、 CPU の特権レベルも 1561 : 3 に戻ります。システムコールの処理は次回に詳しく説明 1562 : 1563 : します。 1564 : } また、各ューザープロセスの仮想アドレス空間の内容 1 , 545 ~ 1 , 546 行目の構造体 mm-struct は、テキスト は、ユーザー領域の内容こそ異なるものの ( スレッドを除 やデータなどの領域やページテープルなど、プロセスの仮 く ) 、カーネル領域は同じであることも重要なポイントです 想アドレス空間を管理する構造体です。そして、プロセス ( 図 4 ) 。 構造体のメンバー変数 rnm には、そのプロセスのための context-switch() mm-struct 構造体を指すポインタが設定されます。 mm に NULL が設疋されている場合、そのプロセスは、、カー context-switch() は、引数 prev で指定されたプロセ ネルスレッド " ということになります。 スを停止し、引数 next で指定されたプロセスを再開して プロセス B の 仮想空間 同じ内容 物理メモリ カーネル カーネル こッヒク ューザー 異なる内容 next—>active_mm = oldmm ・ atomic—inc (&oldmm—>mm—count) ; } else switch—mm(oldmm , mm , next) ; if ( !prev—>mm) { prev—>active—mm = NULL ; oldmm ; rq¯>prev_mm switch—to (prev , next , prev) ; return prev ; 74 UNIX MAGAZ 工 NE 2006 . 2

3. UNIX MAGAZINE 2006年2月号

Linux のプロセス [ 2 ] スケジューラとディスパッチャ 白崎博生 前回は、プロセスのスケジューリングに使われるパラメ に展開されます偂回の説明を参照してください ) 。 ータについて説明しました。 以降のコードでは、ポインタ変数 prev と next と rq が 今回は、スケジューラとディスパッチャのコードをとり 何回も登場します。 prev はカレントプロセスを、 next は あげます。そして、実際にスケジューラカ鮃び出される箇 次に実行するプロセス、 rq はランキューを指すことをしっ 所の例を紹介して、スケジューラに関する説明の締めくく かり頭に入れて読み進めていきましよう。 りとします。 now = sched—clock() ; スケジューラのコード if ( ( no ー prev->timestamp) く NS_MAX_SLEEP_AVG) { run—time = now ー prev—>timestamp ; if ( (now ー prev—>timestamp) く 0 ) run—time = 0 ; } else run—time = NS_MAX_SLEEP_AVG ; run—time / = (CURRENT—BONUS (prev) ? spin—lock—irq(&rq—>lock) ; 2 , 788 ~ 2 , 800 行目では、 prev プロセスのランタイムを 計算しています。詳しくは前回に解説したので、そちらを 参照してください。 こで計算したランタイムは、あとでスリーフ畤間の計 算に使用されます。 2 , 773 行目の current は、実行中た態のプロセス構造体 を指すポインタです。 if (prev—>flags & PF-DEAD) prev—>state = EXIT—DEAD ; 「いま実行されてんのは schedule() とちゃうん ? 」 と首を傾げている人がいるかもしれませんが、 current カ鮨 2 , 804 行目の PF-DEAD は、 exit システムコールの処 すのは、、実行中 " という状態フラグカ咐けられたプロセス 理中にセットされるフラグです。 で、厳密にいうと、 schedule() を呼び出す直前まで CPU exit システムコールを呼び出したプロセスは終了するの が実行していたプロセスのことです ( そのプロセスは停止 で、呼出し元に戻る必要はありません。そのため、 exit シ させられています ) 。 ステムコールは schedule() を呼び出し、プロセス切替え 2 , 776 行目の this-rq() は、ランキューを指すポインタ をおこないます。このとき、 exit システムコールはプロセ 2788 : 2789 : 2790 : 2791 : 2792 : 2793 : 2794 : 2800 : 2801 : 2802 : スケジューラのコードは、 kernel/sched. c で次のよう に定義されています。 asmlinkage VOid ——sched schedule (void) 2745 : 2748 : task—t *prev , *next ; 2749 : runqueue—t *rq; 2771 : need—resched : 2772 : 2773 : prev = Current ; release—kernel—lock(prev) ; 2774 : 2775 : need—resched—nonpreemptible : rq = this-rq() ; 2776 : 2804 : 2805 : 69 UNIX MAGAZINE 2006 . 2

4. UNIX MAGAZINE 2006年2月号

static void process—timeout( unsigned 10 Ⅱ g ——data) wake-up—process ( (task—t * ) ——data) ; 1073 : } 1072 : 1071 : { 1070 : ードです。 以下は、 arch/i386/kernel/entry. S から抜き出したコ ステムコール呼出しの詳細は次回に説明します ) 。 スタックへ待避させ、 system-call() を呼び出します ( シ EFLAGS 、 CS 、 EIP レジスタの内容をカーネルモード・ ールを呼び出します。割込みを受けた CPU は、 SS 、 ESP 、 Linux カーネルでは、 INT 80H を実行してシステムコ システムコールの場合 す。 調べ、セットされている場合はスケジューラを呼び出しま ーザーモードへ戻る直前に再スケジュールを促すフラグを そして、どちらの場合も割込みハンドラを実行したあと、ユ ロセスの実行を一時停止してカーネルモードへ移行します。 行しているときにハードウェア割込みを受けると、そのプ します。同様に、 CPU がユーザーモードでプロセスを実 ると、ソフトウェア割込みによってカーネルモードへ移行 たとえば、ユーザープロセスがシステムコールを発行す ューラカび出されます。 込みハンドラからユーザーモードへ復帰するときにスケジ び出される例をみてみましよう。 Linux カーネルでは、割 次に、プロセスカ噫図しないところでスケジューラカ呼 暗黙的な呼出し サプルーチンです。 中状態のプロセスをランキューに登録して実行可能にする wake-up-process() は、さきほど述べたように、停止 特集▽ Linux のプロセス [ 2 ] 0355 : Jmp resume—userspace system-call() は、まずレジスタの内容をスタックに待 避させます。 227 行目では、カレントプロセスのプロセス・デスクリ プタへのポインタを EBP に設定します。 235 行目の ca Ⅱ命令は、システムコールのサービスルー チンを呼び出します。 241 ~ 243 行目では、プロセスに設定されたフラグを調 べ、何かがセットされている場合は syscall-exit-work() へジャンプします。再スケジュール・フラグもここに凸ま れます。 syscall-exit-work() は、 resume-userspace() へジャ ンプします。 0149 : 0153 : 0154 : 0155 : 0156 : ENTRY(resume—userspace) movl TI—f1ags(%ebp) , %ecx andl $—TIF—WORK_MASK , %ecx jne work—pending 0224 : 0225 : 0226 : 0227 : 0235 : 0241 : 0242 : 0243 : 0347 : ENTRY(system-ca11) pushl %eax SAVE_ALL GET_THREAD_INFO (%ebp) call *sys-call—table( ,%eax,4) movl TI—f1ags(%ebp) , %ecx testw $—TIF—ALLWORK—MASK , %cx Jne syscall—exit—work syscall—exit—work: UNIX MAGAZINE 2006 . 2 resume-userspace() も、プロセスに設定されたフラグ を調べて work-pending() へジャンプします。再スケジ ュール・フラグもここに含まれます。 242 行目と 154 行目では、調べているフラグのグループ こでは気にする必要はありません。 がすこし違いますが、 0311 : 0310 : 0309 : 0308 : 0307 : 0306 : 0302 : 0301 : 0300 : 0299 : 0298 : こまで、何度も、、再スケジュールを促すフラグ " と書い JIIZ work—resched testb $-TIF—NEED_RESCHED , %cl JZ restore—all andl $—T 工 F—WORK-MASK , %ecx movl TI—f1ags (%ebp) , %ecx ca11 schedule work—resched : j z work—notifysig testb $—TIF_NEED_RESCHED , %c1 work—pending ・ 83 フラグがセットされている場合は、 300 行目を通過し、 302 ールを促すフラグを表すマクロ定数です。プロセスにこの 299 行目の -TIF-NEED-RESCHED は、再スケジュ てきましたが、ようやくここでそのフラグが登場します。

5. UNIX MAGAZINE 2006年2月号

おまけ プログラムを実行すると、以下のようなメッセージカ咄 ー空間のスレッドとみなすこともできると思います。 れるプログラムの例です。 proc0() と procl() は、ユーザ ぞれのコンテキスト ( ローカル変数 ) をもち、交互に実行さ 末尾に挙げたリスト 1 は、 proco() と procl() カそれ ます。 クを用意してコンテキスト・スイッチのようなことはでき とではありません。ユーザープロセスでも、複数のスタッ しかし、スタックの切替えはカーネルにしかできないこ イメージするのがハードルになるでしよう。 い ( その必要もない ) ので、スタックの切替えという概念を グラムを書いているときは、スタックを意識することがな っと難しかったかもしれません。そもそも、重に C プロ 今回はプロセス切替えのコードを解説しましたが、ちょ 力され続けます。 $ . /switch work process work process work process work process work process 1 0 1 0 1 C ount C ount C ount 1000 0 1001 1 1002 プログラムの内容は、 switch-to() とほとんど同じです。 ただし、インライン・アセンプラの、、 % 0 " の対応がみやす くなるように 1 行ずつに分解しています。 リスト 1 サンカレ・プログラム (switch. c) #include く stdio . 五 > #include く stdlib . h> void *stackO, *eip0 = NULL; void *stackl, *eipl = NULL; void work(int pid, int count) { printf ("work process %d count %d\n" void procO() { int count = 0 ; while ( 1 ) { wo て k ( 0 , count + + ) ; sleep(l) ; asm volatile("pushl %ebp") ; asm volatile ("movl %%esp, % 0 " UN 工 X MAGAZ 工 NE 2006.2 count) ; (stack0) =m pid, 特集 V Linux のプロセス [ 2 ] このプログラムは、スイッチのイメージを把握するため のものなので、スケジューラやディスパッチャの機能を独 立して用意していません。プログラムを動かしたり、デバ ッガで追ってみたりしてみてください。また、 3 つ目のサ プルーチン proc2() を、実行するスレッドを生成するよう に改造するのは、皆さんの演習課題ということにしておき 次回は、プロセスを生成する fork システムコールと、プ て解説しました。 今回は、スケジューラとディスパッチャのコードについ ☆ ます。 [ 文献 ] ( しらさき・ひろお IIJ) ロセスを終了させる exit システムコールをとりあげます。 ズ・マニュアル」 ( - ヒ・中・一ド ) 、 2004 年 (http://www.intel. [ 1 ] 「 IA ー 32 インテルアーキテクチャ・ソフトウェア・デベロッパー com/jp/developer/download/) 85

6. UNIX MAGAZINE 2006年2月号

てチェックをおこないます。 連載 / ネットワークとセキュリティ UNIX MAGAZINE 2006.2 4 http://www.ossec.net/rootcheck/ に置き換えられているとして検知します。 が含まれている場合に、これらのコマンドがトロイの木馬 ルでは、シグネチャで定義されたコマンド中にある文字列 ャ・ファイルのパスを指定します。 Rootkit 検知モジュー の木馬と、それに含まれている文字列を定義したシグネチ rootkit-trojans 要素では、既知の R00tkit のトロイ といったシステムコールを使ってチェックします。 ァイル名か有在するかどうかを、 stats 、 fopen 、 opendir します。 Rootkit 検知機能は、シグネチャで指定されたフ 名一覧が含まれているシグネチャ・ファイルのパスを指定 rootkit-files 要素で、既知の Rootkit 関連のファイル く /rootcheck> rootkit—trojans . txt く /rootkit—trojans> <rootkit—trojans>/var/ossec/etc/shared/=> ・ rootkit_files . txt く /rootkit_files> く rootkit—files>/var/ossec/etc/shared/* く notify>queue く /notify> く rootcheck> 定は次のとおりです。 Rootkit に関連する /var/ossec/etc/ossec. conf の設 check4 をもとにしたものです。 氏によって作成された Rootkit 検知ソフトウェア Root- HIDS の Rootkit 検知機能は、これも Daniel B. Cid ックすることで Rootkit を検知する機能です。 OSSEC Rootkit 検知機能は、シグネチャと不審な挙動をチェ Rootkit 検知機能 てみるのもよいでしよう。 AIDE など、ほかの整合性チェックツールの利用を検討し ため、より柔軟な設定力球められる場合には Tripwire や なハッシュ・アルゴリズムが MD5 のみという制約がある クの対象となるファイルを細かく指定できない、利用可能 OSSEC HIDS 0.5 の syscheck では、整合性チェッ で指定します。 のように、対象外とするファイルを syscheck-ignore 要素 く syscheck—ignore>/etc/mtab く /syscheck—ignore> 対象から外すには、 <global> と </global> のあいだに、 指定ディレクトリ内のあるファイルを整合性チェックの このほかにも、 Rootkit 検知機能では、次のような手法 を用いてシステムに不審な点がないかどうかをチェックし ます。 ・ / dev ディレクトリの調査 通常、 /dev ディレクトリに置かれているのはデバイスフ ァイルと MAKEDEV スクリプトのみですが、多くの Rootkit は /dev にファイルを隠します。そこで、 /dev ディレクトリに不審なファイルがないかを調査します。 ーミッションのチェック root カ所有者となっているファイルのうち、誰でも書込 み可能なファイルや SUID されたファイル、みつかりに くいディレクトリなどをチェックします。 ・隠しプロセスの調査 getsid() と kill() システムコールを用いてプロセス ID カ坏リ用されているかどうかをチェックし、プロセスの存 在を確認します。そして、この結果と ps コマンドの出 力を比較し、 ps コマンドから隠されているプロセスがな いかを確認します。 ・ポート番号の調査 TCP と UDP のすべてのポート番号に対して bind() を実行し、実際に利用されているポート番号を調査しま す。さらに、 netstat コマンドの出力と比較し、 netstat コマンドカ症しい結果を出力しているかを確認します。 ・ promiscuous モードの不寉言忍 侵入者はホストへの侵入後、スニッフィングをおこなう ために NIC を promiscuous モードにすることがあり ます。このため、 NIC の動作モードとして promiscu- ous モードカ第ス疋されているかどうかを確認し、 ifconfig の出力と比較します。 Rootkit オロツール利用上の注意 OSSEC HIDS にかぎらず、多くの Rootkit 検知ツー ルには次のような制約があるため、利用にあたっては注意 が必要です。 ・ Rootkit 関連ファイルのファイル名がもとの名前から変 更されている場合には、検知できない場合がある。 正常なコマンドにシグネチャで定義されている文字列が 含まれている場合、トロイの木馬として検知されること がある。 53

7. UNIX MAGAZINE 2006年2月号

0661 : 0662. 0663 : 0664 : 0665. 0744 : 0745 : { 0746 : 0747 : 0748 : 0758 : 0764 : 0772 : 0773 : 0774 : 0779 : 0780 : 0781 : 0782 : 0783 : 0784 : 0785 static inline VOid ——activate—task( task—t *p , runqueue—t *rq) enqueue—task (p , rq—>active) ; rq—>nr_running 十十 ; static VOid activate—task(task—t *p , runqueue-t *rq, int 10Ca1 ) unsigned long 10 g now ; now = sched—clock() ; p—>prio = recalc—task—prio(p, now) ; if ( !p—>activated) { —activate—task (p , (q) , p—>timestamp = now ; p¯>activated = 1 ; else { p—>activated = 2 ; if (in—interrupt() ) 758 行目の recalc-task-prio() は、プロセスの優先度 を引するサプルーチンです。 764 ~ 781 行目では、プロセスを起床させる原因を表す 値俵 1 ) を設定しています。この値は、 schedule() で優 先度の言算に使われていたことを思い出してください。 772 行目の if 文では、 activate-task() カリ込みハンド ラの処理から呼び出されたことを確認します。たとえば、 後述する schedule-timeout() は、一定時間後に自分自 身を呼ひ起こすコールバック関数をタイマーに登録します。 タイマーの処理はクロック割込みのなかでおこなわれるた め、このコールバック関数から呼び出されたときの acti- vate-task() は 772 行目の条件を満たしています。 一方、 read や select などのシステムコールにより、プ ロックしているプロセスがシグナルを受け取って起床する 場合は、 779 行目で 1 か第定されます。 最後に、 784 行目で呼び出している --activate-task() は、 active キューにプロセスを登録する処理をおこないま す (enqueue-task() については、 1 月号の図 5 を参照し てください ) 。 80 クロック割込み PC/AT 互換機では、 PIT (Programmable lnterval Timer) というタイマー IC が周期的なクロック信号を CPU に送イ言しています。そして、これを受け取った CPU は IRQO のハードウェア割込みを発生させます。 クロック信号の周期は、 Programmable ということか らも分かるように、カーネルが自由に設定できます。コン パイル時の CONFIG-HZ に 100 を設疋した Linux カー ネルでは、 100HZ の周波数を設定します。つまり、 1 秒間 に 100 回の頻度でハードウェア割込みカ咥生します。 Linux カーネルは、 IRQO のハードウェア割込みを利用 し、実行中のプロセスからタイムスライスをデクリメント します。 scheduler-tick() scheduler-tick() のおもな役目は、 100HZ 周期のクロ ック割込みハンドラから呼び出され、実行中のプロセスの タイムスライスをデクリメントすることです。この呼出し は、 IRQO 発生→ timer-interrupt ( ) → do-timer-interrupt ( ) → do-timer-interrupt-hook ( ) → update-process-times() → scheduler-tick() の順でおこなわれます。 scheduler-tick() のコードは、 kernel/sched. c で次の UNIX MAGAZ 工 NE 2006.2 p—>time—slice task—timeslice(p) ; —p—>time-slice) { = SCHED—RR & & if (p—>policy if (rt-task(p) ) { spin—lock(&rq->lock) ; goto out ; set—tsk—need—resched(p) ; if (p—>array ! = rq—>active) { task—t *P = current ; runqueue—t *rq = this—rq() ; int cpu = smp—processor—id() ; void scheduler—tick(void) ように定義されています。 2521 : 2520 : 2515 : 2507 : 2506 : 2505 : 2504 : 2503 : 2488 : 2487 : 2486 : 2485 : { 2484 :

8. UNIX MAGAZINE 2006年2月号

連載 / ネットワークとセキュリティ UNIX 系 OS で Rootkit を検出するツールとしては、 イルの存在自体を隠蔽する手法もあります。 を用いることでシステムコールを改竄し、プロセスやファ かれてしまうこともあるため、 LoadabIe KerneI ModuIe また、たんにファイルを改竄しただけでは、管理者に気づ スがあります。 マンドを用意しておき、本物のコマンドを上書きするケー セスのみを表示しないように改竄した ps コマンドや ls コ 在を隠蔽するため、不正プログラム関連のファイルやプロ っては不都合です。そこで、これらの不正プログラムの存 せると、管理者にその存在を気づかれやすく、侵入者にと これらのプログラムを通常のプログラムと同様に動作さ 蔽するツールをまとめたものを Rootkit と呼びます。 このような不正プログラム、あるいはこれらの存在を隠 rootkit-hunter. html http://www.rootkit.nl/projects/ ・ Rootkit Hunter http://www.chkrootkit.org/ ・ chkrootkit 下記のものカ陏名です。 になりました。 XCP には、ファイル名が $ sys $ から始ま Windows 用 Rootkit が含まれていたとして大きな問題 最近では、 SONY BMG の CD に「 XCP 」という る場合にそのファイルの存在を隠蔽する機能があり、 らがウイルスなどに悪用されています。 OSSEC HIDS の導入 これ OSSEC HIDS は、 DanieI B. Cid 氏によって開発さ れたホスト型 IDS で、ファイル整合性チェック、ログチェ ック、 Rootkit 検知の機能を備えています。 OSSEC HIDS では、単一ホストのみで運用するローカ ル運用と、ネットワークを介した運用がおこなえます。後 者の方法では、 OSSEC HIDS か彳家動 3- るホストから情報 を収集し、一括管理できます。今回は、ローカル運用に絞 って説明します。 インストールの手順 こでは、 1 台のホストで OSSEC HIDS を運用する 環境でのインストール方法について説明します。 UNIX MAGAZINE 2006.2 原稿執筆時点での OSSEC HIDS の最新バージョンは 0.5 です。今回は、 OSSEC HIDS 0.5 を Red Hat En- terprise Linux 4 のクローン・ディストリビューション CentOS 4.2 上で利用する肘是で説明を進めます。 なお、 FAQ によれば、 OSSEC HIDS は POSIX 互換 システムで動作し、 Red Hat Linux 8 / 9 、 Fedora Core 2 / 3 などの Linux ディストリビューション、 OpenBSD 3.5 / 3.6 / 3.7 、 FreeBSD 5.2.1 / 4.10 ー BETA 、 Mac OS X といった BSD 系 OS 、 SoIaris 9 などでの動作カ蔀寉認 されているそうです。 まず、 Web サイトから OSSEC HIDS のアーカイプを 入手します。アーカイプは、 ・ http://www.ossec.net/hids/index.php の、 Downloads" から入手できます。 ファイルをダウンロードしたら、 SHA-I チェックサム を確認します。チェックサムの値が F 記と同じであればオ リジナルのアーカイプと同一です ( 誌面の都合上、で折 り返しています。以下同様 ) 。 $ shalsum ossec—hids-0.5. tar ・ gz f4080d9ea9C202227232285eb5062C4a7717aae7 => *ossec—hids—0.5. tar ・ gz 続いて、入手したアーカイプを展開します。 $ tar xvzf ossec—hids-O. 5. tar. gz 次に、作成されたディレクトリに移動します。 $ cd ossec—hids-O . 5 そして、 r 。。 t になってインストール・スクリプトを実行 します 2 。 $ su # . /install . sh 以下に、スクリプトによるインストールの手川頁を示しま す。各機能の細かい設疋については、朝行で説明します。 インストール・スクリプトか起動すると、コンパイラがイ ンストールされている場合には図 1 の画面になります。イ 2 . /install. sh は、プログラムのコンパイルだけでなく、インストール先デ ィレクトリ乍成、 OSSEC 実行用アカウント乍成などもおこなうため、 root で実行する必要があります。本来は root 欟艮でのコンパイルは避け たほうがよいのですが、 . /install.sh を利用しない場合には、手でイン ストール作業をしなければならないため、今回はスクリプトを利用する方法を 紹介します。 47

9. UNIX MAGAZINE 2006年2月号

図 8 害みとスタックの潜え ユーザーモード・スタック スタックが伸びる方向 カーネルモード・スタック スタックポインタ→ 割込み発生前の スタックポインタ TSS espO ssO EIP CS EFLAGS ESP SS CPU が 自動的に 退避する 以上をまとめると、次のようになります。つまり、スタ ックを切り替える前に prev プロセス側での変数 prev の 0730 : 0731 : } 内容を EAX レジスタに待避し、スタックを切り替えたら、 EAX レジスタから next プロセス側の変数 prev に一 するわけです ( 図 2 も参照してください ) 。 22 行目で呼び出している --switch-to() のコードは、 arch/i386/kernel/process. c で定義されています。主要 な部分を抜き出してみましよう。 0672 : struct task_struct fastca11 * —switch—to (struct task—struct *prev struct task—struct *next struct thread_struct *prev = &prev—p->thread, *next = &next—p—>thread ; int cpu = smp—processor—id() ; struct tss_struct *tss &per-cpu(init—tss , cpu) ; Ioad—espO(tss , next) ; 10ad—TLS (next , cpu) ; asm volatile("mov %%fs , % 0 " asm volatile("mov %%gs,%0" if (prev—>fs ー next—>fs) loadsegment ()s , next—>fs) ; if (prev—>gs ー next—>gs) loadsegment()s , next—>gs) ; if (prev—>io-bitmap—ptr Ⅱ next—>io—bitmap—ptr) handle—io—bitmap(next , tss) ; return prev—p ; 686 行目の load-esp0() は、以下のように next プロセ スの espO を TSS (Task State Segment) の esp0 に設 定する処理をおこないます。 next->esp() は、カーネルモ ード・スタックの開始アドレスを指しています。 tss—>espO = next—>esp0; これ以降のプロセス切替えの処理が頂調に終ると、 next プロセスがユーザーモードで実行され始めます。その後、 プロセスの実行中になんらかの割込み ( システムコールの 発行など ) が発生して CPU の特権レベルが変化すると、 CPU は自動的に TSS の esp0 と ss0 フィールドの内容 を ESP と SS レジスタにロードします ( 図 8 ) 。つまり、 686 行目では、ユーザーモードからカーネルモードへ切り 替わったときに使われるスタックのアドレスを設疋してい ます。 691 行目の load-TLS() は、 set-thread-area システ ムコールで設疋された TLS (Thread LocaI Storage) を CPU にロードする処理をおこないます。 697 ~ 698 行目では、 FS と GS セグメント・レジスタ の値を prev プロセスの構造体に待避しています。 706 ~ 707 行目では、 prev と next のどちらかのプロセ スが FS レジスタを使用している場合、 next プロセスの構 造体から FS セグメント・レジスタの値をロードします。 709 ~ 710 行目では、 GS レジスタについて同様の処理 をおこないます。 0673 : { 0674 : 0675 : 0676 : 0677 : 0686 : 0691 : 0697 : 0698 : 0706 : 0707 : 0708 : 0709 : 0710 : 0725 : 0726 : 78 UNIX MAGAZ 工 NE 2006 . 2

10. UNIX MAGAZINE 2006年2月号

行目でスケジューラカ鮃び出されます。 つまり、ユーザープロセスからこの処理をみると、シス テムコールを呼び出したところでほかのプロセスに CPU カ簿われ、順番がまわってくるまでじっと我漫して待たさ れることになります。 その後、別のプロセスから呼び出されたスケジューラに よってふたたび実行中状態に戻されると、 302 行目の call 命令の次の行から実行カ舸開されます。 306 ~ 309 行目ではふたたびフラグを調べ、何もセット されていなければ restore-all() へジャンプします。 もし、再度 -TIF-NEED-RESCHED がセットされて いれば、 301 行目に戻って同じ処理を繰り返します。 312 行目以降は、プロセスにシグナルを送信する処理が 続きます。 0412 : 0413 : 0414 : 0415 : 0416 : common_interrupt : SAVE_ALL movl %esp , %eax call d0—IRQ Jmp ret—from—intr common-interrupt() は、 413 行目でレジスタの内容 をスタックに待避させ、さらに共通の割込み処理ルーチン do-IRQ() を呼び出します。 そして、 do-IRQ() は、 IRQ 番号に対応するサプルーチ ンを呼び出します ( カーネルのプート時に対応表が作られ ます ) 。たとえば、 IRQO の場合は timer-interrupt() を 呼び出します。 サプルーチンから戻ってきたら、 ret-from-intr() へジ ャンプします。 0245 : 0256 : 0257 : 0258 : restore_all : RESTORE_REGS addl $ 4 , %esp iret restore-all() は、スタックに待避されたレジスタの内容 をロードして、ユーザーモードへ戻ります。 ハードウェア割込みの場合 ハードウェア割込みの基本的な仕組みは、発生するタイ 84 スタックへ待避し ( 図 8 ) 、割込みハンドラを呼び出します。 EFLAGS 、 CS 、 EIP レジスタの内容をカーネルモード・ 割込みがかかります。割込みを受けた CPU は、 SS 、 ESP 、 プロセスを実行していると、外部のハードウェアから突然 CPU が「ふんふんふ ~ ん」と鼻をならしながらユーザー 様です。 ミングが分からないことを除けばソフトウェア割込みと同 番号は省します。 3 割なハンドラのコードはコンパイル時に自動生成されるため、ラベル名や行 ます。 割込み処理ルーチン common-interrupt() にジャンプし 番号から 256 を引いた値をスタックに積み上げ、共通の ハードウェア割込みのハンドラ 3 は、上記のように IRQ Jmp common—interrupt pushl $ れ一 256 0143 : 0144 : 0145 : 0146 : 0147 : 0148 : 0149 : 0153 : 0154 : 0155 : 0156 : ret_from_intr: GET_THREAD_INFO (%ebp) movl EFLAGS (%esp) , %eax movb CS (%esp) , %al testl $(VM-MASK ー 3 ) , %eax j Z resume_kernel ENTRY (resume—userspace) movl TI—f1ags(%ebp) , %ecx andl $—TIF_WORK_MASK , %ecx jne work—pending 144 行目では、カレントプロセスのプロセス・デスクリ プタへのポインタを EBP に設定します。 ハードウェア割込みは、カーネル内のルーチンを実行し ているときに発生することもあります。 145 ~ 148 行目で は、そのような場合の復帰処理をおこなうルーチンにジャ ンプします。 149 行目以降の処理は、システムコールの場合と同じで す。 クロック割込みによって前述の scheduler-tick() を実 行した結果、、、再スケジュールを促すフラグ " がセットされ いる場合は、 work-pending() から schedule() カび出 されます。つまり、カーネルに、 「 CPU 、使いすぎやで」 UN 工 X MAGAZINE 2006 . 2 にあります。 と言わ楸ほかのプロセスに CPU カ簿われるポイントが