当前位置: 代码迷 >> 综合 >> Reflections on Trusting Trust(对托付信任的反思)文章翻译
  详细解决方案

Reflections on Trusting Trust(对托付信任的反思)文章翻译

热度:43   发布时间:2023-12-17 13:54:21.0

对托付信任的反思(Reflections on Trusting Trust)

一个人应该在何种程度上相信宣称一个程序不包含木马的陈述呢?或许更重要的是信任编写软件的人。

原文作者:肯·汤普森(KEN THOMPSON)
原文参见:https://www.ece.cmu.edu/~ganger/712.fall02/papers/p761-thompson.pdf

引言

感谢国际计算机学会向我颁发这一奖项。我不禁感到我能获得这项荣誉不仅因为我在技术上的贡献,机缘巧合也同等重要。UNIX以席卷之势普及开来的同时伴随着的是全行业范围内由中央主机转向自主微机的变革。我怀疑如果丹尼尔·博布罗(Daniel Bobrow)买不起PDP-10并且拿PDP-11“勉强凑合”,站在这里的就是他而不是我了。此外,UNIX当前的状态也是许多人共同努力的结果。

有一句古谚说:“与带你来的人一同起舞”,也就是说我应该讲讲UNIX。我已经很多年不为主流UNIX工作了,但我依然继续因别人的工作而枉受赞誉。因此,我将不会讲有关UNIX的东西,而是感谢所有作出贡献的人。

这就要说到丹尼斯·里奇(Dennis Ritchie)了。我们的合作是一件美妙的事情,在我们一起工作的十年里,我只能记起一个我们工作不协调的案例。那一次,我发现我们都写了相同的20行汇编语言程序。我比较了源代码,惊讶地发现它们的每个字符完全一致。我们共同努力的结果远远超出我们各自所做的贡献。

我是一个程序员。这就是我在我的1040表格上填写的职业。作为一名程序员,我得写些程序。我想要向你展示我写过的最有趣的程序。我会分成三个部分来说,并尝试在最后加以总结。

第一部分

上大学时,在电子游戏出现以前,我们以编写程序习题自娱自乐。其中我最喜欢的习题之一是写出最短的自复制程序。既然这种习题和现实无甚关联,我们通常选用FORTRAN来编程。事实上,选用FORTRAN进行编程和二人三足比赛流行是出于相同的原因。

更确切地说,我们要做的是写出一个程序的源代码,使得在编译并执行后,程序产生的输出与其源代码完全一致。如果你之前没这么干过,那我建议你赶快自己试试。发现该怎么做到这件事的过程中得到的启示远远超过告诉你该怎么做时你能获得的任何好处。“最短”的要求只是为了鼓励展示技巧并决出胜利者。

图1展示了一个用C语言写成的自复制程序(正统主义者会说这个程序不是准确意义上的自复制程序,而是能生成一个自复制程序的程序)。这个程序实在太长了,拿不到奖项,但它展现了这一技术,并具有我讲述接下来的故事所需要的两个重要性质:1)这个程序很容易用另一个程序写出。2)这个程序可以包含任意量的冗余部分,与主算法一同被复制。在这个例子里,就连注释也被复制了。

char s[ ] = {'\t','0','\n','}',';','\n','\n','/','*','\n',(213 lines deleted)0
};
/*
*The string s is a
*representation of the body
*of this program from '0'
*to the end.
*/
main( )
{int i;printf("char\ts[ ]= {\n");for(i=0; s[i]; i++)printf("\t%d, \n", s[i]);printf("%s", s);
}

以下是一些简单说明,供非C程序员理解此程序。
= 赋值
== 等于
!= 不等于
++ 自增
‘x’ 单字符常量
“xxx” 多字符字符串
%d 转换为十进制格式
%s 转换为字符串格式
\t 制表符
\n 换行符

图1

第二部分

C编译器是用C语言写的。我接下来要描述的是用其自身语言写成的编译器所引起的许多“鸡与蛋”问题中的一个。在这一案例中,我会采用C编译器中的一个特定例子。

C语言允许通过字符串结构来指明一个初始化过的字符数组。字符串中的单个字符可以被转义,以代表无法打印出的字符。例如:
“Hello World\n”
代表一个带有字符"\n"的字符串,"\n"表示换行符。

图2.1是对C编译器中解释字符转义序列的代码的理想化。这段代码写的不错。它以完全可移植的方式“知道”何种字符代码应被编译为任意字符集中的换行。正因为它“知道”这一点,它就能重编译自己,从而永久保持这种知识。

假设我们想要改写C编译器,添加一个"\v"转义序列来代表垂直制表符。对图2.1需要作的增添显而易见,并被展示在图2.2中。然后我们重新编译C编译器,结果却出了语法错误。显然,由于二进制版本的编译器不知道什么是"\v",这段源代码不是有效的C代码。我们必须“教”编译器该怎么做。在它知道什么是"\v"后,我们的代码就变成有效的了。我们查一下ASCII表,找到垂直制表符是11(十进制)。我们把源代码改成图2.3中的样子。现在旧编译器接受了新的源代码。我们把所得的二进制文件作为新的C编译器使用,现在我们就可以按照图2.2中的可移植版本写了。

