2008年7月10日木曜日

世界一汚いCコードを読み解く

昨日のエントリ「世界一汚いCコード」を読み解いてみる。

まずは原型。
printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);
これの結果がunixになるのは何故なのか、少しずつ文を読み解いていく。

実はCでは#define unix 1 となっているので
printf(&1["\021%six\012\0"],1["have"]+"fun"-0x60);
配列を扱う書き方においては、x[1] = *(x+1) = *(1+x) = 1[x] なので
printf(&*("\021%six\012\0"+1),*("have"+1)+"fun"-0x60);
&* は意味が無いので
printf(("\021%six\012\0"+1),*("have"+1)+"fun"-0x60);
"\021%six\012\0"の1つ目の\0はNULL文字かと思ってしまうが、Cでは数値の先頭に0を付けると8進数を表すので、021の部分は8進数21という数値であり\021で1文字と見なされる。
ダブルクウォートで括った文字列"string”はstringという文字列の先頭ポインタとなる。
("\021%six\012\0"+1)ならば+1で先頭の位置が一文字ずれて、"%six\012\0"という文字列の先頭ポインタとなる。
ただし、*("have"+1)は*が付いているので、文字列の2文字目の値自体を指す。
printf(("%six\012\0"),'a'+"fun"-0x60);
'a'のASCIIコードは0x61であり、0x60を引くと1となるので、"fun"+'a'-0x60は"fun"+1となる。
さらに先の"\021%six\012\0"+1→"21%six\012\0"と同様に、"un"になる。
printf(("%six\012\0"),"un");
NULL文字(\0)は文字列の末尾を示すので、"%six\012\0"は実質"%six"
printf(("%six"),"un");
%sは第二引数の値と入れ替わるので、
printf("unix");


汚いっていうか、如何に読みづらくしているか、っていうコンテストじゃねコレ?

0 件のコメント: