秦悦明的运维笔记

linux下汇编语言编程

1. 一般套路

分两布,编译,linux下有两种常用的编译器,gnu的as和nasm,两者的区别是前者用AT&T语法,后者用了intel的语法,个人更喜欢gnu的语法一些。链接,链接分静态链接和动态链接两种。再加一个gdb调试器,基本上齐活了。

2. nasm例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SECTION .data
EatMsg: db "Eat at Joe's",10
EatLen: equ $-EatMsg
SECTION .bss
SECTION .text
global _start
_start:
nop
mov eax,4
mov ebx,1
mov ecx,EatMsg
mov edx,EatLen
int 80H
mov eax,1
mov ebx,0
int 80H

直接抄<汇编语言基于linux环境>第五章的例子,调用了linux的systemcall ,也就是系统调用,eax是4,就是write系统调用。write的参数如下:

  • EAX contains the system call value.
  • EBX contains the file descriptor to write to.
  • ECX contains the start of the string.
  • EDX contains the length of the string.

ebx是文件句柄,1就是标准输出。
ecx是字符串的开始位置,这里是将EatMsg的地址传进去
edx是字符串的长度,这里也是用了equ的伪指令EatLen

全部传进去调用int 80H就调用传说中的的write系统调用了。
之后再调用退出系统调用,结束程序运行。ebx存储返回值,shell 里面可以用echo $?来查看。

1
2
编译:
nasm -f elf -g -F stabs eatsyscall.asm

-g 用于生成调试信息,-F 指定stabs格式,可以方便的用gdb调试。

之后是链接,用标准链接工具ld:

1
ld -o eatsyscall eatsyscall.o

运行就是一个标准输出:

1
2
[root@localhost ~]# ./eatsyscall
Eat at Joe's

3.as例子

跟nasm差不多:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#cpuid.s Sample program to extract the processor Vendor ID
.section .data
output:
.ascii "The processor Vendor ID is 'xxxxxxxxxxxx'\n"
.section .text
.globl _start
_start:
movl $0, %eax
cpuid
movl $output, %edi
movl %ebx, 28(%edi)
movl %edx, 32(%edi)
movl %ecx, 36(%edi)
movl $4, %eax
movl $1, %ebx
movl $output, %ecx
movl $42, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80

这里再抄一下<汇编语言程序设计>里面的例子,cpuid ,可以看到gas的语法明显和intel的不一样,最明显的差别是源操作数和目的操作数的位置是相反的。
这个调用了cpuid 的1号指令。将cpuid信息返回在ebx,ecx,edx三个寄存器中。

  • EBX contains the low 4 bytes of the string.
  • EDX contains the middle 4 bytes of the string.
  • ECX contains the last 4 bytes of the string.

movl将三个值分别送到output的指定位置,也就是’xxxxxxxxxxxx’中去。然后调用write系统调用来输出。最后是一个退出的系统调用。

1
2
3
4
5
6
7
8
编译:
[root@localhost ~]# as -gstabs -o cpuid.o cpuid.s
链接:
[root@localhost ~]# ld -o cpuid cpuid.o
运行:
[root@localhost ~]# ./cpuid
The processor Vendor ID is 'AuthenticAMD'
[root@localhost ~]#

4.section区域

一般linux程序需要定义三个区域,

  1. .data区域,定义已初始化变量
  2. .bss 定义为初始化变量
  3. .text 正文区域,里面要用.globl 指定开始点。

区域根据elf格式而来,不止这三个。linux下nasm和as都适用~

1
2
3
4
5
6
7
8
.section .data
< initialized data here>
.section .bss
< uninitialized data here>
.section .text
.globl _start
_start:
<instruction code goes here>

5.ia32下编程

上面提到的环境都能在ia32的模式下运行的很好,也就是说不用再切到dos下去调试。开个虚拟机就可以愉快的玩耍,很适合汇编的学习。实模式和保护模式一个最大的区别就是内存寻址是一个flat方式,可以直接引用内存。不用加段。