编程语言相关
我们都知道编程语言有好几代,那他们都是什么
第一代语言
最低级的语言,一般有0和1组成,人很难讲指令和数据区分开也称机器语言,字节码,二进制文件
第二代语言
汇编语言,他脱离了机器语言的0和1,使用短小且容易记忆的助记符对应相应的操作码。汇编器是将汇编转为机器码的工具
第三代语言
引入了关键字和结构来描述事物,使得他使用起来更加接近自然语言。他们通常不依赖任何平台,如:C,Java
什么是反汇编
传统软件开发过程中,一般是编译器-汇编器-链接器最后输出可执行程序。所以反汇编就是输入二进制文件或可执行文件输出汇编代码的过程。反编译器通常是输出高级语言
反汇编所面临的问题
编译过程中造成的损失
机器语言没有变量或函数名,变量类型信息只能通过用途来确定,如:看到一个32位的数据被传送到寄存器,需要很好地分析一番才能得出这是一个整数,还是浮点数或指针
编译属于多对多操作
就是源程序可以通过多重凡是转为汇编语言,而机器语言也可通过多重方式转为源程序
依赖于语言和库
用专门反编译c代码的编译器来反编译delphi生成的代码,可能会出现非常乖的语法。同样用对windows api一无所知的反编译器反编译windows二进制,也会出现上述结果
近乎源代码的反编译能力
这是一个很大的挑战,反汇编器在任何反编译阶段都有可能错误或遗漏一些代码而影响反编译结果
为什么要反汇编
分析恶意软件
分析闭源软件漏洞
和闭源软件互操作性
验证编译器的性能和准确性
怎么反汇编
反汇编器工作步骤
确定反汇编代码区域
通常指令和数据是混合在一起的,那怎么去区分呢。首先可执行文件肯定的按照一定的格式规则,如:windows的PE(portable executable),Unix的ELF(executable and linking format),而这些格式通常就会描述怎么确定代码和数据的位置,以及怎么找到程序入口点(一个指令地址,一旦程序加载到内存,操作系统就将控制权交给当前位置的指令)
读取该位置指令
当读取到该位置的指令后,进行一次查表操作,将二进制的与他的汇编助记符对应起来
获取该指令操作数
有些指令还有可能需要操作数和目的操作数,如:mov
输出指令
当助记符和操作数都有之后可以说是这条指令就反编译完成了,然后输出
循环处理
循环处理这个文件直到文件末尾。又大量的算法可以完成上述操作
反汇编算法分类
线性扫描反汇编
从文件的开始一直处理到文件末尾。问题是不太好处理分支或非线性结构的流程。
优点:完全覆盖所以代码段
缺点:有可能代码中混有数据,但当指令处理了
gdb,windbag,objdump都是采用这一算法
递归下降反汇编
它采用了和线性完全不一样的方法定位代码逻辑。递归强调的是控制流,根据指令是否跳转在决定是否反编译。因此他会根据程序不同控制流来反编译,那有哪些控制流呢
顺序流
这个就很简单了,就是一条指令接一条,包括算术运行add,传值mov,堆栈push和pop等指令
跳转指令
分支jnz(x86),他有两条路径真或假,递归反编译会对两条路径都反编译
无条件分支
与顺序流差不多,只是下一条指令不一定紧跟当前指令后面。如果跳转指令的目标地址是一个运行值,那么静态反汇编无法确定该值,也就没法反编译了,如:jmp eax(x86)
函数调用指令
他和无调整指令很相似,同样反编译器也无法识别call eax这样的指令。唯一不同的是函数执行完成后会返回调用指令后面的指令,但是在调用函数时出现错误,就有可能导致反汇编失败,如:在函数中修改了返回地址
返回指令
有时,递归下降算法访问了所有路径,且函数返回指令没有提供将要执行的指令地址。如果这时程序正在运行则可以从栈顶获取一个地址,而从这个地址开始恢复。但是静态反汇编并没有这个能力,这时递归下降反汇编器会继续反汇编原来搁置的反汇编地址列表
优点:具有很强的代码和数据区分能力
缺点:无法处理间接代码路径,如:通过指令表来查找目标地址的跳转或调用。然而采用一种用于识别指向代码的指针的启发式(heuristics)方法,则能够反汇编所有代码
如果我的文章对来带来的帮助或者有不明白的地方,可加QQ群:129961195,大家一起交流