免责声明:本文介绍的安全知识方法以及代码仅用于渗透测试及安全教学使用,禁止任何非法用途,后果自负

前言

我将会把系列文章继续写下去,由于本系列文章novice同学也在写,所以我俩的顺序可能有点乱,不过他写过的内容我不会在重复,可能以后我俩会进行沟通,合理安排内容,争取可以合写好这个系列。

本篇文章按照之前文章所说的,来介绍linux rootkit中的系统调用挂钩技术。

1.背景

本次环境依然是linux 2.6系列内核,ubuntu10.04。

本篇文章及上篇文章的示例代码:Github链接。

通常,通过rootkit来实现对系统控制的主要途径之一就是通过对系统调用进行挂钩(Hook)来实现的,这是因为系统调用本身的重要性质决定的。系统调用提供用户程序与操作系统之间的接口,由操作系统内核来提供,运行在核心态,这意味着一旦掌握系统调用,我们就可以掌握操作系统的权限和功能,来实现用户态所无法完成的事情。而掌握系统调用通常是通过对系统调用挂钩来进行的,也就是通过实现一个自己的系统调用例程来替换操作系统中的系统调用例程。

当然我们也可以通过其他的办法来实现rootkit对系统的控制,比如对内存直接进行操作,或者修改已有的内核模块,这些我们后面会讨论,今天的主题还是系统调用。

往日的岁月总是美好的,在曾经的linux2.4系列内核中,我们可以轻易的获取系统调用表(sys_call_table),并对其进行修改,指向我们自己实现的系统调用历程,从而实现挂钩。(说句题外话,FreeBSD6.0当中的系统调用挂钩方法也是类似的,具体可以参考Joseph Kong的《Designing BSD Rookit》一书,在我学习rootkit过程中该书给我了很大的指导与启发。)extern void *sys_call_table[];

...

...

sys_call_table[__NR_syscall] = (our_sys_call_func);

但是linux2.6版本以后,sys_call_table[]不再是全局变量了,无法通过简单的”extern” 就可以得到它了。当然,天无绝人之路,我们可以曲线救国。在novice同学文章中,他选择了使用暴力搜索内存空间法来获取sys_call_table,简洁有效。不过我们这次将介绍另外两种不同的办法来搞定sys_call_table。

2.通过system.map获取系统调用表

我介绍的第一种方法,也是我认为获取系统调用表的最最简单的方法,是通过system.map来定位系统调用表在内存中的位置。

什么是是system.map?在我们利用它前,首先需要了解它。System.map,顾名思义,系统的映射,但具体映射的是什么东西?其实是内核符号及其所在内存地址两者的映射。通过地址,我们可以找到符号,也就是找到变量及函数;通过符号,我们也可以得知其所在地址。更多请参考该网站。在这里,我们需要通过符号来获取地址,我们已知的符号是sys_call_table,系统调用表,我们在system.map里便可以找到系统调用表所对应,也就是所在的内存地址。说了这么多,该如何具体操作呢。System.map位于/boot目录下,我们可以通过cat命令进行查看。(不同内核版本system.map的后缀不同,需要注意),由于内容太多,我们只展现部分内容。

然后我们来找找系统调用表的内存地址究竟是多少。(注意每个机器的地址会不同,具体以自己的机器为准。我在github中提供的示例代码中sys_call_table的地址是我机器上的地址,如果要使用示例代码,需要根据本地情况修改sys_call_table的内存地址)

OK,接下来的事情也就简单了,我先上代码,这里我们挂钩的是sys_mkdir系统调用:asmlinkage long (*real_mkdir)(const char __user *pathname,umode_t mode);

asmlinkage long fake_mkdir(const char __user *pathname, umode_t mode)

{

printk("Arciryas:mkdir-%s\n", pathname);

return (*real_mkdir)(pathname, mode);

}

real_mkdir = (void *)sys_call_table[__NR_mkdir];

sys_call_table[__NR_mkdir] = fake_mkdir;

相信大家基本都能看懂,我这里解释下那个”__NR_mkdir”是怎么回事,参见该网址。

