stdoutとstderr、ヒープ領域等について

 日常で気になったことを調べ、結果をまとめる。今回のは殆ど講義で聞いた気になる単語を挙げた。

stdout, stderrとは

 C言語あたりでよく聞くstdoutとstderrであるが、講義で聞いて調べてみようと思い立った。fprint()する理由も含めて。
 まず、fprintf(*, *, ...)とは、第1引数にファイルポインタで書き込む場所を、第2引数に書き込む内容を、第3引数以降は書き込む変数の内容を指定する。要は、fprintf()はprintf()のファイル出力版である。それで、stdoutとstderrはfprintf()の第1引数として指定される。つまり、出力する場所を抽象的に指定する変数のようなものであると言える。抽象的に、と言った理由は後記。それぞれどんな機能を持つか分けてまとめる。

  • stdout

 まず、プログラム実行時に結果出力を書き込むファイルがリダイレクトされている場合、stdoutで場所を指定するとそのファイルにfprintf()で指定した内容を書き込む。それに対し、プログラム実行時に結果出力を書き込むファイルがリダイレクトされていない場合は、fprintf()で指定した内容をターミナルに出力する。

  • stderr

 fprintf()にstderrが指定されていた場合は、リダイレクトの有無によらず、内容をターミナルに出力する。

 stdoutを使うことで、出力先を勝手に場合分けして選んでくれる。また、エラーメッセージを表示させる場合はstderrを使うとコードが統一されデバッグがしやすい。

メモリ上のヒープ領域

 データ構造としてのヒープは知っていたが、講義でメモリ上のスタックに対比する概念としてヒープという言葉が出来てたため、調べてみる。
 メモリ空間内で、アプリケーションプログラムから使用可能な領域はスタック領域とヒープ領域に分けられる。スタック領域は、プログラム内のパラメータや関数、処理に従ってメモリ空間の底から積まれてゆく主要な作業領域である。それに対し、ヒープ領域とはメモリの先頭から底へ向かって拡張される領域で、動的確保が可能である。いわゆるmallocにより確保する領域がヒープ領域であるということだ。mallocをしたら必ず必要なことは何だろうか。それはメモリの開放、freeである。動的に確保し、使い終わったメモリ領域は開放しなければ使用可能なメモリ空間がどんどん減ってゆく。これがメモリリークである。
 では、なぜこんな面倒くさい領域を使うのか。それは、大きな領域を確保できるからである。スタックで確保できる領域はあまり大きくないため、大きいデータを扱う時にはヒープ領域から確保しなければならない。また、開放しなければ残ってしまう、という事実を言い換えると、開放さえしなければデータがずっと残っていてくれるのである。普通、関数内で定義された変数はその関数内でしか扱えないが、動的に確保したヒープ領域は、開放するまで何度でも使える。
 ヒープ領域に関してもう一つ問題がある。メモリ領域の動的確保を何度も繰り返し、バラバラに開放していくと未使用の領域がバラバラに細かく断片化し、実質的に使えるメモリ領域が少なくなる。これをフラグメンテーションという。より多くの領域を使えるようにするには、この断片化された未使用領域(ガベージ)を集めて大きな未使用領域としてまとめることが必要で、Javaなどの処理系はその機能(ガベージコレクション)が優れているそうだ。

シンボリックリンクとは

 これも講義で出てきた言葉で、聞いたことがなかったので調べてみようと思った。
 シンボリックリンクとはUNIX系OSで使われる機能で、あるファイルやディレクトリに別の名前を指定し、その名前を元のファイルと同様に扱えるようにする仕組みである。見た目的には、Windowsでいう「ショートカット」に似ているが、これは元ファイルと同様に扱うことが出来ないため別にものであるそうだ。
 「ln」コマンドでシンボリックリンクを作成でき、ファイルシステム上にリンク情報が格納された0バイトのファイルが作成されるそうだ。

関数内で宣言される関数

リスト3 シェルコードとそれを実行するプログラム

  1 #include <stdio.h>
  2
  3 int main()
  4 {
  5     void (*shellfunc)();
  6     const char shellcode[] = {
  7         0x31, 0xc0, 0x31, 0xd2, 0xeb, 0x11, 0x5b, 0x88, ← シェルコードと
  8         0x43, 0x07, 0x89, 0x5b, 0x08, 0x89, 0x43, 0x0c,    呼ばれるマシン語
  9         0x8d, 0x4b, 0x08, 0xb0, 0x0b, 0xcd, 0x80, 0xe8,
 10         0xea, 0xff, 0xff, 0xff, 0x2f, 0x62, 0x69, 0x6e,
 11         0x2f, 0x73, 0x68, 0x00
 12     };
 13
 14     shellfunc = (void(*)())shellcode;   ← シェルコードの先頭アドレスに
 15     shellfunc();                        ← 実行を移す
 16 }
6-1. バッファオーバーラン その1「こうして起こる」

 このソースコードは、講義の際、バッファオーバーフロー攻撃でどのように不正な命令を実行させるかの例を説明する時に使われたものである。講義中は仕組みがよく分からなかったため、ここで見直してこのソースコードの解釈に関して気づいたことを述べる。
 4行目は、引数の無いvoid形関数へのポインタとしてshellfuncが定義されている。その後、5行目から「シェルコマンドをマシン語で表した文字列」が格納された配列shellcodeが定義されている。14行目では、shellcodeの先頭アドレスを、引数の無いvoid形関数へのポインタにキャストしてshellfuncに代入している。そして、15行目ではshellfuncを実行している。本来ならshellfuncという関数は宣言されていない、と処理されるべきところであるが、実際にshellfuncに対応するアドレスが与えられてしまっているため、そのアドレスの内容を実行するしかなくなってしまう。
 こんなに上手い具合に4行目の宣言のような記述が可能であることが驚きで、よくこんな記述を思いつくなあと感心してしてしまう。

クラッキング技術の本

 講義で紹介された本。気になる。

Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際

Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際

fIsは何の略?

 API名にたまに"fIs"という文字が出てくるが、これは何の略であるのか、その由来を知りたくなった。調べても答えが見つからなかったため、このことについて何か知っている方には教えていただきたい。