在Linux操作系统下进行汇编编程时,基本的汇编程序框架通常包括以下几个部分:
①全局段声明(section declarations):定义数据段、代码段等。
②入口点(entry point) :程序的执行起点,通常为_start
或main
。
③系统调用(system calls):通过操作系统提供的接口完成基本功能,如输入输出。
以下是一个简单的32位Linux汇编程序的框架示例,使用了NASM汇编器:
1.创建.asm汇编文件
sudo vim Hello.asm
2.编写汇编代码
这段汇编代码是一个在Linux系统下编写的简单程序,使用系统调用输出"Hello World!"并换行,然后退出程序。
section .data
msg db "Hello World!",0xa
len equ $ - msg
section .text
global _start
_start:
mov edx,len
mov ecx,msg
mov ebx,1
mov eax,4
int 0x80
mov ebx,0
mov eax,1
int 0x80
数据段(section .data):
section .data
:定义数据段,包含已初始化的数据。
msg db "Hello World!",0xa
:定义一个字符串"Hello World!"
,并在末尾添加换行符(ASCII码为0xa
)。db
指令用于定义字节序列(data byte)。
len equ $ - msg
:定义一个常量len
,其值是当前地址($
)减去字符串msg
的起始地址,即字符串的长度。这里,$
表示当前汇编位置的地址,因此$ - msg
计算了字符串msg
的长度。
代码段(section .text):
section .text
:定义代码段,包含程序的指令。
global _start
:定义入口点_start
,告诉链接器程序的起始位置。
程序入口(_start):
_start:
:程序的入口点。
1)打印内容
mov edx, len
:将字符串长度len
(即12
,包括换行符)加载到寄存器edx
中。这是系统调用write
所需的第三个参数,表示要写入的字节数。
mov ecx, msg
:将字符串地址msg
加载到寄存器ecx
中。这是系统调用write
所需的第二个参数,表示要写入的缓冲区地址。
mov ebx, 1
:将文件描述符1
(标准输出stdout
)加载到寄存器ebx
中。这是系统调用write
所需的第一个参数,表示目标文件描述符。
mov eax, 4
:将系统调用号4
(sys_write
)加载到寄存器eax
中。这告诉内核要执行write
系统调用。
int 0x80
:触发中断0x80
,向内核发出系统调用请求。
这段代码执行的是write
系统调用,具体调用格式如下:
ssize_t write(int fd, const void *buf, size_t count);
在汇编中,参数按照以下顺序放入寄存器:
-
fd
(文件描述符):ebx
-
buf
(缓冲区地址):ecx
-
count
(字节数):edx
2)退出程序
mov ebx, 0
:将退出状态码0
(表示正常退出)加载到寄存器ebx
中。
mov eax, 1
:将系统调用号1
(sys_exit
)加载到寄存器eax
中。这告诉内核要执行exit
系统调用。
int 0x80
:再次触发中断0x80
,向内核发出系统调用请求。
这段代码执行的是exit
系统调用,具体调用格式如下:
void _exit(int status);
在汇编中,参数按照以下顺序放入寄存器:
status
(退出状态码):ebx
通过这段代码,程序将输出"Hello World!"并换行,然后正常退出
更多的系统调用可以根据程序架构查看文件:
/usr/include/asm/unistd_32.h #x86架构程序的系统调用头文件 /usr/include/asm/unistd_64.h #x64架构程序的系统调用头文件
这两个文件分别定义了32位架构和64位架构汇编的系统调用的编号;系统调用号在汇编程序中非常重要,因为它们用于指示内核执行特定的操作。例如,sys_write
系统调用的编号在unistd_64.h上是1;在unistd_32.h上是4。
3.生成目标文件(汇编)
nasm -f elf32 Hello.asm
使用 nasm
汇编器将 Hello.asm
汇编成目标文件 Hello.o
;这条命令会生成一个名为 Hello.o
的目标文件。
-f
指定格式位32位的elf格式(因为上述代码中使用的系统调用是32位的系统调用)所以此时在汇编的使用要指定32位架构格式的目标文件。
4.链接
使用 ld
链接器将目标文件 Hello.o
链接成可执行文件 Hello32
;将32位目标文件链接成可执行文件。
bash
ld -m elf_i386 -s -o Hello32 Hello.o
5.执行程序
bash
./Hello32
尽管大多数情况下x64系统可以运行x86程序,但有些情况下可能会出现兼容性问题:
①缺少32位库:如果系统中没有安装需要的32位库或特定的依赖项,32位程序将无法运行。 ②系统调用和内核模块:某些32位程序可能直接使用特定的系统调用或内核模块,这些调用或模块在64位系统中不可用或行为不同。 ③依赖特定硬件特性:某些32位程序可能依赖于特定的硬件特性或设备驱动,这些特性或驱动在64位系统中不可用或需要特定的32位支持。
这里顺便记录一下简答的x64汇编架构;
以下是一个简单的32位Linux汇编程序的框架示例,同样使用NASM汇编器:
这段汇编代码是一个Linux 64平台下的程序,实现了输出"Hello World"并换行,并正常退出的功能。
bash
section .data
msg db "Hello World",0xa
len equ $ - msg
section .text
global _start
_start:
mov edx,len
mov esi,msg
mov edi,1
mov eax,1 ;调用号为1因为是64位程序所以对应的调用号可以在/usr/include/asm/unistd_64.h文件中进行查阅
syscall
mov edi,0 ;调用号为0因为是64位程序所以对应的调用号可以在/usr/include/asm/unistd_64.h文件中查阅
mov eax,60
syscall
这段代码实现了以下功能:
-
输出字符串:
-
使用
sys_write
系统调用(系统调用号为1),它的参数分别是:edx
寄存器存放字符串长度,esi
寄存器存放字符串的地址,edi
寄存器存放文件描述符(1表示标准输出)。 -
执行
syscall
指令来触发系统调用。
-
-
退出程序:
-
使用
sys_exit
系统调用(系统调用号为60),它的参数是退出状态码,这里设置为0表示正常退出。 -
再次执行
syscall
指令来触发系统调用。
-