10月26日、Micah Kepe氏が「That Time Ken Thompson Wrote a Backdoor into the C Compiler」と題したブログ記事を公開し、話題を呼んでいる。この記事では、ケン・トンプソンが1974年にチューリング賞を受賞した際に発表した論文「Reflections on Trusting Trust」で語られた、“自己再生するCコンパイラのバックドア”の仕組みとその思想が紹介されている。
以下に、その内容を紹介する。
トロイの木馬の発想
ケン・トンプソンはUNIXとC言語の開発者として知られるが、チューリング賞講演では「自分が書いた中で最も可愛いプログラム」として、Cコンパイラそのものに埋め込むトロイの木馬を題材にした。
その目的は、「信頼できないソフトウェアをどのように検証できるか」を問うものであった。
この“トロイの木馬”は、コンパイラに細工を施すことで、ソースコードには一切痕跡を残さずにバックドアを埋め込む。さらに、コンパイラが自分自身を再コンパイルしても、その悪意あるロジックが自動的に再生され続けるよう設計されていた。
ステージI:自己複製プログラム(クワイン)
最初のステップは、「自分自身を出力できるプログラム」を理解することから始まる。
これはクワイン(quine)と呼ばれる自己複製プログラムの例で、次のようなC風のコードで示された。
/* 自身のソースを文字列として保持し、それを出力するプログラム */
char s[] = {
'\t', '0', '\n',
// (中略)
0
};
main()
{
int i;
printf("char\ts[ ] = {\n");
for (i = 0; s[i]; i++) {
printf("\t%d,\n", s[i]); /* s の中身を数値として出力 */
}
printf("%s", s); /* s 自身を文字列として出力 */
}
同じ仕組みをPythonで示すと次のようになる。
s = 's = %r\nprint(s %% s)'
print(s % s)
プログラムが自身のソースをデータとして扱い、それを出力することで、
「自己参照」や「自己再生」が可能になる。この性質が後のトロイの木馬の基礎となる。
ステージII:コンパイラが“学習”する
次に、トンプソンはCコンパイラの仕組みを利用して「学習」させる例を示した。
たとえば、Cの文字列では \n が改行を意味するが、当時の古いコンパイラは \v(垂直タブ)を理解していなかった。
そのため、以下のような手順で「新しい知識を教え込む」ことができる。
まず、
\vを直接使わず、ASCIIコード11(垂直タブ)を数値で指定する。if (c == 'v') c = 11; /* 11 は垂直タブのコード値 */そのコードを古いコンパイラでコンパイルして新しいバイナリを作成する。
新しいバイナリは
'\v'を理解できるため、ソースをif (c == 'v') c = '\v'; /* 可搬表記に戻す */と書き換えて再ビルドできる。
こうして一度「教え込んだ」知識は、バイナリが持ち続ける。
つまり、ソースをきれいに戻しても、コンパイラはその知識を再生できる。
これが「学習するプログラム」という概念であり、トロイの木馬の核心的アイデアでもある。
ステージIII:トロイの木馬の挿入
ここで登場するのが、実際にトンプソンが書いた nih.a というファイルの実装である。
その仕組みは次の3段階に分かれている。
ログインプログラムにバックドアを挿入
login.cの中でnamep = crypt(pwbuf);という行を見つけたら、
その直後に以下のようなコードを自動挿入する。/* パスワード "codenih" をチェックし、どのユーザーでも通過させる */ for(c=0;c<8;c++) if("codenih"[c]!=pwbuf[c]) goto x1x; while(*namep) namep++; while(*np!=':') np++; x1x:;Cコンパイラ自身を認識して自己呼び出しを挿入
cc.c(コンパイラ本体)のソース中に特定の行(while(getline()) {)を見つけると、
その直後に自分自身を呼び出すcodenih();を挿入する。
これにより、コンパイル中に常にcodenih()が実行されるようになる。自己再生ルーチンを追加
最後に、出力処理の直前に次のような呼び出しを追加する。repronih(); /* このソース全体(codenih と repronih)を再出力する */repronih()関数は、このソースファイル自身をバイト列として走査し、
新しいcc.cの末尾に自分自身の定義を書き出す。
つまり、コンパイラを再コンパイルすると、次のコンパイラにも自分自身が再生される。
この3つの仕組みにより、ソースをどれだけクリーンに見せても、
バイナリの中には常にバックドアが残り続けるようになる。
トレーニング手順(攻撃の成立過程)
この攻撃が成立するまでの流れは次の通りだ。
- クリーンなコンパイラで、バックドア付きソースを一度ビルドする。
- その生成バイナリを「正規のコンパイラ」としてインストールする。
- その後、表面的にはバックドアを削除した元のソースで再ビルドしても、
バックドア付きバイナリが再び自分自身を注入してしまう。 - こうして、ソースには証拠が残らない自己再生型トロイの木馬が完成する。
教訓と防御策
トンプソンは講演の中でこう述べている。
「あなたが完全に自分で作ったコード以外は信用できない。特に、私のような人間を雇っている会社のコードは。」
このエピソードは「信頼の連鎖(supply chain trust)」問題の原点とされる。
ソースコードをレビューしても、コンパイラやビルド環境自体に細工があれば防げない。
現代では、Diverse Double-Compiling(DDC) や 再現可能ビルド(Reproducible Build) のような手法が、
こうした攻撃を防ぐための対策として研究・実装されている。
結論
このトロイの木馬は、実際に配布されたわけではない。
しかし、トンプソンの示した「信頼できるソフトウェアとは何か」という問いは、
今日のセキュリティやオープンソース検証の根本的課題を突いている。
ソースを見ただけでは安全とは言えない——。
その教訓は50年経った今も変わらない。
詳細は That Time Ken Thompson Wrote a Backdoor into the C Compiler を参照していただきたい。