X86汇编

在网上无意间看到“编程达人”的一个讲汇编的视频,觉得讲的挺好的,刚好我也一直没学明白这个东西,然后就花了半天时间看完了,看到头昏脑涨就记住了下边这些东西。

通用寄存器

32位16位8位
EAXAXAL
EBXBXBL
ECXCXCL
EDXDXDL
ESPSPAH
EBPBPCH
ESISIDH
EDIDIBH
mov eax, 1

使用内存

1.把立即数写入内存

1
mov byte ptr ds:[0018FFF0], 1 # 将立即数1 写入内存地址[0018FFF0]中,写入长度为一个字节

2.把寄存器的值写入内存

1
mov DWORD ptr ds:[0018FFF0], eax # 将立即数1 写入内存地址[0018FFF0]中,写入长度为一个字节

3.把内存中的值写入寄存器

1
mov EAX, BWORD PTR DS:[0018FFF0]

4.内存到内存需要特殊的指令
后边会有

内存地址的五种表示形式

  • 形式一: [立即数]

  • 形式二: [reg] reg代表八个通用寄存器中的任何一个

    1
    
    mov dword ptr ds:[eax], 12345678 # 其中eax中保存内存的地址
    
  • 形式三:[reg+立即数]

    1
    
    mov dword ptr ds:[eax+4], 12345678 # 其中eax+4中保存内存的地址
    
  • 形式四:[reg+reg*{1,2,4,8}]

    1
    2
    3
    
    mov eax, 13ffc4
    mov ecx, 2
    mov dword ptr ds:[eax+ecx*4]
    
  • 形式五:[reg+reg*{1,2,4,8}+立即数]

存储模式

大端模式:数据高位在低地址,数据低位在高地址

小端模式:数据低位在低地址

0x1A2C3E4F的小端模式

地址数字
0x004F
0x013E
0x022C
0x031A

80x86一般都是小端模式

汇编指令

基础指令

1.mov

格式解释
mov r/m8, r8r代表通用寄存器,8代表是8位的
mov r/m16, r16m代表内存
mov r/m32, r32
mov r8, r/m8
mov r16, r/m16
mov r32, r/m32
mov r8, imm8imm8 代表8位立即数
mov r16, imm16
mov r32, imm32

2.ADD

格式解释
ADD r/m8, imm8中间那个表示目的地址
ADD r/m32, r32
ADD r32, r/m32

3.SUB和add指令类似

4.AND

5.OR

6.XOR

7.NOT

连续复制数据指令

8.MOVS 移动数据, 内存-内存

指令简写
MOVS BYTE PTR ES:[EDI], BYTE PTR DS:[ESI]MOVSB
MOVS WORD PTR ES:[EDI], WORD PTR DS:[ESI]MOVSW
MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI]MOVSD

EDI:存储一个地址,目标地址 ESI:存储的是一个地址,源数据的地址

EDI和ESI内的地址会根据EFI的第十位DF的值改变,0 :自增, 1:自减

9.STOS: 将AL/AX/EAX的值存储到[EDI]指定的内存单元

格式解释简写
STOS BYTE PTR ES:[EDI]将AL中的内容写到[EDI]指向的内存空间,EDI内的地址+1/-1(由DF位确定)STOSB
STOS WORD PTR ES:[EDI]STOSW
STOS DWORD PTR ES:[EDI]STOSD

10.REP : 按照计数寄存器(ECX)中指定的次数重复执行字符串指令

MOV ECX, 10
REP MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI] #或者 REP MOVSD

堆栈操作

ESP寄存器存储栈顶指针,从大地址逐渐减小,向低地址生长。

11.push : 向堆栈中压入数据,并修改esp的值。

格式同义
PUSH r32
PUSH r16
PUSH m32PUSH DWORD PTR DS:[18FFA4]
PUSH m16
PUSH imm8/imm16/imm32

12.pop:

格式同义
POP r32
POP r16
POP m32
POP m16

修改EIP的指令

EIP寄存器里边存储的是CPU下一时刻应该执行的指令。

13.jmp :寄存器/立即数/内存地址
14.call : 先push下一行地址,然后jmp,
15.ret : ADD ESP, 4; MOV EIP, [ESP-4];

函数

函数调用

jmp 来执行函数

call 来调用函数

ret 返回call调用的函数

参数传递

寄存器传参:ecx,edx存储参数,eax存储返回值。

堆栈传参:先将参数压入堆栈,然后call调用函数,eax存储返回值。

1
2
3
4
5
6
7
8
9
push 1
push 2
call 41840d
add esp, 8 #外平栈

41840d:
mov eax, dword ptr ds:[esp+8]
mov eax, dword ptr ds:[esp+4]
ret # ret 8 内平栈

堆栈平衡:

  1. 如果要返回父程序,则当我们在堆栈中进行堆栈操作的时候,一定要保证在RET指令前,ESP指向的是我们压入栈中的地址。
  2. 如果通过堆栈传递参数,那么在函数执行完毕后,要平衡参数导致的堆栈变化。

ESP寻址

调用函数的时候要保存现场,函数返回前要恢复现场。

EBP寻址

EBP寄存器保存的是栈底的地址。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
PUSH 1
PUSH 2
CALL 4183EE

