汇编基础-2

上回给大家介绍了32位通用寄存器的概念,也收到了一些大佬的不同响应,感谢各位大佬在百忙之中指点不足,然后呢这一期依然是接着上回的继续讲解。

废话不多说,今天带来的内容主要为以下四个方面:

基于64位的通用寄存器以及16位段寄存器的简单介绍​
内存与内存地址的简单介绍
mov指令以及其他汇编指令​
堆栈指令等​

16位段寄存器以及64位通用寄存器

首先还是声明一点,我这里主要介绍的是继续x86的32位的汇编指令,64位的汇编指令以及用法我这里无法为大家做太过于详细的介绍,原因无它,笔者还没能融会贯通。所以为了不给大家引入一个错误的道路,这里仅做简单的介绍以及补充

首先什么是16位段寄存器呢?

在实地址模式中,16位段寄存器表示的是预先分配的内存区域的基址,这个内存区域被称为段。保护模式中,段寄存器存放的是段描述符表指针。一些段中存放程序的指令或代码,其它段存放变量或数据,还有一个堆栈段存放的是局部函数变量和函数参数。​

16位段寄存器上节内容并没有单独的拿出来介绍,这次作为补充做简单介绍

GS 002B  未使用,80386 之后定义的
FS 0053  分段机制,80386 之后定义的
ES 002B  扩展段,在串操作时(比如cmovs)目标操作数的基址是ES,源操作数是DS
DS 002B  数据段,默认的都是DS
CS 0023  代码段,配合EIP使用
SS 002B  堆栈段,凡是基址是EBP或ESP的,段前缀就是SS

这里我们先介绍简单的概念,我们后续主要遇到的还是 DS以及SS;这里不做过多的介绍。

​EAX是默认使用的乘除指令。它常常被称为扩展累加器寄存器
ECX为CPU默认使用的循环计数器
ESP用于寻址堆栈(一种系统内存结构)数据。它极少用于一般算术运算和数据传输,通常被称为扩展堆栈指针寄存器
ESI和EDI用于高速存储器传输指令,有时也被称为扩展源变址寄存器和扩展目的变址寄存器
EBP在高级语言中用来引用堆栈中的函数参数和局部变量。除了高级编程,它不用于一般算术运算和数据传输。它常常被称为扩展帧指针寄存器。
实地址模式中,16位段寄存器表示的是预先分配的内存区域的基址,这个内存区域称为段。保护模式中,段寄存器中存放的是段描述符表指针。一些段中存放程序指令(代码),其他段存放变量(数据),还有一个堆栈段存放的是局部函数变量和函数参数。
EIP为指令指针寄存器,包含下一条将要执行指令的地址。某些机器指令能控制EIP,使得程序分支转向到一个新位置
FFLAGS寄存器包含了独立的二进制位。用干控制CPU的操作,或是反映一些CPU操作的结果。有些指令可以测试和控制这些单独的处理器标志位
状态标志位状态标志位反映了CPU执行的算术和逻辑操作的结果。其中包括:溢出位、符号位、零标志位、辅助进位标志位、奇偶校验位和进位标志位。
进位标志位(CF),与目标位置相比,无符号算术运算结果太大时,设置该标志位。
溢出标志位(OF),与目标位置相比,有符号算术运算结果太大或太小时,设置该标志位
符号标志位(SF),算术或逻辑操作产生负结果时,设置该标志位。
零标志位(ZF),算术或逻辑操作产生的结果为零时,设置该标志位。
辅助进位标志位(AF),算术操作在8位操作数中产生了位3向位4的进位时,设置该标志位。
奇偶校验标志位(PF),结果的最低有效字节包含偶数个1时,设置该标志位,否则清除该标志位。一般情况下,如果数据有可能被修改或损坏时,该标志位用于进行错误检测。
方向标志位(DF),控制了串操作指令(MOVS,CMPSSCASLODS与STOS)设置DF标志位将使得串操作指令地址自动递减(从高地址向低地址处理串)。清除DF标志位将使得串操作指令自动递增(从低地址向高地址处理串)。
中断允许标志位(IF),控制处理器对干可屏蔽中断的处理,设置该标志位位可使处理器响应可屏蔽中断;清除则禁止响应可屏蔽中断。
跟踪标志位(TF),设置可启用单步运行模式来调试程序,清除则禁用单步运行模式。

在这里插入图片描述

