catalogue

0. 引言
1. 内核ko timer定时器,检测sys_call_table adress变动
2. 通过/dev/kmem获取IDT adress
3. 比较原始的系统调用地址和当前内核态中的系统调用地址发现是否有sys_call_table hook行为

0. 引言

内核rookit通常以系统调用为攻击目标,主要出于两个原因

1. 在内核态劫持系统调用能以较小的代价控制整个系统,不必修太多东西
2. 应用层大多数函数是一个或多个系统调用不同形式的封装,更改系统调用意味着其上层所有的函数都会被欺骗

当前的系统调用地址保存在系统调用表中,位于操作系统为内核保留的内存空间(虚拟地址最高1GB),系统调用入口地址的存放顺序同/usr/include/asm/unistd.h中的排列顺序,按系统调用号递增9

Relevant Link:

http://www.blackhat.com/presentations/bh-europe-09/Lineberry/BlackHat-Europe-2009-Lineberry-code-injection-via-dev-mem-slides.pdf

1. 内核ko timer定时器,检测sys_call_table adress变动

1. The module does a copy of the Syscall Table to save all syscalls pointers
2. After this first step, the module uses the kernel timer to check every X secondes the diff between the Syscall Table and the copy.
3. If a diff is found, the module creates a workqueue to execute the python script and restore the good syscall pointer. 

The python script is executed with root creds and the syscall number which is hooked, is passed as the first argument of script (sys.argv[1]).

0x1: hook_detection.c

/*
**  Copyright (C) 2013 - Jonathan Salwan - http://twitter.com/JonathanSalwan
**
**  This program is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
**
**  For more information about this module,
**  see : http://shell-storm.org/blog/Simple-Hook-detection-Linux-module/
**
*/#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/timer.h>
#include <linux/workqueue.h>#define PATH_PYTHON "/usr/bin/python2.7"
#define PATH_SCRIPT "/opt/scripts/hook_detected.py"#define TIME_SLEEP 30000 /* in msec */static struct timer_list timer_s;
static struct workqueue_struct *wq;
static unsigned int syscall_table_size;
static unsigned long *addr_syscall_table;
static unsigned long *dump_syscall_table;static int exec_python_script(unsigned int sys_num)
{char s_num[32];char *argv[] = {PATH_PYTHON, PATH_SCRIPT, s_num, NULL};static char *envp[] = {"HOME=/", "TERM=linux", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};struct subprocess_info *sub_info;sprintf(s_num, "%d", sys_num);sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC);if (sub_info == NULL)return -ENOMEM;call_usermodehelper_exec(sub_info, UMH_WAIT_PROC);return 0;
}static unsigned long *get_syscalls_table(void)
{unsigned long *start;/* hack :/ */for (start = (unsigned long *)0xc0000000; start < (unsigned long *)0xffffffff; start++)if (start[__NR_close] == (unsigned long)sys_close){return start;}return NULL;
}static unsigned int get_size_syscalls_table(void)
{unsigned int size = 0;while (addr_syscall_table[size++]);return size * sizeof(unsigned long *);
}static void check_diff_handler(struct work_struct *w)
{unsigned int sys_num = 0;while (addr_syscall_table[sys_num]){if (addr_syscall_table[sys_num] != dump_syscall_table[sys_num]){printk(KERN_INFO "hook_detection: Hook detected ! (syscall %d)\n", sys_num);write_cr0(read_cr0() & (~0x10000));addr_syscall_table[sys_num] = dump_syscall_table[sys_num];write_cr0(read_cr0() | 0x10000);exec_python_script(sys_num);printk(KERN_INFO "hook_detection: syscall %d is restored\n", sys_num);}sys_num++;}
}
static DECLARE_DELAYED_WORK(check_diff, check_diff_handler);static void timer_handler(unsigned long data)
{unsigned long onesec;onesec = msecs_to_jiffies(1000);queue_delayed_work(wq, &check_diff, onesec);if (mod_timer(&timer_s, jiffies + msecs_to_jiffies(TIME_SLEEP)))printk(KERN_INFO "hook_detection: Failed to set timer\n");
}static int __init hook_detection_init(void)
{addr_syscall_table = get_syscalls_table();if (!addr_syscall_table){printk(KERN_INFO "hook_detection: Failed - Address of syscalls table not found\n");return -ECANCELED;}syscall_table_size = get_size_syscalls_table();dump_syscall_table = kmalloc(syscall_table_size, GFP_KERNEL);if (!dump_syscall_table){printk(KERN_INFO "hook_detection: Failed - Not enough memory\n");return -ENOMEM;}memcpy(dump_syscall_table, addr_syscall_table, syscall_table_size);wq = create_singlethread_workqueue("hook_detection_wq");setup_timer(&timer_s, timer_handler, 0);if (mod_timer(&timer_s, jiffies + msecs_to_jiffies(TIME_SLEEP))){printk(KERN_INFO "hook_detection: Failed to set timer\n");return -ECANCELED;}printk(KERN_INFO "hook_detection: Init OK\n");return 0;
}static void __exit hook_detection_exit(void)
{if (wq)destroy_workqueue(wq);kfree(dump_syscall_table);del_timer(&timer_s);printk(KERN_INFO "hook_detection: Exit\n");
}module_init(hook_detection_init);
module_exit(hook_detection_exit);MODULE_AUTHOR("Jonathan Salwan");
MODULE_DESCRIPTION("Hook Detection");
MODULE_LICENSE("GPL");

