第一节

Hello,world!

翻译自:https://asmtutor.com/

背景知识

汇编语言是最基本的。程序员在实际硬件之上的唯一接口是内核本身。为了在汇编中构建有用的程序,我们需要使用内核提供的 Linux 系统调用。这些系统调用是内置于操作系统中的库,可提供诸如从键盘读取输入和将输出写入屏幕等功能。

当您调用系统调用时,内核将立即暂停执行您的程序。然后它将执行您请求的任务所需的必要驱动程序,最后再将控制权返回给您的程序。

Note: 驱动程序 (Drivers) 之所以称为“驱动程序”,是因为内核确实是使用它们来“驱动”硬件。

我们可以在汇编中完成这一切,方法是将我们要执行的函数号(即 OPCODE)加载到 EAX 中,并用我们要传递给系统调用的参数填充剩余的寄存器。最后使用 INT 指令请求软件中断,内核将接管并使用我们的参数调用库中的函数。

例如,当 EAX=1 时请求中断将调用 sys_exit,而当 EAX=4 时请求中断将调用 sys_write。如果函数需要,EBXECXEDX 将作为参数传递。单击此处查看 Linux 系统调用表及其相应 OPCODES 的示例。

写程序

首先,我们在 .data 部分创建一个变量“msg”,并为其分配我们想要输出的字符串,在本例中为“Hello, world!”。在我们的 .text 部分中,我们通过为内核提供全局标签 _start: 来指示程序入口点来告诉内核从哪里开始执行。

我们使用系统调用 sys_write 将信息输出到控制台窗口。此函数在 Linux 系统调用表中指定为 OPCODE 4 。在请求执行任务的软件中断之前,该函数还接受 3 个参数,这些参数依次加载到 EDXECXEBX 中。

下面这些就是传入的参数:

  • EDX 字符串的长度
  • ECX.data 段创建的字符串变量
  • EBX 想要写入的文件,例如 STDOUT

传递的参数的数据类型和含义可以在函数的定义中找到。

使用以下命令编译、链接和运行程序。

; Hello, world!; 文件名:helloworld.asm; 编译:nasm -f elf helloworld.asm; 链接(64位系统需要 elf_i386 选项):ld -m elf_i386 helloworld.o -o helloworld; 运行:./helloworld SECTION .datamsg     db      'Hello World!', 0Ah     ; 初始化变量msg SECTION .textglobal  _start _start:     mov     edx, 13     ; 待写入的字节数(每个字母一个字节,再加上换行符0Ah)    mov     ecx, msg    ; 将msg的内存地址移入ecx    mov     ebx, 1      ; 表示写入到标准输出STDOUT    mov     eax, 4      ; 调用SYS_WRITE(OPCODE是4)    int     80h
~$ nasm -f elf helloworld.asm~$ ld -m elf_i386 helloworld.o -o helloworld~$ ./helloworldHello World!Segmentation fault