例えば200ミリリットル入コップに500ミリリットルの水を全て入れるような場合,300ミリリットルはこぼれてしまう.その結果机がぐちゃぐちゃになる.
コンピュータの世界でも同じことが起こる.バッファとして用意したメモリ領域に,バッファサイズ以上のデータを押し込めることで周りに影響を及ぼす.
+--------------+ | x 200byte |←input 500byte +--------------+
char cup[200];
int i;
for(i=1; i<=500; i++)
cup[i] = 1;
あふれたデータは隣接するメモリ領域に上書きされる.
+--cup[200]-+--------------- | 111...111 | 1111... +-----------+--------------- 0xffff010 0xffff
上書きはメモリの低位から高位へ行われる.
int main(int argc, char *argv[]){
char str1[7] = "Hello!";
char str2[5];
strcpy(str2, str1);
return 0;
}
int main(int argc, char *argv[]){
char str1[7] = "Hello!";
char str2[5];
//このif文でサイズチェックを行う
if(sizeof(str1) <= sizeof(str2)){
strcpy(str2, str1);
}
return 0;
}
文字列関数とバッファの関係をうまく応用すると,本来書き換えられると困る変数も書き換えられる.例としてゲームのスコアを書き換える.
#include <stdio.h>
int main(int argc, char *argv[]){
int starge;
int score;
char name[12];
starge = 1;
score = 0;
//game routine
printf("Please input your name.\n");
gets(name);
printf("Score of %s is %d!!\n", name, score);
return 0;
}
関数は内部で使う変数を持っている.main関数も関数の一種として扱う.なんらかの方法で内部で使う変数を確保しないといけない.関数呼び出しの時はスタックを使う.
まず,ソースで一番初めに出てくる変数stargeを確保,次に2番目に出てくるscoreを確保,最後にnameを12文字分確保.スタックはメモリ高位から低位に伸びるので次のようになる.
+----------------+0x00001 | | +----------------+0x00002 | | : : +----------------+0x00078 | name[12] | +----------------+0x00090 | score | +----------------+0x00094 | starge | +----------------+0x00098
後はこの関数内で,stargeに4を入れる場合には0x00094番地に4を入れることになる.nameの場合は1文字目は0x00078に,2文字目は0x00079にいれられる.
gets()などの文字列関数は配列の先頭からアドレスを1づつ加算して,そのアドレスに文字を入れる.つまり,文字を入れてるアドレスが配列かどうかは関知しない.13文字目は0x00078に格納されることになるが,このアドレスはscore用に確保されている.これを無理やり書き換えてしまうことになる.
ユーザ登録のところで、ユーザ名として「aaaaaaaaaaaaaaaaa」とaを17文字入力した時にどのような出力結果が得られるか考えよ.また,その時のメモリ内はそのようになっているか,ラダーモデルを用いて考えよ.