免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2703 | 回复: 3
打印 上一主题 下一主题

麒麟操作系统明显的噱头 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-07-25 13:37 |只看该作者 |倒序浏览
麒麟操作系统是由国防科技大学、中软公司、联想公司、浪潮公司和民族恒星公司五家单位合作研制的服务器操作系统。按照麒麟官方的说法:
“Kylin服务器操作系统是国家863计划的重大研究成果,拥有完全自主版权的内核,与Linux在应用上二进制兼容,并支持64位,是中国独立研发成功的、具有完全自主知识产权的服务器操作系统。”[1] --- 来自麒麟官方网站 http://www.kylin.org.cn/news.htm和 863计划官方网站[2] http://www.863.org.cn/863_105/in ... s/200409160008.html
“银河麒麟操作系统是针对未来的主流网络服务和高性能计算服务的需求,参照国际主流标准,参考Darwin、 FreeBSD、Linux和其它商用操作系统,借鉴UNIX操作系统和微内核操作系统的设计思想,设计并实现具有自主版权的、可支持多种CPU芯片和多种计算机体系结构的、具有高性能、高可用性与高安全性的、并与Linux应用和设备驱动二进制兼容的中文服务器操作系统,” ---摘自麒麟操作系统2.0.21内自带的帮助文档
近日,有不少人对麒麟操作系统宣称的“完全自主版权”和“中国独立研发成功”这两个核心问题产生了质疑。随着麒麟2.0.14和2.0.21系统可以通过麒麟的官方网站下载后( http://www.kylin.org.cn/download.htm ),这种质疑的声音越来越大。麒麟除内核以外的应用大部分都来自自由组织GNU的代码,这些代码并不属于“中国独立研发”,而且他们的版权也不属于麒麟操作系统的开发者。更有甚者,有人开始通过反汇编麒麟操作系统内核发现和美国的FreeBSD开放源代码操作系统非常相似。随后又有人成功的用 FreeBSD的内核启动了麒麟操作系统。按照麒麟官方的介绍,麒麟具有Linux的二进制兼容的能力,可是丝毫没有提及与FreeBSD的兼容性,使得麒麟内核与FreeBSD的关系变得比较引人注目。在官方介绍中的简简单单的“参考”是无法解释这种相似程度的。
在强烈的关注声中,麒麟开发人员在2006年2月16日,给出了一个说明,《关于银河麒麟操作系统的说明》[3],发布在 http://www.kylin.org.cn/download.htm 。其中提到了和FreeBSD的关系:
“课题组通过评测和分析,认为当时正在研发中的FreeBSD 5.0 具有比Unix SVR4.2 更好的发展势头,特别是SMPng 项目的开展,为FreeBSD 5.0 支持SMP 对称多处理器系统奠定了良好的基础,因此银河麒麟操作系统的系统服务层从SVR4.2 升级到当时正在研发中的FreeBSD 5.0。”
声明发出后一定程度上得到了大家谅解,可是虽然提及和FreeBSD的关系,却又十分隐晦,既没有明确的对官方网站新闻中的报道失实承认错误,没有明确阐述麒麟的操作系统是否具有“完全知识产权”以及是否是“中国独立研发”,甚至也没有对官方页面上的事实报道进行修正。而且,既然说明使用了FreeBSD 5.0的代码,却又说仅限于系统服务层,而丝毫未提及所占比例。这依旧让人们对这个获得863计划软件重大专项的资助的操作系统到底有多少创新产生一个大大的疑问。
为了调查清楚麒麟操作系统内核自主创新的百分比,以及与其它操作系统之间的关系,我将麒麟操作系统内核与FreeBSD、NetBSD、OpenBSD、 Linux和Solaris的内核进行了可执行代码的相似度分析。
在整个过程中,我将尽量保持客观的原则进行分析。由于麒麟操作系统属于封闭源代码系统,因此在无法获得内核源代码的情况下,我将只进行二进制可执行代码文件的相似度分析。由于可执行代码受编译环境、内存分布情况以及模块的变动的影响很大,因此,会产生即使采用同一套代码,却产生很低的相似度情况。但是,对操作系统内核这种大型软件系统来说,却不会因为不同的代码而产生很高的相似度的情况。因此,我们将这次对二进制可执行代码分析所得的相似度作为相似度的下限。换句话说,真实的相似度应该会高于此次分析结果,但是由于分析方法的局限性,无法取得上限。
二、可执行文件的相似度比较
二进制可执行文件的相似度分析一直是一个难题。大家都知道,即使是同一份源代码,使用同一个编译器,可用不同的编译参数进行编译后,代码也会产生极大的差异。当发生有人因为盗用别人的源代码而产生的侵权后,如果不能够将二者的源代码拿出进行比较的话,判断是否抄袭非常困难。因此,一直以来或多或少,总会有人无所顾忌的将开放源代码的软件拿来加入到自己的软件中,或者干脆就是在那些源代码的基础上稍加修改和更换了版权信息就宣称是自己研发的。因为他们知道,只要不把自己的源代码公诸于众,那么抄袭就很难判定。下面我就详细说一下我采用的分析方法。
2.1 ELF可执行文件相似度分析方法
这次分析起始,我就碰到了一些难题。如果对二进制可执行文件进行基于字节的相似性分析,即使匹配上某些字节,也很难说明两段代码的相似性,另外匹配也很容易受到各种噪音的干扰而产生很低的相似度,可是噪音却无法被去除。因此,使最小比较单元具有明确的语义和合理的过滤噪音是我首先要解决的问题。
2.1.1 反汇编
二进制文件的比较难以确定最小单元语义的根本问题在于二进制文件是以字节为单位,然而每个字节却没有特定的含义。你很难说89 e5和83 EC 89中的89相同说明什么,在这个例子中,前者的89 e5是i386的一条指令,而后者的89则是一个立即数,所以他们相同实际上什么都不说明。针对这次分析,由于都是可执行代码,而且都采用了ELF的文件格式。由于这个特点,我首先将所有操作系统的内核通过objdump反汇编成汇编代码。这样做有一个直接的好处,就是每一行都是一条汇编语句,而每一条汇编语句又是一个程序不可分的最小逻辑单元。这样,接下来的分析就可以基于行来进行相似性的分析,因为每出现一行相同就说明有一个最小的逻辑单元相同,如果出现连续的行相似,那么就说明有连续的代码段相似。相同的行越多两个内核就越相似。并且经过反汇编后,就避免了因文件内包含的其他无关信息,如字符串、资源文件、数据文件等,对分析结果产生的影响。这个方法依旧无法避免因编译参数差异所造成的相似度下降的影响。虽然如此,但是我很幸运,从这次分析的结果看,依旧得到了不低的相似度。
2.1.2 过滤噪音
噪音的出现有很多原因,可能是内存分布不同、代码的增删导致的偏移地址的变化,对相同含义的常量而数值却不同等等。这些值的差异,可能会造成不同的执行结果,但是却对两段代码的相似性比较影响不大。请看下列两个代码段:
c043e9e8 :                        | c04431d8  :
freebsd4_sigcode():                                 freebsd4_sigcode():
c043e9e8: call   *0x10(%esp)                       | c04431d8: call   *0x10(%esp)
c043e9ec: lea    0x14(%esp),%eax                   | c04431dc: lea    0x14(%esp),%eax
c043e9f0: push   %eax                              | c04431e0: push   %eax
c043e9f1: testl $0x20000,0x54(%eax)               | c04431e1: testl $0x20000,0x54(%eax)
c043e9f8: jne    c043e9fd  | c04431e8: jne    c04431ed
c043e9fa: movl   0x14(%eax),%gs                    | c04431ea: movw   0x14(%eax),%gs
c043e9fd: mov    $0x158,%eax                       | c04431ed: mov    $0x158,%eax
c043ea02: push   %eax                              | c04431f2: push   %eax
c043ea03: int    $0x80                             | c04431f3: int    $0x80
c043ea05: jmp    c043ea05  | c04431f5: jmp    c04431f5
c043ea07: nop                                      | c04431f7: nop

左边的代码是来自FreeBSD 5.3内核的,而右边的代码来自麒麟2.0.21/18的内核。通过人的分析,我们可以得出这两段代码实际上是相同的。可是对于计算机程序比较的时候,就不尽然。请注意上述的有颜色的数字。用蓝色表示的代码地址[4]、绿色表示的偏移地址、红色表示的立即数、深蓝色表示的函数偏移地址和粉色表示的函数地址,这些数字的不同,就造成了代码比较时候的失败。上述13行代码,如果就这样比较的话,只有函数名一行可以匹配。因此虽然是相同的代码,却只有7.7%的相似度。下面我们就来去除这些干扰。
首先,我们将代码行地址、函数跳转地址和函数偏移地址去除。代码行所在的地址,实际上是说明了代码所在内存的位置,内存的位置会随着代码的删改而很容易产生变动,这些对我们比较代码逻辑没有意义。其中有些绝对地址,我们将其替换为“{Address}”,这样既不受地址变化的影响,又不至影响了代码的含义。
然后我们将绿色的偏移地址替换成特定字符串“{Offset}”。产生偏移地址的原因一般有两种,一种是结构体,另一种是数组。即使不对结构体删改,而仅仅是对结构体的声明顺序的变动都可以造成偏移地址的不同,我们在这里只关心程序在这里用到了一个偏移地址,而不关心用的到底是偏移了多少。数组的用法虽然不常出现,但是即使出现其中的位置也是很容易发生变动的。因此在这里,我们也将偏移地址的数值替换成统一的字符串。最后,我们来处理红色的立即数。当然立即数并不是只有上述的几种情况下出现,虽然在上述的例子中,两边的立即数都完全一样,单是在某些情况下还是会出现不同。
立即数在程序中一般是常量,而常量有可能是与系统相关的数值,或者仅仅是一个符号,而不在乎具体数值。无论是什么含义,常量虽然在执行过程中不会改变,在设计过程中却很容易发生变动。不过对我们分析代码逻辑没有太大的影响,因此,在分析的时候我们对数值进行模糊化,将其替换为“{Number}”这个特定字符串。
至此,上述代码将会变为: :                        | :
freebsd4_sigcode():                        | freebsd4_sigcode():
call   *{Offset}(%esp)                   |   call   *{Offset}(%esp)
lea    {Offset}(%esp),%eax               |   lea    {Offset}(%esp),%eax
push   %eax                              |   push   %eax
testl {Number},{Offset}(%eax)           |   testl {Number},{Offset}(%eax)
jne           |   jne   
movl   {Offset}(%eax),%gs                |   movw   {Offset}(%eax),%gs
mov    {Number},%eax                     |   mov    {Number},%eax
push   %eax                              |  push   %eax
int    {Number}                          |   int    {Number}
jmp           |   jmp   
nop                                      |   nop
现在这两段代码的相似度将变成真实的100%。
2.1.3 代码段顺序调整
经过上面的噪音过滤后,代码已经能够在基本不影响代码逻辑的前提下去除了噪音的影响。可是,还有一种情况会对匹配结果带来较大的影响。就是代码块位置的前后变动,我们来看下面这两段代码的比对。
begin():                                   <
        mov    {Address},%eax              <
        lea    {Offset}(%eax),%esp         <
        xor    %ebp,%ebp                   <
        mov    {Address},%esi              <
        mov    %esi,{Offset}(%eax)         <
        pushl {Address}                   <
        call                      <
        add    {Number},%esp               <
        call                   <
sigcode():                                   sigcode():
        call   *{Offset}(%esp)                       call   *{Offset}(%esp)
        lea    {Offset}(%esp),%eax                   lea    {Offset}(%esp),%eax
        push   %eax                                  push   %eax
        testl {Number},{Offset}(%eax)               testl {Number},{Offset}(%eax)
        jne                        jne   
        movl   {Offset}(%eax),%gs          |         movw   {Offset}(%eax),%gs
        mov    {Number},%eax                         mov    {Number},%eax
        int    {Number}                              int    {Number}
        jmp                        jmp   
        nop                                          nop   
                                           > begin():
                                           >         mov    {Address},%eax
                                           >         lea    {Offset}(%eax),%esp
                                           >         xor    %ebp,%ebp
                                           >         mov    {Address},%esi
                                           >         mov    %esi,{Offset}(%eax)
                                           >         pushl {Address}
                                           >         call   
                                           >         add    {Number},%esp
和刚才一样。左边来自FreeBSD 5.3的代码,右边来自Kylin 2.0的代码(但是为了举例,函数前后顺序稍作调整)。在两段代码实际上非常相似,但是由于代码前后的顺序不同,导致只有一个代码块sigcode()可以匹配的上,相似度仅为47.6%。针对这类情况,我的解决办法是将代码块按照标号/函数名进行排序。经过排序,上述代码段比对将变为:
begin():                                   begin():
        mov    {Address},%eax                      mov    {Address},%eax
        lea    {Offset}(%eax),%esp                 lea    {Offset}(%eax),%esp
        xor    %ebp,%ebp                           xor    %ebp,%ebp
        mov    {Address},%esi                      mov    {Address},%esi
        mov    %esi,{Offset}(%eax)                 mov    %esi,{Offset}(%eax)
        pushl {Address}                           pushl {Address}
        call                              call   
        add    {Number},%esp                       add    {Number},%esp
        call                           call   
sigcode():                                 sigcode():
        call   *{Offset}(%esp)                     call   *{Offset}(%esp)
        lea    {Offset}(%esp),%eax                 lea    {Offset}(%esp),%eax
        push   %eax                                push   %eax
        testl {Number},{Offset}(%eax)             testl {Number},{Offset}(%eax)
        jne                      jne   
        movl   {Offset}(%eax),%gs        |         movw   {Offset}(%eax),%gs
        mov    {Number},%eax                       mov    {Number},%eax
        int    {Number}                            int    {Number}
        jmp                      jmp   
        nop                                        nop   
现在,这两段代码只有一行不同,相似度也就变为了95.2%。但是这种依赖于标号/函数名排序的做法有效的程度实际上是有局限的。首先,并不是所有函数名都会保存于可执行代码中,至少inline函数就会在编译时扩展到调用的语句位置,还有一些函数在编译器优化时被优化掉。所以,不同的编译器,或者不同的编译参数都有可能导致某些函数名在执行体中消失,从而导致排序失败。
另外,不是所有的可执行体都会保留函数名,对于Windows的PE文件来说,如果不用debug模式编译的话,除了导出函数外,其他的函数名一般不会保存在执行文件中,在我用同样的方法分析Windows文件内核的时候出现了比较严重的问题,即使血亲关系很近的两个版本的Windows内核,无论排序或者不排序,相似度都非常的低,对于这类PE文件根本无法反映出相似度。所以,在最终的分析中,我剔出了原本列在比较目标中的XP内核。因为ELF的这个特点,这次我的分析将只对使用ELF的文件格式的内核进行分析。
2.1.4 比较
在原本的计划中,我曾考虑采用常用于字符串相似度比较的编辑距离(Levenshtein Distance)算法[5]。这个算法的含义,是计算两字符串之间的距离有多远。编辑距离是指,从原字符串变化到目的字符串最少需要进行多少次包括添加、修改、删除在内的操作。举例而言:如果计算kitten和sitting之间的编辑距离,我们最少需要进行3次操作,
1、  kitten -> sitten (修改s->k)
2、  sitten -> sittin (修改i->e)
3、  sittin -> sitting (增加 g)
因此,kitten和sitting之间的编辑距离是3。这个算法是俄国的科学家Vladimir Levenshtein在1965年提出的。这个算法主要是应用在DNA分析、拼字检查、语音辨识和抄袭侦测上。[6] 但是这个算法的计算复杂度太高,是O(nm)的复杂度。对于平均大小在100万行的操作系统内核源代码来说,就是万亿次级别的比对。对于普通的计算机,平均每两个内核的比对就要花去数小时。而此次参与比对的内核将有20个左右,完成一个比较完整的比对过程将会出现几百次比对,那么就要花数个月的时间,不太现实。因此,这次我采用的是简化的比对办法。通过diff命令来比较两个内核源文件的差异。Diff使用的是一种更聪明的计算方法,虽然最坏情况差不太多,但是大多数情况下具有较高的性能[7]。
通过diff给出的结果可以得知第一个文件增改多少行代码后,就可以变为第二个文件。diff的算法和其实对于修改我们并不介意,我们只关心增加多少行代码就可以变为第二个文件。假设内核A的代码有a行,内核B的代码有b行。而从内核A变化到内核B需要添加c行,由内核B变化到内核A需要d行。由此,我们可以得知,在内核A中,存在有b-c行代码和内核B是相同的。因此,我们将内核A中所存在的内核B的代码行数除以内核A自身的代码行数定义为两个内核的相似度,即  A->B的相似度 = (b-c) / a
由公式可知,A->B和B->A的计算结果将有可能不同。因为我们判断相似度的原因不单纯是看二者的差异,更重要的是看他们之间的血亲关系的远近,因此我们取双向转换中的最大值,作为A<->B之间的相似度。  A-B间的相似度 = max( (b-c) / a, (a-d) / b )
2.1.5 小结
分析方法还有待完善,可以看出,二进制可执行文件的分析依旧还有很大的难度,很容易受到各种外围环境的变化而导致相似度大幅下降,而无法反映真实的相似度。因此对于那些刻意隐瞒相关性的二进制可执行文件来说还是比较容易的逃过这种分析方法的检测。但是,分析方法的缺陷却只会导致相似度的下降,而不会导致差异很大的代码产生很高的相似度。因此,我这次采用这次分析方法主要就是确定麒麟操作系统内核与其他操作系统之间的相似度的下限,并从数据中试图分析出他们的血亲关系。

2.3 结论
经过这次分析,我们比较了麒麟操作系统内核同FreeBSD, NetBSD, OpenBSD, Linux和Solaris内核的相似度。在发现麒麟内核与FreeBSD 5.x有30.43%-48.18%的相似度后,将麒麟内核与FreeBSD 5.x各个版本进行了比对。通过比对看到麒麟系统与FreeBSD 5.3默认内核达到了60.26%的相似度,在经过微小调整内核配置文件后,相似度又得到了进一步的提高,达到了61.19%。在继续调整内核配置文件之后,这个相似度还有进一步提升的空间。随后,我们在不修改FreeBSD 5.3源代码的情况下,仅仅通过配置文件的变动,就使内核相似度降低到了71.62%,而且还有可能降的更低。
经过分析,我们可以看出麒麟操作系统与FreeBSD 5.3具有血亲关系,而且麒麟系统相对于FreeBSD 5.3的改动,还没有FreeBSD 5.3相对于FreeBSD 5.2.1改动大。从61.19%的相似度,我们可以认定,麒麟操作系统中至少有60%的代码是来自于FreeBSD 5.3的源代码。由于简单的修改配置文件就可以使相同代码相似度降低到71.62%,而我们最终所得到的61.19%又是仅仅是麒麟内核同FreeBSD 5.3内核相似度的最小值,因此,实际的麒麟操作系统与FreeBSD 5.3在源代码上的相似度很有可能会达到甚至超过90%。
我们可以推测(但不确定)麒麟操作系统内核可能是通过以下几个步骤产生的。首先是在FreeBSD 5.3内核源代码的基础上进行了部分的修改,可能是为了增强与LSB的兼容性。然后,开发了Keta内核模块,来实现Kernel-based静态页面web加速器。最后,以FreeBSD的默认内核配置文件GENERIC为基础,编译了更多的可选模块进内核。这么做的目的可能是为了让内核更具有通用性。如果确实如此,那么生成的麒麟操作系统内核与其说是一个新的操作系统内核,不如说是被麒麟开发者打了内核补丁的FreeBSD 5.3更为恰当。
三、尾声
我们很难推测麒麟在内核创新的百分比,从已知的数据我们只能说,创新可能只有10%到20%之间。首先,麒麟的官方说明中提到“主要是由具有Mach 风格的基本内核层、具有BSD 风格的系统服务层和具有Windows 界面风格的桌面环境组成,前两层在核态运行。”采用Mach微内核层+FreeBSD内核服务层的做法是其一个亮点。微内核构架加上一个成熟操作系统的服务层,是目前比较流行的一个做法,Mac OS X就是这样。在对Mac OS X的xnu内核源代码分析过程中,就可以看到其中的两层内核结构,很清晰。不过在我们反汇编麒麟操作系统内核的分析过程中,竟然连一个与Mach相关的内核函数都没有看到,许多关键的模块也基本上和FreeBSD相同而看不到Mach的身影,因此我们对于麒麟操作系统内核是否真如官方说明所宣称的那样, “具有Mach 风格的基本内核层”,还是抱有很大疑问的。
其次,从其对外宣称的一些麒麟的特性上看,绝大多数都是FreeBSD已经成熟的特性。比如,对Linux达到二进制兼容,事实上,FreeBSD很早以前就已经做到了和Linux二进制兼容了,麒麟直接采用了FreeBSD的内核源代码,也自然而然的支持了这个特性,无非是在于LSB兼容上进一步的做了一些工作。麒麟系统的所宣称的安全性应该也是继承于FreeBSD长期积累下来的健壮和稳定性上。至于内核级的Web服务器Keta,确实是来自国防科技大学的原创,可以从http://openketa.sourceforge.net/ 取得源代码。不过恰恰是这个内核级的Web服务器Keta,降低了麒麟所宣称的安全性,而且也暴露了国防科技大学在安全性上认知的不足。
内核级代码确实可以大幅提高性能,Linux在2.4的时候也曾经采用一个叫做TUX的内核级的Web加速器来进行静态页面的加速处理。不过这个一直是争论的焦点,相当多的安全人士不推荐Linux这样设计,因为内核级代码虽然较快,但是,一旦发生溢出等安全攻击,那么攻击者就可以直接将自己的代码注入内核空间执行,具有系统最高权限,而不受约束。这将给系统安全性带来极大的隐患。另外,内核级的Web加速器也有其自身弱点,由于功能受限,而不能够用动态页面,只能够支持静态页面,所有的动态页面只有转交给另一个真实的Web服务器,比如Apache Web Server,才能够处理。所以对于动态页面,内核的Web加速器并没有什么明显效果。随着Linux 2.6的NPTL引入,用户模式下的多任务性能得到了大幅提高,TUX就很快被移出标准内核了。而FreeBSD的多任务模型和Linux很不同,特别是在FreeBSD 5.x后多任务模型有很大的改进,FreeBSD的用户模式的多任务性能并不是很差,为了这个不大的性能提高,而严重的降低系统安全性,实在是一个不明智的举动。在这种情况下,还宣称自己是高安全性,显得很不伦不类。
而且,从编译进麒麟内核的模块看,我们可以感到很多桌面系统的模块被加到了默认内核中,但是对外却宣称是服务器操作系统。我们都知道,安全的原则是最小化服务原则。多一个服务,多一个驱动,就多引入了一份不稳定和不安全。可是麒麟内核却加载了很多类似于屏幕保护模块、声卡模块、显卡图形驱动模块、ACPI 电源管理的内核模块,这些模块对于一个服务器来说,并不是必须的,而且其中很多代码会给系统带来很大的安全隐患。这绝不单单是引导速度变慢的问题,麒麟宣称自己是服务器操作系统,但是在这点的选择上也是很不明智的。
最后需要提及的是,麒麟所安装的软件大部分是GNU的开放源代码软件,这些软件遵循GPL。它要求如果对任何代码进行了修改,必须开放修改过的源代码。但不幸的是,麒麟操作系统虽然修改了部分的代码,却没有开放出修改后的源代码。从这种意义上说,麒麟操作系统对这些GNU的开源软件有侵权嫌疑。
摘自51CTO技术圈 Group.51cto.com
有图片文章链接:http://os.51cto.com/art/200604/26034.htm

论坛徽章:
0
2 [报告]
发表于 2006-07-28 10:13 |只看该作者
这个算偷?

论坛徽章:
0
3 [报告]
发表于 2006-08-10 18:00 |只看该作者
天天是自主产权,到底自主到什么程度了?只有鬼才知道!中国人就是能吹

论坛徽章:
0
4 [报告]
发表于 2012-08-26 18:25 |只看该作者
判断相似度的原因不单纯是看二者的差异
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP