linux目录隐藏技术,Linux环境下的高级隐藏技术

摘要:本文深入分析了Linux环境下文件、进程及模块的高级隐藏技术,其中包括:Linux可卸载模块编程技术、修改内存映象直接对系统调用进行修改技术,通过虚拟文件系统proc隐藏特定进程的技术。

隐藏技术在计算机系统安全中应用十分广泛,尤其是在网络攻击中,当攻击者成功侵入一个系统后,有效隐藏攻击者的文件、进程及其加载的模块变得尤为重要。本文将讨论Linux系统中文件、进程及模块的高级隐藏技术,这些技术有的已经被广泛应用到各种后门或安全检测程序之中,而有一些则刚刚起步,仍然处在讨论阶段,应用很少。

1.隐藏技术

1.1.Linux下的中断控制及系统调用

Intel x86系列微机支持256种中断,为了使处理器比较容易地识别每种中断源,把它们从0~256编号,即赋予一个中断类型码n,Intel把它称作中断向量。

Linux用一个中断向量(128或者0x80)来实现系统调用,所有的系统调用都通过唯一的入口system_call来进入内核,当用户动态进程执行一条int 0x80汇编指令时,CPU就切换到内核态,并开始执行system_call函数,system_call函数再通过系统调用表sys_call_table来取得相应系统调用的地址进行执行。系统调用表sys_call_table中存放所有系统调用函数的地址,每个地址可以用系统调用号来进行索引,例如sys_call_table[NR_fork]索引到的就是系统调用sys_fork()的地址。

Linux用中断描述符(8字节)来表示每个中断的相关信息,其格式如下:

偏移量31….16  一些标志、类型码及保留位

段选择符      偏移量15….0

所有的中断描述符存放在一片连续的地址空间中,这个连续的地址空间称作中断描述符表(IDT),其起始地址存放在中断描述符表寄存器(IDTR)中,其格式如下:

32位基址值  界限

其中各个结构的相应联系可以如下表示:

通过上面的说明可以得出通过IDTR寄存器来找到system_call函数地址的方法:根据IDTR寄存器找到中断描述符表,中断描述符表的第0x80项即是system_call函数的地址,这个地址将在后面的讨论中应用到。

1.2.Linux 的LKM(可装载内核模块)技术

为了使内核保持较小的体积并能够方便的进行功能扩展,Linux系统提供了模块机制。模块是内核的一部分,但并没有被编译进内核,它们被编译成目标文件,在运行过程中根据需要动态的插入内核或者从内核中移除。由于模块在插入后是作为Linux内核的一部分来运行的,所以模块编程实际上就是内核编程,因此可以在模块中使用一些由内核导出的资源,例如Linux2.4.18版以前的内核导出系统调用表(sys_call_table)的地址,这样就可以根据该地址直接修改系统调用的入口,从而改变系统调用。在模块编程中必须存在初始化函数及清除函数,一般情况下,这两个函数默认为init_module()以及clearup_module(),从2.3.13内核版本开始,用户也可以给这两个函数重新命名,初始化函数在模块被插入系统时调用,在其中可以进行一些函数及符号的注册工作,清除函数则在模块移除系统时进行调用,一些恢复工作通常在该函数中完成。

1.3.Linux下的内存映像

/dev/kmem是一个字符设备,是计算机主存的映像,通过它可以测试甚至修改系统,当内核不导出sys_call_table地址或者不允许插入模块时可以通过该映像修改系统调用,从而实现隐藏文件、进程或者模块的目的。

1.4.proc 文件系统

proc文件系统是一个虚拟的文件系统,它通过文件系统的接口实现,用于输出系统运行状态。它以文件系统的形式,为操作系统本身和应用进程之间的通信提供了一个界面,使应用程序能够安全、方便地获得系统当前的运行状况何内核的内部数据信息,并可以修改某些系统的配置信息。由于proc以文件系统的接口实现,因此可以象访问普通文件一样访问它,但它只存在于内存之中。

2.技术分析

2.1 隐藏文件

Linux系统中用来查询文件信息的系统调用是sys_getdents,这一点可以通过strace来观察到,例如strace ls 将列出命令ls用到的系统调用,从中可以发现ls是通过sys_getedents来执行操作的。当查询文件或者目录的相关信息时,Linux系统用sys_getedents来执行相应的查询操作,并把得到的信息传递给用户空间运行的程序,所以如果修改该系统调用,去掉结果中与某些特定文件的相关信息,那么所有利用该系统调用的程序将看不见该文件,从而达到了隐藏的目的。首先介绍一下原来的系统调用,其原型为:

