逆向之汇编基础-1
最近在看逆向相关内容,需要一些汇编相关基础,借此机会整理复习一些逆向中需要用到的汇编知识。
计算机专业的学生都学过汇编语言,但是大部分人认为学的只是 Intel 8086 下的汇编 指令,枯燥、乏味、不具备实用性。其实,作为汇编语言的入门,学习 8086 的汇编指令已经 基本足够了。目前的硬件都是 x86 兼容架构的,无论多复杂的程序,最终都将成为 x86 指令。作为逆向的入门,只要掌握 80x86 的常用指令、寄存器的用法、堆栈的概念和数据在内存中 的存储,基本就够用了。
寄存器
寄存器(Register)是 CPU 内部用于高速存储数据的小型存储单元,访问速度比内存快 很多,而且价格也高很多(在单位价格内,寄存器的价格要比内存贵,内存要比硬盘贵),但 是寄存器和内存都是用来存储数据的。 CPU 访问内存中的数据时有一个寻址的过程,因此访 问内存花费的时间会长,寄存器是集成在 CPU 内部的,由于寄存器的数量少,因此每个寄存 器有独立的名字,从而在访问时速度非常的快。 在 x86 寄存器中,与逆向相关的寄存器有基本寄存器、调试寄存器和控制寄存器。基本寄存器分为 4 类,分别是 8 个通用寄存器、 6 个段寄存器、 1 个指令指针寄存器和 1 个标志寄存器,如图 2-2 所示。
通用寄存器
通用寄存器主要用于各种运算和数据的传输。由图 2-1 可以看出,通用寄存器一共有 8 个,分为两组,分别是数据寄存器和指针变址寄存器。数据寄存器一共有 4 个,每个寄存器 都可以作为一个 32 位、 16 位或 8 位的存储单元来使用,如图 2-2 所示。
对于图 2-2 来讲,可以将一个寄存器分别当 8 位、 16 位或 32 位来使用。 EAX 寄存器可 以存储一个 32 位的数据。 EAX 的低 16 位有另外一个名字叫作 AX,可以存储一个 16 位的数 据。 AX 寄存器又可以分为 AH 和 AL 两个 8 位的寄存器, AH 对应 AX 寄存器的高 8 位, AL 对应 AX 寄存器的低 8 位。
只有数据存储寄存器可以按照这样的方式进行使用。由图 2-1 可知,数据存储寄存器有 EAX、 EBX、 ECX 和 EDX 共 4 个寄存器。
指针变址寄存器可以按照 32 位或 16 位进行使用,如图 2-3 所示。
对于图 2-3 来讲,只可以将一个寄存器分为 32 位或 16 位进行使用。 ESI 寄存器可以存 储 32 位的指针,其中低 16 位可以表示为 SI 来存储 16 位的指针。但是无法像 AX 那样能拆 分成高 8 位和低 8 位的 8 位寄存器。
各通用寄存器可以使用的方式如表 2-1 所列。
关于 8 个通用寄存器的解释如下。
① EAX:累加器,在乘法和除法指令中被自动使用;在 Win32 中,一般用在函数的返 回值中。
② EBX:基址寄存器, DS 段中的数据指针。
③ ECX:计数器, CPU 自动使用 ECX 作为循环计数器,在字符串和循环操作中常用, 在循环指令(LOOP)或串操作中, ECX 用来进行循环计数,每执行一次循环, ECX 都会被 CPU 自动减一。
④ EDX:数据寄存器。 以上 4 个寄存器主要用在算数运算与逻辑运算指令中,常用来保存各种需要计算的值。
⑤ EBP:扩展基址指针寄存器, SS 段中堆栈内数据指针。 EBP 由高级语言用来引用参 数和局部变量,通常称为堆栈基址指针寄存器。
⑥ ESP:堆栈指针寄存器, SS 段中堆栈指针。 ESP 用来寻址堆栈上的数据, ESP 寄存 器一般不参与算数运算,通常称为堆栈指针寄存器。
⑦ ESI:源变址寄存器,字符串操作源指针。
⑧ EDI:目的变址寄存器,字符串操作目标指针。
以上 4 个寄存器主要用作保存内存地址的指针。
ESI 和 EDI 通常用于内存数据的传递,因此才被称为源指针寄存器和目的指针寄存器。 ESI 和 EDI 与特定的指令 LODS、 STOS、 REP、 MOVS 等一起使用,主要用于内存中数据的复制。
ESP 指示堆栈区域的栈顶地址, PUSH、 POP、 CALL、 RET 等指令可以直接用来操作 ESP 指针。 EBP 指示堆栈区域的基地址。
指令指针寄存器
指令指针寄存器 EIP 是一个 32 位的寄存器,在 16 位的环境中,它的名称是 IP。 EIP 寄 存器保存着下一条要执行的指令的地址。程序运行时, CPU 会读取 EIP 中的一条指令的地址, 传送指令到指令缓冲区后, EIP 寄存器的值自动增加,增加的大小即是读取指令的字节大小, 即下一条指令的地址为当前指令的地址加上当前指令的长度。这样, CPU 每次执行完一条指 令后,就会通过 EIP 寄存器读取下一条指令给 CPU,从而让 CPU 继续执行。
特殊情况(其实也算不上通常与特殊,因为存在向上或向下的跳转,所以程序的执行并 非是顺序依次往下执行)是当前指令为一条转移指令,比如 JMP、 JE、 LOOP 等指令,会改 变 EIP 的值,导致 CPU 执行指令产生跳跃性执行,从而构成分支与循环的程序结构。
EIP 寄存器的值在程序中是无法直接修改的,只能通过影响 EIP 的指令间接地进行修改, 比如上面提到的 JMP、 CALL、 RET 等指令。此外,通过中断或异常也可以影响 EIP 的值。
EIP 中的值始终在引导 CPU 的执行
段寄存器
段寄存器用于存放段的基地址,段是一块预分配的内存区域。有些段存放有程序的指令, 有些则存放有程序的变量,另外还有其他的段,如堆栈段存放着函数变量和函数参数等。在 16 位 CPU 中,段寄存器只有 4 个,分别是 CS(代码段)、 DS(数据段)、 SS(堆栈段)和 ES(附加数据段)。
在 32 位的 CPU 中,段寄存器从 4 个扩展为 6 个,分别是 CS、 DS、 SS、 ES、 FS 和 GS。 FS 和 GS 段寄存器也属于附加的段寄存器。
注意:
在 32 位 CPU 的保护模式下,段寄存器的使用与概念完全不同于 16 位的 CPU。由于该部分较为复杂,读 者可具体参考 Intel x86 手册和相关知识。
在逆向中经常会用到 FS 寄存器,它用于存储 SEH、TEB、PEB 等重要的操作系统数据结构
标志寄存器
在 16 位 CPU 中,标志寄存器称为 FLAGS(有的书上是 PSW,即程序状态字寄存器)。 在 32 位 CPU 中,标志寄存器也扩展为 32 位,被称为 EFLAGS。
关于标志寄存器, 16 位 CPU 中的标志寄存器已经基本满足于日常的程序设计及逆向所 用,这里主要介绍 16 位 CPU 中的标志位。标志寄存器如图 2-4 所示。
图 2-4 说明,标志寄存器中的每一个标志位只占 1 位,且 16 位的标志寄存器并没有全部 使用。 16 位的标志寄存器可以分为两部分,分别是条件标志和控制标志。
条件标志寄存器
说明如下:
① OF(OverFlow Flag): 溢出标志位, 用来反映有符号数加减法运算所得结果是否溢出。 如果运算超过当前运算位数所能表示的范围,则称为溢出,该标志位被置为 1,否则为 0。
② SF(Sign Flag):符号标志位,用来反映运算结果的符号位。运算结果为负时为 1, 否则为 0。
③ ZF(Zero Flag):零标志位,用来反映运算结果是否为 0。运算结果为 0 时该标志位 被置为 1,否则为 0。
④ AF(Auxiliary carry Flag):辅助进位标志位。在字操作时,发生低字节向高字节进位 或借位时该标志位被置为 1,否则为 0(注意:在字节操作时,发生低 4 位向高 4 为进位或借 位时该标志位被置为 1,否则为 0)。
⑤ PF(Parity Flag):奇偶标志位,用于反映结果中“1”的个数的奇偶性。如果“1” 的个数为偶数,该标志位被置为 1,否则为 0。
⑥ CF(Carry Flag):进位标志位。运算结果的最高位产生了一个进位或借位,则该标 志位被置为 1,否则为 0。
控制标志寄存器
说明如下:
① DF(Direction Flag):方向标志位,用于串操作指令中,控制地址的变化方向。当 DF 为 0 时,存储器地址自动增加;当 DF 为 1 时,存储器地址自动减少。操作 DF 标志寄存 器可以使用指令 CLD 和 STD 进行复位和置位。
② IF(Interrupt Flag):中断标志位,用于控制外部可屏蔽中断是否可以被处理器响应。 当 IF 为 1 时,允许中断;当 IF 为 0 时,则不允许中断。操作 IF 标志寄存器可以使用 CLI 和 STI 进行复位和置位。
③ TF(Trap Flag):陷阱标志位,用于控制处理器是否进入单步操作方式。当 TF 为 0 时,处理器在正常模式下运行;当 TF 为 1 时,处理器单步执行指令,调试器可以逐条指令 进行执行就是使用了该标志位。
在日常使用的过程中,以上的标志位都是常用的标志位,在学习标志位时要掌握标志位 每一位的作用以及该标志位在第几位。
注意: 16 位 CPU 中的标志位在 32 位 CPU 中依然继续使用,32 位 CPU 扩展了 4 个新的标志位。