这里的”__NR_xxxx”是unistd.h中定义的宏,代表着系统调用号,而系统调用号对应着系统调用表的相应的入口,具体参考 该网址。

当然不能这样就完了,内存可不是你想修改就修改的,就像之前novice同学提到的,我们需要关闭写保护,因为之前提过我就不再赘述了。我直接上代码:static int lkm_init(void)

{

write_cr0(read_cr0() & (~0x10000));

real_mkdir = (void *)sys_call_table[__NR_mkdir];

sys_call_table[__NR_mkdir] = fake_mkdir;

write_cr0(read_cr0() | 0x10000);

printk("Arciryas:module loaded\n");

return 0;

}static void lkm_exit(void)

{

write_cr0(read_cr0() & (~0x10000));

sys_call_table[__NR_mkdir] = real_mkdir;

write_cr0(read_cr0() | 0x10000);

printk("Arciryas:module removed\n");

}

ok,我们的任务成功告一段落,makefile文件和上篇文章一样,我们现在就来make,insmod,然后创建一个新文件夹,看看我们的lkm有没有正常运作,如图:

看来一切都在我们掌握之中!

3.通过IDT(中断描述符表)获取系统调用表

为什么我们要介绍不同的几种获取sys_call_table的办法?一招鲜没法吃遍天,就像novice在该系列第二篇文章中所说,暴力搜索内存空间法来获取sys_call_table存在被欺骗的可能,而我们刚才介绍的通过system.map的方法也有缺陷,要知道system.map对于内核来说并非必不可少的,如果没有system.map,我们该怎么做?于是接下来我们将要补充的是第三种方法:通过IDT(中断描述符表)获取系统调用表。

首先,我们还是需要理解下什么是IDT,以及为什么通过IDT可以得到sys_call_table。中断描述符表(Interrupt Descriptor Table,IDT),其作用是将每个异常或中断向量分别与它们的处理过程联系起来,每一个向量在表中有相应的中断或异常处理程序的入口地址。当系统发生中断时,内核根据异常或中断向量来在IDT中选择对应的处理程序的入口地址,进而对中断或异常进行处理。然后是重点:linux中的系统调用,也是通过一个特殊的中断——0x80号中断来实现的(补充一句,linux的系统调用还可以通过sysenter方法进入,所以这里介绍的通过0x80中断获取sys_call_table方法也是有局限的):1.用户进程在执行系统调用前,先把系统调用名(实际上是系统调用号)、输入参数等放到寄存器上(EBX,ECX等寄存器)

2.然后发出int 0x80指令,即触发128号中断

3.系统暂停用户进程,根据128号中断找到中断服务程序system_call

4.128号中断的中断服务程序system_call紧接着执行。在进行必要的处理后,统一调用 call sys_call_table(,eax,4)来调用sys_call_table表中的系统调用服务,eax存放的即时系统调用号;执行完毕后它又会把输出结果放到寄存器中。

5.系统恢复用户进程,进程从寄存器中取到自己想要的东西,然后继续执行。

ok,在这个过程中,我们发现了sys_call_table的出现。在具体操作中,我们应该如何来通过IDT来得到系统调用表呢,以下是我们所需要完成的程序的核心思路:1.利用sidt 指令,得到IDT

2.在IDT中找到0x80号中断的中断服务程序的地址system_call

3.从0x80号中断的中断服务程序system_call的地址开始搜索硬编码 \xff\x14\x85,这块硬编码的后面紧接着就是系统调用表的地址了,因为x86 call指令的二进制格式为\xff\x14\x85,而中断服务程序调用系统调用的语句是call sys_call_table(,eax,4)

IDT和系统调用的关联,以及通过IDT获取sys_call_table的思路已经介绍完毕,接下来我们用代码来进一步说明:struct

{

unsigned short size;

unsigned int addr;

}__attribute__((packed)) idtr;

struct

{

unsigned short offset_1; /*offset bits 0..15*/

unsigned short selector; /*a code segment selector in GDT or LDT*/

unsigned char zero; /*unused, set to 0*/

unsigned char type_attr; /*type and attributes*/

unsigned short offset_2; /*offset bits 16..31*/

}__attribute__((packed)) idt;

