Linux的系统调用过程
用户空间到内核空间的转换阶段;
需要一个指令来完成,也称为操作系统陷入(operating system trap)指令。
Linux 通过软中断指令。X86架构:软中断 0x80,即 int $0x80汇编指令。
通过软中断,系统跳转到一个预设的内核空间地址,它指向了系统调用处理程序 (不要和系统调用服务例程混淆),即在arch/i386/kernel/entry.S文件中使用汇编语言编写的system_call函数。
系统调用处理函数system_call到系统调用服务例程的阶段。
所有的系统调用都会统一跳转到这个地址进而执行system_call 函数,system_call 要把这些系统调用派发到各自的服务例程。
从0x80中断处理程序分析,获取系统调用表的地址。
获得系统调用表
- 通过简单的搜索找到call指令,从而得到sys_call_table的地址:
|
|
Rootkit
定义
- Rootkit是一种特殊的恶意软件,它的功能是在安装目标上隐藏自身及指定的文件、进程和网络链接等信息.
- Rootkit通过加载特殊的驱动,修改系统内核,进而达到隐藏信息的目的。
- 攻击者通过远程攻击获得root访问权限;
- 攻击者会在侵入的主机中安装rootkit;
- 经常通过rootkit的后门检查系统是否有其他的用户登录,如果只有自己,攻击者就开始着手清理日志中的有关信息。
分类:
应用级:通过替换login、ps、ls、netstat等系统工具,或修改.rhosts等系统配置文件等实现隐藏及后门
硬件级:bios rootkit,可以在系统加载前获得控制权,通过向磁盘中写入文件,再由引导程序加载该文件重新获得控制权,也可以采用虚拟机技术,使整个操作系统运行在rootkit掌握之中;
内核级 :最常见。
- lkm rootkit:基于lkm技术,通过系统提供的接口加载到内核空间,成为内核的一部分,进而通过hook系统调用等技术实现隐藏、后门功能。 - 非lkm rootkit:在系统不支持lkm机制时修改内核的一种方法,主要通过/dev/mem、/dev/kmem设备直接操作内存,从而对内核进行修改。
功能
- 隐藏文件:笔者将会利用此实现对ls 系统命令返回结果的篡改。
- 隐藏进程
- 隐藏连接:笔者将会利用此实现对netstat 系统命令返回结果的篡改。
- 隐藏模块
- 嗅探工具
- 密码记录
- 日志擦除
- 内核后门
系统调用劫持
在前述基础上,我们已经可以就行系统调用劫持,也即将系统调用转移到我们自己提供的函数,而不是原始的系统调用。
此处以ps命令为例。
为了实现对诸如ps 命令的返回结果的修改,我们首先需要知道这些命令使用了哪些系统调用来实现。
- 在linux下,我们可以使用strace命令来跟踪一个用户程序在执行过程中所使用的系统调用。在linux 终端输入:
strace ps aux 2>out.txt
查询ps命令所执行的系统调用,结果如下:
- 1234567891011121314......open("/proc/meminfo", O_RDONLY) = 4lseek(4, 0, SEEK_SET) = 0read(4, "MemTotal: 495788 kB\nMemF"..., 2047) = 1114stat("/proc/self/task", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0openat(AT_FDCWD,"/proc",O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 5mmap(NULL,135168,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5330b72000mmap(NULL,135168,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5330b51000getdents(5, /* 140 entries */, 32768) = 3696stat("/proc/1", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0open("/proc/1/stat", O_RDONLY) = 6read(6, "1 (init) S 0 1 1 0 -1 4202752 22"..., 4095) = 326close(6)......
- 在linux下,我们可以使用strace命令来跟踪一个用户程序在执行过程中所使用的系统调用。在linux 终端输入:
其中,我们看到ps命令执行过程中调用了getdents这个函数,而ps命令正是使用这个函数来读取/proc目录下的进程文件的。其对应的系统调用是sys_getdents(),其函数原型如下:
- 1int sys_getdents(unsigned int fd, struct linux_dirent64 __user *dirp, unsigned int count)
其中,fd为指向目录文件的文件描述符,该函数根据fd所指向的目录文件读取相应dirent结构,并放入dirp中,其中count为dirp中返回的数据量,正确时该函数返回值为填充到dirp的字节数。
我们要做的就是代替原先的系统调用,使用自己定义的系统调用hacked_getdents(),加上我们自己的判断语句就能实现对进程文件的过滤。
这里我们采用编写一个内核模块(LKM),通过获得系统调用表的地址来替换原先的系统调用的方法。
- 内核模块是一些可以让操作系统内核在需要时载入和执行的代码,这意味着它可以在不需要时由操作系统卸载。它们扩展了操作系统内核的功能却不需要重新编译内核,因此内核模块机制的好处是:既避免了内核的臃肿不堪,又极大程度地提高了内核的可扩展性。