Linux汇编
前言
为了加深对于汇编的掌握于是好好学习了一下NASM汇编。
以下完全就是个人的学习笔记。
NASM 汇编基本语法
基本结构
段定义:NASM 程序分为不同的的段(section),如.data
、.bss
、.text
。每个段用于
不同的数据类型或代码。
1 |
|
注释
上述代码中以;
号开始的文本就是 NASM 的单行注释。
单行注释
1 |
|
多行注释
1 |
|
指令格式
操作码:指定执行的操作,例如mov
、add
、sub
。
操作数:指令的参数,可以是寄存器、内存位置或立即数。
1 |
|
数据定义
db
:定义一个字节的数据
1 |
|
dw
:定义一个字的数据。
1 |
|
dd
:定义一个双字的数据。
1 |
|
dq
:定义一个四字的数据。
1 |
|
resb
:保留一定数量的字节。
1 |
|
resw
:保留一定数量的字。
1 |
|
resd
:保留一定数量的双字。
1 |
|
常用指令
数据传输
1 |
|
算术运算
1 |
|
逻辑运算
1 |
|
控制流
1 |
|
循环控制
loop
:基于ecx
寄存器的循环,ecx
的值减 1,当ecx
不为0 时跳转。
1 |
|
宏和条件编译
NASM 支持宏,用于简化重复的代码。
1 |
|
条件编译:根据条件编译不同的代码块
1 |
|
伪指令
global
:声明全局符号,其它文件可以引用。extern
:声明外部符号,来自其他文件。equ
:定义变量。
汇编与链接
汇编:将汇编代码编译成目标文件。
1 |
|
链接:将目标文件链接成可执行文件。
1 |
|
系统调用
在 Linux 下开发程序必须了解 Linux 下的系统调用。
32位
eax
用于存储系统调用号。系统调用的参数通过ebx
、ecx
、edx
、esi
、edi
、ebp
寄存器传递。
以下是 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
用于存储系统调用号。系统调用的参数通过rdi
、rsi
、rdx
、rcx
、r8
、r9
寄存器传递。
系统调用 | 调用号 | 描述 |
---|---|---|
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 |
|
主程序文件
主程序文件引用其他模块,并调用模块中定义的函数。
**主程序示例 (main.asm)**:
1 |
|
编译和链接
将各个模块编译成目标文件,并链接成最终的可执行文件。
编译和链接命令:
1 |
|
宏定义
在 NASM(Netwide Assembler)中,宏(Macro)是一种强大的功能,可以简化汇编代码的编写和维护。宏允许你定义代码块,并在需要时插入到源代码中。它们类似于其他编程语言中的函数或宏,但主要用于生成汇编代码。以下是 NASM 宏定义的详细解释。
宏的基本概念
宏是由一段代码组成的模板,你可以在源代码的多个位置插入这段模板,并用实际参数替换宏中的占位符。宏定义的主要目的是代码重用和简化复杂的汇编程序。
宏的定义
在 NASM 中,宏通过 %macro
指令定义。宏定义包括宏名称、参数和宏体。使用宏时,NASM 会用实际参数替换宏体中的占位符。
宏定义的语法
1 |
|
macro_name
是宏的名称。num_params
是宏的参数数量。
宏体
宏体是实际要插入到代码中的内容,可以包含宏参数。宏参数用 %%1
, %%2
等表示(对于第一个、第二个参数)。
宏的使用
宏在定义之后可以在代码中调用,每次调用都会插入宏体并用实际参数替换占位符。
宏调用的语法
1 |
|
宏的示例
简单宏
定义一个简单的宏来生成打印字符串的代码:
1 |
|
使用宏:
1 |
|
带默认值的宏
定义一个宏,使用默认参数:
1 |
|
使用宏:
1 |
|
宏参数
宏可以有多个参数,你可以用参数来生成不同的代码块:
1 |
|
使用宏:
1 |
|
条件宏
你可以使用条件编译来定义条件性的宏代码:
1 |
|
使用条件宏:
1 |
|
- 调试困难:宏的展开可能导致调试困难,因为宏体在编译时被插入到代码中,可能不容易追踪。
- 代码膨胀:过度使用宏可能导致代码膨胀,因为每个宏调用都会展开成实际的代码。
程序开发
第一个程序
Hello world!
接下来我们通过永不过期的经典程序来了解如何初步开发汇编程序。
32位
1 |
|
64位
1 |
|
计算字符串长度
1 |
|
计算器
1 |
|
子程序
1 |
|
外部包含文件
外部包含文件允许我们从程序中移动代码并将其放入单独的文件中。这种技术对于编写干净、易于维护的程序很有用。可重用的代码位可以编写为子程序,并存储在称为库的单独文件中。当您需要一段逻辑时,您可以将该文件包含在您的程序中,并像使用它们属于同一文件一样使用它。
NULL 终止字节
在编程中,0h 表示一个空字节,字符串后面的空字节告诉程序集它在内存中结束的位置。
1 |
|
换行符
1 |
|
传递参数
1 |
|
用户输入
1 |
|
数到 10
名称空间
1 |
|
嘶嘶声
执行命令
1 |
|
处理分叉
报时
文件处理
1 |
|
套接字
1 |
|
下载网页
1 |
|
后言
参考链接:NASM 汇编语言教程
参考书籍:《x86汇编语言:从实模式到保护模式》