int sys_getdents(unsigned int fd, struct dirent *dirp,unsigned int count)

其中fd为指向目录文件的文件描述符,该函数根据fd所指向的目录文件读取相应dirent结构,并放入dirp中,其中count为dirp中返回的数据量,正确时该函数返回值为填充到dirp的字节数。下图是修改后的系统调用hacked_getdents执行流程。

[IMG]/ShowImg.asp?p=/2006-4-5/991518a108929.gif[/IMG]

图 系统调用hacked_getdents执行流程

图中的hacked_getdents函数实际上就是先调用原来的系统调用,然后从得到的dirent结构中去除与特定文件名相关的文件信息,从而应用程序从该系统调用返回后将看不到该文件的存在。

应该注意的是,一些较新的版本中是通过sys_getdents64来查询文件信息的,但其实现原理与sys_getdents基本相同,所以在这些版本中仍然可以用与上面类似的方法来修改该系统调用,隐藏文件。

2.2 隐藏模块

上面分析了如何修改系统调用以隐藏特定名字的文件,在实际的处理中,经常会用模块来达到修改系统调用的目的,但是当插入一个模块时,若不采取任何隐藏措施,很容易被对方发现,一旦对方发现并卸载了所插入的模块,那么所有利用该模块来隐藏的文件就暴露了,所以应继续分析如何来隐藏特定名字的模块。Linux中用来查询模块信息的系统调用是sys_query_module,所以可以通过修改该系统调用达到隐藏特定模块的目的。首先解释一下原来的系统调用,原来系统调用的原型为:

int sys_query_module(const char *name, int which, void *buf, size_t bufsize , size_t *ret)

如果参数name不空,则访问特定的模块,否则访问的是内核模块,参数which说明查询的类型,当which=QM_MODULES时,返回所有当前已插入的模块名称,存入buff, 并且在ret中存放模块的个数,buffsize是buf缓冲区的大小。在模块隐藏的过程中只需要对which=QM_MODULES的情况进行处理就可以达到目的。修改后的系统调用工作过程如下:

1)调用原来的系统调用,出错则返回错误代码;

2)如果which不等于QM_MODULES,则不需要处理,直接返回。

3)从buf的开始位置进行处理,如果存在特定的名字,则将后面的模块名称向前覆盖该名字。

4)重复3),直到处理处理完所有的名字,正确返回。

2.3 隐藏进程

在Linux中不存在直接查询进程信息的系统调用,类似于ps这样查询进程信息的命令是通过查询proc文件系统来实现的,在背景知识中已经介绍过proc文件系统,由于它应用文件系统的接口实现,因此同样可以用隐藏文件的方法来隐藏proc文件系统中的文件,只需要在上面的hacked_getdents中加入对于proc文件系统的判断即可。由于proc是特殊的文件系统,只存在于内存之中,不存在于任何实际设备之上,所以Linux内核分配给它一个特定的主设备号0以及一个特定的次设备号1,除此之外,由于在外存上没有与之对应的i节点,所以系统也分配给它一个特殊的节点号PROC_ROOT_INO(值为1),而设备上的1号索引节点是保留不用的。通过上面的分析,可以得出判断一个文件是否属于proc文件系统的方法:

1)得到该文件对应的inode结构dinode;

2)if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i _dev) == 1) {该文件属于proc文件系统}

通过上面的分析,给出隐藏特定进程的伪代码表示:

hacket_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)

{

调用原来的系统调用;

得到fd所对应的节点;

if(该文件属于proc文件系统&&该文件名需要隐藏)

{从dirp中去掉该文件相关信息}

}

2.4 修改系统调用的方法

现在已经解决了如何修改系统调用来达到隐藏的目的,那么如何用修改后的系统调用来替换原来的呢?这个问题在实际应用中往往是最关键的,下面将讨论在不同的情况下如何做到这一点。

(1)当系统导出sys_call_table,并且支持动态的插入模块的情况下:

在Linux内核2.4.18版以前,这种内核配置是非常普遍的。这种情况下修改系统调用非常容易,只需要修改相应的sys_call_table表项,使其指向新的系统调用即可。下面是相应的代码:

int orig_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)

int init_module(void)

/*初始化模块*/

{

orig_getdents=sys_call_table[SYS_getdents];    //保存原来的系统调用

orig_query_module=sys_call_table[SYS_query_module]

sys_call_table[SYS_getdents]=hacked_getdents;  //设置新的系统调用

sys_call_table[SYS_query_module]=hacked_query_module;

return 0; //返回0表示成功

}

