x86汇编语言基础

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,变成了EAXEBX等等

A表示Accumulator即 累加器,用累加器进行的操作可能需要更少时间。累加器可用于乘、除、输入/输出等操作,它们的使用频率很高。它是很多加法乘法指令的缺省寄存器。

B表示Base Register即 基地址寄存器,它可作为存储器指针来使用,在内存寻址时存放基地址。

C表示Count Register即 计数寄存器,在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数,是重复(REP)前缀指令和LOOP指令的内定计数器。

D表示Data Register即 数据寄存器,在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。总是被用来放整数除法产生的余数。

它们对低16位数据的存取,不会影响高16位的数据。

eax 是32位寄存器,ax 16是位寄存器,alah是八位寄存器,eax存储的数据是ax的两倍,axalah的两倍。

eax可以存储的数字是DWORD(双字),ax存储的是WORD(字),ALAH存储的是BYTE(字节)

az=ah+al
ah存储的是ax的高8位数据,H=hight
al存储的是ax的低8位数据,L=low

如果eax是红色区域,值等于64636261
那么axeax的低16位,也就是6261
al61,ah62

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)。

变址和指针寄存器

ESIEDI

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

进制表示

汇编一般用十六进制表示内存地址,如0x12341234H表示的值一样

字符串与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中存放的数是40F6H40F6H、40F7H两个单元中存放的数分别为22H23H,如

mov ax,[bx]2223H传送到ax

mov ax,bx40F6H传送到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(封锁)

示例

我们可以把栈想象成一摞扑克牌:

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

参考

http://www.cs.virginia.edu/~evans/cs216/guides/x86.html