这两个结构体代表着IDTR和IDT表项,IDTR是中断描述符表寄存器(Interrupt Descriptor Table Register),用来定位IDT的位置,因为IDT表可以驻留在线性地址空间的任何地方,所以处理器专门有寄存器来储存IDT的位置,也就是IDTR寄存器。我们通过sidt指令加载IDTR寄存器的内容,然后储存到我们自己的这个结构体中,然后通过其找到IDT的位置所在,将IDT存到我们所设的结构体中,便于操作。unsigned long  *find_sys_call_table(void)

{

unsigned int sys_call_off;

char *p;

int i;

unsigned int ret;

asm("sidt %0":"=m"(idtr));

printk("Arciryas:idt table-0x%x\n", idtr.addr);

memcpy(&idt, idtr.addr+8*0x80, sizeof(idt));

sys_call_off = ((idt.offset_2<<16) | idt.offset_1);

p = sys_call_off;

for(i=0; i<100; i++)

{

if(p[i]=='\xff' && p[i+1]=='\x14' && p[i+2]=='\x85')

ret = *(unsigned int *)(p+i+3);

}

printk("Arciryas:sys_call_table-0x%x\n", ret);

return (unsigned long**)ret;

}

这是我们这次获取系统调用操作的核心代码,我将详细说明:asm("sidt %0":"=m"(idtr));

这是使用内联汇编的办法调用sidt这一汇编指令,然后将加载出的中断描述符表寄存器中的内容存入我们之前准备好的的idtr结构体。memcpy(&idt, idtr.addr+8*0x80, sizeof(idt));

这条语句的目的是获取0x80中断所对应的IDT中的表项。中断描述符表共256项,每项8字节,每项代表一种中断类型。所以我们要从IDR起始地址后的8*0x80位置拷贝一个IDT表项大小的数据,也就是0x80中断所对应的IDT中的表项,到我们之前准备好的结构体中。sys_call_off = ((idt.offset_2<<16) | idt.offset_1);

这条语句获取的是128号中断的中断服务程序system_call的地址,idt.offset_1和idt.offset_2代表什么参考我之前的注释。for(i=0; i<100; i++)

{

if(p[i]=='\xff' && p[i+1]=='\x14' && p[i+2]=='\x85')

ret = *(unsigned int *)(p+i+3);

}

最后一击,搜索\xff\x14\x85,得到sys_call_table地址。

具体挂钩操作在之前介绍system.map方法的内容中有叙述,在此不再赘述。

代码完成了,看看实验结果如何:

显然我们成功达成了目标。

4.结语

技多不压身,多掌握几种不同的系统调用挂钩法不但有助于开拓我们的视野,还可以让我们在不同的情况下选择更合适的办法来hook。当然系统调用的挂钩法远远不止我介绍的两种和novice同学所介绍的,其他还包括模拟call

sys_call_table(,eax,4)方法,dump_stack法(本质和system.map方法接近),栈结构获取法等等。这些有兴趣的同学可以自行研究,本系列文章就不再进行讲述了,请大家期待接下来的文章!

5.参考链接

作者gmail邮箱:arciryas.yang

* 本文原创作者:arciryas,本文属FreeBuf原创奖励计划,未经许可禁止转载