void cleanup_module(void)

/*卸载模块*/

{

sys_call_table[SYS_getdents]=orig_getdents;    //恢复原来的系统调用

sys_call_table[SYS_query_module]=orig_query_module;

}

(2)在系统并不导出sys_call_table的情况下:

linux内核在2.4.18以后为了安全起见不再导出sys_call_table符号,从而无法直接获得系统调用表的地址,那么就必须找到其他的办法来得到这个地址。在背景知识中提到了/dev/kmem是系统主存的映像,可以通过查询该文件来找到sys_call_table的地址,并对其进行修改,来使用新的系统调用。那么如何在系统映像中找到sys_call_table的地址呢?让我们先看看system_call的源代码是如何来实现系统调用的(代码见/arch/i386/kernel/entry.S):

ENTRY(system_call)

pushl %eax      # save orig_eax

SAVE_ALL

GET_CURRENT(%ebx)

cmpl $(NR_syscalls),%eax

jae badsys

testb $0x02,tsk_ptrace(%ebx)  # PT_TRACESYS

jne tracesys

call *SYMBOL_NAME(sys_call_table)(,%eax,4)

movl %eax,EAX(%esp)    # save the return value

ENTRY(ret_from_sys_call)

这段源代码首先保存相应的寄存器的值,然后判断系统调用号(在eax寄存器中)是否合法,继而对设置调试的情况进行处理,在所有这些进行完后,利用call *SYMBOL_NAME(sys_call_table)(,%eax,4) 来转入相应的系统调用进行处理,其中的SYMBOL_NAME(sys_call_table)得出的就是sys_call_table的地址。从上面的分析可以看出,当找到system_call函数之后,利用字符匹配来寻找相应call语句就可以确定sys_call_table的位置,因为call something(,%eax,4)的机器指令码是0xff 0x14 0x85。所以匹配这个指令码就行了。至于如何确定system_call的地址在背景知识中已经介绍了,下面给出相应的伪代码:

