Linux汇编


前言

为了加深对于汇编的掌握于是好好学习了一下NASM汇编。

以下完全就是个人的学习笔记。

NASM 汇编基本语法

基本结构

段定义:NASM 程序分为不同的的段(section),如.data.bss.text。每个段用于
不同的数据类型或代码。

1
2
3
4
5
6
7
8
9
10
11
section .data ;数据段
mag db 'hello world!',0Ah

section .bss ;未初始化数据段
buffer resb 64

section .text ;代码段
global _start

_start
;程序代码

注释

上述代码中以;号开始的文本就是 NASM 的单行注释。

单行注释

1
;这是一个单行注释

多行注释

1
2
3
/*
这是一个多行注释
*/

指令格式

操作码:指定执行的操作,例如movaddsub
操作数:指令的参数,可以是寄存器、内存位置或立即数。

1
2
mov eax,5
add ebx,eax

数据定义

db:定义一个字节的数据

1
value db 0x1f

dw:定义一个字的数据。

1
value dw 0x1234

dd:定义一个双字的数据。

1
value dd 0x12345678

dq:定义一个四字的数据。

1
value dq 0x123456789abcdef0

resb:保留一定数量的字节。

1
buffer resb 64

resw:保留一定数量的字。

1
buffer resw 32

resd:保留一定数量的双字。

1
buffer resd 16

常用指令

数据传输

1
2
mov eax,10
mov [buffer],eax

算术运算

1
2
3
4
add eax,1
sub ebx,2
mul ecx
imul ecx

逻辑运算

1
2
3
4
and eax,0x0f
or ebx,0xf0
xor ecx,edx
not eax

控制流

1
2
3
4
5
6
7
jmp labe1
je labe1
jne labe1
jl labe1
jg labe1
call function
ret

循环控制

loop:基于ecx寄存器的循环,ecx的值减 1,当ecx不为0 时跳转。

1
2
3
mov ecx,10
loop_start:
loop loop_start

宏和条件编译

NASM 支持宏,用于简化重复的代码。

1
2
3
4
5
6
7
8
9
10
11
%macro add_5 1
add %1,5
%endmacro

section .text
global _start

_start:
add_5,eax
;等效于
;add eax,5

条件编译:根据条件编译不同的代码块

1
2
3
%ifdef DEBUG
;Debugging code
%endif

伪指令

  • global:声明全局符号,其它文件可以引用。
  • extern:声明外部符号,来自其他文件。
  • equ:定义变量。

汇编与链接

汇编:将汇编代码编译成目标文件。

1
2
3
4
5
;汇编 32 位代码
nasm -f elf32 demo.asm -o demo.o

;汇编 64 位代码
nasm -f elf64 demo.asm -o demo.o

链接:将目标文件链接成可执行文件。

1
2
3
4
5
;链接器默认链接 64 位程序
ld ./demo.o

;链接 32 位程序
ld -m elf_i386 demo.o

系统调用

在 Linux 下开发程序必须了解 Linux 下的系统调用。

32位

eax用于存储系统调用号。系统调用的参数通过ebxecxedxesiediebp寄存器传递。

以下是 32 位 Linux 系统中常用的系统调用表。系统调用号可以在 /usr/include/asm/unistd_32.h 文件中找到。

系统调用 调用号 描述
exit 1 退出
open 5 打开文件
read 3 读取文件
write 4 写入文件
close 6 关闭文件
lseek 19 移动文件指针
exit 1 退出进程
fork 2 创建子进程
execve 11 执行程序
waitpid 7 等待子进程结束
brk 12 改变数据段的末尾
mmap 9 映射文件或设备到内存
ioctl 16 控制设备
socket 359 创建套接字
bind 361 绑定套接字到地址
listen 50 监听套接字
accept 43 接受连接
connect 42 连接到套接字
mount 165 挂载文件系统
unmount 166 卸载文件系统

64位

