X86反汇编简要说明

来源:互联网 发布:js json特殊字符处理 编辑:程序博客网 时间:2024/06/03 13:52

一、抽象

在经典的计算机体系结构中,往往将计算机系统表示为一些抽象的层次,来隐藏其实现细节。


机器码: 机器码由操作码(opcode)组成,操作码是一些十六进制形式的数字,用于告诉处理器你想要他做什么。

低级语言: 低级语言是计算机体系结构指令集的人类易读的版本,注意是汇编语言。恶意代码分析师使用这一语言。

高级语言: 大部分程序员使用高级语言。高级语言对机器层做了很强的抽象,从而可以很轻松的使用程序逻辑和流程控制机制。高级语言包括cc++等。它们被一个编译器经过称为编译的过程转化成机器码。

解释型语言:解释型语言位于最高层。C#Perl.NETJava等为解释型语言。这一层的代码不会被编译成机器码,而会翻译成字节码。字节码(bytecode)是特定于该语言的一种中间表示,它在解释器中执行。解释器是一个在运行时将字节码实时翻译成可执行机器码的程序。相比于传统被编译的代码,解释器提供了一种自动的抽象层次,因为它可以独立于操作系统,自己处理错误和管理内存。

 

二、x86体系结构

大部分现代计算机系统结构在内部实现上遵循冯诺依曼结构。这种结构包含三种硬件组件:

A.中央处理单元(CPU),负责执行代码。

B.内存(RAM),负责存储所有的数据和代码。

C.输入/输出系统(I/O)。


1.内存


它们可以在内存中的任何位置。

数据:这个词指的是内存中特定的一个节,名为数据节,其中包含了一些值。这些值在程序初始加载时被放到这里,称为静态值,因为程序运行时它们可能并不发生变化,还可以称为全局值,因为程序的任何部分都可以使用他们。

代码:代码节包含了在执行程序任务时CPU所取得的指令。这些代码决定了程序是做什么的,以及程序中的任务如何协调工作。

堆:堆是为程序执行期间需要的动态内存准备的,用于创建(分配)新的值,以及消除(释放)不再需要的值。将其称为动态内存,是因为其内容在程序运行期间经常被改变。

栈:用于函数的局部变量和参数,以及控制程序执行流。

2.寄存器

寄存器是可以被CPU使用的少量数据存储器,访问其中内容的速度会比访问其他存储器要快。归类为以下四类:

A.通用寄存器,CPU在执行期间使用

B.段寄存器,用于定位内存节

C.状态标志

EFLAGS寄存器是一个标志寄存器。在x86架构中,它是32位的,每一位是一个标志。在执行期间,每一位表示要么置位(1),要么清零(0),并且由这些值来控制CPU的运算,介绍几个常用的标志位:

ZF 当一个运算的结果等于0时,ZF被置位,否则被清除。

CF 当一个运算的结果相对于目标操作数太大或太小时,CF被置位,否则清除。

SF 当一个运算的结果为负数,SF被置位;若结果为正数,SF被清除。

TF TF用于调试。当它被置位时,x86处理器每次只执行一条指令。

D.指令指针,用于定位要执行的下一条指令

EIP寄存器,又称为指令指针或程序计数器,保存了程序将要执行的下一条指令在内存中的地址。

3.指令

只介绍几个常用指令

Mov 指令  mov eaxebx

Lea指令  lea eax[ebx+8] 相当于 mov eaxebx+8,但是这个是不符合规则的,mov指令是不支持后面有运算的

Jmp 指令  无条件跳转

Jzjnz 或者jejne,根据ZF标志位进行跳转

4.函数调用

函数是程序中的一段代码,执行一个特定的任务,并与其他代码相对独立。主代码调用函数,并在其返回到主代码前,临时将执行权交给函数。程序如何使用栈,这对一个二进制文件是贯穿始终的问题。现在,我们将关注最常见的约定,称之为cdecl。

许多函数包含一段“序言”,它是在函数开始处的少数几行代码,用于保存函数中要用刀的栈和寄存器。相应的,在函数结尾的“结语”则将栈和这些寄存器恢复至函数被调用前的状态。

列举函数调用最常见的实现流程:

  • 使用push指令将参数压入栈中。
  • 使用call memory_location来调用函数。此时,当前指令地址(指EIP寄存器中的内容)被压入栈中。这个地址会在函数结束之后,被用于返回到主代码。当函数开始执行时,EIP的值被设置为memory_location(即函数的起始地址)。
  • 通过函数的序言部分,分配栈中用于局部变量的空间,EBP(基址指针)也被压入栈中。这样就达到了为调用函数保存EBP的目的。
  • 函数开始做它的工作。
  • 通过函数的结语部分,恢复栈。调整ESP来解释局部变量,恢复EBP,以使得调用函数可以准确的定位它的变量。leave指令可以用作结语,因为它的功能是使ESP等于EBP,然后从栈中弹出EBP。
  • 函数通过调用ret指令返回。这个指令会从栈中弹出返回地址给EIP,因此程序会从原来调用的地方继续执行。
注意:栈的分配是自顶向下的,高内存地址先被使用。每一次函数调用,就生成了一个新的栈帧。函数维护它自己的栈帧,直至它返回,这时调用者的栈帧被恢复,执行权也返回给了调用函数。


0 0
原创粉丝点击