4183EE:
# 保存
PUSH EBP
MOV EBP, ESP

# 执行函数
MOV EAX, DWORD PTR SS:[EBP+8]
ADD EAX, DWORD PTR SS:[EBP+C]

# 准备返回
MOV ESP, EBP
POP EBP

# 返回
ret 8

JCC指令

标志寄存器EFL

31-1211109876543210
OFDFIFTFSFZF0AF0PF1CF
溢出方向中断使能单步符号0辅助进位0奇偶1进位
  • CF (Carry flag):若算术操作产生的结果在最高有效位发生进位或者借位将其置1,反之清零。

  • PF(parity flag): 如果结果的最低有效字节(最低位的一个字节)包含偶数个1,则置1,否则清零。

  • AF (Auxiliary Carry Flag):如果算术操作在结果的第三位发生进位或者借位则将该标志置1,主要应用于BCD运算。

  • ZF (Zero Flag): 如果运算的结果为0,则设置为1,否则清零,经常和cmp或者test等指令一起使用

1
2
3
4
5
mov eax, 100
mov ecx, 100
cmp eax, ecx # cmp指令相当于SUB指令,但是相减的结果不保存到第一个操作数中

test eax, eax # test指令相当于AND指令,但是与的结果并不保存在第一个操作数中
  • SF (Sign Flag): 该标志被设置为有符号整数的最高有效位,0为正,反之为负

  • OF (Overflow flag) :溢出标志用于反映有符号数加减运算所得出结果是否溢出。

    如果是无符号数运算,是否溢出看CF位;

    有符号数看OF

  • DF (Direction Flag):这个方向标志控制串指令(MOVS, CMPS, SCAS, LODS以及STOS)。设置DF标志使得串指令自动递减,清除该标志则使得串指令自动递增。STD和CLD指令分别用于设置以及清除DF标志。

指令意义标志位
JE, JZ结果为零则跳转(相等时跳转)ZF=1
JNE, JNZ结果不为零则跳转(不相等时跳转)ZF=0
JS结果为负则跳转SF=1

清华大学-汇编语言程序设计

基础知识

各类指令集初步

数制与整数表示

浮点数表示

X86汇编(AT&T风格)

AT&T风格的汇编

movl source, dist

其中mov后边的l表示操作的字长, AT&T先原地址后目标地址

80x86计算机组成与保护模式

​ 80386有三种工作模式:

1
2
3
- 实模式:操作相当于一个可进行32位快速运算的8086
- 保护模式:是80x86设计目标全部达到的工作模式,通过对程序使用的存储区采用分段、分页的存储管理机制,以达到分级使用、互不干扰的保护目的。能为每个任务提供一台虚拟处理器,使每个任务单独执行,快速切换。
- 虚拟8086模式:保护模式下同时模拟多个8086处理器

X86指令系统与寻址方式

  • 间接寻址 (R): Mem[Reg[R]] 表示寄存器R指定内存的地址,例:movl (%ecx), %eax
  • 基址+偏移量 寻址 D(R) : Mem[Reg[R]+D] 寄存器R指定内存起始地址,常数D给出偏移量, 例,movl 8(%ebp), %edx表示把%ebp中取出一个数加上8作为内存的地址。
  • 变址寻址 D(Rb, Ri, S) : Mem[Reg[Rb] + S* Reg[Ri] +D], 常量D给出地址偏移量,基址寄存器Rb是8个通用寄存器之一,索引寄存器Ri,S是比例因子,取值为1,2,4,8。

leal src, dist 指令将src表示的地址计算出来并传递给dist。 比如:leal (%edx, %edx, 2), %edx

C与x86汇编

x86汇编语言程序格式与基本编程

AT&T和Intel汇编格式主要区别

  • 在Intel格式中大多使用大写字母,而AT&T格式中都使用小写字母
  • 在AT&T格式中,寄存器名上要加上“%”作为前缀,而Intel格式则不带前缀。
  • 在AT&T格式中,指令的源操作数在前,目标在后,恰好和Intel格式完全相反。
  • 在AT&T格式中,访问内存指令的操作数大小由操作码后缀来决定。用作操作码后缀的字母有b(表示8位),w(表示16位), l(32位)。而在Intel格式中,则是在表示内存单元的操作数前面加上“BYTE PTR” , “WORD PTR”, “DWORD PTR"来表示的。
  • AT&T中的立即数要加上”$“作为前缀,Intel格式不带前缀。
  • AT&T格式中,绝对转移或者调用指令jump/call的操作数,要加上”*“作为前缀。Intel格式不用。
  • 间接寻址的一般格式:Intel格式:SECTION:[BASE + INDEX*SCALE+DISP];AT&T格式:section:disp(base, index, scale)
  • 远程的转移指令和子程序调用指令的操作码名称,在AT&T中为"ljmp"和"lcall”,而在Intel格式中为“JMP FAR”和"CALL FAR"。

linux内核中的汇编语言规则

哈工大李治军操作系统

linux内核中使用了三种汇编

  • as86汇编:能产生16位代码的Intel 8086汇编。 主要构成了bootsect, setup代码。
  • GNU as汇编:能产生32位代码的AT&T系统V语法。主要构成保护模式代码。
  • GNU内嵌汇编:进程调度代码。