2001/11/01 STREAM RG チュートリアル


Practical C 言語プログラミング

0) はじめに
1) コンパイラとdefine の使い方
2) 簡単な Makefile の書き方
3) 複数ファイルに分けて書く
5) ライブラリを書く
課題) DoS(denial of Service)プログラムを書く
Appendix 1 サンプルファイル
Appendix 2 DoS ってなに?

はじめに

本日は、STREAM RG チュートリアルにご出席頂き、ありがとうございます。
今回のチュートリアルの方式ですが、皆様に少しずつ C のコードを書いて頂きます。
STREAM メンバーが絶えず教室内をうろうろする予定ですので、わからない事があったら、呼んで聞いて下さい。

DoSというテーマを扱っていますが、内容的には、「C 言語と make」です。

コンパイラと define の使い方

cc(もしくはgcc)は、1つのプログラムが複雑な翻訳作業と各種ファイルの生成を行なっているわけではなく、複数のコマンドを作業内容に応じて順次呼び出す形態を取っています。 例えば、#include や #define はプリプロセッサ(preprocessor)と呼ばれる前処理機構がその記述内容を展開したり、マクロの最終的な値を決定しています。 また、プリプロセッサが生成した中間ファイルはパーサによって構文が解析され、さらに別の中間ファイルへと置き換えられます。 つまり、cc のコマンド行から入力されたファイル名やオプションは、Cコンパイラを形成する各コマンドへ渡され、その処理結果が一時的な中間ファイルに残されます。 このようなコマンドの呼び出しと処理パラメータの設定、及び中間ファイルによる情報の受渡しを行なう機構が C コンパイラの実態です。 cc がどのような処理をしているかは、「cc -v」などのコマンドを実行するとわかります。 基本的には、以下のように cc はコンパイル作業を行ないます。
1) プリプロセッサ 「#」で始まる構文の処理(include,define,ifdef など)
コメントの除去(/* */)
2) コンパイラ本体 アセンブラコードの生成
3) アセンブラ アセンブラコードのコンパイル
4) リンケージエディタ 外部参照の関数や変数の解析など

ここでは、cpp の機能と define の使い方を練習します。
#define 文を利用する事により、プログラミング効率を上げる事ができます。
#include、#define、#ifdef などは、cpp により、処理されます。

サンプルコードの stream-rg-tutorial/00 ディレクトリへ進んで下さい。
「cpp main.c | less 」としてみて下さい。
「#include <stdio.h>」が展開されています。
次に、「cpp main.c -DDEBUG | less」としてみて下さい。
main の中で printf が一つ追加されているのがわかると思います。
ここでは、「-DDEBUG」というオプションを使い、「#define DEBUG」とコード内に書くのと同じ処理をしています。 「#ifdef、#endif」というのは、「もし define されていたら #endif までのプログラムを含む」という内容です。cpp により処理されます。 -DDEBUG をすると余計に出てくる printf があります。
__FILE__ には、コードのファイル名が入ります。複数ファイルを利用してコードを書いている場合には、どの部分を処理しているかがわかり、便利です。 __LINE__ には、コードの何行目であるかが入ります。これもコードのどの部分が printf されているのかがわかり、便利です。 これらのマクロは、両方とも cpp により処理されます。 debug にいきづまったら、使ってみて下さい。

/usr/include/stdio.h を見てみて下さい。 COPYRIGHT の後に #ifndef _STDIO_H_ という文と #define _STDIO_H_ という文があると思います。 これは、ヘッダファイルを書く時に一般的に使われる手法です。 #ifndef というのは、「もし、〜がdefine されていなければ」という意味です。 その直後で #define _STDIO_H_ をしているので、/usr/include/stdio.h の内容は2度コンパイルされることはありません。 これにより、C のプログラム内で以下のような事をしても問題がなくなります。

==============
#include <stdio.h>
#include <stdio.h>
#include <stdio.h>
#include <stdio.h>

main()
{
  printf("hello\n");
}
==============

簡単な Makefile の書き方

次に、簡単な Makefile の書き方をやります。stream-rg-tutorial/00 に移動して下さい。
Makefile は、make コマンドの挙動を記述するためのファイルです。
Makefile を書く事により、複雑なコンパイル処理を簡単にする事が出来ます。
「make」と、コマンドを打ってみましょう。 勝手に gcc が実行されます。
次に、Makefile に書かれた内容を見てみましょう。
次に、実際に自分で Makefile を書いてみましょう。

複数ファイルに分けて書く

make を利用して複数ファイルを分割コンパイルする練習をしてみましょう。stream-rg-tutorial/01 に移動して下さい。
ここでは、a.c と main.c をコンパイルしています。
次に、「touch a.c」として下さい。touch は、ファイルの最終更新日を更新するためのコマンドです。 その後で、make とすると、a.c だけコンパイルされ、a.o と main.o を利用して実行ファイルが出来ます。 このように、make は、最後にコンパイルしてから更新されたファイルだけを選んでコンパイルしてくれます。

それでは、2つ以上のファイルに分割してプログラムを作ってみましょう。 また、それらのファイルをコンパイルするための Makefile も同時に作ってみましょう。

ライブラリを書く

ライブラリは、あらかじめコンパイルしておいたプログラムのアーカイブです。 今回は、ライブラリを作ってみましょう。

DoS(Denial of Service)プログラムを書く

今週の RG の課題は、実際に DoS をするプログラムを書く事です。
以下のファイルが用意してあります。
必要に応じて参考にして下さい。

UDP パケットを送るサンプル stream-rg-tutorial/net/simple-udp/
チェックサムを計算するライブラリ stream-rg-tutorial/net/lib/
ICMP Echo を送るプログラム stream-rg-tutorial/net/simple-icmp/
src アドレスを誤魔化して UDP パケットを投げるプログラム stream-rg-tutorial/net/src-fake/

さらに、ダサダサな DoS ライブラリのサンプルも同時に用意させて頂きました。
UDP パケットを送るサンプル stream-rg-tutorial/dos-lib-sample/udp/
ICMP パケットを送るサンプル stream-rg-tutorial/dos-lib-sample/icmp/

わからない事などがございましたら、お近くの STREAM メンバーにお聞き下さい。

サンプルファイル

サンプルファイル集 : stream-rg-tutorial.tar.gz

DoS って何?

DoS(Denial of Service)とは、ある特定のホストやネットワークに対して、特定のサービスが出来ない状態に陥れることを示しています。

最も簡単な DoS としては、単純に UDP パケットなどを大量に送信し、ネットワークを混雑させて通信をしにくくするというのが一般的です。

また、複数の場所から同時に大量のパケットを送りつけて行なう DoS は DDoS(Distributed DoS)と呼ばれます。