[-]=======================================================================[-] Wizard Bible vol.26 (2006,5,7) [-]=======================================================================[-] x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ---- 第0章:目次 --- x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ○第1章:パッカーの仕組みと構造 suma 著 ○第2章:スクリプト言語を作ってみよう Kenji Aiko 著 ○第3章:.NET APP Cracking 右サイド 著 ○第4章:Rapid Development of Packer Vol.1 suma 著 ○第5章:基礎暗号学講座 〜 第1回 〜 IPUSIRON 著 ○第6章:お知らせ ○第7章:著者プロフィール x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x --- 第1章: パッカーの仕組みと構造 --- 著者:suma x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ■0x01.) はじめに  最初はなるべく初心者向けの説明にし、中盤以降はより細かいパッカーの特徴 ・欠点などを解説したいと思います。「パッキング」「パック」という単語につ いて、すべて「パック」に統一しました。 ■0x02.) パッカーと布団のイメージ  ターゲットとなる実行ファイルを布団、パッカーが掃除機・布団・ビニール袋 ・ロボットを持った人と考えてください。パッカーは次の作業をします。布団を ビニール袋に入れ、掃除機で空気を抜いて真空状態にします。これがパックです。 布団はビニールに包まれ、つぶされて小さくなっています。まさにパックされた (包まれた)状態です。  布団を使いたくなったらどうするのでしょうか? ロボットにビニール袋のつ ぶれた布団を元の状態に戻してもらいます。実はこのロボット、包まれた布団と 一緒にくっつけられています。布団を元に戻す作業がアンパック、布団を元に戻 してくれるロボットのことをローダーといいます。 ・ターゲットの実行ファイル:布団 ・パッカー:掃除機、ビニール袋、ロボットを持つ人 ・ローダー:布団を元に戻してくれるロボット ・パックされた実行ファイル:ビニールに包まれてつぶれた布団とローダー  つぶれた布団は小さくなるので収納に便利です。これと同じことが実行ファイ ルのパックにもいえます。圧縮すれば実行ファイルは小さくなります。ハードデ ィスク容量の節約・インターネットなどの媒体を利用して配布する際にファイル サイズが小さいと都合がよいかもしれません。しかし、現在のハードディスクの 大容量化や通信速度から考えて気にするものではないでしょう。これは十分な場 所があるにもかかわらず、無理に布団をつぶしていることとかわりません。  実行ファイルのサイズが小さくなることの効果は他にもあります。ハードディ スクなどから実行ファイルを読み込む時間が短くなります。ファイルの読み込み とアンパックにかかる時間が元の実行ファイルの起動時間より早ければそのぶん 起動が早くなります。人間が大きな布団を取り出すより、つぶれた布団をロボッ トが人間より素早く運び、取り出したほうがかかる時間は短い、というイメージ です。当然ながら、アンパックにかかる時間の方が多くて逆にプログラムが起動 する時間が増えてしまうことも考えられます。  また、実行ファイルのサイズが減っても、実行時にメモリ使用量が減るわけで はありません。つぶれた布団が元の大きさに戻れば、布団を利用するための相応 の広さが必要になります。布団に加え、ビニール袋・ロボットのぶんだけ必要と する場所は増えることになるでしょう。 ■0x03.) アンパッカーとマニュアルアンパック  アンパッカーはパックされた実行ファイルから元の実行ファイルを復元するプ ログラムです。例えばUPXはオプションに「-d」を指定することでアンパックが可 能です。UPXはパッカーでもありますがアンパッカーでもあります。  アンパッカーを使わず、デバッガやダンプツールを使って手動でアンパックす ることをマニュアルアンパックをといいます。アンパック用のダンプツールとし て、Windows用デバッガOllyDbgプラグインのOllyDumpなどがあります。  アンパッカーがあればマニュアルアンパックをする必要はありません。しかし、 いつもアンパッカーが手に入るとも限りません。マイナーなパッカーにが対応す るアンパッカーがないかもしれません。また、アンパッカーを作るのは多くの労 力が必要です。ローダーの仕組みを調べ、それとほぼ同じプログラムを作成しな くてはなりません。そこで、「アンパック」までの処理をローダーに任せ、復元 後のメモリをダンプして横取りする方法を取ります。こちらの方がはるかに労力 が少なくなります。 ■0x04.) Generic Unpacker  アンパックはローダに任せてしまう。これが最も簡単方法(アンパッカーは労 力がかかるという面で除外)ですが、すべての状況でこれが実行可能とは限りま せん。後述するプロテクターにはダンプを防ぐ機能を実装しているはずです。メ モリのダンプの方法ですが、デバッガでトレースしてからダンプする、あるいは、 プログラムを実行してからダンプするという流れになります。  Windows用の逆アセンブラのdispeのを例にあげて説明します。この逆アセンブ ラは「-d」オプションを指定することで、パックされた実行ファイルを自動でア ンパックしてから逆アセンブルします。dispe本体のコードまで解析したわけなの で確実にこの方法を取っているとはいえません。しかし、このアンパックの考え 方は次のようになっていると考えられます。「ローダーの処理が終わるまでプロ グラムを実行させ、元のコードに実行が移る前に逆アセンブルを実行する」  このような方法によるアンパッカーは海外ではGeneric Unpackerといわれてい るようです。この方法の欠点は、実際にコードを実行しているということです。 実行されるコードはツール任せであり、危険性がないとも限りません。コードが 実行できるという点から柔軟性があるともいえますが、コードを実行してしまう から柔軟性に欠けるという見方があるかもしれません。自動化のひとつの手段と して知っておいて損はないでしょう。 ■0x05.) 完全な元の実行ファイルの復元  アンパッカーを使う、あるいはマニュアルアンパックによって完全な元の実行 ファイルは復元可能でしょうか? これはできないと考えてほぼ間違いありませ ん。なぜなら、パッカーによってはパックの際にプログラムの実行に必要ないデ ータを削除するもの、不可逆な処理をするパッカーも存在するためです。  非常に簡単な例をあげましょう。Windows実行ファイルのPEファイルにはセクシ ョンという単位でデータを区切っています。また、そのセクションには名前が付 けることができますが、名前そのものは実行に何も影響を与えません。パックに 「セクション名を変更する」という処理が含まれただけで、完全に元に戻せない ものとなってしまいます。  パッカーが何も削除することなく、パッカーの処理が完全にわかっている場合 はどうでしょうか。完全な元の実行ファイルが復元できそうに思えます。この条 件であれば私も可能だと思います。しかし、次の条件が暗黙についてまわるでし ょう。「パックされた実行ファイル」しか手に入れられない場合です。そもそも 元の実行ファイルがないからアンパックという手段にでるのですが、元の実行フ ァイルがないので比較・検証する手段はありません。いってみれば、「完全に復 元しましたが、それを確かめる手段はありません」ということです。 ■0x06.) プロテクターとしての役割  プロテクターとは、リバースコードエンジニアリングを防ぐ(妨害する)ため に作られるパッカーです。パックされたコード・データは外部から直接参照する ことができません。そのため逆アセンブルしてもパックされる前のコードを逆ア センブルできなくなります。また、前述したダンプの対策や、デバッガのトレー スの妨害もプロテクターの役割となります。パックには圧縮アルゴリズムだけで なく、多重にコードが暗号化されることもあります。 ■0x07.) 暗号化  「暗号化する」といっても、結局は実行時にローダーのアンパックの処理の中 で復号化されます。そうでなければ元のプログラムを実行することができません。 暗号化したデータを復号化するためのコードがローダーに含まれて実行されるた め、人間が読み解いていくことは困難であっても不可能ではありません。  ここでいう暗号化は共通鍵暗号(秘密鍵暗号)のことを指します。暗号化した データを復号化するには、暗号化に使った鍵でのみ復号化できます。プロテクタ ーの内部で共通鍵暗号が使われるということは、その鍵もプログラム中に含まれ ています(※1)。  そのため、暗号アルゴリズムの強度は低くてもかまわないでしょう。鍵が埋め 込まれているのですから、アルゴリズムよりも鍵を隠すほうが大事といえます。 また、暗号化が多重に施してあった場合を考えてみてください。アンパックする ための手間が増えていきますね。 (※1)鍵をプログラム内に含まないで実装することも可能です。しかし、実行フ ァイル中に埋め込まれていなくても、結局は復号化の際に鍵が使用されるため、 その鍵を見つけてしまえば復号化が可能です。 ■0x08.) ダンプに弱い  何重にパックされていても、前述したダンプにはとても脆弱です。暗号といっ ても、アンパックされるまでのデータを保護するだけです。 ■0x09.) アンチデバッガ・アンチダンプ  デバッガによるトレース、ダンプの対策が「アンチデバッガ」と「アンチダン プ」です。アンチデバッガはデバッガを検出してプログラムを強制終了します。 アンチダンプはローダーの元のコードのアンパック後、ダンプしても実行できな いようにアンパックされたメモリを改変したりします。 ■0x0A.) おわりに  パッカーについてまとまった文書がなかったので挑戦してみました。Windows向 けのパッカーが多いこと、私がWindowsだけでしかパッカーを使用した経験がない ため、例としていくつかWindows向けのツールを紹介することにしました。抽象的 でわかりにくくなってしまったかもしれませんが、パッカーについてより理解が 深まれば幸いに思います。 x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x --- 第2章: スクリプト言語を作ってみよう --- 著者:Kenji Aiko x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ■0x01.) はじめに  コンパイラやリンカを作るのは難しいですし、かといってオープンソースのス クリプト言語を読んで理解するのも量が膨大すぎてちょっと…、という私は、1年 ほど前にネット上からシンプルなスクリプト言語のソースコードを探しました。 でも、これがなかなか見つかりません。ただ、興味本位でスクリプト言語の大ま かな動作が知りたかっただけなのですが、ちょっとした解説なども見つかりませ んでした。というわけで、今回はシンプルなスクリプト言語を作ってみることに します。  あと、あらかじめ断っておきますが、私はこういうプログラミング言語作成に 関しては、かなり無知なので、ご了承ください。言語処理系の基本なんて全然学 んでいません。ごめんなさい。サンプルも、行き当たりばったりでなんとかかん とか作成したプログラムです。申し訳ありません。  なので、本格的にこういうものを学びたい方はそれなりの専門書を読むなりし てください。ネット上にはあまり情報がなくとも、書籍ではすばらしいものが多 数出版されていますし、オープンソースのプログラミング言語も多数あるので、 とりあえず学ぶには申し分ないと思います。少なくとも、このテキストでは、本 格的なものは学べないので(^^;。  ただ、そこまで本格的にやりたいとは思ってないけれど、スクリプト言語って なんかちょっと気になるなぁ、くらいの方はきっとタメになると思います(タメ にならなかったらすみません)。  さて、では今回取り扱うプログラムは以下です。gcc、VS.NET、BCCでコンパイ ルを確認しています。次のソースコードを見ながら、読み進めてください。 http://ruffnex.oc.to/kenji/src/kbasic/kbasic-0.1.zip ■0x02.) 解釈  まずは、スクリプト言語の本当に大まかな流れを説明します。まず、最初に「 ソースコードが書かれてあるファイルを開き」続いて「ファイル内容を読み込み」 そして「ソースコードを解釈、実行」して「ファイルを閉じ」ます。えっと、本 当に大まかな流れでしたが(笑)、これはmain関数の処理です。  それで、問題は「ソースコードを解釈し、実行する」部分です。まず「解釈」 という部分に焦点を当てます。大抵のプログラミング言語のソースコードは、ア ルファベットと数字といくつかの特殊文字によって記述されるため、それらを適 切に解釈する必要があります。では、具体的に解釈とはいったいどういうことで しょうか。 ----- for(i=0; str[i] != '\0'; i++) str[i] = (char)tolower((int)str[i]); -----  例えば、プログラミング言語に使用される単語は、大きく分けて「変数名」「 数字」「文字列」「特殊文字」「予約語」といった分別を行うことが可能です。 上のプログラムを例にとると、「for」は予約語、「(」は特殊文字、「i」は変数 名で「=」は特殊文字、そして「0」は数字で、「;」は特殊文字、となります。な ので、解釈する側としては、まず、それらを正確に認識する必要があります。 ----- test.bs num = 105 PRINT "num = ", num, "\n" -----  上のプログラムを実行すると、スクリプトが一番最初に読み込む文字は「n」と なります。これはアルファベットであるため、変数名であるかまたは予約語とな ります。続いて「u」「m」が読み込まれ、そしてスペースが読み込まれます。こ のスペースが読み込まれた時点で、この単語が終了であることがわかります。そ して、この単語が「num」であり、登録されているすべての予約語と一致しないた め、変数名であると解釈できます。というわけで、とりあえず、変数名「num」と いうものを作成します。  再びソースコードを読み込むと、今度は「=」です。これは特殊文字で確定なの で、そのまま特殊文字として解釈します。  次はスペースなので読み飛ばし、その次は「1」という数字となります。これは 数値で確定なのですが、数値としてどれだけの値であるかがわからないので、数 字以外の文字が出現するまで、次の文字を読んでいきます。まず「0」が読まれ、 続いて「5」が読まれます。そして改行(数字以外)が読まれることになるので、 結果、「105」という単語であることがわかります。さて、これまでの一連の流れ から「num」「=」「105」という3つの単語が一列になっていることがわかります。  次に改行を挟んで、「P」という文字が読み込まれます。これだけでは変数名か 予約語かわからないので、とりあえず次の文字、その次の文字へと、どんどん読 み進めて行きます。すると、スペースを見つけることで単語の終端とわかり、最 終的に「PRINT」という予約語であることがわかります。PRINT文であることがわ かったら、次は、文字列、変数名、区切りのための特殊文字、のいずれかとなり ますので、ダブルクォーテーション「"」ならば文字列とし、「,」なら区切り、 それ以外ならば変数名として解釈します。  このような単語の解釈を行う処理は次の関数で行われています。 ----- kbasic.c int getlex( void ); // 次の単語を取得 void ungetlex( void ); // 取得している単語を戻す char GetChar( void ); // 1文字取得 int lex_ident( void ); // 変数or予約語を取得 int lex_const( void ); // 数値を取得 int lex_string( void ); // 文字列を取得 -----  基本的にgetlexとungetlexを使って単語の取得や返却を行います。これらは、 簡単に言うと、getc関数やungetc関数の単語版だと言えます。この関数を使うこ とで、好きな時に単語の取り出しや返却が可能になります。また、単語のデータ は構造体として定義しており、次のようになっています。 ----- kbasic.c typedef struct { /* 上記に定義しているデータを格納する変数 */ int type; /* 予約語以外のデータの場合は、そのデータを入れる変数群 */ union{ int num; /* 数字 */ char ident[MAX_IDENT_LEN]; /* 変数名 or ラベル名 */ char string[MAX_STRING_LEN]; /* 文字列 */ char symbol; /* 特殊文字 */ }detail; } LEX_T; -----  typeには「予約語」「変数名」「数値」「文字列」といったどのタイプの単語 であるかを識別する識別子が入り、そのタイプによって、次のunionで定義されて いる変数にデータが格納されます。  プログラム内では、この構造体はグローバル変数として定義しています。よっ て、getlex関数を呼び出したらこの構造体に単語データが格納され、ungetlex関 数を呼び出したら、この構造体にある単語データがファイルへ(正確にはファイ ルではないが)戻ります。  以後、getlexとungetlexを利用して様々な関数を作成していきます。 ■0x03.) 計算  プログラミング言語である以上、最低限、数式を計算できなければなりません。 括弧つきの整数の四則演算を数式どおりに計算するためには、再帰下降構文解析 を利用します。再帰下降構文解析というとなにやら難しそうですが、実際には大 したことはありません。まず、最初に次のような演算を行う関数をそれぞれ作成 します。 ----- int expression( void ); // 入口(入口としての機能しか果たしていない) int AndOr( void ); // かつ、もしくは int NotFunc( void ); // 否定 int Compare( void ); // 等号、不等号 int AddSub( void ); // 加算、減算 int MulDiv( void ); // 乗算、除算 int Factor( void ); // 変数名、数値、括弧() -----  expression関数はただの入口ですので、それ自体には意味はありません。AndOr は「&&」や「||」を計算する関数で、NotFuncは否定「!」、Compareは等号や不等 号「=<>」を計算する関数です。そして、加算や減算を行うのがAddSub関数で、乗 算や除算を行うのがMulDiv関数です。最後のFactorは、変数名をその変数の値に 変換したり、数値そのものを扱ったり、また括弧「()」を処理する関数です。 ----- keisan.bs num = 5 * 4 * (3 + 4 - 5) + 1 PRINT "num = ", num, "\n" -----  さて、問題はここからです。上のソースコードを見てください。まずnumという 変数名が定義され、その次の単語が「=」であることを解釈したら、続いてexpre ssion関数が呼び出されます。expression関数内ではいきなりAndOr関数が呼び出 され、AndOr関数内ではいきなりNotFunc関数が呼び出されます。そしてNotFunc関 数内ではCompare関数が呼び出され、Compare関数内ではAddSub関数が…、といっ た具合に、次から次へと下の関数が呼び出されていきます。それで、とりあえず その調子でFactor関数まで呼び出されます。そして、Factor関数は「数値」を処 理するので、結果「5」という数値を認識します。  Factor関数の処理が終わると、呼び出しもとのMulDiv関数へと処理が移ります。 MulDiv関数は乗算と除算を処理するため、乗算命令である「*」を発見します。す ると、再びFactor関数を呼び出します。再度呼び出されたFactor関数は、今度は 「4」という数値を認識することになり、そして、またMulDiv関数へ戻ります。こ れで「5」と「4」という数値を認識したことになるので、乗算を行い「20」とい う演算結果が算出されます。  そして、次の単語を調べると、再び「*」となります。 ----- num = 5 * 4 * (3 + 4 - 5) + 1 ------> ここまで完了 -----  よって、またFactor関数を呼び出します。Factor関数内では、今度は「(」であ るため、expression関数を呼び出します。この辺りは少しややこしいかもしれま せんので、図にします。 ----- int expression( void ); int AndOr( void ); int NotFunc( void ); int Compare( void ); int AddSub( void ); int MulDiv( void ); ---> int Factor( void ); ---> int expression( void ); int Factor( void ); int AndOr( void ); int NotFunc( void ); int Compare( void ); int AddSub( void ); int MulDiv( void ); int Factor( void ); -----  MulDiv関数は次の値が知りたいので、再度Factor関数を呼び出しましたが、次 の単語は括弧であったため、Factor関数がexpression関数を呼び出しました。2度 目のexpression関数が呼び出されたため、再度、処理はFactor関数まで進みます。 ただ、これは括弧内の話です。つまりFactor関数は「3」を認識し、次は「+」な ので、AddSub関数まで上ります。そしてAddSub関数でまたFactor関数が呼び出さ れ、4が認識されます。これで「7」が得られます。また次は「-」なのでFactor関 数が呼び出され、「5」が認識され、AddSub関数に戻ってきたときに「7 - 5」で 「2」が得られます。そして、次は「)」しかないので、このまま2度目のexpress ion関数は処理を終えます。 ----- int expression( void ); int AndOr( void ); int NotFunc( void ); int Compare( void ); int AddSub( void ); int MulDiv( void ); ---> int Factor( void ); -----  よって、最終的にFactor関数が得られたデータは「2」となります。これがその ままMulDiv関数へ渡ることになります。MulDiv関数はすでに「20」というデータ を持っており、さらにFactor関数から受け取った「2」を乗算して「40」というデ ータを持つことになります。これで次の図までの計算が行われたことになります。 ----- num = 5 * 4 * (3 + 4 - 5) + 1 -------------------> ここまで完了 -----  そして、次の単語は「+」であるためAddSub関数へ処理が行きます。同じように して、AddSub関数はFactor関数を呼び出すので、「1」を取得し、それを「40」に 加算して、結果的に「41」というデータが取得できます。これで計算の処理は終 わりなので、そのままexpression関数は「41」というデータを返却することにな ります。  再帰下降構文解析は、優先順位の低い演算処理関数から順番に呼び出していき、 また、括弧が見つかるごとに新しい演算処理を生成しながら、最終的な計算結果 を得るアルゴリズムです。このような方法で、四則演算を正確に計算することが できるというわけです。ここはアルゴリズムに慣れてない方は、少し難しく感じ るかもしれませんが、再帰処理を理解していれば、それほど難解なものではない と思います。 ■0x04.) 変数  変数は、「識別子」と「データ」を、それぞれが対応した状態で保存しておく 必要があります。よって、今回は二分探索木のアルゴリズムを利用して変数を管 理しました。変数の管理については次の関数で行っています。 ----- kbasic.c int ValLabel( char *ident, int data, int flag ); VAL_LABEL *MakeValLabelTree( char *ident, int data ); void AddValLabelTree( VAL_LABEL *t, VAL_LABEL *n ); int MakeAddValLabelTree( VAL_LABEL *t, char *ident, int data ); VAL_LABEL *SearchValLabelTree( VAL_LABEL *t, char *ident ); void DeleteValLabelTree( VAL_LABEL *t ); -----  変数名を識別子として、二分探索木を作成します。変数管理のためのメモリ領 域は動的に確保します。二分探索木のアルゴリズムは、かなり有名なもので、ネ ットで調べればすぐに発見できるので、詳細な説明は割愛させていただきます( ※1)。 (※1)アルゴリズムとデータ構造編 第17章 二分探索木     http://www.geocities.jp/ky_webid/algorithm/017.html     二分探索木[1] — 初期設定、追加、検索     http://tdweb.am.chs.nihon-u.ac.jp/ds/ds07.html ■0x05.) 実行  実際の実行を担っているのはInterpreter関数です。次のそれぞれの予約語に対 して、処理を実行しています。 ----- PRINT データを標準出力へ出力する INPUT データを標準入力から受け取る IF 条件が真なら以下のプログラムを実行 LABEL GOTO文のあて先を設定する GOTO LABELで設定されたあて先へ飛ぶ ----- ----- kbasic.c(PRINT) case _PRINT: do{ /* 出力すべきデータを読み込む */ getlex(); switch( lex.type ) { case TYPE_STR: /* "Hello! World" というような ダブルクォーテーションで囲まれた文字列だったら */ printf( "%s", lex.detail.string ); break; case TYPE_IDENT: case TYPE_NUM: /* 変数名もしくは数字だったら */ ungetlex(); printf( "%d", expression() ); break; default: Error("PRINT文が正しくありません"); } getlex(); }while( lex.type == TYPE_SYM && lex.detail.symbol == ',' ); /* ','だったら続く文字列を連結 */ /* バッファを吐きだす */ fflush( stdout ); /* 先読みした状態なので戻す */ ungetlex(); break; -----  PRINT命令は文字列と変数の値を出力するだけです。よってまずは文字列である か、変数値であるかを調べ、それに対応した処理を行っています。また、連結を 行う「,」が発見されたら、引き続き出力を行うようにしています。 ----- kbasic.c(INPUT) case _INPUT: /* 標準入力からデータを受け取る変数名を読み込む */ getlex(); if( lex.type == TYPE_IDENT ){ int num; char buf[1024]; fgets( buf, sizeof(buf), stdin); num = atoi(buf); ValLabel( lex.detail.ident, num, VAL_FLAG_SEARCH_W ); } /* 変数名じゃなければエラー */ else Error("INPUT文が正しくありません"); break; -----  INPUT命令は標準入力から変数へデータを受け取る処理を行います。fgetsで文 字列を受け取り、atoiで数値に変換して保存します。 ----- kbasic.c(GOTO) case _GOTO: /* 飛ぶべきラベル名を読み込む */ getlex(); /* ラベル名なら読みこみ */ if( lex.type == TYPE_IDENT ){ /* 実行ポインタを変更 */ int point = ValLabel( lex.detail.ident, -1, VAL_FLAG_SEARCH_R ); if( point == -1 ) Error("指定されたラベルがみつかりません"); pg.pt = point; } /* ラベル名でなければエラー */ else Error("GOTO文が正しくありません"); break; -----  GOTO命令は指定されたラベルへ実行ポインタを移動させます。 ----- kbasic.c(IF) case _IF: if( expression() ){ /* 条件式の解析「真」ならばTHENの位置へ */ do{ getlex(); }while( lex.type != _THEN && lex.type != _EOF ); if( lex.type == _EOF ) Error("IFに対するTHENがありません"); } else{ /* 条件式の解析「偽」ならばENDIFの位置へ */ int if_flag = 1; do{ getlex(); if( lex.type == _IF ) if_flag++; if( lex.type == _ENDIF ) if_flag--; }while( (if_flag != 0 || lex.type != _ENDIF) && lex.type != _EOF ); if( lex.type == _EOF ) Error("IFに対応するENDIFがありません"); } break; -----  IF命令は条件分岐を行います。expression関数を呼び出し、条件式の真偽を確 かめて、プログラムを実行させるかどうかを決めます。真ならTHENの位置へ、偽 ならENDIFの位置まで移動します。 ----- kbasic.c(変数名) case TYPE_IDENT: /* 変数名もしくはラベル名だったならば */ strcpy( ident, lex.detail.ident ); getlex(); if( lex.type == _EOF ) Error("予期しない終了がみつかりました"); if( lex.detail.symbol != '=' ) Error("代入文が正しくありません"); ValLabel( ident, expression(), VAL_FLAG_SEARCH_W ); break; -----  何の予約語もなく、いきなり変数名が現れたならば、それは代入式なので、ex pressionを呼び出します。これで、変数に適切な値が代入されます。 ■0x06.) 動作  では、最後にスクリプト言語の動作を確認します。次のプログラムを見てくだ さい。 ----- label.bs PRINT "Start Number: " INPUT x PRINT "End Number: " INPUT y PRINT "Ans: ", x, " + ... + ", y, " = " z = 0 LABEL LOOP IF x > y THEN GOTO END_OF_LOOP ENDIF z = z + x x = x + 1 GOTO LOOP LABEL END_OF_LOOP PRINT z, "\n" -----  2つの数値を入力させ、1番目の数値から2番目の数値までの数をすべて足し合わ せます。そして、その合計値を出力します。実行例を次に示します。 ----- 出力例 $ ./kbasic label.bs Start Number: 1 End Number: 100 Ans: 1 + ... + 100 = 5050 $ -----  うまく動作していることが分かります。私のページ(※2)には他のサンプルプ ログラムも置いていますので、ぜひ試してみてください。 (※2)http://ruffnex.oc.to/kenji/src/kbasic/ ■0x07.) さいごに  さて、いかがだったでしょうか。今回はこれまでとは少し趣向を変えて、スク リプト言語を作ってみようと題してお送りしました。といっても、取り扱ったプ ログラム自体は1年くらい前に書いたもので、1年前から書こう書こうと思いなが ら結局こんなに経ってしまった代物です(汗)。というのも、なんかスクリプト 言語の作成って説明が難しくて、なかなか進まなくて…(だとしても1年はあんま りだけど)。  実行方法や、実際に動作するプログラムについては「http://ruffnex.oc.to/k enji/src/kbasic/」を参照してください。こんなものをスクリプト言語だという と、もしかしたら怒られるかもしれませんが、スクリプト言語の大まかな動作ア ルゴリズムは理解できたと思います。これから学ぼうという人にとってみれば、 膨大な量のソースコードというのはやる気を奪うので(笑)、1000行足らずのソ ースコードで動作するスクリプト言語を作成してみました。  さて、最後になりましたが、ここまで読んでくれて本当にありがとうございま す。  では、また会う日まで…。 x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x --- 第3章: .NET APP Cracking 〜 バイナリを書き換えちゃいますのよ!! 〜 --- 著者:右サイド x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ■0x01.) はじめに  ヽ(゜∀゜)ノウンコー!ソフトウェアはクラックしても、女の子は製品版として使 いたい”うぃざーどばいぶる”の汚点右サイドでございます!!  えーと今回はですね、前回のアホなレポートで得た不名誉(被害妄想)をこと ごとくぬぐい去るために、別にすごくはないけどこれから解析環境が整うまで主 流になるであろう.NET APPのバイナリ書き換えによる解析方法を書きたいのじゃ !!!!!!!! ■0x02.) 準備  もう、いいよね。………そこまで辿り着いたら、準備してもいいよね(※1)。 華麗にスルーして結構です。今回は堅苦しくないふれんどり〜な文体でいきたい と思います。てなわけで、恒例の準備物です。 ●必要条件 ・Reflector for .NET http://www.aisto.com/roeder/dotnet/ ・バイナリえぢた(何でもよい)  お勧めは、StirlingまたはQuickBe ・私を生暖かい目で見てくれる優しい心 ●十分条件 ・C#に関しての知識 ・プログラミングに関する知識 ■0x03.) えーマジ マネージドコード!!マネージドコードが許されるのは小学生までだよね(※2)  タイトルに内容はさほど関係はありません。ただ単に、テンションが高かった のでつけてみました。てへ♪ 関係あることとええば、マネージドコードのこと。 e-wordsから説明を引っ張ってくると、次のような解説になっています。 ----- 引用 マネージドコード: Microsoft .NETの標準動作環境である、CLRの管理下で実行されるプログラムコー ド。C#などの.NET対応言語によって、.NETのプログラミング規約を守って開発さ れたプログラムがマネージドコードである。CLRが提供するガーベジコレクション などの機能をフルに利用できるコードである。CLRの管理を受けないプログラムの ことはアンマネージドコードという。 -----  マネージドコードの利点はなんぞや? というと、あらゆるプラットフォーム で実行できる利点があるから。要するにJavaと同じ。他にも理由はあるけど、大 体これがメインかな? このマネージドコードにより、Ollyでは解析できません。 でも、中間言語なので、ソースの復元はかなりの復元力ってかほぼ100% Visual Studioでコンパイルするときに、最適化されているんで多少は内容変わります。 でも難読化なんかされると、(゜Д゜)マズー。後、なんだか知らないけど私の恋のバ イナリは、おにゃのこで実行できないらしいです。  バイナリ バイナル バイナロ バイナラッラ(恋のバイナリ)。 ■0x04.) さくらんぼキッス 〜解析だも〜ん〜(※3)  今回のた〜げっとは次のURLからDLできます。 http://www3.pf-x.net/~right-hand-side/RE/WB/mee_wb.zip  制作時間3分と言う代物。だって、簡単な方が解説とか解析とかしやすいじゃな いですの♪ 至ってシンプル、ひ○らしの"な"く頃に仕様でございますです♪  起動して、真ん中のボタンを押してみると「あぼーん」なんてならないので、ど うぞどうぞお気楽に押してください。押したところで「ほかのメッセジーボック スを出すのでございますのよ!」との作者の頭の悪さが伺えるような言葉が出て きますです。書いてあるとおり、ここは分岐処理されていてバイナリを書き換え ることにより、他のメッセージボックスを出すことができます。このレポートの 目的はこれですよ!!  「なぜこんな簡単な物を[k]するのか?」と聞かれれば、「そこに.NETがあるか ら」と答えるしかないじゃあ〜りませんか!!普通にアンマネージドコードだっ たら、美咲ちゃんを読めば即解決ばいばいきーんになるんですけど、奥さん.NET ですよ!!.NET!! どっとねっと て 響きが(・∀・)イイ。  そんじゃ早速始めましょ!!  前回使ったReflectorつかって、さっきDLした「くらっくみ〜☆」を開いてくだ さいまし。使い方は前回書いてあるのでそれを参照にしてね♪ さいどちゃんか らのお願いだよ♪ きんもー☆  気を取り直していきましょ〜。  読み込んで、クリックした後の処理を追っていくために button1_Clickのとこ ろまでたどり着けたかな?  無事たどり着くと、次のようになります。 ----- private void button1_Click(object sender, EventArgs e) { if (true) { MessageBox.Show("ほかのメッセジーボックスを出すのでございますのよ!"); } else { MessageBox.Show("せいかいですわ!"); } } /*メッセージボックスのメッセージは \u305b\u3044\u304b みたいに書かれているけど  読みにくいから、直しちゃいました てへ☆ */ -----  Reflectorの上の方に、コンボボックス。あえて、言葉で説明するなら「うえか らび〜〜ってなって選べるやつ」。余談なんですが、私は擬音語・擬声語・擬態 語のマスターなんだそうですよ?  そこから「IL」ってのを選んでくださいまし。  以下、C#にかわりまして MSILがお送りします(※4)。 ----- .method private hidebysig instance void button1_Click(object sender, [mscorlib]System.EventArgs e) cil managed { .maxstack 1 .locals init ( [0] bool flag1) L_0000: ldc.i4.1 L_0001: stloc.0 L_0002: ldloc.0 L_0003: brfalse.s L_0011 L_0005: ldstr "ほかのメッセジーボックスを出すのでございますのよ!" L_000a: call [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string) L_000f: pop L_0010: ret L_0011: ldstr "せいかいですわ" L_0016: call [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string) L_001b: pop L_001c: ret } -----  どっかに書いてありましたけど、ILはアセンブラより簡単らしいですよ?  今回は資料として以下のサイトを参考にさせてもらっています。 http://www.atelier-blue.com/program/il/index.htm http://d.xenowire.net/mog/ http://dotnet.di.unipi.it/EcmaSpec/PartitionIII/  では、ひとつずつ見ていきましょうか('A`)マンドクセ。 ○PUSHとPOP  PUSH、POPという言葉が出てくるのでお父さんがんばって補足しちゃうぞ!!  PUSHとはスタックに数値・文字などを置く操作、POPとはスタックから数値・文 字引き出す操作。当然疑問に思うスタックの説明をするんだが、言葉で説明して もややこしい。。要するにスタックとは、本を積み上げるような構造のようなこ となんですね。具体例を示すと次のようになります。 ----- PUSH ゲームラボ スタックの内容{ゲームラボ} PUSH 広辞苑 スタックの内容{広辞苑,ゲームラボ} POP       スタックの内容{ゲームラボ} PUSH ハッカーの教科書 スタックの内容{ハッカーの教科書,ゲームラボ} POP       スタックの内容{ゲームラボ} POP       スタックの内容{} ----- ○.maxstack 1  maxstackとはその関数で使用されるスタックの最大使用量らしいので、この場 合スタックの最大使用量は1らしい。 ○.locals init ( [0] bool flag1)  .localsはローカル変数の宣言。initというのは自動で初期化してくれる機能ら しい。bool flag1はローカルなブーリアンを宣言でちゅ。[0]とはローカル変数の 番号らしいです。 ※stloc./ldloc.の後に来る数字はローカル変数への番号なのですよ〜(たぶん。 ○L_0000: ldc.i4.1  スタックにin32型の1をpush。バイナリコードは0x17。 ○L_0001: stloc.0  スタックから、ローカル変数の0番へ値を移動。この場合のローカル変数の0番 とはbool flag1なのですよ。バイナリコードは0x0a。 ○L_0002: ldloc.0  ローカル変数の0番から、スタックへ値をプッシュ。ローカル変数の0番の数字 は1なので1がスタックにpush!push。バイナリコードは0x06。 ○L_0003: brfalse.s L_0011  brfalse.s L_0011 popした値がfalseならLABELへ移動。0はfalse。バイナリ コードは0x2c。 ○L_0005: ldstr "ほかのメッセジーボックスを出すのでございますのよ!"  ldstr "文字列"で文字列をスタックにpushする。バイナリコードは0x72。 ○L_000a: call [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)  関数の呼び出しなのですぅ。バイナリコードは0x28。 ○L_000f: pop  スタックの先頭の要素を削除するのだ〜。バイナリコードは0x26。 ○L_0010: ret  retは「return from method 」とのことですので、button1_Clickのところまで 戻されるんだって〜。バイナリコードは0x2a。 ○L_0011: ldstr "せいかいですわ!"  バイナリコードは0x72。 ○L_0016: call [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)  関数の呼び出しなのですぅ。バイナリコードは0x28。 ○L_001b: pop  スタックの先頭の要素を削除するのだ〜。バイナリコードは0x26。 ○L_001c: ret  retは「return from method」とのことですので、button1_Clickのところまで 戻されるんだって〜。バイナリコードは0x2a。  これじゃあ、非常にわかりづらいのでスタックの変化とどんな流れでいくのか を書いていきますよ〜。 #1 L_0000:スタックに1をpushする スタック[1] | #2 L_0001:スタックから、ローカル変数の0番へ値を移動 スタック[ ] flag1 == 1 | #3 L_0002:ローカル変数の0番から、スタックへ値をプッシュ スタック[1] flag1 == Null? | #4 L_0003:popした値がfalseならL_0011へ移動。0はfalse。 スタック[] スタックの値が1なので下に移動 | #5 L_0005:スタックに文字列をpush スタック[文字列] | #6 L_000a:メッセージボックス関数を呼び出して表示 | #7 L_000f:スタックの先頭の要素を削除 スタック[] | #8 L_0010:    おしまい  わかりましたかご主人様? わかりやすく説明したおつもりなのですが……。  well…このレポートの目的である、他のメッセージボックスを表示するために は分岐を無理矢理ねじ曲げる方法とスタックの数字を変える、2つの方法があるの ですが、どっちがイイでしょうかね? どちらかと言うと、新スクより旧スクの ほうがよい様な気がしますが、参考までにどっちも書いちゃいませう!! ■0x05.) 1-branch ranch(※5)  ネタがそろそろ尽きてきて、テンションも下がって参りました。タイトルの通 り分岐の書き換えです。方法は簡単「falseがダメならtrueにすればいいじゃない 」(※6)。これにぴったりの命令があるんですね〜。世の中便利になりました。  brtrue.s targetとは「branch to target if value is non-zero (true), shor t form」。バイナリコードは0x2D。  英語って見るとアレルギー起こしませんか? そうですか…。訳すと「もし0じ ゃなかったら指定された場所(target)に島流し」。.sはたぶんスタックのこと だと思われますです。  次に、バイナリえぢたでファイル開いて、分岐箇所を特定します ----- L_0000: ldc.i4.1 バイナリコード--0x17 L_0001: stloc.0 バイナリコード--0x0a L_0002: ldloc.0 バイナリコード--0x06 L_0003: brfalse.s L_0011 バイナリコード--0x2c -----  となっている箇所を探すとADDRESS[106C---106F]のところにあります。バイナ リえぢたの使い方がわからないとかいっている人は('A`)シラネ。最後の2cの箇所を 2dに書き換えてしまうと、あら不思議!!なんと他のメッセージボックスが表示 されます。ぱちぱち。 ■0x06.) 魔砲「ファイナルマスタースタック」(※7)  これも簡単スタックに1をpushするldc.i4.1を0をpushするldc.i4.0にすれば解 決。ldc.i4.1をldc.i4.0(バイナリコードは0x16)にするだけでおわり〜。 ■0x07.) 見果てぬネタを掴むさっちんアーム(※8)  本当にネタばっかですんません…。ココまで読んでいると言うことは、痛々し いネタ・文章・言動の数々をくぐり抜けてきた猛者でしょう。元々こんなつもり ではなかったのですが、なんだかそのとき超ハイテンションだったので勢いで書 いてしまいました。まあなんだかんだで、内容はちゃんとした内容になっている と思います。でも、途中でILの資料を読みあさったりして悩んだりしていたらテ ンション下がって最後の方は文体がぐだぐだに〜。やっぱり文体を保つためには、 テンションが一定水準になっているときに書いたほうがよいですね〜。内容の話 しになりますけれども今回は、今まで誰も扱わなかったILを扱ってみました。ア センブラはバリバリできるぜ ばっちこーい!!って人は多いのですが、IL(゜Д゜)ウマー ってな人なかなかいないんですよね。書いている最中、日本語での資料の少なさ に本当に苦労しましたよ…。参考にした2つのサイトにホント助けられました。結 局は英語の資料も参考にしましたけど…。でも何も知らなかったILについて、し れたことは大きな利益利益♪ IL用のデバッガ出ないのですかね?Ollyライクな やつ。スタックの内容表示とBPとバイナリ書き換えさえしてくれれば文句はない です。  目指せILM@STER!!(※9) ■0x08.) ネタ補足 (※1)AIRの観鈴ちんの名言。 (※2)にったじゅん氏の「マジ童貞!?」の名言。 (※3)カラフルキッス〜12コの胸キュン!〜の主題歌。 (※4)VIPでの名無しの名前。 (※5)Orange Rangeをもじった(branchには分岐の意味がある)。 (※6)マリーアントワネットがほざいた血も涙もないような迷言。 (※7)東方永夜抄で霧雨 魔理沙が使うスペルカード:魔砲「ファイナルマスタ ースパーク」。 (※8)Melty Blood Act Cadenzaにでてくる弓塚さつきが使う技「見果てぬユメ を掴むさっちんアーム」さっちんかわいいです。 (※9)IDOLM@STERをもじった。  他にもネタが書いてあります。 x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x --- 第4章: Rapid Development of Packer Vol.1 --- 著者:suma x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ■0x01.) はじめに  WindowsのPEフォーマット向けのパッカーの作成について、ソースコードを追い ながら説明します。言語にはC/C++とインラインアセンブラを組み合わせて使い、 圧縮アルゴリズムにC言語ライブラリのzlibを使用します。また、DLL・VBなどの パックには対応しません。Windows XP SP2(32ビット版)の環境で動作を確認し ており、コンパイラにVisual Studio .NET 2003を使用します。 ■0x02.) Rapid Development  「素早いパッカーの開発」について念頭におきます。まず、私が使い慣れてい るという理由で言語にC/C++を使い、アセンブリ言語の使用を控えました。そして、 ライセンス・C言語で呼び出せることかあら圧縮ライブラリにはzlibを採用しまし た。 ■0x03.) パッカーの流れ  パッカーという名前を聞くとひとつのプログラムのように思えますが、パッカ ー本体とローダーのふたつの独立プログラムからできています。おおまかに次の ような流れです。 ●パッカー 1:ターゲットの実行ファイルを開く 2:パックする 3:ローダーを付加する 4:保存する ●パックされた実行ファイルの動作 1:ローダーが実行される 2:ローダーがメモリ上にアンパックする 3:ローダーから元のプログラムへ実行が移る ■0x04.) 何をパックするのか  実行ファイルに含まれるデータをパックします。今回はコードのみパックしま す。必要に応じてデータセクション、インポートセクション等をパックしてもか まいません。以下に主なセクションを紹介します。これらすべてが実行ファイル に含まれているとは限りません。 ・コードセクション:実行されるコード ・データセクション:参照される文字列・初期化された値など ・リソースセクション:アイコン・メニュー・画像・バージョン情報などのリソ ースデータ ・インポートセクション:インポートするDLL・関数名の情報 ・エクスポートセクション:エクスポートする関数の情報 ■0x05.) フォーマットとヘッダー  PEフォーマットとはWindows用の実行ファイルのフォーマットで、Windowsの実 行ファイルのほとんどがこのフォーマットです。ヘッダーはファイル先頭に含ま れる、そのファイルの情報を示すものです。PEフォーマットに準拠した実行ファ イルの場合、先頭にDOSヘッダー、次にPEヘッダーという順でヘッダーが格納され ています。実行ファイルのセクションを参照したり、操作する時にはPEヘッダー を参照します。ヘッダーをテスト用の自分だけのプログラムとしてなら問題ない かもしれませんが、オフセットアドレスが読み込んだファイルサイズを超えない ようチェックしたり、数値のオーバーフロー・アンダーフローに気をつけてくだ さい。  PEフォーマットについては以下のサイトを参考にするとよいでしょう。また、 解説を読まずにパッカーのソースコードを直接読んで理解してもよいかもしれま せん。 ・マシン語大研究 http://hp.vector.co.jp/authors/VA015412/ ・WinAPI Database for VB Programmer http://www.winapi-database.com/special/2001_10_11/index.html ■0x06.) パッカーの作成と実装  パッカー、ローダーは個別のプロジェクトとして作成しました。ふたつが独立 したプログラムであることと、後述するローダー作成のためです。そして、これ らふたつのプロジェクトをひとつのソリューションでまとめます。また、プロジ ェクトの中間ファイルの生成ディレクトリはそれぞれ分別し、実行ファイルの生 成ディレクトリは同じものを指定します。これはパッカー・ローダーの動作を確 認する際に役立ちます。  作成したパッカーは次のファイルにまとめています。 http://wizardbible.org/file/suma/rdp/pack_20060430.zip ■0x07.) パッカーの作成  パッカー本体のプログラムの作成はさほど難しくありません。しかし、実行フ ァイルのヘッダーを参照して操作するという細かい処理が非常に多くあります。 そこでパッカー本体の処理が膨れ上がらないよう、PEヘッダーを操作する部分は ライブラリとして作成しました。ライブラリとパッカー本体はpackフォルダ内に あります。 ■0x08.) ローダーの作成  パッカーとローダーは独立したプログラムです。特にローダーは、パックする 実行ファイルに付け加えるものであるため、単独で実行可能である必要がありま す。それにもかかわらず、圧縮ライブラリにzlibを使用するため、単独での実行 は困難になります。  ローダーの単独での実行とは、コードだけで動く必要があるということです。 絶対アドレスを指定するような、グローバル変数は使えず、静的なデータ(文字 列・配列の初期値など)を含んだり、Win32APIの呼び出しも困難になります。こ れらすべては克服可能ですが、インラインアセンブラを使用する上、制限もあり、 それが労力に見合うかどうかはわかりません。  まず、グローバル変数の使用、静的なデータの使用ですが、これはコード中に データを埋め込むことが可能です。 ----- #include #define db _asm _emit void main() { _asm { jmp data_end data: db 'h' db 'o' db 'g' db 'e' db 0 data_end: push offset data call [printf] add esp, 4 } } -----  次に、Win32APIの呼び出しも不可能ではありません。これはWizard Bible vol .17でmuffinさんの記事「ウイルスプログラミングへの招待 〜インポートセクシ ョンを持たずにAPIを使う方法〜」で解説された原理を使います。  このようなテクニックを使うと、データを含めることは可能となります。しか し、zlibの内部では静的なデータを多用しているのでこの方法は使えません。そ こで、次のZloaderとFirstLoaderという名前のローダーあわせてふたつのローダ ーを作成するという手段を考えました。 ・ZLoader  コードセクション・データセクションなどをひとつのセクションにまとめ、ベ ースアドレスを指定して生成。 ・FirstLoader  ZLoaderのベースアドレスと同じアドレスに、VirtualAllocでメモリを確保して コピーし、実行させる。  結果的にはうまくいきましたが、メモリを確保するアドレスがいつも有効に使 えるという保証はありません。そこで、アドレスの再配置をすることで克服する ことができます。 ■0x09.) アドレスの再配置  DLLはいつも同じアドレスにロードされるとは限らないため、そのための再配置 セクションというセクションを持っています。アドレスを再配置によって異なる アドレスにロードされても、同じコード・データを参照することができる仕組み です。 ■0x0A.) アドレス・データの受け渡し  パックされたデータは、セクションとして実行ファイルに追加します。ローダ ー側からはこのデータのアドレスや、アンパック先のアドレスを知る必要があり ます。これを実現するために、ローダーの変数の初期化領域を上書きします。  アドレスの受け渡しに使う構造体をLoaderInfo.hで定義しています。ローダー のコード内ではグローバル変数としてLoaderInfoを使い、初期値を適当な値で初 期化しています。パッカー側から初期化した値を検索することで、どの部分を書 き換えるか見つけることができます。  または、生成したローダーの実行ファイルからせクションを抽出し、バイナリ エディタで初期化した値がセクションの先頭部分に存在することを確認してくだ さい。実装が簡単なため、今回はパッカーで「構造体の値はセクションの先頭に ある」ということを利用しました。注意しなければならないことは、これはコン パイラの実装上こうなっただけで、条件によってはこの方法が使えないことも考 えられます。 ----- ZLoader.c static LoaderInfo LInfo = { 0x44332211, 0x88776655 }; ----- ----- LoaderInfo.h typedef struct { DWORD_PTR DataAddress; DWORD DataLength; DWORD_PTR CodeAddress; DWORD CodeLength; DWORD_PTR CodeEntryPoint; } LoaderInfo; ----- ■0x0B.) 簡単なテストと、手間のかかるデバッグ方法  パッカーが正しく動作するように見えても、それが正しく動いているかは実際 に動かしてみないとわかりません。そこで、すぐに実行を確認できる環境を準備 しておきます。パッカーを作成しながら、Debugディレクトリに適当な実行ファイ ルをコピーしておき、そのプログラムをパック・実行して正しくパッカーが動作 するか確認します。  パッカー本体が何もなく動いても、ローダーの方に問題があればパックされた 実行ファイルは正しく動きません。パックされた実行ファイルをデバッガで地道 にトレースし、おかしな動作がないか確認します。 ■0x0C.) おわりに  プログラムを作る前には「素早いパッカーの開発」なんて考えていませんでし た。私がこのパッカーを作成して気付いたことは、実行ファイルの操作の処理が 複雑で、バグを誘発しすく、デバッグに手間がかかること、パッカー・ローダー を正しく作ることが難しいことでした。パッカー作成に慣れないことも原因だと 考えられますが、細かい処理が多く積み重なるパッカー作成で、素早くバグを発 見できる体勢を整えていなかったことが問題でした。次回ではこの素早いバグの 発見について考察、実行していきます。  プログラムの解説は少なく、「パッカー作成のためのチュートリアル」として は物足りなく感じるかもしれません。ソースコードの量は決して多くないので、 ぜひすべて読んでみてください。また、ソースコードを見ながらパッカー自作に も挑戦してみてください。 x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x --- 第5章: 基礎暗号学講座 〜 第1回 〜 --- 著者:IPUSIRON x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ■0x01.) はじめに  今年の4月から、暗号の研究室に配属されました。現在セミナーで修士論文を書 くために文献を調べたり、暗号に関する知識の底上げをしています。セミナーは 週に2回あり、そのうち1回は私の発表することになっています。1回あたり3時間 の発表を行い、『現代暗号の基礎数理』(コロナ社)を半年で終わらせて、その 後修士論文の研究テーマを進めるということになっています。一応すべての章は 個人的に終わらせましたが、1ヶ月ずっとこの本をメインにして学んできたので、 これをWBのネタにすることにしました。これでネタに困ることは当分ないようで す(1年間はこのシリーズを続けることができる)。この本をベースにして解説し ますが、よくわからない方は『暗号技術入門 秘密の国のアリス』をお勧めしま す。この本は万人にわかりやすいように書かれていますし、例も面白くてどんど ん読めると思います。  ちなみに、今月号のネタではあまり数学的記述がありませんが、段々と数学を 使うことになります。あまり難しい数学はやりませんが、最低限でも高校卒業レ ベルは習得しているという仮定で話をします。もしわからない概念が登場したら 自分で勉強してみてください。 ■0x02.) 古典暗号と現代暗号  スキュタレー暗号やシーザー暗号の原理はあまり興味がないので、どんどん飛 ばしていきたいところですが、古典暗号と現代暗号の違いはしっかり理解してお く必要があります。 ●古典暗号  古典暗号とは、次の特徴を持つ暗号の総称です。 ・暗号の作り方も鍵も秘密。 ・暗号の安全性を言語の統計的特徴を基に議論する。 ・暗号を使用する目的が主に軍事と外交。  例えば、換字式暗号、転置式暗号などがそうです。有名なスキュタレー暗号、 シーザー暗号などは古典暗号です。  古典暗号の暗号アルゴリズムを作るということは、一種の芸術的なものであり、 数学的な安全性評価というものがありません。 ●現代暗号  現代暗号とは、次の特徴を持つ暗号の総称です。 ・暗号アルゴリズムは公開され、鍵のみが秘密。  暗号・復号のアルゴリズムは公開されているので、誰でも知ることができ、そ のためプログラムとして実装することも可能。 ・暗号の安全性は計算量を基に議論する。 ・一般社会において民間・個人にも使われる。  例えば、DES暗号、AES暗号などの共通鍵暗号系、RSA暗号、ElGamal暗号などの 公開鍵暗号系が含まれます。そして、ゼロ知識型認証などの暗号プロトコルも含 まれます。  この講座では現在暗号のみについて取り上げていきます。 ■0x03.) 共通鍵暗号系  共通鍵暗号系とは、送信者が平文Mと鍵Kを入力として暗号化アルゴリズムによ り暗号文Cを出力します。その暗号文Cを通信路に通じて受信者の復号アルゴリズ ムに入力します。その際、鍵Kを用います。その結果、平文Mを出力します。共通 鍵暗号系という名前からわかると思いますが、暗号化アルゴリズムと復号アルゴ リズムで使われている鍵は両方とも共通するものです。あくまで暗号化アルゴリ ズムと復号アルゴリズムは一般公開されているものなので、アタッカーももちろ ん知っています。ということは、鍵Kがこの送信者と受信者の二人だけで秘密にし ておかなければならないのです。もし、アタッカーの手に鍵Kが渡ってしまった場 合、盗聴した暗号文Cから簡単に平文Mを得てしまうことができてしまうわけです。  ここで重要なのは、暗号化によって盗聴を防ぐわけではありません。盗聴され ても内容がわからないようにするわけです。  共通鍵暗号系は、ストリーム暗号とブロック暗号に大別されます。ストリーム 暗号とはその名の通りビットごとに処理し、ブロック暗号はビット列というブロ ック単位で処理します。 ■0x04.) XOR演算  集合{0,1}上に、演算+を次のように定義します。普通は○の中に+を書いた記 号を使いますが、テキスト表示では無理なので、+を代用することにします。 0+0=0 0+1=1 1+0=1 1+1=0  これは、mod 2の世界の足し算と思えばよいでしょう。 0+0≡0 (mod 2) 0+1≡1 (mod 2) 1+0≡1 (mod 2) 1+1≡0 (mod 2)  デジタル回路の世界では排他的論理和(XOR演算)として登場します。この講座 では今後XOR演算と呼ぶことにします。  なぜこのような演算を定義したのかというと、共通鍵暗号系の仕組みのおいて XOR演算が利用されているからです。  2つのnビット列(a1,…,an)、(b1,…,bn)(∈{0,1}^n)があったとします。この 2つのビット列に対して、XOR演算をすることを次のように定義します。 (a1,…,an)+(b1,…,bn)=(a1+b1,…,an+bn) ■0x04.) ストリーム暗号 [定義] 平文のビット列M=(b1,b2,…)を、鍵のビット列K=(k1,k2,…)を用いて、 c1=b1+k1、c2=b2+k2、… と暗号化する暗号系をストリーム暗号という。 このとき、暗号文は(c1,c2,…)となる。 (図)http://akademeia.info/main/image8/stream.jpg ただし、iは1以上の整数。  ストリーム暗号の特徴として次が挙げられます。 ・シーザー暗号などの古典暗号、エニグマ暗号機を使う機械的暗号の多くが該当 する。 ・ストリーム通信などで使われることもある。 ・平文のサイズと暗号文のサイズが一致する。つまり、データのサイズが増えな い。 ・ソフトウェア実装だと効率が悪いが、ハードウェア実装だと回路規模が小さく 処理速度が高い。 ・ブロック暗号と比べてストリーム暗号の研究は遅れていて、安全性の評価手法 の研究には長い時間を要する。そのため、ブロック暗号ベースのストリーム暗号 (例えば後ほど解説するがCFBやOFBがそう)を利用したほうがよいという考えも ある。 ・疑似乱数列の安全性に根拠を置くことになり、暗号学的に安全な疑似乱数を使 用する必要がある。 ・送信側と受信側の間で鍵系列の同期の取り方により、同期式ストリーム暗号と 自己同期式ストリーム暗号に分類できる。 [定理] Kが真にランダムならば、無限大の能力を有する敵に対しても安全である。  Kを真にランダムにするには、平文MごとにKを新しく選びなおさなければなりま せん。これを実現したものがバーナム暗号です。しかし、それでは効率が悪くな ります。特にMが長ければ長いほど困難になってきます。  そこで、疑似乱数生成器を用いて、短い鍵から長い疑似ランダムな鍵系列を生 成する方法が提案されています。 (図)http://akademeia.info/main/image8/stream2.jpg  疑似乱数生成器を使うときのランダム値をシード(種)といいます。  疑似乱数生成器はLFSR型と状態遷移型に大別されます。LFSR型には、線形フィ ードバックシフトレジスタ(Line Feedback Shift Register:LFSR)、非線形な FSR(Nonliner Feedback Shift Register:NFSR)があります。一方、状態遷移型 にはRC4やSEALがあります。  これらの名前は今覚えなくてもいいです。ここで重要なのは完全な乱数列を作 ることは困難なので、一部の乱数値を入力として疑似乱数生成器を利用すること で疑似乱数列を作られることが多いといことです。 ■0x05.) ブロック暗号  平文のビット列をブロック長と呼ばれるある一種の長さnごとに分割し、そのブ ロック単位で暗号化を行います。この長さnは多くの場合、n=64やn=128であるこ とが多いです。それはコンピュータが扱いやすい数にしたほうが便利だからです。 [定義] ブロック長と呼ばれる長さnビットの平文mをまとめて暗号化する共通鍵暗号系を ブロック暗号という。  暗号化アルゴリズムをE_K、復号アルゴリズムをD_Kと表すことにします。Kと いう添え字が付いているのは、Kを入力とされるということを明示するためです。  ストリーム暗号では同一の平文m_i(iは添え字)が続けて入力されたとしても、 鍵k_iが次々変化していくために、暗号文c_iは異なる値になります。しかし、ブ ロック暗号では、ストリーム暗号とは異なり、通常ある一定の期間同一の鍵が使 用されます。 ■0x06.) 敵のモデル  敵は何らかの方法で(平文,暗号文)の組(m1,c1),…,(m_q,c_q)を入手したとしま す。この情報をもとに、鍵Kを求める攻撃を考えます。  暗号攻撃にはあらゆるアプローチがあります。まずブルートフォースタック、 サイドチャネルアタック、ショートカット方式に分類できます。 ●ブルートフォースアタック ○ブルートフォースアタック(全数探索攻撃)  これは考えられる鍵をすべて総当り式に調べていくという方法です。このアタ ックはすべての暗号において用いることができます。これはアタックの方法の中 でも一番時間がかかるもので、そのため通常このアタックで解読されてしまう可 能性のあるものは暗号として使えません。 ○誕生日攻撃(バースデーアタック)=暗号文一致攻撃  バースデーパラドックスと呼ばれる「ランダムに23人を集めると誕生日が同じ 人間が2人以上いる確率が高い」という理論に基づいている攻撃方法です。一般化 すると、Nビットの出力がある場合は、2^(N/2)個程度の出力を集めると、その中 に同じデータが存在する可能性が高いという確率性をもとにして解析を進めます。 ○辞書攻撃(ディクショナリーアタック)  事前に、できる限り平文と暗号文のペアを管理しておいて、解析対象の文章と 一致するものを探し出す方法です。 ●サイドチャネルアタック  実装するハードウェアの特性を狙う方法です。個人的に大いに興味はあるが、 あまり数学的アプローチとは関係ないので、この講座では扱いません。よって、 軽く紹介するだけにします。 ○SPA攻撃(Simple Power Analysis attack:単純電力解析攻撃) ○DPA攻撃(Differential Power Analysis attack:電力差分解析)  SPA攻撃とDPA攻撃は、計算中に消費電力が増えることに注目したアタックです。 ○タイミング攻撃  計算している時間に注目したアタックです。 ○キャッシュ攻撃  自動的に鍵が変化していく暗号に対して、キャッシュメモリ内にデータにヒッ トしたかどうかで計算時間が変化することに注目したアタックです。 ○DFA攻撃(Differential Fault Analysis attack)  外部から強力な光(レーザー光)などを照射することにより、意図的にエラー を発生させて、それをもとに解読の手がかりを得るアタックです。 ●ショートカット方式  数学的に解を求める方式です。ここがこの講座のメインとなるところです。 ○暗号文攻撃  敵が知っているのは暗号文のみで、その暗号文からそれに対応する平文あるい は鍵を解読しようとする方法です。 ○既知平文攻撃  敵に平文と暗号文のペアである(m1,c1)、…、(m_q,c_q)がランダムに与えられ、 そこから鍵Kを解読する方法です。 (図)http://akademeia.info/main/image8/attacker1.jpg ○選択平文攻撃  敵は平文m1,…,m_qを自由に選び、それに対応する暗号文を入手できるときに、 鍵Kを解読する方法です。 (図)http://akademeia.info/main/image8/attacker2.jpg ○選択暗号文攻撃  敵は選択平文攻撃に加えて、暗号文c1,…,c_qを自由に選び、それに対応する平 文を入手することができます。そういった状況で鍵Kを解読する方法です。 (図)http://akademeia.info/main/image8/attacker3.jpg  例として、すべてが1や0の暗号文を想定して、それを復号するとどうなるのか を調べることができるので、敵の解読能力はとても高いといえる。  ショートカット方式の場合は下の解読法のほうが敵に与えられる情報量が多く なります。つまり、Kを解読できる可能性も高くなるわけです。  ちなみに、暗号がある程度わかっているひとのために一応述べておきます。本 当は適応的かどうかも考慮しなければなりませんが、ここではとりあえずそれは 考えずに上記のことだけを理解しておいてください。 ■0x07.) ブロック暗号の代表的な解読法  ブロック暗号の代表的な解読法として差分解読法、線形解読法などがあります。 内容をきちんと説明すると膨大になってしまうの軽く紹介だけしておきます。ち なみに、内容を理解できなくても、今後の講義には何の影響もないので安心して ください。単にこういった解読法があると知っておくだけで十分です。 ●差分解読法  これは選択平文攻撃の一種です。1989年にイスラエルのEli BihamとAdi Shamir によって発見されました。対応範囲が広く、多くのブロック暗号に有効です。NT TのFEAL暗号もこれで解読されました。  暗号鍵の全数探索法より高速な初めての解読法です。  DESの暗号アルゴリズムは公開されていましたが、「なぜそのような計算をする のか?」ということについて何も説明はありませんでした。後にIBMから1979年に DESが発表されるより前の1974年の時点で、すでに差分解読法があることに気付い ており、その対策を施していたという逸話もあります。  解読者の目標は、うまく選んだ平文と暗号文のペア、および暗号化アルゴリズ ムについての完全な情報のもとで鍵Kを特定することです。または、現実的には鍵 Kの可能性を、計算機で総当りに調べることが可能になる程度まで絞り込めればよ いわけです。  暗号化アルゴリズムがよほど簡単でない限り、ペアをたくさん集めても平文と 暗号文の相関はなかなか得ることはできません。しかし、平文を少し変化させた ときに、その暗号文がどのくらい変化するのかを見てみたらどうなるだろうか?  このアプローチが差分解読法です。  まず2つのペア(m0,c0),(m1,c1)があったとします。このときペアの選び方は特 別の違いがあるようにします。すると、m0+m1=δ、c0+c1=δ'を求めることができ る。このδ(∈{0,1}^n)は差分です。この差分が相関関係を表します。分布に偏 りがあれば、それを利用して鍵Kを求めます。つまり、「c1+c2=E_K(m1)+E_K(m2) 〜m1+m2」において、関係「〜」に何かあるかを調べるわけです。こおような関係 があるのは、統計的に確率は小さいかも知れません。しかし、期待できる理由は あります。ブロック暗号の中では、鍵(の一部)が平文(の一部)とXOR演算を取 る操作が含まれることが多いからです。しかも、あらわに含まないとしても、結 果的に暗号化アルゴリズムのある部分がそのような操作に対応してしまっている かもしれません。もしそうなら、暗号文2つの差分を取ることで、鍵の情報(の一 部)がXOR演算を2回取ることによって、キャンセルされて消えてしまうのです。 これはXOR演算の表を見てみればわかります。  同じものをXOR演算するわけなので、「0+0」または「1+1」のどちらかです。ど ちらの場合も結果は0になるわけなので、何かに0をXOR演算しても結果は変わりま せん。軽くまとめておきます。 ・0+0=0 ・1+1=1 ・A+0=A ・XOR演算は並びに依存しない。普通の加法+のように扱える。  入力X,Y、鍵の部分Kとします。 (X+K)+(Y+K)=(X+Y)+(K+K)=(X+Y)+0=X+Y  結果として、X+Yは鍵Kの影響を受けてないことがわかります。よって、入力差 分がある値になるように平文たちを選択して、このキャンセルの原理を利用して 鍵空間を狭めることができて、対応する暗号文たちをもとにして鍵を推定するの です。実際、DES暗号の場合は、ラウンドを通り抜けるにつれて、違いがどう発展 するかを分析します。 ●線形解読法  平文と暗号文の組(m,c)と鍵Kの間の何らかの線形関係式を考えます。ここで、 鍵Kを固定したとき、その線形関係式を成立させる(m,c)の個数がある程度以上に 多い場合、その偏りの事実を利用して鍵を求める解読法です。  1994年に12台のワークステーションを利用して、線形解読法を利用して50日間 でDES暗号を解析しました。これが世界初のDES暗号の解読となりました。DESを設 計したIBMの研究者は差分解読法は想定していたが、線形解読法は予想外であった といいます。 ■0x08.) 終わりに  今回の講義は基本中の基本なので特に難しいところはなかったと思います。も し何かわからないことがありましたら、質問掲示板の質問スレに投稿お願いしま す。私の知ってる範囲で答えられるかもしれません。 http://ruffnex.oc.to/ipusiron/cgi/forum/patio.cgi?mode=view&no=427  次回はまず最初に情報理論的に安全ということを定義し、ランダム置換やラン ダム関数を解説します。そして、ブロック暗号の各モードを紹介しつつ、それら で使われる暗号化アルゴリズムをランダム置換やランダム関数にしたときに情報 理論的に安全かどうかを数学的にきちんと考えていきます。 x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x --- 第6章:お知らせ --- x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ○Wizard Bible(http://akademeia.info/wizardbible/)では随時、執筆ライタ ーを募集しています。  扱う内容のテーマは広義での「under ground」です。例えば、ハッキングから サリンガスの合成法などと幅広い内容を考えています。また、各種、特殊な職業 や趣味を持った方のレクチャーなども含まれます。  一回きりでも構いません。また、必ず、毎回連載する義務もありませんのでで きる範囲で構いません。気軽に声をかけてください。もちろん一回書いたことが ある人も気軽に声をかけてください(全く気にしていない性格なので)。 ○Kenji AikoさんがQ&Aを作ってくれました。初めて参加する人でもわかりやすく 書かれていますので、参考にしてください。 http://akademeia.info/wizardbible/wbQandA.html ○支援者、参加希望者用のスレッドを立てました。 http://ruffnex.oc.to/ipusiron/cgi/forum/patio.cgi?mode=view&no=17 x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ---- 第7章:著者プロフィール --- x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x x0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0xx0xXx0x ■suma ●Job: Student ●Web: http://beautiful.homelinux.net/~sky-software/ ●Mail: shu_2001jp(at)yahoo.co.jp ●Team(Group): secret ●Comment:  プログラミングしたい、特にバイナリエディタが作りたい、ネットワークプロ グラミングをやってみたいと最近嘆いています。 ●OSについて:  Windows 98を2000〜2003年頃に使い、それ以降はWindows XP、最近になってWi ndows 2000 Proを併用しています。2004年からはLinuxもたまに使いつつ、結局は Windowsに大きく偏っています。普段使用したり、プログラミングに使うのがWin dows、サーバーとして使うのがLinuxになっています。クライアントPCやプログラ ミング環境としてLinuxも使ってみたいとは思っていますが、いかんせんPCのスペ ックなどが低く…。 ■Kenji Aiko ●Job: Student ●Web: http://ruffnex.oc.to/kenji/ ●Mail: kenji@ruffnex.oc.to ●Team(Group): N/A ●Comment:  おかげさまで、『ハッカー・プログラム大全 攻撃編』が発売されました。技 術的な話題を多数扱っているので、興味があればぜひ読んでみてください。 ●OSについて:  使ったことがあるものは、Windows 95/98/XP/2003、Vine Linux、Fedora Coreで す。基本的にWindowsをメインに使用して、ネットワーク関連のプログラムを組む ときにLinuxをよく使います。好き嫌いはあまりないので、環境さえあればどんな OSでも使いたいです。 ■右サイド ●Job:Student ●Web:右サイド(http://www3.pf-x.net/~right-hand-side/) ●Mail:right-hand-side@mail3.pf-x.net ●Team(Group):N/A ●Comment:  ぶっちゃけWB書いてよい身分じゃないのですが、書きたかったんだもん……。 もし、一年後にNEETになっていたら誰か拾ってお世話をしてくださいなんじゃ!! お元気で!! ●OSについて:  OSってOperation Sanctuary(はにはに)のことですか?(毎度毎度すまない) 2日間ぐらいこのテーマについて考えたけど結局何もよいことが思い浮かばなかっ たり…。未来のOSについて、妄想だらだら書いてもよいのだろうけど、考えもま とまらず…。  せっかくの毎回楽しみにしているテーマを考えることが…(´・ω・`)ショボーン ■IPUSIRON ●Job:student ●Web:http://akademeia.info/ ●Mail:ipusiron@ruffnex.oc.to ●Team(Group):ruffnex ●Comment:  毎日が今までにないぐらい忙しい日々ですが、非常に充実しています。とりあ えず修士論文を完成するまで、全力で走りたいと思います。 ●OSについて:  以前、良書・悪書の違いに関しても述べたと思いますが、どのOSが最高という 考えはないと思います。あくまで使用者や使い方に依存することであって、自分 が気に入ったならどのOSでもよいのです。そのためには数多くのOSを経験するこ とは悪いことではないと思います。「あれかこれか」ではなく「あれもこれも」 という考えで接していけば、可能な限り客観的に見れることでしょう。  なお、私はWindows 2000をメインに使っています。