这是一个深奥的概念。是我迄今为止见过的最像是“学习”的程序。你只需要告诉它一遍,然后就可以使用这个自引用的定义了。

c = next();
if(c!='\\')return(c);
c = next();
if(c == '\\')return('\\');
if(c == 'n')return('\n');
图2.1
c = next();
if(c!='\\')return(c);
c = next();
if(c == '\\')return('\\');
if(c == 'n')return('\n');
if(c == 'v')return('\v');
图2.2
c = next();
if(c!='\\')return(c);
c = next();
if(c == '\\')return('\\');
if(c == 'n')return('\n');
if(c == 'v')return(11);
图2.3

第三部分

再回到C编译器,图3.1代表C编译器中的高级控制,在这里常规“编译”被调用,以编译下一行源代码。图3.2显示了对编译器的简单修改,使之在匹配到特定模式时对源代码故意进行错误编译。如果不是故意的,这会被叫做编译器"bug",但既然这是有意的,那就应该称为“木马”。

我在编译器里植入的实际bug可以匹配UNIX系统中“登录”命令的代码。经过替换的代码会对登录命令进行错误编译,使之既接受想要的加密口令,又接受一个特定的已知口令。这样,如果这段代码被加入二进制文件,而二进制文件又被用来编译登录命令,我就可以作为任意用户登录进系统。
如此直露的代码不可能长久不为人知。即使对C编译器源代码最漫不经心的检查者也会对此感到怀疑。

最终步骤展示在图3.3中。这只是在原有木马的基础上添加了第二个木马。要匹配的第二个模式是针对C编译器的。替换后的代码是第一部分中的自复制程序,可以把两个木马都插入编译器中。这需要经过第二部分中所展示的“学习”阶段。首先我们用普通的C编译器编译修改过的源代码来产生有bug的二进制文件。我们把这个二进制文件作为官方的C语言。现在我们就可以删掉编译器源代码中的bug了,而二进制文件会在每次编译后重新插入这个bug。当然,登录命令也会保留下bug,源代码中却不留一丝痕迹。

compile(s)
char *s;
{…
}
图3.1
compile(s)
char *s;
{if(match(s, "pattern")) {compile ("bug");return;}…
}
图3.2
compile(s)
char *s;
{if(match(s, "pattern1 ")) {compile ("bug1");return;
}if(match(s, "pattern 2")) {compile ("bug2");return;}…
}
图3.3

总结

故事的寓意很明显。你不能信任任何不是完全由你自己写的代码(特别是来自那些雇了像我这种人的公司的代码)。在源代码层面的审核和检查无论多少都不能保护你免受不可信的代码的威胁。在展示这种攻击的可能性时,我挑了C编译器。我本来也可以挑任何处理程序的程序,比如汇编器,引导程序,甚至是硬件中的微代码。当程序的层级变的更低,这些bug也会变得越来越难发现。一个在微代码中隐藏得好的bug几乎不可能被发现。

在试图向你证明我不可信任后,我想要说教一番。我想要批评媒体对“黑客”的处理,例如414帮,道尔顿帮等等。这些孩子们做的事情中好的可以说成扰乱秩序,坏的很可能就是非法入侵和盗窃。只是刑法条文的缺失才使这些黑客免遭重罪起诉。在这种活动面前脆弱的公司(大多数大公司都非常脆弱)正致力于更新刑法。未经授权进入计算机系统在某些州已经是严重的罪行,越来越多的州和国会也正在进行立法程序。

爆炸性的情势正在酝酿。一方面,媒体,电视,电影把做了这些行为的孩子称为小天才。另一方面,这些孩子们所做的行为很快就可以以多年监禁作为惩罚。

我看过孩子们在国会面前的证词。他们完全不清楚自己行为的严重性。这里显然存在文化断层。入侵计算机系统必得在社会上被看作与侵入邻居家中相同的恶行。至于邻居家门有没有锁好并不重要。媒体必须明白,对计算机的滥用并不比酒后驾车更令人惊奇。

致谢

我是在空军关于实现Multics系统的评论文章中第一次读到这种木马的可能性的。我找不到这份档案更精确的来源。如有人能提供来源请告诉我,十分感谢。

参考文献

  1. Bobrow, D.G., Burchfiel, J.D., Murphy, D.L., and Tomlinson, R.S.TENEX, a paged time-sharing system for the PDP-10. Commun. ACM 15, 3 (Mar. 1972), 135-143.

  2. Kernighan, B.W., and Ritchie, D.M. The C Programming Language. Prentice-Hall, Englewood Cliffs, N.J., 1978.

  3. Ritchie, D.M., and Thompson, K. The UNIX time-sharing system. Commun. ACM 17, 0uly 1974), 365-375.

  4. Unknown Air Force Document.

原文附加的许可条款

Permission to copy without fee all or part of this material is granted provided that the copies are not made or distributed for direct commercial advantage, the ACM copyright notice and the title of the publication and its date appear, and notice is given that copying is by permission of the Association for Computing Machinery. To copy otherwise, or to republish, requires a fee and/or specific permission.