rax用于存储系统调用号。系统调用的参数通过rdirsirdxrcxr8r9寄存器传递。

系统调用 调用号 描述
open 2 打开文件
read 0 读取文件
write 1 写入文件
close 3 关闭文件
lseek 8 移动文件指针
exit 60 退出进程
fork 57 创建子进程
execve 59 执行程序
waitpid 7 等待子进程结束
brk 12 改变数据段的末尾
mmap 9 映射文件或设备到内存
ioctl 16 控制设备
socket 41 创建套接字
bind 49 绑定套接字到地址
listen 50 监听套接字
accept 43 接受连接
connect 42 连接到套接字
mount 165 挂载文件系统
unmount 166 卸载文件系统

内存模型与寻址模式

I/O操作:包括标准输入/输出、磁盘读写等基本操作的汇编实现。

宏定义、模块化编程

在 NASM 汇编语言中,模块化编程是指将程序分解成多个模块,以便更好地组织和管理代码。模块化编程的主要优点包括代码重用、简化复杂性以及提高维护性。以下是关于 NASM 模块化编程的详细说明:

模块化编程的基本概念

模块化编程涉及将程序分成多个逻辑模块或单元,每个模块实现特定的功能。每个模块可以独立开发、测试和调试。模块间通过接口进行交互,模块的实现对其他模块是透明的。

使用 NASM 实现模块化

在 NASM 中,模块化编程通常涉及以下几个步骤:

创建模块

每个模块通常包含数据部分和代码部分。模块可以被放在不同的文件中,并在主程序中引用。

**模块示例 (module.asm)**:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
section .data
msg db "Hello from module!", 0Ah

section .text
global print_message

print_message:
; 使用 sys_write 打印消息
mov rax, 1 ; sys_write
mov rdi, 1 ; 文件描述符: stdout
mov rsi, msg ; 消息地址
mov rdx, 17 ; 消息长度
syscall
ret

主程序文件

主程序文件引用其他模块,并调用模块中定义的函数。

**主程序示例 (main.asm)**:

1
2
3
4
5
6
7
8
9
10
11
section .text
extern print_message ; 声明外部函数
global _start

_start:
call print_message ; 调用模块中的函数

; 退出程序
mov rax, 60 ; sys_exit
xor rdi, rdi ; 退出码: 0
syscall

编译和链接

将各个模块编译成目标文件,并链接成最终的可执行文件。

编译和链接命令

1
2
3
nasm -f elf64 module.asm -o module.o
nasm -f elf64 main.asm -o main.o
ld module.o main.o -o program

宏定义
在 NASM(Netwide Assembler)中,宏(Macro)是一种强大的功能,可以简化汇编代码的编写和维护。宏允许你定义代码块,并在需要时插入到源代码中。它们类似于其他编程语言中的函数或宏,但主要用于生成汇编代码。以下是 NASM 宏定义的详细解释。

宏的基本概念

宏是由一段代码组成的模板,你可以在源代码的多个位置插入这段模板,并用实际参数替换宏中的占位符。宏定义的主要目的是代码重用和简化复杂的汇编程序。

宏的定义

在 NASM 中,宏通过 %macro 指令定义。宏定义包括宏名称、参数和宏体。使用宏时,NASM 会用实际参数替换宏体中的占位符。

宏定义的语法

1
2
3
%macro macro_name num_params
; 宏体
%endmacro
  • macro_name 是宏的名称。
  • num_params 是宏的参数数量。

宏体

宏体是实际要插入到代码中的内容,可以包含宏参数。宏参数用 %%1, %%2 等表示(对于第一个、第二个参数)。

宏的使用

宏在定义之后可以在代码中调用,每次调用都会插入宏体并用实际参数替换占位符。

宏调用的语法

1
macro_name arg1, arg2, ...

宏的示例

简单宏

定义一个简单的宏来生成打印字符串的代码:

