Linux 可卸载内核模块完全指南之四
the definitive guide for hackers, virus coders and system administrators
(作者:pragmatic/THC,(版本1.0) 2000年05月16日 16:44)
1.5使用用户空间函数的方法
正如你在1.4中看到的我们利用一个syscall宏来构造我们自己的brk调用,就像我们从用户空间中调用一样,实际上那些用户空间的很多库函数(并不是所有的)都是通过这种syscall宏调用实现的,下面的代码显示了在1.4中调用的_syscall1(.....)宏。(摘自/asm/unistd.h)
#define _syscall1(type,name,type1,arg1) \\
type name(type1 arg1) \\
{ \\
long __res; \\
__asm__ volatile (\"int $0x80\" \\
: \"=a\" (__res) \\
: \"0\" (__NR_##name),\"b\" ((long)(arg1))); \\
if (__res >= 0) \\
return (type) __res; \\
errno = -__res; \\
return -1; \\
}
你并不需要理解这个函数的全部代码.他不过是调用中断0x80并把_syscall提供的参数(->1.2)传进去.name表示我们所需要的系统调用.(name被展开为__NR_name,这是在/asm/unistd.h中定义的).通过这个方法我们实现了brk函数.其他的拥有不同数量参数的函数通过其他的一些宏实现(_syscallX,X代表参数的个数).我个人用另一种方法来实现这个函数,看下面的例子:
int (*open)(char *, int, int);
/*声明一个函数原型*/
open = sys_call_table[SYS_open];
/*你也可以用__NR_open*/
在这个方法中你并不需要使用任何syscall宏,你只需要使用sys_call_table中的函数指针就可以了.在网上,我发现这种构造用户空间函数的方法也被SVAT的著名的LKM传播者使用.依我的观点,这种方法是好一些的方法.但是你应该测试并且做出自己的判断.
当提供参数给这些系统调用时你要小心,他们需要这些参数在用户空间,而不是内核空间.阅读1.4来找到从内核空间拷贝到用户空间的方法.
一个很简单的实现这个的方法(在我看来是最好的方法)就是利用所需的寄存器.你必须知道linux通过利用段选择器来区别用户空间,系统空间等等.系统调用使用的参数都是通过数据段(DS)来寻址的.(我没有在1.4中提到这些,因为我觉得放在这里更合适)
DS可以通过asm/segment.h中的get_ds()获得.因此如果我们设置段选择器为一个合适的值,我们就可以直接获得系统调用的参数.设置段选择器可以用set_fs(....)实现.但是要小心,在你获得系统调用的参数以后必须恢复FS的值.OK,现在让我们来看一段有用的代码:
->filename 是在我们的内核空间;比如说,一个我们刚刚创建的字符串
unsigned long old_fs_value=get_fs();
set_fs(get_ds);
/*从这以后我们可以存取用户空间的数据*/
open(filename, O_CREAT|O_RDWR|O_EXCL, 0640);
set_fs(old_fs_value);
/*恢复fs.....*/
在我看来,这是最简单也是最好的解决这个问题的方法.但是你必须亲自测试他(再一次:P).记住到目前为止我演示的几个函数(brk,open)都是通过很简单的系统调用实现的.你可以看一眼我们感兴趣那些系统调用列表(1.2);比如说,sys_socketcall和所有对sockets的操作的实现有关(创建,关闭,发送,接收......).因此在创建你自己的函数时要十分的小心.要常常看一看内核的源代码.
1.6常用内核空间函数列表
在本文开始的时候我介绍过printk(....)函数.那是一个每个人在内核空间都可以使用的函数,可以叫做内核函数.这些函数是给那些需要复杂函数的内核开发者的,常常由一些库提供.下面的表列出了一些最为重要的我们常使用的函数:
函数/宏 描述
int sprintf (char *buf, const char *fmt, ...);
int vsprintf (char *buf, const char *fmt, va_list args);
用来格式化字符串的函数
printk (...)
与用户空间的printf一样
void *memset (void *s, char c, size_t count);
void *memcpy (void *dest, const void *src, size_t count);
char *bcopy (const char *src, char *dest, int count);
void *memmove (void *dest, const void *src, size_t count);
int memcmp (const void *cs, const void *ct, size_t count);
void *memscan (void *addr, unsigned char c, size_t size);
内存函数
int register_symtab (struct symbol_table *intab); see I.1
char *strcpy (char *dest, const char *src);
char *strncpy (char *dest, const char *src, size_t count);
char *strcat (char *dest, const char *src);
char *strncat (char *dest, const char *src, size_t count);
int strcmp (const char *cs, const char *ct);
int strncmp (const char *cs,const char *ct, size_t count);
char *strchr (const char *s, char c);
size_t strlen (const char *s);
size_t strnlen (const char *s, size_t count);
size_t strspn (const char *s, const char *accept);
char *strpbrk (const char *cs, const char *ct);
char *strtok (char *s, const char *ct);
字符串比较等操作的函数
unsigned long simple_strtoul (const char *cp, char **endp, unsigned int base);
将字符串转换成数字
get_user_byte (addr);
put_user_byte (x, addr);
get_user_word (addr);
put_user_word (x, addr);
get_user_long (addr);
put_user_long (x, addr);
用来访问用户内存的函数
suser();
fsuser();
检查超级用户权限的
int register_chrdev (unsigned int major, const char *name, struct file_o perations *fops);
int unregister_chrdev (unsigned int major, const char *name);
int register_blkdev (unsigned int major, const char *name, struct file_o perations *fops);
int unregister_blkdev (unsigned int major, const char *name);
注册设备驱动的函数
..._chrdev -> 字符设备
..._blkdev -> 块设备
请记住这里的一些函数也可以通过1.5中提到的方法使用.但是你必须明白,当内核无偿提供了这些函数时,通过用户空间函数来使用他们没有什么意义.
随后你会看到这些函数(特别是字符串比较函数)很重要的应用.
发布人:netbull 来自:Linux中文资料