天天看點

[Rootkit] dll 隐藏 - VAD

3環下要想隐藏dll,僅僅靠斷鍊和抹去PE頭資訊是不夠的;這樣做能騙過同樣在3環運作的調試器,但是騙不過在0環通過驅動做檢測的PChunter、Process Hacker等工具;要想徹底隐藏,需要更進一步搞定驅動層的各種檢測,下面會詳細介紹隐藏的細節原理和操作方法!

1、VAD 虛拟記憶體管理

記憶體分兩種:實體記憶體和虛拟記憶體;作業系統和程序共享實體記憶體,程序獨享虛拟記憶體;實體記憶體可以通過CR3在程序之間互相隔離,確定程序之間互不侵犯;那麼程序内部的虛拟位址該怎麼管理了? 32位下,每個程序獨享4GB記憶體,怎麼知道哪些記憶體已經使用過,哪些沒用過? 已經使用的記憶體,是可讀可寫可執行的麼? 還是隻讀的了? 該怎麼記錄這些關鍵資訊了?windwos采用一種叫做virtual address descripot的自平衡二叉樹來管理虛拟記憶體,低端的記憶體位址放在根節點左子樹,高端記憶體位址放根節點右子樹,大緻的結構如下:每當程序調用virtualAlloc配置設定虛拟記憶體時,作業系統會先周遊這個樹,看看還有哪些地方的虛拟記憶體還未使用,然後傳回給開發人員:

[Rootkit] dll 隐藏 - VAD

windbg能查到每個程序VAD的root節點:

[Rootkit] dll 隐藏 - VAD

VAD裡面也記錄了該程序dll的使用情況。

[Rootkit] dll 隐藏 - VAD

當記憶體使用完畢,建議立即調用VirtualFree,将這段虛拟記憶體從VAD從抹去,後續再次周遊時才能繼續使用!正常情況下,如果要解除安裝dll,可以調用windwos提供的freeLibrary接口,裡面有關鍵的函數:ZwUnmapViewOfSection,可以直接把dll對應的記憶體從VAD中删除(這裡多說兩句:ZwUnmapViewOfSection 功能很強大,可以替換程序的代碼,讓其稱為傀儡執行惡意的代碼)。

2、之前分享過一個驅動隐藏的思路: 讓driver entry傳回false,作業系統會認為驅動加載失敗,不會記錄。但在driverentry裡面把自己想要執行的代碼拷貝到堆上,然後将代碼入口點作為imageLoad回調函數的入口點。雖然驅動加載“失敗”,但代碼已經拷貝到堆,并且注冊成為了回調函數,dll隐藏也可以借鑒類似的思路:

  • 先重新申請一個新空間,把需要隐藏的dll拷貝到新空間備份
  • 用freelibrary釋放需要隐藏的dll,VAD中會删除這個dll的。此時如果eip跳轉到dll執行,肯定報錯
  • 重新用virtualAlloc申請原dll位址,再把第一步備份的原dll代碼拷貝到這次申請的位址(其實就是dll原來加載的位址)
  • 此時如果eip跳轉到這個位址執行代碼是ok的

這麼做的本質是:把dll從vad的記錄中抹去,重新申請記憶體來存放dll的代碼。雖說在vad還是有記憶體的使用記錄,但因為并未使用loadlibrary,是以也不會在vad中留下dll的記錄(這是本質是把dll變相當成shellcode在用,至于全局變量、導入函數、重定位這些,由編譯器和作業系統都做好了,不需要開發人員操心);核心代碼如下:

/************************************************************************/
/* 把目前程序的所有DLL(除開需要隐藏的那個)都使用LoadLibrary再次加載一邊,增加引用計數,                */
/* 使得Free時對應的DLL資源不釋放                                            */
/************************************************************************/

void LockAllModules()
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());

    if (hSnapshot != INVALID_HANDLE_VALUE)
    {
        MODULEENTRY32 me = { sizeof(me) };

        BOOL fOk = Module32First(hSnapshot, &me);
        for (fOk = Module32Next(hSnapshot, &me); fOk; fOk = Module32Next(hSnapshot, &me))
        {
            //跳過第一個(自身)  
            CString wInfo;
            wInfo.Format(_T("%s"), me.szModule);
            wInfo.MakeLower();
            if (wInfo != _T("dlls.dll"))LoadLibrary(me.szModule);//加載除了dlls.dll以外的所有記憶體
        }
    }
}

BOOL CopycatAndHide(HMODULE hDll)
{
    // 整體思路:先把DLL加載到目前程序,然後将該加載的DLL再備份到目前程序空間;  
    // 接下來該DLL再Free了,此時程序再通路該DLL的話會出錯;  
    // Free後,再把預先備份的DLL資料還原,而且還原的資料位址是原先DLL加載的位址  
    // 如此,程序内再調用該DLL的話,由于資料完整,一切OK  

    DWORD g_dwImageSize = 0;
    VOID* g_lpNewImage = NULL;

    IMAGE_DOS_HEADER* pDosHeader;
    IMAGE_NT_HEADERS* pNtHeader;
    IMAGE_OPTIONAL_HEADER* pOptionalHeader;
    LPVOID lpBackMem = 0;
    DWORD dwOldProtect;
    DWORD dwCount = 30;


    pDosHeader = (IMAGE_DOS_HEADER*)hDll;
    pNtHeader = (IMAGE_NT_HEADERS*)(pDosHeader->e_lfanew + (DWORD)hDll);
    pOptionalHeader = (IMAGE_OPTIONAL_HEADER*)&pNtHeader->OptionalHeader;

    LockAllModules();

    // 找一塊記憶體把需要隐藏而且已經加載到記憶體的DLL備份  
    // SizeOfImage,4個位元組,表示程式調入後占用記憶體大小(位元組),等于所有段的長度之和。  
    lpBackMem = VirtualAlloc(0, pOptionalHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpBackMem)
        return FALSE;
    if (!VirtualProtect((LPVOID)hDll, pOptionalHeader->SizeOfImage, PAGE_EXECUTE_READWRITE, &dwOldProtect))
        return FALSE;

    g_dwImageSize = pOptionalHeader->SizeOfImage;
    memcpy(lpBackMem, (LPVOID)hDll, g_dwImageSize);
    // 抹掉PE頭  
    //memset(lpBackMem, 0, 0x200);
    *((PBYTE)hDll + pOptionalHeader->AddressOfEntryPoint) = (BYTE)0xc3;

    //  DWORD dwRet =0;  
    // Free掉DLL  
    do
    {
        dwCount--;
    } while (FreeLibrary(hDll) && dwCount);

    // 把備份的DLL資料還原回來,使得預先引用該DLL的程式能夠繼續正常運作  
    g_lpNewImage = VirtualAlloc((LPVOID)hDll, g_dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (g_lpNewImage != (LPVOID)hDll)
        return FALSE;

    memcpy(g_lpNewImage, lpBackMem, g_dwImageSize);
    VirtualFree(lpBackMem, 0, MEM_RELEASE);

    return TRUE;
}      

參考:

1、https://wenku.baidu.com/view/439526b369dc5022aaea0077 記憶體管理

2、https://bbs.pediy.com/thread-257179.htm VC黑防日記(二):DLL隐藏和逆向