Relevant Link:

http://shell-storm.org/blog/Simple-Hook-detection-Linux-module/hook_detection.c
http://shell-storm.org/blog/Simple-Hook-detection-Linux-module/

2. 通过/dev/kmem获取IDT adress

核心逻辑是通过idt获取80中断的地址,获取了sys_call_table之后,进行一次拷贝,之后就可以进行diff对比

#include < stdio.h >
#include < sys/types.h >
#include < fcntl.h >
#include < stdlib.h >int kfd;struct{unsigned short limit;unsigned int base;} __attribute__ ((packed)) idtr;struct{unsigned short off1;unsigned short sel;unsigned char none, flags;unsigned short off2;} __attribute__ ((packed)) idt;int readkmem (unsigned char *mem, unsigned off, int bytes){if (lseek64 (kfd, (unsigned long long) off, SEEK_SET) != off){return -1;}if (read (kfd, mem, bytes) != bytes) {return -1;}}int main (void){unsigned long sct_off;unsigned long sct;unsigned char *p, code[255];int i;/* request IDT and fill struct */asm ("sidt %0":"=m" (idtr));if ((kfd = open ("/dev/kmem", O_RDONLY)) == -1){perror("open");exit(-1);}if (readkmem ((unsigned char *)&idt, idtr.base + 8 * 0x80, sizeof (idt)) == -1){printf("Failed to read from /dev/kmem\n");exit(-1);}sct_off = (idt.off2 < < 16) | idt.off1;if (readkmem (code, sct_off, 0x100) == -1){printf("Failed to read from /dev/kmem\n");exit(-1);}/* find the code sequence that calls SCT */sct = 0;for (i = 0; i < 255; i++){if (code[i] == 0xff && code[i+1] == 0x14 && code[i+2] == 0x85)sct = code[i+3] + (code[i+4] < < 8) + (code[i+5] < < 16) + (code[i+6] < < 24);}if (sct)printf ("sys_call_table: 0x%x\n", sct);close (kfd);}

Relevant Link:

http://www.rootkitanalytics.com/kernelland/IDT-dev-kmem-method.php
http://www.phpweblog.net/GaRY/archive/2007/06/17/get_sys_call_table_address.html

3. 比较原始的系统调用地址和当前内核态中的系统调用地址发现是否有sys_call_table hook行为

原始的系统调用地址在内核编译阶段被指定,不会更改,通过比较原始的系统调用地址和当前内核态中的系统调用地址我们就可以发现系统调用有没有被更改。原始的系统调用地址在编译阶段被写入两个文件

1. System.map: 该文件包含所有的符号地址,系统调用也包含在内
2. vmlinux-2.4.x: 系统初始化时首先被读入内存的内核映像文件

vmlinux-2.4.x文件通常以压缩的格式存放在/boot目录下,所以在比较之前必须解压这个文件,另一个问题是: 我们的比较的前提是假设system.map及vmlinuz image都没有被入侵者更改,所以更安全的做法是在系统干净时已经创建这两个文件的可信任的拷贝,并创建文件的md5 hash

在大多数被装载内核后门情况中,内核在系统初始化之后才被更改,更改发生在加载了rootkit的module或者被植入直接读写/dev/kmem的on-the-fly kernel patch之后。而通常情况下rootkit并不更改vmlinuz和system.map这两个文件,所以打印这两个文件中的符号地址就可以知道系统原始的系统调用地址,系统当前运行中的系统调用地址(可能被更改)可以同过/proc下的kcore文件得到,比较两者就知道结果

0x1: 获取原始内核系统调用函数地址

/boot/System.map-3.10.0-327.el7.x86_64

0x2: 获取当前内核系统调用地址

cat /proc/kallsyms 

0x3: 对比区别

从里面枚举sys_call_table的function point地址
/boot/System.map-3.10.0-327.el7.x86_64和
cat /proc/kallsyms | grep sys_fork
进行diff对比 cat System.map-4.4.0-53-generic | grep sys_fork

这里遇到一个问题,ubuntu对sys_socket相关的系统调用会进行内核地址重定位,因此需要对检测结果进行一个误报过滤,看是否所有的函数的gap都相同,如果相同,则说明是系统自己的function address relocation行为

def hex2dec(string_num):return str(int(string_num.upper(), 16))def check_rookit(diff_func_table):last_func_address_gap = 0cur_func_address_gap = 0for item in diff_func_table:cur_func_address_gap = abs(int(hex2dec(item['cur_funcaddress'])) - int(hex2dec(item['ori_funcaddress'])))if last_func_address_gap != 0 and cur_func_address_gap != last_func_address_gap:return Trueelse:last_func_address_gap = cur_func_address_gapreturn False

