《professional Assembly Language》 Richard Blum一书不错,练习一下读字母的能力。

 

大多数操作系统提供许多应用程序能够访问的内核函数,Linux操作系统亦不例外。Linux提供许多用户程序能够访问的内核函数。用户程序通过访问这些内核函数能够更易的“访问文件”、“设置用户和用户组的权限”、“访问网络资源”以及“检索和显示数据”。这些可被用户程序访问的内核函数被称为“系统调用”函数。调用这些内核函数的过程叫作系统调用。

 

众多的系统调用函数都是被内核提供的。知道怎么查询和使用系统调用对编写汇编程序有莫大的帮助。

 

1查看系统调用

查看系统提供哪些系统调用的手段很简单:看内核代码。

 

不同版本的内核,此系统调用的宏定义的位置不同。以下是在windows下查看到的两个不同linux版本所定义系统调用的文件目录。

Linux.2.26: *\linux-2.6.26\include\asm-x86\unist_32.h

 

Linux-3.12:*\linux-3.12\arch\sh\include\uapi\asm\unist_32.h

 

  • 系统调用宏名用“__NR_前缀 +内核函数名”的形式定义。
  • 内核函数名”是提供给高级程序设计语言的接口,供C/C++等程序调用。
  • “宏名”对应的“数字”是提供给汇编语言的接口,供汇编程序调用。

 

不管是高级程序设计语言还是汇编语言,当它们调用一个相同的内核函数时,虽然方式不同,但所达到的功能都是一样的。

 

Linux内核提供的系统调用包含“内存访问”、“设备访问”、“文件系统访问”及“进程访问”相关的接口。对于汇编程序来说,系统调用的接口就是数字;对于C程序来说,系统调用提供的接口就是一些函数名。

 

2 查看系统调用函数的定义

在汇编程序中,软件中断(int $0x80)发生时,eax内的值决定系统调用类型。在linux下,可以使用man page来查看各系统调用函数功能,从而决定对eax寄存器的赋值。

 

如查询1系统调用的功能。man 2exit

  • man 2表示查看系统调用对应的内核函数。
  • 得到的man page页面是以C调用的形式展现。

 

感:C程序中除了“C标准库函数”及“用户自定义函数”之外,还可以调用内核所提供的函数

 

3 在汇编中使用系统调用

Linux内核给汇编提供系统调用的接口是数字,eax寄存器用来保存这个系统调用的数字,这些数字在linux 内核源代码中所对应的平台下的unist_32.h中可以查到。Linux通过系统调用软件中断的方式来完成一个系统调用。int是AT&T汇编中的中断指令,Linux 的系统调中断号为0x80。故而int $0x80指令引发一个Linux系统调用软件中断。系统调用软件中断发生时,根据eax中所定的系统调用去调用系统调用所对应的的内核函数,然后根据相关寄存器内所保存的数据实现一次系统调用。

 

完成一个系统调用的涉及的方面:

[1] 决定系统调用类型

在明确目标系统调用对应的数字后,给eax寄存器赋所需系统调用对应的数字。eax寄存器用来保存系统调用接口

movl  $n, %eax       #n为对应的系统调用提供的接口

 

[2]为系统调用准备相关的数据

C中,程序所需要的数据保存在栈、堆等内存中。而对于汇编来说,这些数据需要保存在寄存器中,在将数据存到各寄存器的过程中要保证正确的顺序,否则有可能为程序带来灾难性的后果。

 

在一次系统调用中,只使用ebxecxedxesiedi五个寄存器来保存相对程序来说的输入数据,这些输入数据其实是传递给在系统调用过程中所访问的内核函数的实参。输入数据被保存的顺序如下:

  • ebx---第一个参数
  • ecx—第二个参数
  • edx—第三个参数
  • esi—第四个参数
  • edi—第五个参数

这里第*个参数的含义是指系统调用函数的参数次序,可以通过“man 2函数名”的方式查询到。在之前的“汇编程序打印字符”汇编程序中,往ebx,ecx,edx寄存器中保存的参数依次对应“写系统调用”函数write(int  fd, const  void  *buf,size_t  count)的三个参数。所以,ebx中保存的是文件描述符(STDOUT:1),ecx中保存的是要向控制台输出的字符串地址,edx保存的是字符串的长度。So,使用系统调用,man 2相应的内核函数也是必须的。

 

对于超过六个参数的情况是使用ebx保存相继而存的数据所在内存的起始地址。系统调用通过ebx内的地址访问这些数据。

 

[3]系统调用的返回值

执行一次系统调用的本质是调用了对应的内核函数,一部分内核函数是有返回值的。一次系统调用完成后(内核函数执行完毕)的返回值保存在eax寄存器中。这里突然想起以前的笔记,关于栈地址的返回:return 栈地址

 

[4] 总结使用系统调用

  • 搭一个汇编程序框架。
  • 查询应赋给eax的系统调用接口。[最好的地方是内核源代码unist_32.h +搜索引擎]
  • man 2 系统调用函数的参数,按照顺序将函数参数赋给ebxecxedxesiedi
  • 使用int $0x80系统调用软件中断。
  • …….
  • 退出汇编程序的系统调用。

 

此次笔记记录完毕。

Logo

更多推荐