Linux Rootkit实验|01 基于修改系统调用表的Hook

Linux Rootkit实验|01 基于修改系统调用表的Hook

文章目录

  • Linux Rootkit实验|01 基于修改系统调用表的Hook
    • 实验说明
    • 实验环境
    • 实验过程
      • 一 基于修改sys_call_table的系统调用挂钩
        • 1 寻找sys_call_table内存地址
        • 2 关掉写保护
        • 3 修改sys_call_table
      • 二 基于系统调用挂钩的初级文件监视
    • 实验问题
    • 实验总结与思考
    • 参考资料
      • 已参考
      • 拓展阅读

08 May 2017

夜阑风静縠纹平。小舟从此逝,江海寄余生。

实验说明

Rootkit的一种经典形式是通过Hook系统调用实现。在本次实验中,我们将实现简单的系统调用挂钩方案,并且基于这个方案实现最基本的文件监视工具,同时加深对LKM的理解。

实验环境

uname -a:
Linux kali 4.6.0-kali1-amd64 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) x86_64 GNU/Linux

GCC version:6.1.1

上述环境搭建于虚拟机,另外在没有特殊说明的情况下,均以root权限执行。

注:后面实验参考的是4.11的源码,可以在线阅览。

实验过程

一 基于修改sys_call_table的系统调用挂钩

Linux内核在内存中维护了一份系统调用向量表,它是一个元素为函数指针的一维数组,定义见arch/x86/entry/syscall_64.c

asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
	/*
	 * Smells like a compiler bug -- it doesn't work
	 * when the & below is removed.
	 */
	[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/syscalls_64.h>
};

所以最直接的思路就是修改这张表,把对应的系统调用地址更换为我们的函数地址。问题转化为三个子问题:

  • 找到这张表在内存中的地址
  • 这张表所在内存有写保护,我们要关掉写保护
  • 修改这张表(之后要开启写保护)

接下来按这个步骤进行实验。

1 寻找sys_call_table内存地址

寻找系统调用表的地址的方法不止一种。这里先介绍一种,并为其他方法留坑。

要注意的一点是,只有内核中导出的函数和变量符号才能被我们直接引用,没有导出的那些对我们是透明的。参考网友的评论可知,在2.6内核后sys_call_table是不可见的。

① 暴力搜索

原理:内核内存空间的起始地址PAGE_OFFSET变量和sys_close系统调用对我们是可见的(sys_open/sys_read等并未导出);系统调用号(即sys_call_table中的元素下标)在同一ABI(x86与x64属于不同ABI)中是高度后向兼容的;这个系统调用号我们也是可以直接引用的(如__NR_close)。所以我们可以从内核空间起始地址开始,把每一个指针大小的内存假设成sys_call_table的地址,并用__NR_close索引去访问它的成员,如果这个值与sys_close的地址相同的话,就可以认为找到了sys_call_table的地址(但是师傅说这种方法可能被欺骗)。

我们先简单看一下PAGE_OFFSET的定义(x64):

#define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
#define __PAGE_OFFSET           page_offset_base
unsigned long page_offset_base = __PAGE_OFFSET_BASE;
EXPORT_SYMBOL(page_offset_base);
#define __PAGE_OFFSET_BASE      _AC(0xffff880000000000, UL)

接下来看我们的搜索函数:

unsigned long **get_sys_call_table(void)
{
	unsigned long **entry = (unsigned long **)PAGE_OFFSET;
    for(; (unsigned long)entry < ULONG_MAX; entry += 1){
    	if(entry[__NR_close] == (unsigned long *)sys_close)
        	return entry;
    }
    return NULL;
}

测试用LKM模块代码如下(后面将在此模块上添加代码):

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/syscalls.h>

unsigned long **real_sys_call_table;

int init_module(void)
{
	printk("%s\n", "Greetings the world!\n");
    real_sys_call_table = get_sys_call_table();
    printk("PAGE_OFFSET = %lx\n", PAGE_OFFSET);
    printk("sys_call_table = %p\n", real_sys_call_table);
    printk("sys_call_table - PAGE_OFFSET = %lu MiB\n",\
    	((unsigned long)real_sys_call_table - \
         (unsigned long)PAGE_OFFSET) / 1024 / 1024);
    return 0;
}

void cleanup_module(void)
{
	printk("%s\n", "Farewell the World!");
    return;
}

Makefile:

TARGET = sys_call_table
obj-m := ${TARGET}ko.o
${TARGET}ko-objs := ${TARGET}.o 

default:
        ${MAKE} modules \
                --directory "/lib/modules/$(shell uname --release)/build" \
                M="$(shell pwd)"