后续在第三篇内容我会详细的为大家说明标志寄存器的用法,这里不做过多介绍。

状态标志位的第几位第几位是怎么来的呢?
在这里插入图片描述

00000246

0000 0000 0000 0000 0000 0010 0100 0110

DF位为方向位,为EFL寄存器转换为2进制后低位下标为10的数(下标从0开始),当该位置为0时,movs指令每执行一次,EDI/ESI寄存器地址值增加1或2或4位,取决于是byte还是word还是dword;当DF方向位的值为1时,则会递减1/2/4

64位通用寄存器仅为了解,大家感兴趣的可以自己私底下去学习哈

8  位  AL BL CL DL DIL SIL BPL SPL R8L R9L R10L R11L R12L R13L R14L R15L
16 位  AX BX CX DX DI SI BP SP R8W R9W R10W R11W R12W R13W R14W R15W
32 位 EAX EBX ECX EDX EDI ESI EBP ESP R8D R9D R10D R11D R12D R13D R14D R15D 
64 位 RAX RBX RCX RDX RDI RSI RBP RSP R8 R9 R10 R11 R12 R13 R14 R15

内存和内存地址的简单介绍

每个程序在运行之前都会分配一个4GB的虚拟的内存空间,如下图
在这里插入图片描述

32位​内存地址示例:

​0X00000001​

宽度是32位指的是2进制 32位,一个0代表4位0000,这里是16进制展示,所以是8位;一个字节是8位

​宽度是32位指的是2进制 32位,一个0代表4位0000,这里是16进制展示,所以是8位;一个字节是8位

因为内存太大没有办法取名字,所以使用编号来表示。当我们向内存中存储数据,或者从内存中读取数据的时候,必须使用这个编号。​

一个程序被分配4GB内存是如何计算的呢?

地址能找到多少块内存,按照16进制表示如下(一个0代表二进制的4个0)

ffffffff+1=100000000

每一块内存占8位,因为一个字节占8位

100000000 * 8 = 800000000

转成十进制

34359738368

整个内存能找到多少个字节

34359738368 / 8 = 4294967296

32位地址能寻找的内存有多少k

4294967296 / 1024 = 4194304

32位地址能寻找的内存有多少M

4194304 / 1024 = 4096

32位地址能寻找的内存有多少G

4096 / 1024 = 4

汇编指令

mov指令

mov指令是我们在汇编中最常见的指令,所以这里需要较为详细的讲解

mov 指令
从立即数到内存
从寄存器到内存
从内存到寄存器

在这里插入图片描述

例1:

把一个立即数写到内存

byte这里指的是数据宽度

mov byte ptr ds:[地址]

一个字节是byte 对应8位(AL/AH…)

两个字节是 word 对应16位(AX/CX…)

四个字节是 dword 对应32位(EAX/ECX…)

mov dword ptr ds:[0X0019FA20], 0X12345678

在这里插入图片描述

f8运行后,0X0019FA20内存地址的值已发生改变
在这里插入图片描述

例2:

把寄存器的值写入内存(偷个懒,运行前的就不截图了,直接截运行后的图)

mov dword ptr ds:[0019B000],ecx

在这里插入图片描述

例3:

把内存的值写入寄存器

mov eax, dword ptr ds:[0019B000]

在这里插入图片描述

从内存到寄存器,从寄存器到寄存器,从寄存器到内存可以,但是不能内存到内存或者内存/寄存器到立即数,立即数只是一个数,无法存储

大端存储与小端存储

在这里插入图片描述

对于数据而言,0x1A2C 1A是数据高位,2C是数据低位;对于内存而言,0x00000000是低位,0x00000001是高位(内存地址里有ffffffff个字节,每个字节仅能存储1个byte)

在ollydbg中,可以使用 db dw dd命令获取内存地址

在这里插入图片描述

在 xdbg中,可以ctrl+g 输入内存地址

在这里插入图片描述

在这里插入图片描述

或者直接命令 dump 缺点就是不能指定数据长度
在这里插入图片描述

在这里插入图片描述

采用小端存储
每个字节占一个位,数据低位占内存低位,所以是小端存储,exe基本都是小端存储
在这里插入图片描述

add指令(加法)

在这里插入图片描述

执行前:
在这里插入图片描述

执行后,eax变成eax与ecx的和
在这里插入图片描述

sub指令(减法)

在这里插入图片描述

sub al,byte ptr ds:[0019fa30]

al - 0019fa30内存地址的值的值而后将结果放入 al里