Relevant Link:

http://www.xfocus.net/articles/200411/754.html
http://www.magicsite.cn/blog/Linux-Unix/Linux/Linux41576.html
http://blog.csdn.net/tommy_wxie/article/details/8039695

Copyright (c) 2017 LittleHann All rights reserved

转载于:https://www.cnblogs.com/LittleHann/p/6541683.html

Linux sys_call_table变动检测相关推荐

  1. 日常工作问题解决:centos/linux系统如何检测端口是否打开

    日常工作问题解决:centos/linux系统如何检测端口是否打开 参考文章: (1)日常工作问题解决:centos/linux系统如何检测端口是否打开 (2)https://www.cnblogs. ...

  2. 13 种在 Linux 系统上检测 CPU 信息的工具

    13 种在 Linux 系统上检测 CPU 信息的工具 问题: 我想要了解我的电脑关于CPU处理器的详细信息,查看CPU信息比较有效地方法是什么? 根据你的需要,有各种各样的关于你的CPU处理器信息你 ...

  3. Linux系统状态检测及进程控制--2

    Linux系统状态检测及进程控制--1(http://crushlinux.blog.51cto.com/2663646/836481) 4.僵死(进程已终止,但进程描述符存在,直到父进程调用wait ...

  4. Linux系统磁状态检测,检测Linux硬件状态

    计算机系统是由软件系统和硬件系统共同组成的.检测硬件状态对于保障整个系统的稳定是非常重要的.不论操作系统是使用Linux.还是Windows,一旦硬件出现故障,那么整个系统的安全就严重了.这里我们主要 ...

  5. 在linux下面实现检测按键(Linux中kbhit()函数的实现)

    //在linux下面实现检测按键(Linux中kbhit()函数的实现) #include <stdio.h> #include <termios.h> #include &l ...

  6. linux系统检测硬盘物理损坏,linux系统下检测硬盘上的坏道和坏块

    linux系统下检测硬盘上的坏道和坏块 发布时间:2020-02-28 22:22:57 来源:51CTO 阅读:1127 作者:赤练挚爱 磁盘坏道检测 当磁盘出现以下情况:io wait 无故增高或 ...

  7. linux 怎么查看usb设备端口号,嵌入式Linux USB设备检测端口和地址

    我发展我的板USB应用.它有两个USB端口.当我插上USB驱动器在他们每个人我得到控制台以下消息:嵌入式Linux USB设备检测端口和地址 端口1: usb 1-1: new high speed ...

  8. linux下面实现检测按键(Linux中kbhit()函数的实现)

    在linux下面实现检测按键(Linux中kbhit()函数的实现)   #include <stdio.h> #include <termios.h> #include &l ...

  9. Linux下内存检测工具:asan

    Linux下内存检测工具:asan ASAN(Address-Sanitizier)早先是LLVM中的特性,后被加入GCC 4.8,在GCC 4.9后加入对ARM平台的支持.因此GCC 4.8以上版本 ...

最新文章

  1. CodeForces - 632E Thief in a Shop 完全背包
  2. 死磕 java集合之ConcurrentSkipListMap源码分析——发现个bug
  3. ZOJ-2366 Weird Dissimilarity 动态规划+贪心
  4. [哈夫曼树] Jzoj P4210 我才不是萝莉控呢
  5. bash-高级编程--位置变量
  6. android 学习记录-----------android 活动 意图 碎片
  7. Spark2.4.0 SparkEnv 源码分析
  8. [html] js放在html的<body>和<head>有什么区别?
  9. thinkCMF----导航高亮显示
  10. android判断主线程_android中从子线程切换到主线程,但是显得代码很臃肿,请教大牛是怎么自定义的?...
  11. php aes java_AES php java 互转
  12. ASP.NET vs ASP—编译与解释的区别
  13. apr内存池简单应用
  14. NLP-文本摘要:Rouge评测方法【Rouge-1、Rouge-2、Rouge-L、Rouge-W、Rouge-S】
  15. 寻找平面上的极大点(信息学奥赛一本通-T1230)
  16. linux 下简单安装 pathogen.vim
  17. XDOJ-直角三角形
  18. 漏洞建议:实施 TLS_FALLBACK_SCSV。此外,要么完全禁用 SSLv3,要么禁用以通过 SSLv3 的 CBC 模式操作的所有密码套件
  19. cad注释比例和打印比例不一样_CAD注释比例与打印比例不相等怎么办?
  20. mldonkey安装

热门文章

  1. 安卓桌面整理app_升级到 iOS 13,你还会删除 APP 和整理桌面了吗?
  2. 文件的记录c语言程序,急求如何将下列C语言程序数据存储到文件中?
  3. java 示例_Java最终关键字示例
  4. java关键字_Java关键字
  5. CloudEra Hadoop VMWare单节点环境设置
  6. linux压缩命令gzip_Linux gzip命令示例
  7. web前端开发示例_40多个针对Web开发人员HTML5教程和示例
  8. 什么是Reactive Streams in Java 译
  9. AI机器视觉技术在生活中的应用
  10. [总]Android高级进阶之路