clean:
        ${MAKE} clean \
                --directory "/lib/modules/$(shell uname --release)/build" \
M="$(shell pwd)"

我们没有使用第一次实验中的module_initmodule_exit两个宏去指定入口函数和出口函数,那样也是可以的,这里只是使用了默认的入口函数名和出口函数名。

测验结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

② 从/boot/System.map提取

暂略,见【拓展阅读】3。

③ 使用未导出函数机器码搜索

暂略,见【拓展阅读】4。

2 关掉写保护

找到地方了,下面要关闭写保护。CR0寄存器从0数的第16比特控制了对只读内存的写保护是否开启,详见【已参考】3。巧的是,我们可以用内核自己的read_cr0/write_cr0去读写CR0,并用它提供的clear_bit/set_bit接口去做位运算。我们把它们封装一下:

void disable_write_protection(void)
{
    unsigned long cr0 = read_cr0();
    clear_bit(16, &cr0);
    write_cr0(cr0);
}

void enable_write_protection(void)
{
    unsigned long cr0 = read_cr0();
    set_bit(16, &cr0);
    write_cr0(cr0);
}

接着在入口函数中添加一些测试代码:

unsigned long cr0;
cr0 = read_cr0();
printk("Old: %d\n", test_bit(X86_CR0_WP_BIT, &cr0));
disable_write_protection();
cr0 = read_cr0();
printk("New: %d\n", test_bit(X86_CR0_WP_BIT, &cr0));
enable_write_protection();
cr0 = read_cr0();
printk("Now: %d\n", test_bit(X86_CR0_WP_BIT, &cr0));

测试结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3 修改sys_call_table

至此,修改就很简单了。配合后面第二部分文件监视,我们将修改三个系统调用:sys_open/sys_unlink/sys_unlinkat。我们的思路是,在入口函数中先备份原始的系统调用,然后修改成我们自己的。在出口函数中恢复原始的系统调用。

修改

disable_write_protection();
real_open = (void *)real_sys_call_table[__NR_open];
real_sys_call_table[__NR_open] = (unsigned long*)fake_open;
real_unlink = (void *)real_sys_call_table[__NR_unlink];
real_sys_call_table[__NR_unlink] = (unsigned long*)fake_unlink;
real_unlinkat = (void *)real_sys_call_table[__NR_unlinkat];
real_sys_call_table[__NR_unlinkat] = (unsigned long*)fake_unlinkat;
enable_write_protection();

恢复

disable_write_protection();
real_sys_call_table[__NR_open] = (unsigned long*)real_open;
real_sys_call_table[__NR_unlink] = (unsigned long*)real_unlink;
real_sys_call_table[__NR_unlinkat] = (unsigned long*)real_unlinkat;
enable_write_protection();

至此,系统调用挂钩就完成了。缺少的函数定义和声明在下一部分加上,同时在下一部分一并演示。

二 基于系统调用挂钩的初级文件监视

这里补上缺少的函数定义:

asmlinkage long (*real_open)(const char __user *, int, umode_t);
asmlinkage long fake_open(const char __user *filename, int flags, umode_t mode)
{
        if((flags & O_CREAT) && strcmp(filename, "/dev/null") != 0){
                printk(KERN_ALERT "open: %s\n", filename);
        }
        return real_open(filename, flags, mode);
}

asmlinkage long (*real_unlink)(const char __user *); 
asmlinkage long *fake_unlink(const char __user *pathname)
{
        printk(KERN_ALERT "unlink: %s\n", pathname);
        return real_unlink(pathname);
}

asmlinkage long (*real_unlinkat)(int, const char __user *, int);
asmlinkage long *fake_unlinkat(int dfd, const char __user *pathname, int flag){
        printk(KERN_ALERT "unlinkat: %s\n", pathname);
        return real_unlinkat(dfd, pathname, flag);
}

编译加载模块,测试结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

中间多出来的/tmp/sh-thd-那两行是我在rm hello时按了tab进行文件名补全才出现的,应该是补全功能产生的临时文件。

unlinkunlinkat几乎相同,关于差异可man unlinkat

注意在测试结束后卸载模块,恢复默认系统调用。

实验问题

【问题一】

KERN_ALERT是干嘛的?

【问题二】

前面说暴力搜索系统调用表的方法可能被欺骗,具体是怎样的欺骗方法?

实验总结与思考

配合着源码在线阅览,边做边能查到内核代码的感觉非常棒。

本次实验中的dmesg -C && dmesg -w比第一次实验中的grep要方便许多。

