深入解析编程与编译过程:信息丢失与代码理解挑战

要理解这个问题,先要看「正」编译的过程是怎样的。

深入解析编程与编译过程:信息丢失与代码理解挑战

你拥有一个想法,这个想法是能够用人类自然语言进行表达的。你凭借自身的编程技能,将其“转化”为你所熟悉的一种编程语言。

这个过程叫做编程。

接着你运用编译器去进行翻译,将其转化为机器能够理解的语言。

这个过程被称作编译。编程和编译均为“信息丢失”的过程。例如,你讲道,我拥有一组整数,我要对这些数进行排序,接着便熟练地编写了一个冒泡排序。然而在一定程度上,你的最初意图实际上已从代码中消失了——有经验的人能够一眼就看出这段代码是在进行排序,而新手小明所看到的仅仅是一些 for 和 if 等东西。如果是更为复杂的功能,或许过一段时间后你自己都难以理解自己当时的意图。从程序语言到机器语言的这个过程,其实和前面所说的情况是一样的。这两个过程实际上都是将“做什么”转化为“怎么做”的过程,而在转换完成之后,最初到底是要做什么这个信息就已经消失不见了。

所谓「反编译」,指的是找回那些丢失信息的过程。从这个视角来看,你阅读一段代码的时候,实际上就是在把它「反编译」成自然语言。如果要实现完美的反编译,存在一种可能,即信息完全没有丢失。比如你正在阅读的这段代码有充足的注释,或者它采用了一种你所了解的模式,这也是大家反复强调注释和设计模式重要性的原因。对于从机器语言到程序语言的反编译过程,情况也是如此。

比如说有比反编译更低级(非贬义)的过程,叫做反汇编:

严格来说,汇编语言属于编程语言的范畴。然而,在我们当下的语境中,会将它与我们通常所说的高级编程语言(例如 C 语言)区分开来。

在这个步骤中,我在汇编和机器语言中使用双向实线箭头。因为汇编语言和机器语言可以互相转换,所以在从汇编语言到机器语言的过程中没有丢失任何信息。由于两者的指令是一一对应的,所以反汇编能够轻松达成。

很多程序语言只能反汇编,而难以反编译,这就是其原因。通常我们把这种语言称作“编译语言”,也叫“原生语言”。像 C、C++等就是这类语言的代表。

为什么有的语言可以反编译呢?这需要从机器语言开始讲起。就如同不同地域的人使用的语言存在差异,不同的机器所使用的语言也各不相同。用专业术语来说,就是“指令集不同”。例如,你的电脑和你的手机,它们的指令集通常是不一样的。一段程序若要让不同的机器都能够执行,就只能分别将其翻译(编译)成相应的机器语言。这个过程较为麻烦,于是人们想出了一个办法,进而搞出了一种被称作解释语言的东西。(此处未考证解释语言是否就是因这个过程而发明的,只是用于帮助理解。)如下所示:

解释语言有两种执行方式。一种是直接解释执行,在这种方式中,中间没有机器语言的参与。然而,这种方式的效率很低。所以,现代的解释语言通常会采用第二种方式,即先通过图中上部的路径,利用 JIT 编译的方式将其翻译成机器语言,然后再执行,以此来保证执行效率。JIT 编译的意思大致是“用到什么就对其进行编译”,这个过程通常是在执行的过程中同步开展的。

“解释器”的英文是“interpreter”,其实际上就是名词“翻译”的含义。这就如同我国的外交部向各国大使馆发送了一份文件,然后由大使馆的工作人员分别将其翻译成相应的语言,并传达给目标国的有关部门。具有代表性的解释语言如 Javascript,因为它需要在不同机器的浏览器上都能够正确执行,所以采用了这种方式。

但是这样做的话,程序代码就需要提供给每一台执行端机器。这是存在泄密风险的。在防止泄密方面,最为直接的办法自然是进行加密。

有锁就会有与之对应的钥匙,同时也会有开锁的技术;有加密和解密的过程,也会有相应的破解方法。在这种情况下,所谓的“反编译”,实际上就是对加密算法进行破解。关于这一点,就不再详细阐述了。

后来,人们认为解释语言的执行速度有些慢。于是,他们想到了一个办法:先把一些能够在前期完成的工作做完,只留下那些与目标机器相关的工作,等到需要的时候再去处理。这样,程序就被处理成了一种被称为“中间语言”或者“字节码”的东西。

这个过程通常被称作编译。中间语言词汇量少且较为精炼,执行速度较快。这些语言通常会运用 JIT 技术,能够进一步将中间语言编译为机器语言(而非进行解释执行),其执行效率也就与那些原生的编译语言差不多了。具有代表性的这类语言有 C#、Java 等。

程序语言能够被编译成为中间语言,同时,中间语言在一定程度上也可以被反编译成程序语言。因为采用这种编译方式的编程语言要保证其高级特性(例如反射),所以在编译过程中保留了源程序的绝大部分信息,只有少量信息丢失;也正是由于丢失了这部分信息,中间语言通常无法完美地反编译,最常见的情况是反编译出来的程序中局部变量的名字丢失了,被反编译器自动生成的名字所替换。反编译出来的程序,其结构是完备的,功能也是完备的。同时,这类程序的可读性也有一定的保障。通常情况下,我们所说的可以反编译的程序指的就是由这类语言写就的程序。

中间语言能够被反编译;加密之后会被破解,并且在执行之前还需要进行解密,这会带来额外的性能开销。是否存在一种方法,可以让代码既能够有效地执行,又不会被截获代码的人所利用呢?就在这时,人们从一些职业素养很低的程序员那里获得了启发。

实现相同功能,代码可有无数种形式。你认真写注释,准确命名函数和变量,严格按规范缩进和换行。小明则完全相反,没有注释,变量全用 abcd 等,甚至故意误导(如 var mySon = laowang.Son),缩进换行邋遢,大括号前不换行,让大家很不满。老板想,先把小明开除。接着给你发奖金,同时要求大家按照你的方式写代码。还要开发一个工具,名叫“混淆器”,在发布时把代码处理成小明写的那种样子。

这样的代码即便被反编译和解密了,他人看了也难以理解,而且一不小心还会陷入其中。代码主要是写给人看的,只是偶尔让机器运行一下,倘若没有可读性,那这样的代码就没有价值。这种方法一经推出,就受到广泛好评,随后便成为一种极为普遍的做法。需注意,图中省略了中间代码和 JIT 的步骤,混淆通常会与这些技术一同使用。

你可能想看:
« 上一篇
下一篇 »

相关推荐

FreeBasic:强大免费开源的64位basic编译器及应用优势

2025年05月10日

82阅读

脊柱侧弯矫正方法大盘点:运动康复、支具矫正等多种方式

2025年05月09日

87阅读

如何在winForm标题栏去掉标题文字且任务栏图标正常显示

2025年05月09日

86阅读

十一黄金周促销广告词大全,国庆购物狂欢等你来

2025年05月09日

78阅读

关闭快速编辑模式及插入模式,设置控制台指定文字颜色举例

2025年05月09日

63阅读

黄金搭档低糖高钙饮品包装及成分功效全解析,适合人群要知晓

2025年05月09日

91阅读