struct{ //各字段含义可以参考背景知识中关于IDTR寄存器的介绍

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 kmem;

/ *下面函数用于从kemem对应的文件中偏移量为off处读取sz个字节至内存m处*/

void readkmem(void *m,unsigned off,int sz) {………}

/*下面函数用于从src读取count个字节至dest处*/

void weitekmem(void *src,void *dest,unsigned int count) {………..}

unsigned sct;  //用来存放sys_call_table地址

char buff[100]; //用于存放system_call函数的前100个字节。

char *p;

if((kmem=open(“/dev/kmem”,O_RDONLY))<0)

return 1;

asm(“sidt %0” “:=m” (idtr));          //读取idtr寄存器的值至idtr结构中

readkmem(&idt,idtr.base+8*0x80,sizeof(idt))    //将0x80描述符读至idt结构中

sys_ call_off=(idt.off2<<16)|idt.off1;       //得到system_call函数的地址。

readkmem(buff,sys_call_off,100)   //读取system_call函数的前100字节至buff

p=(char *)memmem(buff,100,”xffx14x85”,3);  //得到call语句对应机器码的地址

sct=(unsigned *)(p+3)            //得到sys_call_table的地址。

至此已经得到了sys_call_table在内存中的位置,这样在根据系统调用号就能够找到相应的系统调用对应的地址,修改该地址就可以使用新的系统调函数,具体的做法如下:

readkmem(&orig_getdents,sct+ SYS_getdents*4,4)//保存原来的系统调用

readkmem(&orig_query_module,sct+SYS_query_module*4,4);

writekmem(hacked_getdents,sct+SYS_getdents*4,4);//设置新的系统调用

writekmem(hacket_query_module,sct+SYS_query_module*4,4);

2.5 其他的相关技术

上面已经完全解决了隐藏的相关技术问题,在实际应用中,可以把启动模块或者进程的代码做成脚本加入到相应的启动目录中,假设你的Linux运行级别为3,则可以加到目录rc3.d中(该目录常存在于/etc/rc.d或者/etc目录下),然后把该脚本的名字改为可以隐藏的名字。另一种方法就是在一些启动脚本中加入启动你的模块或者进程的代码,但这样比较容易被发现,一个解决思路就是进程或模块启动以后马上恢复正常的脚本,由于系统关机时会向所有进程发送SIGHUP信号,可以在进程或模块中处理该信号,使该信号发生时修改启动脚本,重新加入启动模块的代码,这样当系统下次启动时又可以加载这个的模块了,而且管理员察看启动脚本时也不会发现异常。

3.结束语

本文对Linux环境下的一些高级隐藏技术进行了分析研究,其中所涉及的技术不仅可以用在系统安全方面,在其他方面也有重要的借鉴意义。由于Linux的开放特性,使得攻击者一旦获得了root权限就能够对系统进行较多的修改,所以避免第一次被入侵是至关重要的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python代替shell脚本_python的简洁是shell无法代替的

之前线上服务器分发配置都是用shell和expect脚本分发&#xff0c;脚本写了很长&#xff0c;上周换了ansible&#xff0c;现在自己用python写一个&#xff0c;就30行代码就可以实现需求&#xff0c;之前的shell写了快200行了&#xff0c;蛋疼&#xff0c;代码如下&#xff1a; f…

linux命令cp命令行参数,linux命令之cp命令参数及用法详解

cp (复制档案或目录)[rootlinux ~]# cp [-adfilprsu] 来源档(source) 目的檔(destination)[rootlinux ~]# cp [options] source1 source2 source3 …. directory参数&#xff1a;-a &#xff1a;相当于 -pdr 的意思&#xff1b;-d &#xff1a;若来源文件为连结文件的属性(link…

python电脑编程求圆的面积案例_学Python划重点七 网络编程(UPD Socket编程、上传文件实例、计算圆的面积实例)...

一、UPD Socket 编程 socket 对象中与UDP Socket 服务器编程有关的方法是bind() &#xff0c;注意不需要listen() 和accept() &#xff0c; 这是因为UDP 通信不需要像TCP 一样监听端口&#xff0c;建立连接。 socket.recvfrom(buffsize) &#xff1a; 接收UDP Socket 数据&…

alert获取输入框内容_获取由 AlertDialog 生成的对话框中EditText的文本内容

在Android开发中&#xff0c;AlertDialog常用于处理用户的登录等。那么如何获取由 AlertDialog 生成的对话框中EditText的文本内容呢&#xff1f;其实Alertdialog弹出的Activity 可以认为是默认的Activity的子Activity。不能直接使用findViewById()来获取。强行使用会提示一个空…

linux中将 home目录备份成,6种优化Linux系统备份的应用与技巧

【51CTO.com快译】当转为使用Linux时&#xff0c;你可能会注意到你所使用的版本会带有一个默认的备份工具。然而&#xff0c;可能该工具并没有被设置好去备份你系统中所有重要的部分。而且&#xff0c;使用该默认工具也可能导致它以比预期更低的效率去产生一些更大的备份数据。…

db2嵌套查询效率_db2性能优化

性能优化概述DB2 的性能优化可以从三个方面分析&#xff1a;内存&#xff0c;CPU 和 I/O 。DB2 性能优化是一件较为复杂的综合性的工作 , 需要对问题的根源作全方位的探索和思考。同时也需要较深厚的数据库管理经验与优化知识。这对于初学者来说可能有些勉为其难。但是在很多情…

python数据分析函数大全_Python常用数据分析函数集合

1.Map函数 - 列表解析 ①.map()函数解析 (1).python源码信息C:\Users\ArSang>python Python3.6.3rc1 (v3.6.3rc1:d8c174a, Sep 19 2017, 16:39:51) [MSC v.1900 64bit (AMD64)] on win32 Type"help", "copyright", "credits" or "licens…

我的世界linux开服权限不足,我的世界路由器开服怎么获得超级管理员权限

我的世界路由器开服怎么获得超级管理员权限。小伙伴们使用路由器开服的时候会发现一个明显的问题&#xff0c;部分路由器并没有给你最高管理员的权限&#xff0c;导致大家开服的时候出现各种问题。那么路由器开服怎么才能快速获得超级管理员的权限呢?说明&#xff1a;为什么路…

python无法调用安装的包_如何使用python命令和安装包进行安装Django框架

在python语言的框架中&#xff0c;Django框架是一种Web应用框架&#xff0c;这个框架的版本需要跟python的版本搭配上。其中&#xff0c;Django框架可以使用命令安装&#xff0c;也可以使用下载压缩包进行安装。下面利用实例说明Django框架安装方法和过程&#xff0c;操作如下&…

vs传值接收页面怎么直接打开_VS2003传值

估计楼主应该是用了ORM,只知道定义泛型类,然后ORM自己就完成映射,具体怎么得到的数据,就不知道了.所以,请退回到旧时代,用DataTable传递数据.cmdText:这个是SQL查询语句;connStr:这个是数据库连接字符串.public static DataTable(string cmdText,string connStr){SqlConnection…

linux摄像头内核驱动开发,怎么在Linux下开发摄像头驱动

无根之木不活&#xff0c;无基之楼不立&#xff0c;无论是学习哪个领域知识&#xff0c;基础是重中之重。针对学习linux驱动&#xff0c;我们来仔细谈谈&#xff1a;个人认为C语言和数据结构就是重中之重&#xff01;Linux系统最优秀的地方就在于内核。无论是进程调度&#xff…

idea卡顿的解决方法_lt;绝地求生/PUBG/解决卡顿方法(停机9小时)维护gt;9月16日(星期三) 08点30分amp;崩溃...

【正式服不停机维护公告】我们将在北京时间10月7日(星期三) 10点开始正式服的不停机维护。- 维护开始时间&#xff1a;10月7日(星期三) 10点 - 维护内容&#xff1a;优化服务器以及BUG修复1. 修复了在四排模式下队伍列表中无法显示队员的问题&#xff1b;2. 修复了警示胶带背包…

安装应用需要打开未知来源权限_打开安卓这个开关 不让流氓软件肆意妄为

1月21消息&#xff0c;国外媒体Cybernews曝光了数款存在安全隐患的安卓应用&#xff0c;从这些应用中我们发现大都是相机类应用。根据Cybernews的说法这些应用会寻求多项权限&#xff0c;而多数权限和应用本身功能并不相关。其实在国内这类软件也并不缺乏&#xff0c;他们通常会…

git for windows_手把手教会舍友玩 Git (包教包会,再也不用担心他的学习)

作 者&#xff1a; BWH_Steven 原文链接&#xff1a;https://www.cnblogs.com/ideal-20/p/13644420.html舍友长大想当程序员&#xff0c;我和他爷爷奶奶都可高兴了&#xff0c;写他最喜欢的喜之郎牌Git文章&#xff0c;学完以后&#xff0c;再也不用担心舍友的学习了(狗头)哪里…

合并的表格怎么加横线_word怎么合并表格边框

回答&#xff1a;方法如下:去掉Word文字保留表格的方法一&#xff1a;1、全选表格&#xff0c;按 Delet 键&#xff0c;表格的所有文字被删除&#xff0c;如图3所示&#xff1a;2、图3是图1所示是表格&#xff0c;文字都没有了。去掉Word文字保留表格的方法二&#xff1a;全选表…

Linux免设置路由端口映射,2014/04/01 演示中设置linux路由器、端口过滤的使用、路由设置...

2.linux主机做为路由器&#xff0c;具有数据转发和随机丢包的功能1.1.iptables 基于内核的防火墙1.2.编写防火墙设置的脚本及鸟哥的linux私房菜(服务器篇)linux 路由器的设置参考鸟哥的linux私房菜(服务器篇)使能数据包转发功能即可&#xff0c;linux内核本身就支持1.#cat /pro…

stotybord如何添加子视图_SQL复杂查询-子查询

视图&#xff1a;1.视图的概念引入&#xff1a;SQL数据库里面存放的是SQL语句&#xff0c;视图中查看到的SQL查询语句&#xff0c;当我们运用客户端连接到视图时候&#xff0c;视图会运行里面的查询语句&#xff0c;从库中查找出数据&#xff0c;保存在临时表里面。当客户端和数…

python模拟登录页面下载_python爬虫之模拟登录

最近应朋友要求&#xff0c;帮忙爬取了小红书创作平台的数据&#xff0c;感觉整个过程很有意思&#xff0c;因此记录一下。在这之前自己没怎么爬过需要账户登录的网站数据&#xff0c;所以刚开始去看小红书的登录认证时一头雾水&#xff0c;等到一步步走下来&#xff0c;最终成…

springboot 2.3_Spring Boot 应用监控,早发现早

小Hub领读&#xff1a;服务都需要监控&#xff0c;SpringBoot项目中&#xff0c;你常用哪些监控模块呢&#xff1f;actuator?admin?作者&#xff1a;小小____https://segmentfault.com/a/1190000022945443当一个 Spring Boot 应用运行的时候&#xff0c;开发者需要对 Spring …

linux 内核 xfs ail,linux 3.10内核 xfs的一次io异常导致的hung crash

8556395.925003] XFS (sdi): xfs_log_force: error -5returned.[8556407.425047] INFO: task umount:19286 blocked for more than 1200seconds.----这个默认是120&#xff0c;该环境上是被人为设置1200[8556407.425653] "echo 0 > /proc/sys/kernel/hung_task_timeout…