实验过程中深感自己学识浅薄,静水流深呐。一个朋友在FreeBuf文章下评论说:“写得不错。但获取sys_call_table的地址对hook这一大目标并没有起到多大作用,甚至是多余的。”后来他又说:“回复有所歧义,说不需要知道sys_call_table的地址是针对2.6以前的内核版本,之前的版本可以直接引用sys_call_table变量,多谢提醒!另外除了利用system.map获取table的地址外,可以读取IDT的值,之后找到int $0×80的入口点,后三个字节的值就是table的地址,还没验证。”另一个朋友会说:“这就是Windows的SSDT HOOK在Linux核上的翻版啊。”作者回复说:“是的,眼力不错。都是基于修改系统调用表的系统调用挂钩。Linux的系统调用表叫sys_call_table / ia32_sys_call_table,Windows的系统调用表大家通常叫SSDT。显然,从学习、实践与理解的角度看,Linux更适合用来起步。”

他们的讨论让我学到了知识。社区需要的正是这种讨论,正是这种学习的氛围。谢谢各位师傅的分享。

参考资料

已参考

  • Linux Rootkit 系列二:基于修改 sys_call_table 的系统调用挂钩
  • Github:NoviceLive/research-rootkit
  • Control register

拓展阅读

  • linux-insides
  • How does the Linux kernel handle a system call
  • Hooking the Linux System Call Table
  • Kernel-Land Rootkits

tps://0xax.gitbooks.io/linux-insides/content/SysCall/syscall-2.html)

  • Hooking the Linux System Call Table
  • Kernel-Land Rootkits

您已读完,点此回到顶部

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/374033.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

告别mPDF迎来TCPDF和中文打印遇到的问题

mPDF是一个用PHP编写的开源PDF生成库。它最初由Claus Holler创建&#xff0c;于2004年发布。原来用开源软件打印中文没有问题&#xff0c;最近发现新的软件包中mPDF被TCPDF代替了&#xff0c;当然如果只用西文的PDF是没有发现问题&#xff0c;但要打印中文就有点抓瞎了如图1&am…

【python数据分析基础】—dataframe中index的相关操作(添加、修改index的列名、修改index索引值等)

文章目录 前言一、添加、修改index的列名二、修改index索引值 前言 本文主要讲dataframe结构中index的相关操作&#xff0c;index相当于是数据表的行。 一、添加、修改index的列名 新建一个dataframe表&#xff0c;我们可以自定义index的值&#xff0c;如下&#xff1a; imp…

数据结构高级算法

目录 最小生成树 Kruskal(克鲁斯卡尔)(以边为核心) 9) 不相交集合(并查集合) 基础 Union By Size 图-相关题目 4.2 Greedy Algorithm 1) 贪心例子 Dijkstra Prim Kruskal 最优解(零钱兑换)- 穷举法 Leetcode 322 最优解(零钱兑换)- 贪心法 Leetcode 322 3)…

html5 audio video

DOMException: play() failed because the user didn‘t interact with the document first.-CSDN博客 不可用&#xff1a; 可用&#xff1a; Google Chrome Close AutoUpdate-CSDN博客

基于CNN+LSTM深度学习网络的时间序列预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 卷积神经网络&#xff08;CNN&#xff09; 4.2 长短时记忆网络&#xff08;LSTM&#xff09; 4.3 CNNLSTM网络结构 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MA…

2020年通信工程师初级 综合能力 真题

文章目录 第1章 通信职业道德&#xff0c;1-4第2章 法律法规&#xff0c;5-16第3章 计算机应用基础&#xff0c;第5章 现代通信网&#xff0c;38英语题&#xff0c;91 第1章 通信职业道德&#xff0c;1-4 1、职业道德在形式上具有()特点。 A.一致性 B.统一性 C.多样性 D.一般性…

PHP实现DESede/ECB/PKCS5Padding加密算法兼容Java SHA1PRNG

这里写自定义目录标题 背景JAVA代码解决思路PHP解密 背景 公司PHP开发对接一个Java项目接口&#xff0c;接口返回数据有用DESede/ECB/PKCS5Padding加密&#xff0c;并且key也使用了SHA1PRNG加密了&#xff0c;网上找了各种办法都不能解密&#xff0c;耗了一两天的时间&#xf…

【UE】游戏运行流程的简单理解

流程图 官方的游戏流程图&#xff1a; 一般顺序为初始化引擎、创建并初始化 GameInstance、加载关卡&#xff0c;最后开始游戏。 总的来说就是&#xff1a; 开始游戏-》游戏实例-》关卡-》游戏模式-》玩家控制器-》Pawn、玩家状态、HUD、UMG&#xff08;可有可无&#xff09; …

idea运行程序报错 java 程序包org.junit不存在

