ネットワークセキュリティのためのTips
- デーモン(ネットワークサービス)の起動
- rc.d(古い方法)
- systemd(新しい方法)
- スーパーサーバによる起動
- ファイアウォール
- 検査手法
- Linuxで開いているポートを確認する方法(netstat)
- ポートスキャン
- パケットキャプチャ
- Webサーバへの攻撃方法
- 一般的な知識
- バックドアの確認方法
- 仕掛けられたバックドアの検出と対処
- maldetect
- ルートキット検出ツールの比較(rkhunter,chkrootkit)
ファイル操作演習 回答編
Linux の操作演習をやったけど、興味を持った人はそれなりに楽しんでくれたみたい。ひっかけのネタになっていた部分を抜粋して簡単に説明。
基本は、以下のようにディレクトリに入って、ファイルだったら表示…の繰り返し。
$ cd /home0/Challenge/1-CTF.d/Task1 $ ls -al 0ReadMe brain concussion persons $ cat 0ReadMe ... $ cd brain $ ls company concussion of-brains $ cat * ... $ cd ../concussion $ ls ...
Task4では、特殊なファイル名が入れてあるので、注意が必要。
$ cd /home0/Challenge/1-CTF.d/Task4 $ cd dir1 $ cat 'file name.txt' 空白を含むファイル名は''で囲む : $ cat ./--file.txt -で始まるファイル名はコマンドオプションと 勘違いされないように ./ をつける。
dir3 の中には、シンボリックリンク(Windowsでいうところのショートカット)があるので要注意。
$ cd ../dir3 $ ls -al lrwxrwxrwx 1 t-saitoh t-saitoh 2 Dec 20 10:45 Task4 -> .. -rw-rw-r-- 1 t-saitoh t-saitoh 39 Dec 20 10:47 ZFile4 # cd Task4 をすると、1つ上のディレクトリへのシンボリックリンクがあるので # 無限の繰り返しになる。
Task5 には、unix の基礎的なセキュリティトラップを体感してもらう。
$ cd /home0/Challenge/1-CTF.d/Task5 $ ls 0ReadMe Bomb $ cd Bomb $ ls 接続が切れてしまう。
実は、今回 Login すると、環境変数PATH の設定に不備が仕掛けてある。また Bomb のディレクトリ内には、ls , cat といった名前で、強制的に接続をするコマンドを置いてある。この仕掛けにより、Bomb ディレクトリで ls を実行すると、接続が切れてしまう。
対策1
Bomb ディレクトリに入らずにファイルを探る
$ cd /home0/Challenge/1-CTF.d/Task5 $ ls Bomb 0ReadMe Bomb $ cat Bomb/cat $ cat Bomb/flag
対策2
実は、環境変数 PATH (利用者がよく使うコマンドが保存されているディレクトリ一覧)が “.:/usr/bin:/bin” となっている。先頭に . が入っているため、カレントディレクトリの中に ls といったコマンドがあると実行してしまう。
環境変数 PATH にカレントディレクトリ(.)が入っていると、悪意のあるプログラムを実行させたい人が、危険な実行プログラムを置き逃げしてあると、実行してしまう可能性が高い。このため、(.)無しにすべき。
$ export PATH=/usr/bin:/bin $ cd /home0/Challenge/1-CTF.d/Task5/Bomb $ ls cat flag less ls lv more nkf $ cat flag FLAG{DoNotTouchBomb}
Linux演習用リンク
演習の中では、クイズ形式などの回答を聞いたり、講義後の質問などのために、以下の Twitter ハッシュタグを用いることとする。#2020nitfcei3os って書いたけど、学内WiFi SNS系全カットじゃん。
- 第1回Linux演習(1/21)
- 第2回Linux演習(1/28)
なお、教室で BYOD パソコンで同時接続を行うため、この授業中だけ増設の WiFi アクセスポイントを設置します。
- SSID = fnct-class , password = fnct-class にて接続してください。
Windowsでsshが使えない場合
情報構造論2019-講義録
- 2019年度情報構造論ガイダンス
- ループ処理時間とオーダー記法と再帰
- 処理時間のオーダー(回答編)
- 再帰呼び出しと再帰方程式
- ポインタを使った処理
- ソートアルゴリズム
- ポインタの加算と配列アドレス
- 効率のよいメモリ使用と動的メモリ確保
- malloc()とfree()
- mallocを使った課題
- リスト構造について
- リスト処理
- リストへの追加処理
- スタックと待ち行列
- 集合とリスト処理
- 双方向リスト
- 2分探索木
- 2分探索木にデータ追加と演習
- AVL木と2分ヒープ
- 意思決定木と構文解析
- 演算子と2分木による式の表現
- B木とデータベース
- ハッシュ法
- 文字列のハッシュ値と共有のあるデータの取り扱い
- 動的メモリ確保(malloc()とfreelist)
- オブジェクト指向と情報構造論と演習
オブジェクト指向と情報構造論と演習
データ構造を扱うプログラムの書き方を説明してきたので、それらを便利に書くためのオブジェクト指向の入り口を紹介する。
データ指向のプログラム記述
名前と年齢のデータを扱うプログラムを書く時、私なら以下のようなプログラムを作成する。
このプログラムの書き方では、saitohというデータにset_NameAge() , print_NameAge() を呼び出していて、データに対して処理を加えるという雰囲気がでている。このようにプログラムを書くと、saitoh というデータに対して命令するイメージとなり、擬人化したデータに向かってset,printしろ…って命令しているように見える。
// 名前と年齢の構造体 struct NameAge { char name[ 20 ] ; int age ; } ; // NameAgeを初期化する関数 void set_NameAge( struct NameAge* p , char s[] , int a ) { strcpy( p->name , s ) ; p->age = a ; } // NameAgeを表示する関数 void print_NameAge( struct NameAge* p ) { printf( "%s %d¥n" , p->name , p->age ) ; } void main() { struct NameAge saitoh ; set_NameAge( &saitoh, "t-saitoh" , 53 ) ; print_NameAge( &saitoh ) ; // NameAge の中身を知らなくても、 // set_NameAge(),print_NameAge() の中身を見なくても、 // saitoh を set して print する....という雰囲気は伝わるよね!! }
このプログラムでは、例えば、データに誕生日も覚えたいという改良を加えるとしても、main の前のデータ構造と関数の部分は色々と書き換えることになるだろうけど、main の内部はあまり変わらないだろう。こういう状態なので、プログラムを作成するときには、データ構造とそれを扱う関数を記述する人と、データ構造を使う人(main内部を書く人)と、分業ができるようになる。
隠蔽化
このような記述では、データ構造の中身を知らなくても、main で、setしてprintして…という処理の雰囲気は分かる。さらに、set_NameAge()とか、print_NameAge() の処理の中身を知らなくても、設定するとか表示するとか…は予想できる。
これは、NameAge というデータをブラックボックス化して捉えていると見れる。データ構造の中身を知らなくてもプログラムを理解できることは、データ構造の隠蔽化という。また、関数の中身を知らなくても理解できることは、手続きの隠蔽化という。
オブジェクト指向プログラミング
前述のように、プログラムを書く時には、データ構造とそのデータを扱う関数を一緒に開発するのが一般的である。オブジェクト指向プログラミングでは、データ構造とその関数(メソッドと呼ぶ)をまとめてクラスと呼ぶ。
class NameAge { private: // データ構造の宣言 char name[ 20 ] ; int age ; public: // メソッドの定義 void set( char s[] , int a ) { // 初期化関数 strcpy( name , s ) ; age = a ; } void print() { // 表示関数 printf( "%s %d¥n" , name , age ) ; } } ; void main() { NameAge saitoh ; saitoh.set( "t-saitoh" , 53 ) ; saitoh.print() ; }
このプログラムでは、saitoh というデータ(具体的なデータはオブジェクトと呼ぶ)に対して、set() , print() を呼び出している。
オブジェクト指向では、データに対して private を指定すると、クラス以外でその要素を扱うことができなくなる。これにより、クラスを設計する人と、クラスを使う人を明確に分けることができ、クラスを使う人が、クラス内部の変数を勝手に触ることを禁止できる。
プログラムを記述する時には、データ件数を数える時に、カウンタの初期化を忘れて動かないといった、初期化忘れも問題となる。オブジェクト指向のプログラム言語では、こういうミスを減らすために、データ初期化専用の関数(コンストラクタ)を定義することで、初期化忘れを防ぐことができる。
// コンストラクタを使う例 class NameAge { // 略 public: NameAge( char s[] , int a ) { // データ初期化専用の関数 strcpy( name , s ) ; // コンストラクタと呼ぶ age = a ; } // 略 } ; void main() { NameAge saitoh( "t-saitoh" , 53 ) ; // オブジェクトの宣言と初期化をまとめて記述できる。 saitoh.print() ; }
プログラムにオブジェクト指向を取り入れると、クラスを利用する人とクラスを記述する人で分業ができ、クラスを記述する人は、クラスを利用するプログラマーに迷惑をかけずにプログラムを修正できる。
この結果、クラスを記述する人はプログラムを常により良い状態に書き換えることができるようになる。このように、よりよく改善を常に行うことはリファクタリングと呼ばれ、オブジェクト指向を取り入れる大きな原動力となる。。
最近のC++なら
最近のオブジェクト指向プログラミングは、テンプレート機能と組み合わせると、単純リスト処理が以下のように書けてしまう。struct 宣言やmalloc()なんて出てこない。(^_^;
#include <iostream> #include <forward_list> #include <algorithm> int main() { // std::forward_list<>線形リスト std::forward_list<int> lst{ 1 , 2 , 3 } ; // リスト先頭に 0 を挿入 lst.push_front( 0 ) ; // 以下のような処理を最新のC++なら... // for( struct List*p = top ; p != NULL ; p = p->next ) // printf( "%d¥n" , p->data ) ; // 通常の反復子iteratorを使って書いてみる。 // auto は、lst の型推論。 // 本来なら、std::forward_list<int>::iterator itr = lst.begin() と書く。 for( auto itr = lst.begin() ; itr != lst.end() ; itr++ ) { std::cout << *itr << std::endl ; } // 同じ処理を algorithm を使って書く。 std::for_each( lst.begin() , lst.end() , []( int x ) { // 配列参照のコールバック関数 std::cout << x << std::endl ; } ); // 特に書かなくてもデストラクタがlstを捨ててくれる。 return 0 ; }
関数ポインタ
前プログラムのC++のfor_each アルゴリズムでは、コールバック関数が使われていたが、この仕組みを分かるために関数ポインタの考え方が重要。
int add( int x , int y ) { return x + y ; } int mul( int x , int y ) { return x * y ; } void main() { int (*f)( int , int ) ; // fは2つのintを引数とする関数へのポインタ f = add ; // f = add( ... ) ; ではないことに注意 printf( "%d¥n" , (*f)( 3 , 4 ) ) ; // 3+4=7 f = mul ; printf( "%d¥n" , (*f)( 3 , 4 ) ) ; // 3*4=12 }
演習(ハッシュ法)
ハッシュ法のプログラム(オープンアドレス法もしくはチェイン法)を用いて、
(1)名前と電話番号,(2)名前と住所,(3)名前と誕生日について、名前をキーとして検索するプログラムを作成せよ。
原則として「出席番号 % 3 + 1」の番号のテーマに取り組むこと。
レポートを作成する際には、ハッシュ関数を変更してどういった変化があるか確認せよ。
ハッシュサイズは、10〜20件程度で良い。
B木とB+木とハッシュ法
B木
データベースのデータを扱う場合には、B木を用いることが多い。
複数のデータを格納するノードは、位数Nであれば、2✕N個のデータと、その間のデータを持つノードへの2N+1個のポインタで構成される。
ノードにデータを加える場合(あるいは削除する場合)は、頻繁にノードのポインタの付け替えが発生しないように、データがN個を下回った時や、2N個を超える場合に以下のような処理を行う。ノード内のデータ数が2Nを超える場合は、均等に木構造が成長するように、中央値を上のノードに移動し、ノードを2分割する。
データを削除することでN個を下回る場合は、隣接するノードからデータを移動する。(上図の緑部分のように上位ノードの値を交えながら移動する)
このような処理を行うことで、極力不均一に成長した木構造が発生しないようにB木は管理されている。
B+木とシーケンスセット
再帰的な木構造のB木では、特定のデータを探す場合には、O(log N)で検索が可能である。
しかしながら、直積のようなすべてのデータを対象とする処理を行う場合、単純なB木では再帰呼出しをしながらの処理を必要とすることから、複雑な処理が発生する。そこで、データ列を横方向にアクセスするための単純リストであるシーケンスセットをB木と並行して管理するデータ構造がB+木である。
データを検索する場合は、B木構造部を用い、全データ処理は、シーケンスセットを用いる。
ハッシュ法
ハッシュ表は、データの一部をとりだしてハッシュ値を求め、そのハッシュ値を番地とする場所にデータを保存する方法である。しかし、データの一部を取り出すため、異なるデータに対して同じハッシュ値となる場合がある。これをハッシュ衝突とよぶ。この際のデータの保存の方法から、2つの方式がある。
- オープンアドレス法
ハッシュ表がすでに埋まっていたら、別の保存場所を探す方式。 - チェイン法
同じハッシュ値となるデータをリスト構造で保存する方法。
(2019-01-29) 図が見にくかったので差し替え
トランザクション処理
トランザクション処理
トランザクション処理とは、相互に依存関係にある複数の処理を矛盾なく処理することであり、データベースでは、ACID特性(原子性,一貫性,隔離性,耐久性)がもとめられる。この時、直列化可能(様々な順序で処理できるかもしれないけど、矛盾しない結果となる処理順序が存在すること)であることが求められる。
例えば、以下のように、50万円のデータがあった時、入金処理と出金処理がほぼ同じタイミングで開始された場合、入金処理が終わらないうちに、出金処理が開始されると、以下の例では入金処理が無視されてしまう。
上記のような問題が発生しないようにするには、以下のように、入金処理の時点で他の更新処理を排除するLOCK処理を行い、入金データの書き込みを終えた時点でUNLOCK処理を行う、排他処理が重要となる。(ロックされている間は、アクセスを禁止する。)
同時実行制御
複数のトランザクションによるデータアクセスで、トランザクション処理を直列化可能にすることを、同時実行制御と呼ぶ。この方式には、2つの方法がある。
- ロッキング方式(悲観的制御)
先行するトランザクションは、データにロックをかけ、他のトランザクションを一時的に排除する方式。後発の処理はアンロックされるまで待たされることことから、これが処理効率の低下となる。- ロッキング方式では、ロックをかける大きさ(粒度)が大きいと、待ち処理が発生する可能性が高い。一方で、粒度を小さくしようとすると、ロックの判定が難しくなり効率が低下する可能性も出てくる。
- ロックの種類
ロックには、読み出し中心のデータと書き込みで更新のかかるデータでは、ロックのかけ方が異なる。例えば、読み出し中のデータは値が変化しないことから、同じタイミングで読み出し処理が発生しても、待たせる必要は無い。
この時、データを読み出す際にかける共有ロック(Read Lock)と、書き込みの際にかけるロック占有ロック(Write Lock)がある。 - 2相ロッキングプロトコル
トランザクションのロックの操作は、ロックをかける操作が続く成長相と、ロックを解除する操作が続く縮退相に分けて行うことが多い。これを2相ロッキングプロトコルと言う。
- 時刻印処理(楽観的制御)
データの競合の発生頻度が低い場合には、ロッキング方式は待ち処理時間が無駄となるため、同時アクセスを許す方式。ただし、あとで処理の発生した時間(タイムスタンプ)を確認し不都合が判明した場合は、処理の記録をもとにロールバックしてやり直す方式。
デッドロック
複数のトランザクションの実行時には、相互の関係から、処理がうまく進まない場合も発生する。(お互いが相手の処理をロックする状態で、ロック解除が発生しない。)
このような状態をデッドロックと呼び、この状態が発生すると処理が停止してしまうこともある。このような状態は、避けられない場合もあるが、どの処理が何を使うのか、どのデータはどの処理の終了を待っているのかといった資源の状態をグラフ理論で表現したもの資源グラフをで表現し、グラフが巡回するようであれば、デッドロックが発生する。
動的メモリ確保(malloc()とfreelist)
C言語では、動的メモリ領域をどのように管理していくのか解説する。
局所変数とスタック
局所変数は、関数に入った時に作られるメモリ領域であり、関数の処理を抜けると自動的に開放されるデータ領域である。
関数の中で関数が呼び出されると、スタックには戻り番地情報を保存し、関数に移動する。最初の処理で局所変数領域が確保され、関数を終えると局所変数は開放される。
この局所変数の確保と開放は、最後に確保された領域を最初に開放される(Last In First Out)ことから、スタック上に保存される。
baz()の中で、「*((&c)+8) = 123 ;」を実行したら、bar()のxを書き換えられるかも…
動的メモリ領域とフリーリスト
動的なメモリ領域(ヒープ領域)は、malloc()関数で処理用のメモリを借り、free()関数で使わなくなったメモリを返却する。
この返却されたメモリ領域は、改めて malloc() が呼び出されたときに再利用を行う。この再利用するメモリ領域は、簡単に扱えるようにリスト構造にして保存する。この free された再利用候補のリスト構造は、free_list と呼ばれる。
mallocが一定サイズの場合
仕組みを理解する第1歩として、free_list の考え方を説明するために、malloc() でのメモリサイズが一定として説明を行う。free_list には、貸し出すためのメモリ空間をリスト構造で繋がった状態にしておく。
malloc() が呼び出される度に、free_list の先頭から貸し出すメモリを取り出し(a=malloc(),b=malloc(),c=malloc()まで)、free() が呼び出されると、返却されたメモリは、free_list の先頭につないでおく。
任意サイズのメモリ確保の場合
最初のステップでの説明は、mallocのメモリサイズを一定としていたが、本来は確保するメモリサイズが指定する。この場合は、以下の様に管理されている。mallocで貸し出されるメモリ空間には、ヒープメモリの利用者が使うブロックの前に、次のメモリブロックへのポインタとブロックサイズを記憶する領域をつけておく。こういったメモリブロックを free_list の考え方と同じようにリスト構造となるようにつないで保存されている。
この図の一番下の赤部分は、次のメモリブロックへのポインタとブロックサイズの大きさが20byteの場合の例。
malloc() で、指定されたサイズのものが、free_list の中にあれば、それを使う。malloc(40)
丁度いいサイズが無い場合は、それより大きいメモリブロックの後半を切り分けて、貸し出す。malloc(60)
free()の処理とメモリブロックの併合
この例の最後の処理では、20byte,60byte,40byte,50byteが併合された例。併合後のブロックサイズは、すこしいい加減に書いてある。
使用されていたメモリブロックが free() で返却された場合は、free_list につないでいく。ただし、単純にリストに繋ぐだけであれば、malloc(),free() を繰り返すと、小さなメモリブロックばかりになってしまい、大きいメモリのmalloc()ができなくなる。
そこで、free() で返却される際には、隣り合うメモリブロックと併合できるかを確認し、大きなメモリブロックになるような処理を行う。
また、隣り合うメモリブロックが併合できるかの判定が簡単になるように、free_listにつなぐ際は、次のメモリブロックへのポインタは、昇順となるように並べる。
一般的には、上記のようにmalloc(),free()を行うが(K&Rのmallocアルゴリズム)、mallocのサイズが小さい場合には小さいメモリブロック毎にnextブロックポインタやブロックサイズを記憶する場合、メモリのムダが多い。
そこで、最初に説明した一定サイズのmalloc()の手法で、8byte専用のfreelist,16byte専用のfreelist,32byte専用のfreelistのように2Nbyteのfreelistで管理する。10byteといった中途半端なサイズの時は、それより大きい16byteのfreelistを使う。(dlmallocのアルゴリズム)
ヒープメモリの断片化
ヒープメモリの malloc() , free() を繰り返すと、最悪、以下の図の様に、使用中領域(赤)とfreeされた未使用領域(黒)が交互に並ぶ状態が発生するかもしれない。この場合、全体の未使用領域の合計では十分なサイズでも、小さなメモリブロックばかりとなって、大きなメモリブロックを要求されても十分な大きさのメモリが見つからない状態が発生する場合がある。
この状態をヒープメモリの断片化といい、使用しづらい小さなメモリブロックはヒープホールと呼ばれる。
(補足) 断片化
断片化というと、OSではハードディスクの断片化(フラグメンテーション)を思い浮かべるかもしれない。ハードディスクの断片化とは、ファイル領域の割り当てとファイルの削除を繰り返すことで、ファイルのセクタが不連続となり、アクセス効率が悪くなる現象。OSによっては、ファイル実体の位置を動かすことで断片化を改善できる。以下の図のようにフラグメンテーションを防ぐための実体の移動を行う最適化はデフラグと呼ばれる。
上記の図では、上の青の図が断片化が発生している事例で、a1→a2,a2→a3の時にヘッド移動(シーク時間)が発生する。下の赤の図のように、デフラグ処理を施すことでシーク時間が減らせる。
Windows が 95,98,Me といった時代ではOSが不安定で、フラグメントが多く発生する場合Windowsがフリーズすることが多く、OSが不安定になったらデフラグを実行する…というテクニックが定番だった。最新のWindowsでは、デフラグが自動的に実行されるのでユーザが意識的に実行する機会はほぼなくなった。
卒業論文について
起承転結のはっきりした文章を書いてください—————
起 まえがき
- 背景と目的
何がおもしろくて, そのテーマを追求するのか, そのテーマの追求によって、どのような進歩が期待されているのか? - 先行研究 *1
scienceなら、今までに、何がわかっていて、何がわかっていないのか? technologyなら、何ができていて、何ができていないのか? - 目標
この研究では,どこまで達成するのか? 以後の章の概要
承 基礎となる理論
自分の主張を論理的に展開するために必要な基礎理論、用語の定義, 公理や定理, 実験法 *2 などの説明 (卒論の場合は、一般の研究論文と異なり、勉学の記録でもあるので、後の論理展開に、直接必要のないものが、多少、あってもよい)
転 論理的展開
承で述べた道具を使って, 自分の研究内容を飛躍,誇張,矛盾,循環がないように論理的に展開したうえで、結果の記述.*3
結 結論,考察, 今後の課題と展望
- まとめ,
承と転で述べてきたことの要点. - 結論
目標は達成されたのか, この論文で新たに加わった知見や技術と、その意義を具体的に述べる. - 今後の課題,展望
この論文では、明らかになっていない、もしくは、達成できていない課題とそれらをクリアするための方法や今後の研究展開の可能性について考察.
*1 *2
参考(引用)文献は, 余すところなく, 本論に現れる順番で挙げること.*2 *3
幾何学の証明問題と同じで, その時点までの既出の用語, 式, 概念を使って論理展開すること. 説明の都合上、用語などが現れる順番が前後する場合には, 「このことについては、…章…節で詳説する」などの断り書きを入れること.
明らかに、推敲不足の文章は、添削しません————
自分だけがわかる研究メモ程度の文章を卒論として提出してはいけません. どんなに素晴らしい研究をしても、人に読んでもらって理解してもらわなければ、研究していないのと同じことになってしまいます. 会ったことのない読者にも、わかってもらえる文章になっているか、つまり、コンテンツの順番は最適か? 不足しているものがないか? 複数の解釈や誤解の恐れのある文章はないか? 誤字はないか? 式や用語を間違って使っていないか?
グラフや図の配置やキャプションが適切か, また, それらを本論できちんと記述しているか? 式やグラフの番号が間違っていないか? 何十回も推敲し、その中で 最善の文章を求めて、躊躇することなく書き換えを行うこと.
** 下條先生の卒業研究学生のメモを斉藤が代理で掲載