优良的rootkit(以下简写为rk)通常应该具备隐蔽稳定的通信功能。正如优秀的程序员一直追求用最简洁的代码完成功能需求一样,优秀的rk coder也一直秉承着绝不在rk中加入过多无用的远程命令,让rk尽量回归其原始定义--获得“root”权限的kit的宗旨。而如何实现隐蔽是rk通信首先要考虑的问题。 因此,出现了各式各样的隐蔽隐藏技术。几年前的高级手段随着内核技术的公开与普及在时下被理所当然地判定为浅“藏”辄止,而时受深度检测工具干扰的在内核级深藏不露的rk亦不能使rk coder感到一劳永逸。因为构造隐蔽通信中的无人之境才是他们永恒不变的追求。
一、浅“藏” 一直以来隐蔽战线采取着一种策略,即:藏得了就藏,藏不了就“装”。靠在ring3层隐藏自己端口的rk如今肯定是回天乏术了,而像ring3级别的Hacker Defender以及Byshell则是通过挂接网络native api来实现端口复用从而达到不开端口隐蔽通信的伪装效果。尽管作者们使用的技术与技巧十分之精湛,但面对如今这个内核代码满天飞的局面,ring3对抗ring0还是显得势单力薄了一些。ring3级别的挂钩很容易被检测,端口复用同样会在ring0层的连接枚举下无可遁形,同时又受到基于主机的防火墙的封杀。与此同时或是更早,PCC(Passive Convert Channels)进入到某些人的视线之中。这种将后门或者rk通信数据嵌入到正常的数据包之中的方法的确很有创意(例如通过TCP报头中的ISN来传递数据的NUSHU),但却无法逃脱本身缺陷导致的局限性以及被检测的厄运。
二、深藏不露? 伴随着rk面向内核层的全线出击,隐蔽通信方面也催生出了新技术。而这些新技术的答案也往往可以在NDIS_PROTOCOL_BLOCK之中找到。为什么?因为协议驱动,中间层驱动和微端口驱动在NDIS体系中按照自顶向下的顺序排列。所以位于最上层的协议驱动首当其冲地成为了宰割对象。并且NDIS_PROTOCOL_BLOCK这个结构可以用很强大来形容,它下面的各个成员链接到的结构可以说是五花八门、不尽其数,这里面当然就包括NDIS_OPEN_BLOCK,NDIS_PROTOCOL_CHARACTERISTICS等结构。这些结构之中包含有N多函数指针,于是很容易联想到对它们进行暗箱操作。 协议驱动在DriverEntry()中会调用NdisRegisterProtocol()向NDIS库注册自己的协议,获取到一个指向NDIS_PROTOCOL_BLOCK的指针。NDIS做的则是把这个结构放到协议块链表的最前面,链表头由ndisProtocolList指定。事实上,每个协议的NDIS_PROTOCOL_BLOCK通过一个单向链表链在了一起。 Uay通过挂接NDIS_PROTOCOL_CHARACTERISTICS结构中的发送和接收例程实现自己的隐蔽通信;Deepdoor则是挂接了TCP/IP协议的NDIS_OPEN_BLOCK块中的发送和接受处理例程达到目的。Uay和Deepdoor有些类似,都是在协议层上进行挂接,因此它们要自己实现封包解包等一些功能。其实可以做手脚的地方还有很多,比如DKOM NDIS_MINIPORT_BLOCK。 另一方面,检测与防护战线也不甘示弱。中间层驱动,设备过滤,MajorFunction hooking,NDIS.SYS导出表挂接,NDIS代码补丁...战火一度在内核层引爆。随着基于主机的网络嗅探器与防火墙在内核位置的下移,上述原本深藏不露的隐蔽通信方法也因为疲于应付而变得捉襟见肘。其中某些手段在协议链遍历和NDIS_OPEN_BLOCK函数指针完整性检测的排挤下也开始逐渐失效。更严重的是,它们的通信数据包在Iris等网络嗅探工具下可以马上现形。因此,这就需要我们开辟新的方法。
三、无人之境? 那么究竟如何才能绕过现有一切检测技术,并且在通信数据包不被基于主机的嗅探器截获的情况下实现隐蔽的通信呢?答案即:回归原始,直接和网卡芯片控制器交互。下面的文字将围绕这一主题展开:以PCI总线类型的Realtek RTL8139高速以太网卡为例,具体阐述rk同网卡芯片控制器实施交互的过程,探讨打造出一个如入无人之境的隐蔽rk的可能。由于某些原因,所以仅仅描述原理,感兴趣的朋友自己动手也完全没有问题。 同控制器直接交互的优点在于通过直接I/O芯片上的一些端口寄存器避免了一切NDIS层面上的挂接,嗅探器完全不能抓到任何我们的数据包,因为它在我们的上面!还有,这种方法根本不受操作系统环境下对网卡状态的影响:将网络连接关闭,网卡停止等都是徒劳,因为生杀大权牢牢被我们掌控。只要网卡连在总线上,供电,网路畅通即可实现数据传输。 作为目前应用范围较广的RTL8139芯片控制器提供了众多特性。比如高度集成的以太网MAC,物理芯片同收发器的集成,支持远程唤醒,最大可支持128K EPROM和Flash Memory。与此同时,RTL8139提供了为数众多的端口寄存器供映射到I/O空间,下面仅结合网卡初始化、数据包的发送与接收讲解常用的几个,其它的参考datasheet即可。
1. 8139初始化 网卡在初始化时涉及到的寄存器有:CR(Command Register),TCR(Tx Configuration Register),RCR(Rx Configuration Register),IMR(Interrupt Mask Register),RBSTART(Rx Buffer Start Address)。它们很多可以顾名思义。初始化过程大致为: (1) 设置CR:将CR中的RE和TE标志位置1并写入CR,即启用8139网卡芯片的收发器。这两个标志位置1后,接收状态机和发送状态机将由空闲变为活动状态 (2) 设置RCR: a 将RCR中的RBLEN(BIT12-11)相应位置1并写回RCR:即指定接收环形缓冲区(Rx Ring Buffer)的大小(通常设为16K+16bytes); b 将MXDMA(BIT10-8)标志中的相应位置1,写回RCR:即指定接收的DMA传输数据最大值(通常为1024bytes); c 将RXFTH(BIT15-13)标志中的相应位置1并写回RCR:即指定Rx FIFO的接收数据阀值(通常为64bytes),这个下面会讲到 d 将AB,AM,APM位置1写回,即指明接受广播、组播以及匹配网卡地址的数据包
(3) 设置TCR: a 将TCR中的IFG(BIT25-24)标志位置为全1并写回TCR,指定帧间隔时间; b 将MXDMA(BIT10-8)相应位置1(视不同情况而定),指明每次DMA发送数据的最大字节数(推荐值为1024bytes) (4) 设置RBSTART:将事先分配的物理地址写入Receive Buffer Start Address,8139芯片会将Rx FIFO中的数据移动到这块物理内存 (5) 设置IMR:开启除系统错误外的所有中断,并写入IMR。这个寄存器指明了在什么条件下会致使芯片的中断控制逻辑产生一个中断并调用ISR
2. 发送数据包 其实在我们的rk中完全没必要再对网卡进行初始化,因为网卡驱动已经帮助我们做了这项工作。如果需要,rk可以读出这些端口寄存器的值进行修改(比如直接让网卡halt掉)。让我们把注意力放在8139芯片的收发数据包上。根据RTL8139 datasheet中的规定,其在进行数据发送时要用一对描述符用以描述待发送的数据。这对描述符分别是TSD(Transmit Status Descriptor)和TSAD(Transmit Start Address Descriptor)。而8139共提供了4对这样的描述符用于数据的发送,即TSD0-3,TSAD0-3。它们分别位于I/O空间的0x10偏移和0x20偏移处,每个描述符4字节大小。这四个描述符是轮流使用的。 从硬件层讲,当对一对transmit描述符I/O完毕之后,8139芯片就会在指令的控制下通过PCI接口将数据包从物理内存以总线主控的DMA传输模式搬运到网卡芯片的发送FIFO(Tx FIFO)。而这个数据包的物理地址和大小恰好是对TSD和TSAD进行I/O写时指定过的,所以它可以很轻松地得到该数据包。8139芯片内置了两个大容量(2K)的收发FIFO用于暂存即将送上电缆及从电缆上接收的报文。如果FIFO中包含了一个完整的数据包抑或包大小到达了在TSD中指定的阀值,芯片便开始继续向下传输。接下来数据包将会经由FIFO控制逻辑部件,到达收发逻辑接口,然后再经MII接口出去离开MAC到达物理芯片。物理芯片中经过编码后再通过发送器部件送至RJ-45端口。 当然,软件层没必要关心太多FIFO之后的事情,rk只需要按照规矩做就行了。发送包的流程大概如下: (1) 分配好一块物理内存,将待发送数据包拷贝到这片内存 (2) I/O写transmit描述符,将数据包的内存地址和大小填入 当整个数据包被送上电缆时,TSD中的TOK(Transmit OK)标志位被置为1,如果之前在IMR中开启了TOK中断,此时中断就会触发。
3. 接收数据包 接收模块相对就要复杂一些。因为transmit是一个主动行为,而receive是被动的。rk毕竟不是硬件,没有一个机制触发它使其能比网卡更早的得到数据包到达的中断信号。因此我们不得不对网卡驱动的ISR做些处理。 之前提到了所谓的Tx FIFO,那么自然就会有一个Rx FIFO。从电缆上接收的数据包首先被放置在芯片的Rx FIFO中。当Rx FIFO到达了RCR中预先设定的数据量阀值时,芯片就会发信号申请占用PCI总线,以总线主控模式通过DMA将数据传送到接收缓冲区。 而这个接收缓冲区是以一个环形的方式组织而成的,其实质还是一片连续的物理内存。这里面涉及到一个CAPR(Current Address of Packet Read),其中包含了当前读到的数据包的地址。综上,那么网卡处理一个包的整个过程为: (1) 线路上接收到的数据存储在芯片的Rx FIFO中 (2) 当到达接收阀值时,数据被移动到Rx Buffer(RBSTART指定的物理内存) (3) 当整个包移动完毕时,接收包头信息被写入包的起始。 (4) CR中的BUFE标志位被置为1,ISR中的TOK标志位置为1 (5) 网卡的ISR例程被调用,由ISR来清TOK位并更新CAPR 讲到这里,原理已经很明显了。rk能做的就是替换掉网卡驱动的ISR,当接收中断产生时自己同芯片控制器交互,读包并判断是否是自己的包,如是做特殊处理,否则交由原始ISR处理,当然,也可以邪恶地对包任意进行涂改让Sniffer去嗅去吧。
不得不提地是,这种方法明显存在严重依赖网卡芯片型号,通用性不好的弊端,但鱼和熊掌不可兼得,我们毕竟迈出了一步,但要走的路还很远。
PS:由于条件受限,测试用bin会在适当的时候放出
四、参考资源 [1] Realtek RTL8139C(L) datasheet Rev1.4, 2002 [2] Sean. Programming guide and sample code for RTL8139 family. 1999 [3] Alex Tereshkin. Rootkits: Attacking Personal Firewalls. BH Vegas 2006 [4] Joanna Rutkowska. Fighting Stealth Malware-Towards Verifiable OSes. CCC 2006 [5] uty. Uay Rootkit source code [6] Azy. AK922 Rootkit (http://hi.baidu.com/azy0922)
|