x86 汇编 AsSemBly
寄存器(Registers)
eax
, ebx
, ecx
, edx
, esi
, edi
, ebp
, esp
等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。如果用C语言来解释,可以把这些寄存器当作变量看待。
段寄存器 (Segment Registers)
ECS
—— 代码段寄存器(Code Segment),其值为代码段的段值。
EDS
—— 数据段寄存器(Data Segment),其值为数据段的段值。
EES
—— 附加段寄存器(Extra Segment),其值为附加数据段的段值。
EFS
—— 附加段寄存器(Extra Segment),其值为附加数据段的段值。
EGS
—— 附加段寄存器(Extra Segment),其值为附加数据段的段值。
ESS
—— 堆栈段寄存器(Stack Segment),其值为堆栈段的段值。
变址寄存器(Index Registers)
DI destination index (16 bit)
SI source index (16 bit)
基址寄存器(Index Registers)
数据寄存器
EAX
EBX
ECX
EDX
其中E
代表extended
。在i386以上的32位CPU中,这些寄存器扩展成了32位的,名字就是在原来16位的名字前面加一个字母E,变成了EAX
,EBX
等等
A
表示Accumulator
即 累加器,用累加器进行的操作可能需要更少时间。累加器可用于乘、除、输入/输出等操作,它们的使用频率很高。它是很多加法乘法指令的缺省寄存器。
B
表示Base Register
即 基地址寄存器,它可作为存储器指针来使用,在内存寻址时存放基地址。
C
表示Count Register
即 计数寄存器,在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL
来指明移位的位数,是重复(REP
)前缀指令和LOOP指令的内定计数器。
D
表示Data Register
即 数据寄存器,在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。总是被用来放整数除法产生的余数。
它们对低16位数据的存取,不会影响高16位的数据。
eax
是32位寄存器,ax
16是位寄存器,al
和ah
是八位寄存器,eax
存储的数据是ax
的两倍,ax
是al
和ah
的两倍。
eax
可以存储的数字是DWORD
(双字),ax
存储的是WORD
(字),AL
和AH
存储的是BYTE
(字节)
az
=ah
+al
ah
存储的是ax
的高8位数据,H=hight
al
存储的是ax
的低8位数据,L=low
如果eax
是红色区域,值等于64636261
那么ax
是eax
的低16位,也就是6261
al
是61
,ah
是62
eax
保存所有API函数的返回值
由于存储的数据大小关系,AX、BX、CX和DX不能作为基址和变址寄存器来存放存储单元的地址, 32位寄存器EAX、EBX、ECX和EDX不仅可传送数据、暂存数据保存算术逻辑运算结果,而且也可作为指针寄存器,所以,这些32位寄存器更具有通用性。(什么是基址,什么是变址以后会说到)
ESP
(Extended Stack Pointer) 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP
也就越来越小。在32位平台上,ESP
每次减少4字节。
ESI/EDI
分别叫做"源/目标索引寄存器"(source/destination index),因为在很多字符串操作指令中, DS:ESI
指向源串,而ES:EDI
指向目标串.
EBP
是"基址指针"(Extended Base Pointer), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer)。
变址和指针寄存器
ESI
和EDI
EDI
(Destination Index) 目的变址寄存器 16位
ESI
(Source Index) 源变址寄存器 16位
指针寄存器
ESP
和EBP`
ESP
(Extended Stack Pointer)
EBP
Extended Base Pointer
IP
(Instruction Pointer) 指令指针寄存器
MOV
传送字或字节.
MOVSX
先符号扩展,再传送.
MOVZX
先零扩展,再传送.
PUSH
把字压入堆栈.
POP
把字弹出堆栈.
PUSHA
把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
POPA
把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
PUSHAD
把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
POPAD
把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
BSWAP
交换32位寄存器里字节的顺序
XCHG
交换字或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数)
CMPXCHG
比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )
XADD
先交换再累加.( 结果在第一个操作数里 )
XLAT
字节查表转换.
── BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即
0-FFH); 返回 AL 为查表结果. ( [BX+AL]->AL )
输入输出端口传送指令
IN
I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )
OUT
I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )
输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,
其范围是 0-65535.
目的地址传送指令.
LEA
装入有效地址.
例: LEA DX,string ;把偏移地址存到DX.
LDS
传送目标指针,把指针内容装入DS.
例: LDS SI,string ;把段地址:偏移地址存到DS:SI.
LES
传送目标指针,把指针内容装入ES.
例: LES DI,string ;把段地址:偏移地址存到ES:DI.
LFS
传送目标指针,把指针内容装入FS.
例: LFS DI,string ;把段地址:偏移地址存到FS:DI.
LGS
传送目标指针,把指针内容装入GS.
例: LGS DI,string ;把段地址:偏移地址存到GS:DI.
LSS
传送目标指针,把指针内容装入SS.
例: LSS DI,string ;把段地址:偏移地址存到SS:DI.
标志传送指令.
LAHF
标志寄存器传送,把标志装入AH.
SAHF
标志寄存器传送,把AH内容装入标志寄存器.
PUSHF
标志入栈.
POPF
标志出栈.
PUSHD
32位标志入栈.
POPD
32位标志出栈.
算法指令
ADD
两数相加,不加进位位
ADDC
两数相加,同时再加个进位位。进位当时为1就加1,为0就加0(相当于不加)
ADC
带进位加法.
INC
加 1.
DEC
减 1.
AAA
加法的ASCII码调整.
DAA
加法的十进制调整.
SUB
减法.
SBB
带借位减法.
NEC
求反(以 0 减之).
CMP
比较.(两操作数作减法,仅修改标志位,不回送结果).
AAS
减法的ASCII码调整.
DAS
减法的十进制调整.
MUL
无符号乘法.
IMUL
整数乘法.
以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),
AAM
乘法的ASCII码调整.
DIV
无符号除法.
IDIV
整数除法.
以上两条,结果回送:
商回送AL,余数回送AH, (字节运算);
或 商回送AX,余数回送DX, (字运算).
AAD
除法的ASCII码调整.
CBW
字节转换为字. (把AL中字节的符号扩展到AH中去)
CWD
字转换为双字. (把AX中的字的符号扩展到DX中去)
CWDE
字转换为双字. (把AX中的字符号扩展到EAX中去)
CDQ
双字扩展. (把EAX中的字的符号扩展到EDX中去)
逻辑运算指令
AND
与运算.
or
或运算.
XOR
异或运算.
NOT
取反.
TEST
测试.(两操作数作与运算,仅修改标志位,不回送结果).
SHL
逻辑左移.
SAL
算术左移.(=SHL)
SHR
逻辑右移.
SAR
算术右移.(=SHR)
ROL
循环左移.
ROR
循环右移.
RCL
通过进位的循环左移.
RCR
通过进位的循环右移.
以上八种移位指令,其移位次数可达255
次.
移位一次时, 可直接用操作码. 如 SHL AX,1
移位>1次时, 则由寄存器CL给出移位次数.
如
MOV CL,04
SHL AX,CL
返回
ret
(return)
retn
(return near)
retf
(return far)
调用
call
proc near
endp
进制表示
汇编一般用十六进制表示内存地址,如0x1234
和1234H
表示的值一样
字符串与16进制对应
'/' - 0x2f
'b' - 0x62
'h' - 0x68
'i' - 0x69
'n' - 0x6e
'n' - 0x73
对应表 http://www.mokuge.com/tool/asciito16/
符号
[]
中括号,表示间接寻址
如指令:
mov ax,[bx]
bx
和[bx]
的区别是,bx
操作数的是bx
中存放的数,[bx]
操作数是以bx
中存放的数为地址的单元中的数。
就是说,bx
存放的数据是一个地址,而[bx]
就是指bx
存放的这个地址上的数据。
比如bx
中存放的数是40F6H
,40F6H、40F7H
两个单元中存放的数分别为22H
和23H
,如
mov ax,[bx]
将2223H
传送到ax
中
mov ax,bx
将40F6H
传送到ax
中
数据类型
dword
(double word)它是无符号long型(UNSIGN LONG),4个字节长度,一个word
是2个字节长度,那么dword
是4个字节。计算机1个字节是8位,一个dword
是4*8=32位(32bit)
计算机通常用十六进制表示内存地址,1位=4个2进制,那么
字符串'abcd'的DWORD
值为0x64636261
ptr
pointer缩写 即指针
db
定义字节类型变量,一个字节数据占1个字节单元,读完一个,偏移量加1
dw
定义字类型变量,一个字数据占2个字节单元,读完一个,偏移量加2
dd
定义双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加4
dq
由4个字(8个字节)组成一个四字类型,它总共有64个二进制位,当然,也就有更大的数据表示范围,但在汇编语言中很少使用该数据类型。
串指令
DS:SI
源串段寄存器 :源串变址.
ES:DI
目标串段寄存器:目标串变址.
CX
重复次数计数器.
AL/AX
扫描值.
D
标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量.
Z
标志 用来控制扫描或比较操作的结束.
MOVS
串传送.
( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
CMPS
串比较.
( CMPSB 比较字符. CMPSW 比较字. )
SCAS
串扫描.
把AL或AX的内容与目标串作比较,比较结果反映在标志位.
LODS
装入串.
把源串中的元素(字或字节)逐一装入AL或AX中.
( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. )
STOS
保存串.
是LODS的逆过程.
REP
当CX/ECX<>0时重复.
REPE/REPZ
当ZF=1或比较结果相等,且CX/ECX<>0时重复.
REPNE/REPNZ
当ZF=0或比较结果不相等,且CX/ECX<>0时重复.
REPC
当CF=1且CX/ECX<>0时重复.
REPNC
当CF=0且CX/ECX<>0时重复.
程序转移指令
1>
无条件转移指令 (长转移)
JMP
无条件转移指令
CALL
过程调用
RET/RETF
过程返回.
2>
条件转移指令 (短转移,-128到+127的距离内)
( 当且仅当(SF XOR OF)=1时,OP1<OP2 )
JA/JNBE
不小于或不等于时转移.
JAE/JNB
大于或等于转移.
JB/JNAE
小于转移.
JBE/JNA
小于或等于转移.
以上四条,测试无符号整数运算的结果(标志C和Z).
JG/JNLE
大于转移.
JGE/JNL
大于或等于转移.
JL/JNGE
小于转移.
JLE/JNG
小于或等于转移.
以上四条,测试带符号整数运算的结果(标志S,O和Z).
JE/JZ
等于转移.
JZ(Jump if Zero)是此前的运算结果为0时跳转。
JNE/JNZ
不等于时转移.
JC
有进位时转移.
JNC
无进位时转移.
JNO
不溢出时转移.
JNP/JPO
奇偶性为奇数时转移.
JNS
符号位为 "0" 时转移.
JO
溢出转移.
JP/JPE
奇偶性为偶数时转移.
JS
符号位为 "1" 时转移.
3>
循环控制指令(短转移)
LOOP CX
不为零时循环.
LOOPE/LOOPZ CX
不为零且标志Z=1时循环.
LOOPNE/LOOPNZ CX
不为零且标志Z=0时循环.
JCXZ CX
为零时转移.
JECXZ ECX
为零时转移.
4>
中断指令
INT
中断指令
INTO
溢出中断
IRET
中断返回
5>
处理器控制指令
HLT
处理器暂停, 直到出现中断或复位信号才继续.
WAIT
当芯片引线TEST为高电平时使CPU进入等待状态.
ESC
转换到外处理器.
LOCK
封锁总线.
NOP
空操作.
STC
置进位标志位.
CLC
清进位标志位.
CMC
进位标志取反.
STD
置方向标志位.
CLD
清方向标志位.
STI
置中断允许位.
CLI
清中断允许位.
伪指令
DW
定义字(2字节).
PROC
定义过程.
ENDP
过程结束.
SEGMENT
定义段.
ASSUME
建立段寄存器寻址.
ENDS
段结束.
END
程序结束.
处理机控制指令
标志处理指令
CLC
(进位位置0指令)
CMC
(进位位求反指令)
STC
(进位位置为1指令)
CLD
(方向标志置1指令)
STD
(方向标志位置1指令)
CLI
(中断标志置0指令)
STI
(中断标志置1指令)
NOP
(无操作)
HLT
(停机)
WAIT
(等待)
ESC
(换码)
LOCK
(封锁)
示例
-
mov eax, dword ptr [12345678]
//把内存地址12345678
中的双字型(32位)数据赋给eax
-
add eax,-2 ;
//可以认为是给变量eax
加上-2
这样的一个值。 -
push ebp ;保存当前ebp mov ebp,esp ;EBP设为当前堆栈指针 sub esp, xxx ;预留xxx字节给函数临时变量.
-
栈的说明
我们可以把栈想象成一摞扑克牌:
PUSH
为栈增加一个元素的操作是push,相当于在这摞扑克牌最上面再放一张
POP
从栈中取出一个元素的操作叫做POP,相当于从这摞扑克牌取出最上面的一张。
TOP
标识栈顶位置,并且是动态变化的。每做一次PUSH 操作,它都会自增1;相反,每做一次POP 操作,它会自减1。
栈顶元素
相当于扑克牌最上面一张,只有这张牌的花色是当前可以看到的。
BASE
标识栈底位置,它记录着扑克牌最下面一张的位置。BASE
用于防止栈空后继续弹栈(牌发完时就不能再去揭牌了)。一般情况下,BASE 是不会变动的。
- 简单例子对应
C语言
void funtction1() {
int A = 10;
A += 66;
}
汇编:
funtction1:
pushl %ebp #
movl %esp, %ebp #,
subl $4, %esp #,
movl $10, -4(%ebp) #, A
leal -4(%ebp), %eax #,
addl $66, (%eax) #, A
leave
ret
解释:
1. push ebp
2. copy stack pointer to ebp
3. make space on stack for local data
4. put value 10 in A (this would be the address A has now)
5. load address of A into EAX (similar to a pointer)
6. add 66 to A