在这里插入图片描述

运行后
在这里插入图片描述

and指令(与运算)

在这里插入图片描述

and dword ptr ds:[0019fa30],eax

用 0019fa30内存地址的值与 eax的值进行与运算,将结果存放入内存地址0019fa30中
在这里插入图片描述

and dword ptr ds:[0019fa30],eax
在这里插入图片描述

or指令(或运算)

在这里插入图片描述

or dword ptr ds:[0019fa30],eax

在这里插入图片描述

或运算,将内存地址的值与eax的值进行或运算,而后将值存入值内存地址里
在这里插入图片描述

xor指令(异或运算)

在这里插入图片描述

xor dword ptr ds:[0019fa30],eax

在这里插入图片描述

异或运算只有不一样的才是真,一样的就为0
在这里插入图片描述

not指令(非指令)

在这里插入图片描述

not dword ptr ds:[0019fa30]

在这里插入图片描述

执行结束后,取反,0为1,1为0,所以变成 FFFFFFFF
在这里插入图片描述

movs指令

默认的就用ESI和EDI;ESI和EDI存储的是内存地址的编号

在这里插入图片描述

在这里插入图片描述

运行后,
在这里插入图片描述

stos指令

在这里插入图片描述

在这里插入图片描述

执行后,成功将AL的值(EAX的后两位)存储到EDI寄存地的内存地址中,变成986D344

rep指令

ecx相当于计数器,执行一次减一
在这里插入图片描述

mov ecx,10 这里是16进制的10,会重复执行16次
rep movsd

在这里插入图片描述

运行第一次,ECX变成00000010

在这里插入图片描述

继续运行,会重复执行将ESI的值赋给EDI,执行16次,
在这里插入图片描述

堆栈的使用

运行程序后,堆栈内存地址如何寻找?运行程序后,ESP寄存器的值即为当前堆栈的地址值
在这里插入图片描述

push指令

在这里插入图片描述

使用push相当于执行了三步,本来需要指定当前堆栈的位置,使用PUSH指令后,程序会自动执行到当前位置,esp会自动跟进。栈顶指针esp从大值到小值,如果不知道堆栈位置,那么数据可能会被覆盖

push指令可以push立即数、eax寄存器、甚至是内存地址

push 3
push eax
push dword ptr ds:[0019fa20]

在这里插入图片描述

pop指令

在这里插入图片描述

push 1 //将1值存储至堆栈内
push 2 //…
push 3 //…
push ecx //ECX计数器此时有初始值,我需要使用ECX,但是为了不影响后续,只能将他的初始值存储到堆栈中,使用完后再进行恢复
mov ecx,10 //更改ECX的值
mov ecx, dword ptr ds:[esp] //使用完ECX后,再将存储在堆栈(esp的值就是初始ecx的堆栈地址,因为Mov ecx,10的时候并没有对堆栈地址进行修改sub指令,所以ecx的堆栈地址依然是push ecx的地址,里面存储的值也是ecx初始的值)
add esp,4 //由于esp堆栈地址的值是递减的,所以不使用堆栈地址后应恢复,将ecx占用的地址覆盖掉,使用add 4命令会将堆栈地址往前移动4位,下次使用会直接覆盖

在这里插入图片描述

执行一部分后

在这里插入图片描述

使用pop指令可直接达到这个效果,
pop eax 将堆栈当前地址的值存储到eax里,同时esp(堆栈地址)的值递增4个
pop ebx

在这里插入图片描述

EIP指令​

EIP存储的值是CPU下一次执行的地址(就是汇编里的地址)

EIP寄存器是32位,所以执行写地址的时候只能是dword

JMP指令

JMP指令相当于mov指令,只是他是专门用来修改EIP寄存器的

JMP 1

JMP EAX

JMP dword ptr ds:[0019fa20]

运行结束后,EIP地址会跳转至77891005

在这里插入图片描述

在这里插入图片描述

call指令(普通指令f8执行,call指令f7执行)

call指令会将当前的cpu执行地址传入EIP寄存器当中,同时ESP会减少四个,会将call指令的下一个指令的内存地址写入到ESP(堆栈)当中

在这里插入图片描述

ret指令

ret执行前
在这里插入图片描述

ret执行后
在这里插入图片描述

INT 3 指令

相当于CC 断点,F2

猜你喜欢

转载自blog.csdn.net/weixin_48421613/article/details/124230058