linux mkdir 系统调用,Linux Rootkit 系列四:对于系统调用挂钩方法的补充相关推荐

  1. Linux Rootkit 系列四:对于系统调用挂钩方法的补充

    本篇文章按照之前文章所说的,来介绍linux rootkit中的系统调用挂钩技术. 1.背景 本次环境依然是linux 2.6系列内核,ubuntu10.04. 本篇文章及上篇文章的示例代码:Gith ...

  2. linux mkdir 参数,Linux基础命令之 mkdir

    mkdir 功能: 创建文件夹(目录),就和Windows下的新建文件夹的工能一样,只是这个是在字符界面由命令生成文件夹的方式 注: 一: mkdir 可以同时创建多个目录: mkdir a b  c ...

  3. Linux从入门到精通系列之sed命令使用方法

    今日目标 掌握sed的基本语法结构 熟悉sed常用的命令,如打印p,删除d,插入i等 一.文件编辑器知多少 Windows系统 SublimeNotepad++WordTypora Linux系统 v ...

  4. Linux Rootkit 系列三:实例详解 Rootkit 必备的基本功能

    本文所需的完整代码位于笔者的代码仓库:https://github.com/NoviceLive/research-rootkit. 测试建议: 不要在物理机测试!不要在物理机测试! 不要在物理机测试 ...

  5. linux rootkit 端口复用,Linux Rootkit系列三:实例详解 Rootkit 必备的基本功能

    前言鉴于笔者知识能力上的不足,如有疏忽,欢迎纠正. 测试建议: 不要在物理机测试!不要在物理机测试! 不要在物理机测试! 概要 在 上一篇文章中笔者详细地阐述了基于直接修改系统调用表 (即 sys_c ...

  6. linux swi 内核sp,应用调试(四)系统调用SWI

    title: 应用调试(四)系统调用SWI date: 2019/01/19 18:05:39 toc: true 应用调试(四)系统调用SWI 系统调用 我们App中的open,read等实际上会触 ...

  7. Linux Shell脚本入门教程系列之(十四) Shell Select教程

    本文是Linux Shell脚本系列教程的第(十四)篇,更多Linux Shell教程请看:Linux Shell脚本系列教程 在上一篇文章:Linux Shell系列教程之(十三)Shell分支语句 ...

  8. Linux Shell脚本入门教程系列之(四)Shell注释

    本文是Linux Shell脚本系列教程的第(四)篇,更多shell教程请看:Linux Shell脚本系列教程 与许多的编程语言一样,Shell中也有注释符号,继上一篇之后,今天就为大家来介绍下Sh ...

  9. 【转载】linux进程控制-exec系列 exec系统调用

    inux进程控制-exec系列 说是exec系统调用,实际上在Linux中,并不存在一个exec()的函数形式,exec指的是一组函数,一共有6个,分别是: #include <unistd.h ...

最新文章

  1. python a any_Python any() 函数
  2. 数据库视频总结二(Transact-SQL语言)
  3. redis 依赖_springboot|springboot集成redis缓存
  4. cocos2d-x初探学习笔记(4)--触屏事件
  5. 让主页或Blog日访问量增加3000
  6. centos 安装sublime3
  7. linux完全卸载texlive
  8. TED 演讲 How we can control the coronavirus pandemic? 我们如何控制新冠病毒的传播 ?
  9. Event 事件对象 (target)帆软修改提示框样式
  10. n9 android模拟器,Android软件将兼容诺基亚N9
  11. paraview的安装和使用
  12. Ubuntu切换阿里源
  13. IOS逆向-LLVM、代码混淆
  14. unity 特效_介绍几个 Unity 开源视觉特效项目
  15. 使用js,对数值保留小数点后两位的处理(两种情况)
  16. VS2015远程白屏的解决办法——亲测可用
  17. 解决微信访问 80 端口的限制问题
  18. 今天来聊一聊互联网35岁梗,这个行业真的不需要35岁以上从业人员?
  19. 嵌入式Web项目(二)——CGI的引入
  20. 【读书笔记】《未来闪影》罗伯特·J·索耶

热门文章

  1. 基于JAVA+Servlet+JSP+MYSQL的问卷调查管理系统
  2. 基于JAVA+SpringMVC+Mybatis+MYSQL的闲置物品交易平台
  3. 测试人员,今天再不懂BDD就晚了!
  4. 【two pointers 细节题】cf1041dD. Glider
  5. Linux下使用Nginx端口转发出现502错误的一种解决办法
  6. iOS开发-CocoaPods使用详细说明
  7. JS_call_APP native 与 html的交互
  8. 三星如何抄袭苹果 产品对比一目了然
  9. c语言实现线程相关操作,如何用C语言实现多线程
  10. springboot 优雅停机_Spring boot 2.3优雅下线,距离生产还有多远?