荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: cycker (快过年吧.我想回家), 信区: Linux
标  题: Linux 可卸载内核模块完全指南(三)(转寄)[转载]
发信站: 荔园晨风BBS站 (Thu Jan  2 18:22:25 2003), 站内信件

【 以下文字转载自 cycker 的信箱 】
【 原文由 xiaofong@bbs.pku.edu.cn 所发表 】
发信人: chenhao (努力学习), 信区: Linux
标  题: Linux 可卸载内核模块完全指南(三)
发信站: 北大未名站 (2000年11月20日01:09:21 星期一) , 转信

第一部分. 基础知识

1.3什么是内核符号表(Kernel-Symbol-Table)


OK,我们现在已经理解了最基本的系统调用和模块的概念,但是还需要继续理解另一个十分
关键的概念--内核符号表。

让我们看一眼/proc/ksyms.在里面的每一个表项代表着一个公共的内核符号.这些符号是可
以被我们的LKM引用的.再认真的看一眼这个文件,你会发现很多有趣的事情.


这个文件真的很有趣,他可以帮助我们看到我们的LKM到底可以调用那些函数.但是这也同时
带来一个问题,在我们的LKM中所存取的每一个符号(像函数名)也会被列在这个文件里面,也
会被其他人看到.因此,一个经验丰富的系统管理员就会发现我们小小的LKM并且杀掉他.


有很多方法可以阻止管理员发现我们的LKM,这可以看第二章节.在第二章节中提到的方法可
以被称之为'Hacks',但是当我们在看第二章节的内容时你会发现里面并没有提到"如何使你
的LKM符号不列在/proc/ksyms中"这样的方法.在第二章中我们没有提到这个问题是基于以
下理由的:


你并不需要什么特殊的技巧来使你的模块的符号不在/proc/ksyms中出现.LKM开发者们用如
下的常用代码来声明他们模块的符号.


static struct symbol_table module_syms= {

/*定义自己的符号表!!*/

#include <linux/symtab_begin.h>

/*我们想引用的符号表*/

...

};


register_symtab(&module_syms);

/*实际的注册工作*/


正如我所说的,我们并不需要对外公开我们的符号,所以我们只要用如下的语句就可以了

register_symtab(NULL);

这条语句必须放在init_module()函数中,要记住!


1.4如何实现从用户空间到内核空间的转换


直到现在这篇文章所介绍的都是非常基本和简单的一些东西.从现在开始会有一些稍微难一
点的.(但是并不是太难的).


在内核级别内编程带给我们很多好处,同时也给我们带来了不少缺点.系统调用必须从用户
空间中获得调用的参数,(系统调用是通过包装在像libc这样的函数库中实现的)然而我们的
LKM是运行在内核级别的.在第二章中我们会看到对于我们来说,通过检查某个系统调用的参
数来使LKM正确的运行是十分重要的.但是,在内核空间的模块如何才能存取到用户空间分配
的参数呢?


解决方法是:我们必须做一个传输转换.


对于那些非内核探索者来说,这听上去可能有些奇怪.但是这真的很简单.比如说下面这个系
统调用:


int sys_chdir (const char *path)


设想系统正在调用这个函数,并且我们拦截了这个调用(我们会在第二章解释这个).我们想
检查用户想去的路径.因此我们必须获得const char* path.如果你企图通过下面的

方法直接获得path变量:


printk("<1>%s\n", path);


你就会遇到真正的问题了.....

要记住你现在是在内核空间中,你不能很轻松的读取用户空间的内存.好了,你可以获得一个
plaguez提供的解决方法.他通过使用内核的函数(宏)从用户空间的内存中中提取一个字符
串到内核空间:


#include <asm/segment.h>


get_user(pointer);


传给这个函数一个*path的指针从而把他从用户空间的内存拷贝到内核空间.让我们看看这
段由plaguez写的从用户空间移动字符串到内核空间的代码:


char *strncpy_fromfs(char *dest, const char *src, int n)

{

  char *tmp = src;

  int compt = 0;


  do {

dest[compt++] = __get_user(tmp++, 1);

  }

  while ((dest[compt - 1] != '\0') && (compt != n));


  return dest;

}


如果我们想转换我们的*path变量,我们可以用如下的内核代码:


char *kernel_space_path;


kernel_space_path = (char *) kmalloc(100, GFP_KERNEL);

/*在内核空间中分配内存*/


(void) strncpy_fromfs(test, path, 20);

/*调用plaguez's的函数*/



printk("<1>%s\n", kernel_space_path);

/*现在我们可以使用这个变量了*/


kfree(test);

/*记得释放内存*/


上面的代码工作的很好.由于一个通用的转换程序有点复杂了,plaguez仅仅做了字符串的拷
贝(这个函数只能用于字符串的拷贝).对于普通的数据,下面的函数是最简单的做法:


#include <asm/segment.h>

void memcpy_fromfs(void *to, const void *from, unsigned long count);


很显然,两个函数都是同一种类型的命令.但是第二个几乎和plaguez的自己新定义的函数一
模一样.我推荐用memcpy_fromfs(.....)用于通用的数据传输而将plaguez的那个用于字符
串拷贝.


现在我们知道了如何将数据从用户空间传送到内核空间.但是相反方向该怎么办呢?这就有
点难了,因为我们并不能很容易的在内核为用户空间分配内存.如果我们可以那样做的话,我
们就可以用:


#include <asm/segment.h>

void memcpy_tofs(void *to, const void *from, unsigned long count);


来做实际的转换工作.但是我们如何在用户空间分配内存给*to指针呢?plaguez的文章里面
给出了最好的解决方案:

/*我们需要brk系统调用*/


static inline _syscall1(int, brk, void *, end_data_segment);


...


int ret, tmp;

char *truc = OLDEXEC;

char *nouveau = NEWEXEC;

unsigned long mmm;


mmm = current->mm->brk;

ret = brk((void *) (mmm + 256));

if (ret < 0)

return ret;

memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);


在这里使用了一个非常漂亮的小技巧.current是当前进程的任务结构(task structure)指
针,mm是mm_struct(用于那个进程的内存管理的)的指针.通过对current->mm->brk使用brk
系统调用我们可以增加数据段没有使用的内存的大小.我们都知道分配内存只不过是和数据
段打交道.因此通过增加未用区域的大小,我们实际上为当前的进程分配了一些内存.这块内
存就可以用于从内核到(当前进程的)用户空间的拷贝.


你可能会对上面代码的第一行感到困惑.这行代码将帮助我们在内核空间使用用户空间的函
数.每一个提供给我们的用户空间的函数(像fork,brk,open,read,write,......)都可以用
一个_syscall(.....)宏来表示.因此我们可以构造某个系统调用宏来确切的表示特定用户
空间的函数(用系统调用表示的);在这儿这个函数就是(brk.....)


看1.5获得更为详细的解释.


--
※ 来源:.北大未名站 bbs.pku.edu.cn [FROM: 162.105.45.129]
--
※ 转寄:·北大未名站 bbs.pku.edu.cn·[FROM: 210.39.3.50]
--
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.36.220]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店