【舊文章搬運】對抗記憶體搜尋物件
原文發表於百度空間,2009-07-08
==========================================================================
還是從邪惡的源頭--隱藏說起,以隱藏Driver為例,當我們從PsLoadedModuleList、\Driver物件目錄、TypeList都消失以後,要怎麼去找到我們這個Driver呢?
這時,很多ARK的方法就是暴搜,暴搜DriverObject(比如Sysnap的《記憶體物件搜尋》),或者暴搜PE映象,這個很多人都知道。
今天要說的,就是如何對抗暴力搜尋記憶體物件.
假設這樣一個場景:我列舉PspCidTable時得到了一個個的物件,然後如何判斷這些物件哪些是EPROCESS,哪些是ETHREAD呢?
一個常用的方法(當然還有其它判斷方法)就是由Object到ObjectHeader,然後判斷ObjectHeader->Type是PsProcessType還是PsThreadType.
我想很多人的程式碼都是這樣寫的,包括以前我的也是。但是,這裡一定要是PsProcessType或PsThreadType?答案是:NO
OBJECT_TYPE不過是一塊儲存著該物件型別的相關資訊的記憶體而已,我們完全可以自己“造”一個出來,來代替原有的ObjectHeader->Type躲過匹配檢查。
這就是為什麼要有Fake ObjectType這個東西的理由~
怎麼造?我試過直接分配記憶體,直接拷貝整個OBJECT_TYPE結構,很不幸問題很多,似乎跟其中某些域的值有關係,我替換PsProcessType之後連程序都建立不了。
還好,ObCreateObjectType函式是匯出的,我們可以直接使用此函式創造自己的記憶體物件型別。
建立之前,需要獲取原始的ObjectType資訊,畢竟你不可能自己實現該物件型別相關的所有操作,因此有些域直接填充原始ObjectType的值,把工作仍然交給系統去做。在獲取時ObjectType時,無所謂匯出的未匯出的型別,都可以通過直接開啟"\ObjectTypes\TypeName"的方式直接獲取到,這裡不多說。
有了這些資訊,就可以開始我們的“山寨”工作了.程式碼如下:
NTSTATUS CreateNewObjectTypeByName( IN PCWSTR ObjectTypeName,//要建立的物件型別的名字 IN POBJECT_TYPE pObjectTypeForCopy,//原始的ObjectType OUT POBJECT_TYPE *pNewObjectType)//返回新建立的ObjectType { UNICODE_STRING NameString; OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; NTSTATUS status; RtlInitUnicodeString (&NameString, ObjectTypeName); RtlZeroMemory (&ObjectTypeInitializer, sizeof (OBJECT_TYPE_INITIALIZER)); ObjectTypeInitializer.Length = sizeof (ObjectTypeInitializer); ObjectTypeInitializer.SecurityRequired = TRUE; ObjectTypeInitializer.PoolType = NonPagedPool; ObjectTypeInitializer.InvalidAttributes= OBJ_PERMANENT |OBJ_EXCLUSIVE |OBJ_OPENIF; ObjectTypeInitializer.DefaultPagedPoolCharge = pObjectTypeForCopy->TypeInfo.DefaultPagedPoolCharge; ObjectTypeInitializer.DefaultNonPagedPoolCharge = pObjectTypeForCopy->TypeInfo.DefaultNonPagedPoolCharge; //以下這些資訊並不總是全都需要,比如PsProcessType與IoDriverObjectType就有一些不同~ ObjectTypeInitializer.DeleteProcedure = pObjectTypeForCopy->TypeInfo.DeleteProcedure; ObjectTypeInitializer.ValidAccessMask = pObjectTypeForCopy->TypeInfo.ValidAccessMask; ObjectTypeInitializer.GenericMapping = pObjectTypeForCopy->TypeInfo.GenericMapping; ObjectTypeInitializer.ParseProcedure = pObjectTypeForCopy->TypeInfo.ParseProcedure; ObjectTypeInitializer.DeleteProcedure = pObjectTypeForCopy->TypeInfo.DeleteProcedure; ObjectTypeInitializer.SecurityProcedure = pObjectTypeForCopy->TypeInfo.SecurityProcedure; ObjectTypeInitializer.QueryNameProcedure = pObjectTypeForCopy->TypeInfo.QueryNameProcedure; ObjectTypeInitializer.MaintainTypeList = pObjectTypeForCopy->TypeInfo.MaintainTypeList; status=ObCreateObjectType (&NameString, &ObjectTypeInitializer, (PSECURITY_DESCRIPTOR) NULL, pNewObjectType); if (NT_SUCCESS(status)) { dprintf("NewType=0x%08X\n",*pNewObjectType); } else { dprintf("Failed! status=0x%08X\n",status); } return status; }
建立成功之後,我們就有了一個正常的、合法的ObjectType了(可以自己拿WinObj檢視之),然後替換掉我們要隱藏的物件的ObjectHeader->Type就可以了。
(我想在ObCreateObject時直接替換會是什麼效果?純YY,未實踐~~)
以Driver為例:
POBJECT_TYPE DarkMoonDriverType; CreateNewObjectTypeByName(L"DarkMoonDriver",*IoDriverObjectType,&DarkMoonDriverType);
好了,現在有一個屬於我們自己的功能完全一樣的ObjectType了.
設某個驅動名為XXXX,本身無Device,以DarkMoonDriverType替換ObjectHeader->Type後,不抹Driver物件目錄,不斷TypeList,不抹PE映象,甚至連PsLoadedModuleList都不用斷,直接bypass RKU!
為什麼呢?因為RKU認定了每個DriverObject的ObjectHeader->Type必須是*IoDriverObjectType,所以我們用Fake ObjectType替換後就直接bypass了,這就是我今天要說的Fake ObjectType大法。
對於程序隱藏,此法對RKU同樣適用,分別用偽造的DarkMoonProcessType,DarkMoonThreadType掉EPROCESS、ETHREAD物件頭中的Type域,再結合一些常規隱藏手段,就可以bypass RKU的程序檢測了,比bypass其它一些ARK還容易一些~~
Sysnap的《核心搜尋物件》其實存在和RKU類似的問題(這裡抱怨一下,YasFindOb在我的虛擬機器中總是因記憶體訪問錯誤而藍屏),以ScanDriverObject為例,Sysnap以*IoDriverOjbectType->Key做為比對資訊。事實上,ObjectType->Key(偏移為0xAC)其實是物件型別名稱的前四個字元(不足4個以空格補齊),以這個作為比對資訊,自然是找不到上面的DarkMoonDriver的,因為我們的Key是"Dark"。這還不是關鍵的,關鍵是DriverObject->DriverName可以抹掉,你又如何拿來和Key對比?
程式碼中還有if(pTmpDriObject->Type == 4)這樣的判斷,4代表了DRIVER,但是如果這裡不是4,我們仍然會活得好好的~
改了這麼多,雖然此時DriverObject已經有點面目全非了,但是在拋棄了DeviceObject之後,就已經不需要DriverObject了.
"若為隱藏故,二者皆可拋"!如此以來,搜DriverObject是無法檢測到我們的HideDriver的,即使無Device無DriverObject,我們還是可以進行通訊的...