📄 00000005.htm
字号:
<BR> 如果再用CC2来compile一个正常的login程式,由於CC2中有了patch1,所以 <BR>compile出来的login程式也会有後门,让我任意的login; <BR> <BR> ,--------. +-----------+ ,----------. <BR> | login | | Compiled | | login | <BR> | source |=====>| by CC2 |=====>| Program | <BR> | (clean)| |(patch 1,2)| |(patched!)| <BR> `--------' +-----------+ `----------' <BR> <BR> 如果用CC2 compile另一个compiler CC3,由於CC2中已经被加入了 patch2, <BR>CC3又会被污染,也就是说CC3这个compiler中还是会有patch1和patch2......如 <BR>此一来,全世界的每一套UNIX都种下了这个後门,可以让我任意login! <BR> <BR> 然而这些patch都只在binary档之中出现。CC2的source code一切正常,所以 <BR>从source code完全看不出有什麽不对劲呢!我们还可以进一步湮灭证据。一旦装 <BR>好一套系统,公开的CC1 source code中不必有动过手脚的程式码,只要让它被动 <BR>过手脚的compiler编译就可以了。 <BR> <BR> 有著无辜的包装,事实上内容暗藏玄机的程式,称作″特洛伊木马″。 这个 <BR>特洛伊木马的故事有趣吗?(注1) <BR> <BR> 用C语言写C compiler,写出来的程式会是个什麽样子呢? 举个例子,一个C <BR>compiler可能有一段前置处理程式在处理C字串中的溢出字元。比如说,compiler <BR>需要把如下的字串: <BR> "Figure listings :\nFigure1\tA Complete Tree\n....." <BR>给转换成: <BR> Figure listings :<换行码>Figure1<TAB的码>A Complete..... <BR>这段程式可能看起来像这样(为简单起见,这个程式从标准输入读进原始码,送到 <BR>标准输出): <BR> <BR> if ((c=getchar())=='\') <BR> switch (getchar()) { <BR> case 'n' : putchar('\n'); break; <BR> case 't' : putchar('\t'); break; <BR> : <BR> } <BR> else <BR> putchar(c); <BR> <BR> 好像有点奇怪,是吗?明明用if和switch把溢出字元'\'以及後面的'n','t', <BR>分开了,在putchar的地方又送出'\n', '\t'。如果您见多了用某语言写自己的 <BR>compiler的情况,对於这种程式段落也就见怪不怪了(注2)。 <BR> <BR> C语言是个处在大家周遭而不常被注意到的例子。LISP语言只须简单的parser, <BR>不分程式和资料,使得用LISP写LISP interpreter的情形更是普遍,也是常用的教 <BR>材。85级用的PL课本Chezzi & Jazayeri 的functional programming一章中,最後 <BR>附的LISP程式就是一个LISP interpreter。如果您研究一下,会发现一些感觉挺像 <BR>上面那个例子的段落。我自己玩了几年的LISP,到头来反倒除了LISP interpreter <BR>之外,就不会写其他什麽有用的程式了。这也是一个奇怪的现象呢。 <BR> <BR>参考资料: ACM Turing Award Lectures : <BR> the first twenty years 1966 to 1985 <BR> QA76.24 <BR> <BR> # <BR> <BR>注:1. 原本我以为Ken Thompson只是写写罢了。後来据一些人说,这完全是Ken <BR> Thompson 本人干过的真人真事。想像他老远到交大来参加conference, <BR> 大摇大摆的走上二楼机房,若无其事地login成root的情况吧。你相不相 <BR> 信呢? <BR> <BR> 2. 假设旧的compiler CC1并看不懂'\v'这个控制字元(垂直对齐), 我们 <BR> 想要有一个具有这个字元的compiler。新compiler CC2的source code可 <BR> 能是这样: <BR> <BR> switch (getchar()) { <BR> case 'n' : putchar('\n'); break; <BR> case 't' : putchar('\t'); break; <BR> case 'v' : putchar('\v'); break; <BR> : <BR> } <BR> <BR> 但是compiler CC1并没有办法compile这段程式,因为CC1看不懂程式 <BR> 中的'\v'!这似乎是一个逻辑陷阱,在实际develop的时候得多花手续。 <BR> 详见Ken Thompson的原文。 <BR> <BR><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -