汇编语言入门 第一章介绍了基础的数据存储及表示形式,并且在 OllyDbg 中具体地查看了数据在内存 中的形式。关于逆向,掌握逆向工具与掌握逆向知识是同等的重要,在学习逆向知识的时候 配合工具一起去分析、去查看、去调试,这样的效果是最好的。当然,能更高效率、效果地 进行逆向是开发自己个性化的逆向工具、插件等。 本章主要介绍关于汇编语言的知识,同样在介绍这些知识时是结合 OllyDbg 一起进行 学习的。在 OllyDbg 中可以直观地观察寄存器、内存以及堆栈的变化。读过计算机专业的 读者可能都清楚,当时在学校上汇编课程时,对于代码、寄存器、数据和堆栈的概念等都 是在大脑中进行想象,即使是使用 debug 进行调试,也并不能很直观地将这些概念具体地 展现出来。 通过本章的学习,笔者会将常用的汇编指令、关于堆栈的操作都介绍给读者,使读者可 以快速地对汇编相关的知识有一个快速的了解,具体到以后的实际情况中,读者可以通过查 询指令手册或帮助文档来进一步地深入学习。 本章关键字: 汇编 指令
2.1 x86 汇编语言介绍 |
读者希望在逆向方面有一定发展的话,最好买一本关于汇编语言的文章来进行学习。现 在计算机专业毕业的学生都学过汇编语言,但是大部分人认为学的只是 Intel 8086 下的汇编 指令,枯燥、乏味、不具备实用性。其实,作为汇编语言的入门,学习 8086 的汇编指令已经 基本足够了。目前的硬件都是 x86 兼容架构的,无论多复杂的程序,最终都将成为 x86 指令。 作为逆向的入门,只要掌握 80x86 的常用指令、寄存器的用法、堆栈的概念和数据在内存中 的存储,基本就够用了。 对于入门,有以上要求就足够了。如果希望有深入的发展,对于汇编语言的学习还 是要深入进行研究。本章站在逆向工程入门的起点,抛开各种复杂的原理及理论知识, 只简单讲述 x86 常用的汇编指令的用法。当然,笔者依然是结合 OllyDbg 这款调试器来 直观地介绍汇编语言,以让读者在学习的过程中能够清楚地了解汇编指令每一步都在做 什么。 | 2.1.1 寄存器 任何程序的执行,归根结底,都是存放在存储器里的指令序列执行的结果。寄存器用来 存放程序运行中的各种信息,包括操作数地址、操作数、运算的中间结果等。下面就来熟悉 各种寄存器。 1. CPU 工作模式 x86 体系的 CPU 有两种基本的工作模式,分别是实模式和保护模式。 实模式也称为实地址模式,实现了 Intel 8086 处理器的程序设计环境。该模式被早期的 Windows 9x 和 DOS 所支持,实模式下可以访问的内存为 1MB,实模式可以直接访问硬件, 比如直接对端口进行操作、对中断进行操作。现在的 CPU 仍然支持实模式,一是为了与早期 的 CPU 架构保持兼容,二是因为所有的 x86 架构处理器都是从实模式引导起来的。 保护模式是处理器主要的工作模式, Linux 和 Windows NT 内核的系统都工作在 x86 的 保护模式下。保护模式下,每个进程可以访问的内存地址为 4GB,且进程间是隔离的。 2.寄存器介绍 寄存器(Register)是 CPU 内部用于高速存储数据的小型存储单元,访问速度比内存快 很多,而且价格也高很多(在单位价格内,寄存器的价格要比内存贵,内存要比硬盘贵),但 是寄存器和内存都是用来存储数据的。 CPU 访问内存中的数据时有一个寻址的过程,因此访 问内存花费的时间会长,寄存器是集成在 CPU 内部的,由于寄存器的数量少,因此每个寄存 器有独立的名字,从而在访问时速度非常的快。 在 x86 寄存器中,与逆向相关的寄存器有基本寄存器、调试寄存器和控制寄存器。在本 章要讲解的寄存器是基本寄存器。基本寄存器分为 4 类,分别是 8 个通用寄存器、 6 个段寄 存器、 1 个指令指针寄存器和 1 个标志寄存器,如图 2-1 所示。 3.通用寄存器 通用寄存器主要用于各种运算和数据的传输。由图 2-1 可以看出,通用寄存器一共有 8 个,分为两组,分别是数据寄存器和指针变址寄存器。数据寄存器一共有 4 个,每个寄存器 都可以作为一个 32 位、 16 位或 8 位的存储单元来使用,如图 2-2 所示
对于图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 指示堆栈区域的基地址。 4.指令指针寄存器 指令指针寄存器 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 的执行。 5.段寄存器 段寄存器用于存放段的基地址,段是一块预分配的内存区域。有些段存放有程序的指令, 有些则存放有程序的变量,另外还有其他的段,如堆栈段存放着函数变量和函数参数等。在 16 位 CPU 中,段寄存器只有 4 个,分别是 CS(代码段)、 DS(数据段)、 SS(堆栈段)和 ES(附加数据段)。 在 32 位的 CPU 中,段寄存器从 4 个扩展为 6 个,分别是 CS、 DS、 SS、 ES、 FS 和 GS。 FS 和 GS 段寄存器也属于附加的段寄存器 |
---|
6.标志寄存器 在 16 位 CPU 中,标志寄存器称为 FLAGS(有的书上是 PSW,即程序状态字寄存器)。 在 32 位 CPU 中,标志寄存器也扩展为 32 位,被称为 EFLAGS。 |
------------------------------------------------------------------------------------------------关于标志寄存器, 16 位 CPU 中的标志寄存器已经基本满足于日常的程序设计及逆向所 用,这里主要介绍 16 位 CPU 中的标志位。标志寄存器如图 2-4 所示。 |
---|