什么样的语言可以进行反编译
本文从知乎扒的,没扒图,感觉文字足以表述.中间加了一些注释,如需详情请移步原文.
概念介绍
要理解这个问题,先要看「正」编译的过程是怎样的。
- 你有一个想法,这是一种人类自然语言可以表达出来的东西。你利用编程技能,把它「翻译」成你熟悉的一种编程语言这个过程叫做编程。
- 你使用编译器(compiler)将它翻译成机器所能理解的语言:这个过程叫做编译。
编程和编译都是「信息丢失」的过程。比如你说,我有一组整数,我要把这些数排个序,然后轻车熟路地写了个冒泡排序。然而一定程度上,你的原始动机其实已经从代码里丢失了。
有经验的人可以一眼看出这段代码是在排序,而新手小明看到的只有一些 for 和 if 之类的东西。
如果是更复杂的功能,可能过一段时间你自己都看不懂自己当时是想干什么。从程序语言到机器语言的过程其实也是一样的。
这两个过程其实都是把「做什么」转换成「怎么做」的过程,转换完成之后,究竟一开始是要做什么,这个信息已经丢失了。
所谓「反编译」,其实就是找回这些丢失的信息的过程。从这个角度上来说,你阅读一段代码的过程,其实就是在将它「反编译」成自然语言。
如果要完美地反编译,那只存在一种可能,就是信息完全没有丢失——比如说你阅读的这段代码有充分的注释,或者它使用了一种你所知晓的模式(这也是为什么大家一再强调注释和设计模式的重要性)。
对于从机器语言到程序语言的反编译过程,也是一样。比如说有比反编译更低级(非贬义)的过程,叫做反汇编。
严格来说汇编语言也是一种编程语言,不过我们在这里把它和我们常说的高级编程语言(包括C语言)区分开。
这个步骤里,我在汇编和机器语言里使用的是双向实线箭头,因为它们是可以互相转换的。从汇编语言到机器语言的过程中没有丢失任何信息——因为两者的指令是一一对应的,因此反汇编可以轻松达成。这就是很多程序语言只能反汇编、不能(难以,下同)反编译的原因。
一般我们管这种语言叫「编译语言」,又称「原生语言」。代表有C、C++等。那为什么有的语言可以反编译呢?
这又要从机器语言说起。就像不同地域的人所用的语言不同一样,不同的机器说的语言也不尽相同。用行话说,叫「指令集不同」。
比方说,你的电脑和你的手机,指令集通常是不一样的。一段程序要让不同的机器都能执行,只能分别翻译(编译)成相应的机器语言。
这个过程太麻烦了,于是人们想了个办法,搞出了一种叫解释语言的东西(此处未考证解释语言是否就是因此发明的,只是帮助理解)。
问题分析
解释语言有两种执行方式,这取决于执行端的「解释器」是如何工作的。
- 一种是直接解释执行,中间就没有机器语言什么事情了,但这种方式效率很低。(不能发挥批处理以及指令流水线的优势,所以效率较低).
- 先通过JIT编译的方式将源码翻译成机器语言,然后再执行,保证执行效率。JIT编译大致可以理解为「用到什么就编译什么」,这个过程常常是在执行过程中同步进行的。(php8中有这个东西,要注意理解).
现代的解释语言基本上都会采用第二种方式,,先通过JIT编译的方式翻译成机器语言,然后再执行,保证执行效率。「解释器」的英文interpreter,其实就是名词「翻译」的意思。
这好比你国外交部发了封文件(源码
)到各国大使馆,再由大使馆的工作人员分别翻译成相应的语言,传达给目标国相关部门。
代表性的解释语言如Javascript,它要在不同机器的浏览器上都能正确执行,所以采用这种方式。
但是这样一来,程序代码就必须提供给每一台执行端机器了。
这可是泄密啊。对于防止泄密,最直接的方式自然是加密。(在浏览器中js文件是可以被爬取的,所以一些敏感功能的实现需要加密防止技术外泄.比如直播场景中的h5播放器代码.)
有锁就有钥匙,同时也有开锁术
;有加密解密,也有相应的破解方式。这时候所谓的「反编译」,其实就是破解加密算法。这一点就不展开聊了。
后来,人们觉得解释语言执行得实在有点慢,于是又想了一个办法:把一些可以前期做掉的工作先做掉,只留着那些跟目标机器有关的工作,到时候再说。于是程序被处理成了一种叫做「中间语言」,或者叫「字节码」的东西这个过程一般也叫做编译
。
中间语言词汇少,比较精炼,执行起来也更快。这些语言一般也会用上JIT技术,进一步把中间语言编译成机器语言(而非解释执行),执行效率也就跟那些原生的编译语言不相上下了。这种语言代表性的有Java等。
程序语言可以编译成中间语言,反过来,中间语言也可以在一定程度上反编译成程序语言
。这是因为采用这种编译方式的编程语言为了保证它们的高级特性(比如说反射),在编译的过程中保留了源程序的绝大部分信息,
只有很少的信息丢失;也正是因为丢失了这一部分信息,中间语言通常不能完美地反编译——最常见的就是反编译出来的程序中局部变量的名字都丢了,被替换成了由反编译器自动生成的名字。
但这样反编译出来的程序,结构和功能都是完备的,可读性也有一定的保障
。一般来说,我们所说的可以反编译的程序都是指这样一类语言写就的程序。
中间语言可以被反编译;加密又会被破解,而且执行前还要解密,会带来额外的性能开销。有没有办法能让代码既能有效执行,又不被截获代码的人所利用呢?这时候人们从一些职业素养很差的程序员那里得到了启发,并且开发一个工具,唤作「混淆器」。
这样代码即使被反编译和解密了,别人看也看不懂,不小心还会被带到坑里去。代码毕竟是写给人看的,只是偶尔让机器跑一跑,所以没有可读性的代码是没有价值的
。
这种方法一出,广受好评,于是变成了一种非常普遍的做法。在中间代码和JIT的步骤,混淆通常会跟这些技术一起使用。
结论
- 汇编语言编译得到binary可以直接进行反汇编. 没有信息丢失.
- C语言等编译型语言的binary反编译难度较大. 逆向过程存在信息丢失
- Java等解释型语言的反编译难度较上者低.逆向过程存在信息丢失
所以:很多程序语言只能反汇编、难以反编译.
原文地址
作者:hillin
链接:https://www.zhihu.com/question/21853681/answer/74134768
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。