在 IntelliJ IDEA 中运行程序时遇到错误提示&#xff1a;“java: 程序包org.junit不存在”&#xff0c;针对这一问题&#xff0c;我们可以考虑以下三步来解决&#xff1a; 第一步&#xff1a;检查JUnit依赖 尽管现代项目创建时通常会默认引入JUnit依赖&#xff0c;但仍需检查…

postman 文档、导出json脚本 导出响应数据 response ,showdoc导入postman json脚本 导出为文档word或markdown

保存、补全尽可能多的数据、描述 保存响应数据 Response&#xff1a;&#xff08;如果导出接口数据&#xff0c;会同步导出响应数据&#xff09; 请求接口后&#xff0c;点击下方 Save as Example 可以保存响应数据到本地&#xff08;会在左侧接口下新增一个e.g. 文件用来保…

仅一个月获推荐170w+,视频号近期爆火的秘诀是什么?

为了保证良好的创作环境&#xff0c;视频号的原创标准在1月做了新调整&#xff0c; 视频时长小于5秒则不能声明为原创 &#xff0c;纯图片轮播也不能声明为原创&#xff0c;只有持续输出优质内容的账号才能显示原创标识及原创保护功能&#xff0c;这样的改动也给了不少创作者…

基于Vue的移动端UI框架整理

一、Vant 官方地址&#xff1a;https://youzan.github.io/vant/#/zh-CN/ 简介&#xff1a;有赞公司开发。 特性&#xff1a;60 高质量组件、90% 单元测试覆盖率、完善的中英文文档和示例、支持按需引入、支持主题定制、支持国际化、支持 TS、支持 SSR。 特别说明&#xff1…

Redis 命令大全

文章目录 启动与连接Key&#xff08;键&#xff09;相关命令String&#xff08;字符串&#xff09;Hash&#xff08;哈希&#xff09;List&#xff08;列表&#xff09;Set&#xff08;集合&#xff09;Sorted Set&#xff08;有序集合&#xff09;其他常见命令HyperLogLog&…

Unity类银河恶魔城学习记录3-1 EnemyStateMachine源代码 P47

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Enemy.cs using System.Collections; using System.Collections.Generic;…

Java 学习和实践笔记(1)

2024年&#xff0c;决定好好学习计算机语言Java. B站上选了这个课程&#xff1a;【整整300集】浙大大佬160小时讲完的Java教程&#xff08;学习路线Java笔记&#xff09;零基础&#xff0c;就从今天开始学吧。 在这些语言中&#xff0c;C语言是最基础的语言&#xff0c;绝大多…

抢 React 饭碗!如何在 Vue3 中使用 JSX TSX

首先&#xff0c;恭喜一下 React&#xff0c;再过 4 个月&#xff0c;就达成了两年无更新记录 反观隔壁的 Vue&#xff0c;稳定迭代更新 之前写 React 的时候&#xff0c;最喜欢的是 JSX/TSX 语法&#xff0c;把 HTML 组件当做 JavaScript/TypeScript 代码片段处理 const Ap…

数学建模:数据相关性分析(Pearson和 Spearman相关系数)含python实现

相关性分析是一种用于衡量两个或多个变量之间关系密切程度的方法。相关性分析通常用于探索变量之间的关系&#xff0c;以及预测一个变量如何随着另一个变量的变化而变化。在数学建模中&#xff0c;这是常用的数据分析手段。   相关性分析的结果通常用相关系数来表示&#xff…

一个查看armv8系统寄存器-值-含义的方式

找到解压后的SysReg_xml_v86A-2019-12目录 wget https://developer.arm.com/-/media/developer/products/architecture/armv8-a-architecture/2019-12/SysReg_xml_v86A-2019-12.tar.gz wget https://developer.arm.com/-/media/developer/products/architecture/armv8-a-archi…

k8s中cert-manager管理https证书

前言 目前https是刚需,但证书又很贵,虽然阿里云有免费的,但没有泛域名证书,每有一个子域名就要申请一个证书,有效期1年,1年一到全都的更换,太麻烦了。经过搜索,发现了自动更新证书神器cert-manager;当然cert-manager是基于k8s的。 安装采用Helm方式 Chart地址: ht…

在没有鼠标或键盘的情况下在 Mac 上如何启用蓝牙?

通过这个技巧&#xff0c;小编将向您展示几种无需鼠标或键盘即可在 Mac 上重新启用蓝牙的方法。如果您想开始使用蓝牙配件&#xff0c;但还没有连接&#xff0c;这会很有用。 无需鼠标即可启用蓝牙 蓝牙是iPhone、iPad和 Mac 的标准配置。它确保您可以无线使用各种配件&#…