1
2
3
4
5
6
7
%macro PRINT_STR 2
mov rax, 1 ; sys_write
mov rdi, 1 ; 文件描述符: stdout
mov rsi, %1 ; 消息地址
mov rdx, %2 ; 消息长度
syscall
%endmacro

使用宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
section .data
msg db "Hello, World!", 0x0A
msg_len equ $ - msg

section .text
global _start

_start:
PRINT_STR msg, msg_len

; 退出程序
mov rax, 60 ; sys_exit
xor rdi, rdi ; 退出码: 0
syscall

带默认值的宏

定义一个宏,使用默认参数:

1
2
3
4
5
6
7
%macro PRINT 1
mov rax, 1
mov rdi, 1
mov rsi, %1
mov rdx, $ - %1
syscall
%endmacro

使用宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
section .data
msg db "Hello, World!", 0x0A

section .text
global _start

_start:
PRINT msg

; 退出程序
mov rax, 60
xor rdi, rdi
syscall

宏参数

宏可以有多个参数,你可以用参数来生成不同的代码块:

1
2
3
%macro MOV_REG 2
mov %1, %2
%endmacro

使用宏:

1
2
3
4
5
6
7
8
9
10
11
section .text
global _start

_start:
MOV_REG rax, 5 ; mov rax, 5
MOV_REG rbx, rax ; mov rbx, rax

; 退出程序
mov rax, 60
xor rdi, rdi
syscall

条件宏

你可以使用条件编译来定义条件性的宏代码:

1
2
3
4
5
6
7
8
9
10
%macro DEBUG 1
%if %1
; 只有在参数为真时编译这段代码
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall
%endif
%endmacro

使用条件宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
section .data
msg db "Debug Mode", 0x0A
msg_len equ $ - msg

section .text
global _start

_start:
DEBUG 1 ; 激活调试模式

; 退出程序
mov rax, 60
xor rdi, rdi
syscall
  • 调试困难:宏的展开可能导致调试困难,因为宏体在编译时被插入到代码中,可能不容易追踪。
  • 代码膨胀:过度使用宏可能导致代码膨胀,因为每个宏调用都会展开成实际的代码。

程序开发

第一个程序

Hello world!

接下来我们通过永不过期的经典程序来了解如何初步开发汇编程序。

32位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
;定义数据段,用于存放程序中的静态数据。
section .data
msg db "hello world",0Ah

;定义代码段,用于存放程序的指令
section .text
global _start

;程序的入口点
_start:
mov edx,13
mov ecx,msg
mov ebx,1
mov eax,4
int 80h

mov ebx,0
mov eax,1
int 80h

64位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
section .data
msg db "hello world",0Ah

section .text
global _start

_start:
mov rdi,1
mov rsi,msg
mov rdx,13
mov rax,1
syscall

mov rdi,0
mov rax,60
syscall

计算字符串长度

1

计算器

1

子程序

1

外部包含文件

外部包含文件允许我们从程序中移动代码并将其放入单独的文件中。这种技术对于编写干净、易于维护的程序很有用。可重用的代码位可以编写为子程序,并存储在称为库的单独文件中。当您需要一段逻辑时,您可以将该文件包含在您的程序中,并像使用它们属于同一文件一样使用它。

NULL 终止字节

在编程中,0h 表示一个空字节,字符串后面的空字节告诉程序集它在内存中结束的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
%include "functions.asm"

section .data
msg1 db "hello world!",0Ah,0h
msg2 db "This is how we recycle in NASM",0Ah,0h

section .text
global _start

_start:
mov eax,msg1
call sprint

mov eax,msg2
call sprint

call quit

换行符

1

传递参数

1

用户输入

1

数到 10

名称空间

1

嘶嘶声

执行命令

1

处理分叉

报时

文件处理

1

套接字

1

下载网页

1

后言

参考链接:NASM 汇编语言教程
参考书籍:《x86汇编语言:从实模式到保护模式》