学习汇编的第三天,记录学习汇编的最后一部分。

逻辑指令

逻辑指令有

  • AND-与
  • OR-或
  • XOR-异或
  • TEST-配合跳转,工作原理与AND相同,但不改变操作数
  • NOT-取反

条件

条件本质上就是利用跳转,实现走入不同的分支
可以分为

  • 无条件跳转
  • 条件跳转

无条件跳转

由JMP指令执行。JMP指令提供一个标签名称,其中控制流立即转移,语法为:

1
JMP	label

示例:

1
2
3
4
5
6
7
8
MOV  AX, 00    ; Initializing AX to 0
MOV BX, 00 ; Initializing BX to 0
MOV CX, 01 ; Initializing CX to 1
L20:
ADD AX, 01 ; Increment AX
ADD BX, AX ; Add AX to BX
SHL CX, 1 ; shift left CX, this in turn doubles the CX value
JMP L20 ; repeats the statements

条件跳转

条件跳转需要使用CMP指令与J?指令共同完成。

CMP指令

比较两个操作数,不会干扰目标或源操作数,与条件跳转指令一起用于决策,语法为:

1
CMP destination, source

示例:

1
2
3
4
5
CMP DX,	00  ; Compare the DX value with zero
JE L7 ; If yes, then jump to label L7
.
.
L7: ...

条件跳转指令

其实我还不熟悉,用的时候再熟悉吧。

用于算术运算的有符号数据上使用的条件跳转指令

说明 描述 已测试标志
JE/JZ Jump Equal or Jump Zero ZF
JNE/JNZ Jump not Equal or Jump Not Zero ZF
JG/JNLE Jump Greater or Jump Not Less/Equal OF, SF, ZF
JGE/JNL Jump Greater/Equal or Jump Not Less OF, SF
JL/JNGE Jump Less or Jump Not Greater/Equal OF, SF
JLE/JNG Jump Less/Equal or Jump Not Greater OF, SF, ZF

用于逻辑运算的无符号数据的条件跳转指令

说明 描述 已测试标志
JE/JZ Jump Equal or Jump Zero ZF
JNE/JNZ Jump not Equal or Jump Not Zero ZF
JA/JNBE Jump Above or Jump Not Below/Equal CF, ZF
JAE/JNB Jump Above/Equal or Jump Not Below CF
JB/JNAE Jump Below or Jump Not Above/Equal CF
JBE/JNA Jump Below/Equal or Jump Not Above AF, CF

条件跳转指令有特殊用途并检查标志的值

说明 描述 已测试标志
JXCZ Jump if CX is Zero none
JC Jump If Carry CF
JNC Jump If No Carry CF
JO Jump If Overflow OF
JNO Jump If No Overflow OF
JP/JPE Jump Parity or Jump Parity Even PF
JNP/JPO Jump No Parity or Jump Parity Odd PF
JS Jump Sign (negative value) SF
JNS Jump No Sign (positive value) SF

循环

语法

1
LOOP label

其中,label是目标标签,用于识别跳转指令中的目标指令。
LOOP指令假定ECX寄存器包含循环计数,知道ECX寄存器值归零。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
section	.text
global _start ;must be declared for using gcc

_start: ;tell linker entry point
mov ecx,10
mov eax, '1'

l1:
mov [num], eax
mov eax, 4
mov ebx, 1
push ecx

mov ecx, num mov edx, 1 int 0x80

mov eax, [num]
sub eax, '0'
inc eax
add eax, '0'
pop ecx
loop l1

mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .bss
num resb 1

数字

没什么内容,就是介绍汇编语言中,可以使用ASCII与BCD表示数字。

字符串

指定字符串长度的方法:

  • 显示存储字符串长度
  • 使用哨兵字符

可以使用$位置计数器符号来显示存储字符串长度

1
2
msg  db  'Hello, world!',0xa ;our dear string
len equ $ - msg ;length of our dear string

或者
也可以存储带尾随哨兵字符的字符串来分割字符串

1
message DB 'I am loving it!', 0

数组

与C语言类似,只是初始化方式,例如:

1
2
3
4
5
6
7
8
9
10
INVENTORY   DW  0
DW 0
DW 0
DW 0
DW 0
DW 0
DW 0
DW 0
INVENTORY DW 0, 0 , 0 , 0 , 0 , 0 , 0 , 0
INVENTORY TIMES 8 DW 0

过程

感觉类似实现函数与调用
定义过程的语法为

1
2
3
4
proc_name:
procedure body
...
ret

使用CALL指令从另一个函数调用该过程,语法为

1
CALL proc_name

被调用过程使用 RET 指令将控制权返回给调用过程。

堆栈数据结构

提供两种堆栈操作指令,其语法如下

1
2
PUSH    operand
POP address/register

递归

递归有两种:直接递归和间接递归。
直接递归,自己调用自己
间接回归,一个过程调用另一个过程,另一个过程调用前一个过程

编写宏是确保汇编语言模块化编程的另一种方法。

  • 宏是一系列指令,由名称指定,可以在程序中的任何位置使用。
  • 在 NASM 中,宏是使用 %macro 和 %endmacro 指令定义的。
  • 宏以 %macro 指令开始,以 %endmacro 指令结束。

宏定义的语法

1
2
3
%macro macro_name  number_of_params
<macro body>
%endmacro

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
; A macro with two parameters
; Implements the write system call
%macro write_string 2 mov eax, 4
mov ebx, 1
mov ecx, %1
mov edx, %2
int 80h
%endmacro

section .text
global _start ;must be declared for using gcc

_start: ;tell linker entry point
write_string msg1, len1 write_string msg2, len2 write_string msg3, len3 mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel

section .data
msg1 db 'Hello, programmers!',0xA,0xD
len1 equ $ - msg1

msg2 db 'Welcome to the world of,', 0xA,0xD
len2 equ $- msg2

msg3 db 'Linux assembly programming! '
len3 equ $- msg3

文件管理

由于暂时并不操作文件,故本部分略过。

内存管理

sys_brk() 系统调用由内核提供,用于分配内存而无需稍后移动它。 此调用在内存中的应用程序映像后面分配内存。 该系统函数允许您设置数据部分中的最高可用地址。
该系统调用有一个参数,即需要设置的最高内存地址。 该值存储在 EBX 寄存器中。
如果出现任何错误,sys_brk() 返回 -1 或返回负错误代码本身。 以下示例演示了动态内存分配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
section	.text
global _start ;must be declared for using gcc

_start: ;tell linker entry point

mov eax, 45 ;sys_brk
xor ebx, ebx
int 80h

add eax, 16384 ;number of bytes to be reserved
mov ebx, eax
mov eax, 45 ;sys_brk
int 80h

cmp eax, 0
jl exit ;exit, if error mov edi, eax ;EDI = highest available address
sub edi, 4 ;pointing to the last DWORD mov ecx, 4096 ;number of DWORDs allocated
xor eax, eax ;clear eax
std ;backward
rep stosd ;repete for entire allocated area
cld ;put DF flag to normal state

mov eax, 4
mov ebx, 1
mov ecx, msg
mov edx, len
int 80h ;print a message

exit:
mov eax, 1
xor ebx, ebx
int 80h

section .data
msg db "Allocated 16 kb of memory!", 10
len equ $ - msg

参考

[1] https://www.w3schools.cn/assembly/index.html