本環(huán)境是蛇矛實驗室基于"火天網(wǎng)演攻防演訓(xùn)靶場"進行搭建,通過火天網(wǎng)演中的環(huán)境構(gòu)建模塊,可以靈活的對目標網(wǎng)絡(luò)進行設(shè)計和配置,并且可以快速進行場景搭建和復(fù)現(xiàn)驗證工作。
背景概述
在安全研發(fā)的過程中,難免會遇到在用戶模式對抗 AV/EDR 的掛鉤,應(yīng)對的方法有很多,比如可以使用直接系統(tǒng)調(diào)用,還可以將殺軟的所掛的鉤子解除,還有一種就是尋找具有相同功能未被掛鉤的 API。本文將講述直接系統(tǒng)調(diào)用的原理,并對一些相關(guān)的項目進行分析。
API 的調(diào)用過程分析
首先,我們編寫一段測試程序(x64)并使用相關(guān)工具對其調(diào)用流程進行分析。
#include?#include? #include? int?main() { ????PROCESS_INFORMATION pi{}; ????STARTUPINFO si{ sizeof(si) }; ????CreateProcess(nullptr, nullptr, nullptr, nullptr, 0, 0, 0, nullptr, &si, &pi); ????system("pause"); ????return?0; }
使用 Process Monitor 工具進行過濾監(jiān)測,可以看到 CreateProcess API 調(diào)用流程。
從上圖可以看到,當(dāng)我們在程序中調(diào)用 CreateProcess 之后,實際上是調(diào)用了用戶層下 kernel32.dll 這個 DLL 中的 CreateProcessW ,從這個調(diào)用堆棧可以看出,在用戶模式下最終會調(diào)用 ntdll.dll 中的 NtCreateUserProcess 函數(shù),并通過這個函數(shù)進入內(nèi)核。
使用 IDA 對這個函數(shù)進行查看。
通過 IDA 中查看這個函數(shù)的實現(xiàn)可以看到,NtCreateUserProcess 函數(shù)的主體,函數(shù)以 rcx 寄存器作為參數(shù),然后將其值復(fù)制到 r10 寄存器中,由于在函數(shù)主體中后續(xù)沒有用到 r10 寄存器,所以這句沒有實際的作用,接下來,將 0C8h 賦值給 eax 寄存器,其中 0C8h 為系統(tǒng)調(diào)用號,在 Windows 中,系統(tǒng)調(diào)用通常使用特定的調(diào)用號來標識,在這個例子中,調(diào)用號 0C8h 就表示 NtCreateUserProcess 系統(tǒng)調(diào)用,隨后執(zhí)行一條測試指令,檢查 ds:7FFE0308h 位置處的字節(jié)值是否為 1,這條指令是用來判斷 CPU 是否支持快速調(diào)用(即是否支持 syscall 指令),如果支持,則會使用 syscall 指令來執(zhí)行系統(tǒng)調(diào)用,否則會通過中斷調(diào)用(int 2eh) 指令來執(zhí)行系統(tǒng)調(diào)用進入內(nèi)核。
使用 Native API
Native API 是一種用于訪問 Windows 操作系統(tǒng)內(nèi)部功能的 API。它位于高于應(yīng)用程序級別和內(nèi)核級別之間,可以讓開發(fā)人員訪問操作系統(tǒng)的一些底層功能。上文通過對用戶層的 API 的調(diào)用流程進行分析,可以發(fā)現(xiàn),用戶層的大多數(shù) API 調(diào)用最終都會轉(zhuǎn)到 ntdll.dll 中去執(zhí)行,并通過相對應(yīng)的 Native API 中轉(zhuǎn)最終進入內(nèi)核層執(zhí)行,通過 ntoskrnl.exe 實現(xiàn)具體的功能。
熟悉 Windows 編程的人應(yīng)該知道,Native API 一般是不直接對外公開的,它通常作為操作系統(tǒng)內(nèi)部的一個組件,并且只能由操作系統(tǒng)內(nèi)部的組件或應(yīng)用程序調(diào)用,所以在使用一些未文檔的化的 Native API 時,需要通過網(wǎng)上公開的資料或者通過逆向取獲取其函數(shù)原型和相關(guān)參數(shù)。
下面使給出一段使用 Native API 的代碼(x64),注意 NtCreateThreadEx 32 位和 64 位函數(shù)原型不同,具體差別可自行上網(wǎng)查閱或者逆向。
#include?#include? // 定義 NtCreateThreadEx 函數(shù)指針 using?NtCreateThreadExT = NTSTATUS(NTAPI*)( ????OUT PHANDLE ThreadHandle, ????IN ACCESS_MASK DesiredAccess, ????IN LPVOID ObjectAttributes OPTIONAL, ????IN HANDLE ProcessHandle, ????IN PVOID StartRoutine, ????IN PVOID Argument OPTIONAL, ????IN ULONG CreateFlags, ????IN SIZE_T ZeroBits, ????IN SIZE_T StackSize, ????IN SIZE_T MaximumStackSize, ????IN LPVOID AttributeList OPTIONAL); EXTERN_C DWORD WINAPI ThreadProc(LPVOID param) { ????std::cout?<< GetCurrentThreadId() << std::endl; ????return?0; } int?main() { ????// 獲取 NtCreateThreadEx 函數(shù)的地址 ????auto?NtCreateThreadEx = (NtCreateThreadExT)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateThreadEx"); ????if?(NtCreateThreadEx == NULL) ????{ ????????std::cout?<< "get NtCreateThreadEx func address failed!"?<< std::endl; ????????return?-1; ????} ????HANDLE thread = NULL; ????NTSTATUS status = NtCreateThreadEx( ????????&thread, // 輸出線程句柄 ????????GENERIC_EXECUTE, // 指定線程的訪問權(quán)限 ????????NULL, // 指定線程安全描述符 ????????GetCurrentProcess(), // 指定線程所在的進程句柄 ????????ThreadProc, // 指定線程函數(shù) ????????NULL, // 指定線程函數(shù)的參數(shù) ????????FALSE, // 指定是否在創(chuàng)建時掛起線程 ????????0, // 指定堆棧中保留的字節(jié)數(shù) ????????0, // 指定堆棧中分配的字節(jié)數(shù) ????????0, // 指定堆棧中總共保留的字節(jié)數(shù) ????????NULL?????????????????// 指定附加的參數(shù) ????); ????if?(status != 0) ????{ ????????printf("thread create failed: %d ", status); ????????return?-1; ????} ????std::cout?<< "thread created successfully "?<< std::endl; ????// 等待線程結(jié)束 ????WaitForSingleObject(thread, INFINITE); ????// 關(guān)閉線程句柄 ????CloseHandle(thread); ????system("pause"); ????return?0; }
上述代碼先定義了 NtCreateThreadEx 類型的函數(shù)指針,并通過 GetModuleHandleA + NtCreateThreadEx 來獲取 NtCreateThreadEx 的函數(shù)地址,之后通過使用 NtCreateThreadEx 創(chuàng)建一個線程,在創(chuàng)建的線程回調(diào)函數(shù)中,輸出了創(chuàng)建線程的 ID 號,最終待線程執(zhí)行完畢后,關(guān)閉了線程句柄。
執(zhí)行的結(jié)果如下:
在本節(jié)演示程序中,我們通過從已加載的 ntdll.dll 內(nèi)存中獲取NtCreateThreadEx 的函數(shù)地址,這能有效的繞過 AV/EDR 關(guān)于 Kernel32、KernelBase 的掛鉤,但是卻不能繞過 AV/EDR 對 ntdll的掛鉤。目前來說,大多數(shù) AV/EDR 在用戶模式下都是對 ntdll 中的相關(guān) API 進行掛鉤了,所以接下來還需要了解直接系統(tǒng)調(diào)用,也就是通常說的重寫 R3 下的 API。
直接系統(tǒng)調(diào)用
前文中對 CreateProcess 的調(diào)用流程進行了分析介紹了如何使用 Native API ,下面將解釋一下系統(tǒng)調(diào)用,在理解系統(tǒng)調(diào)用之前,首先需要了解一下現(xiàn)代操作系統(tǒng)的基礎(chǔ)結(jié)構(gòu),用戶模式(空間)和內(nèi)核模式(空間)。在 Windows 中,操作系統(tǒng)提供了一個內(nèi)核空間,用于處理所有的底層系統(tǒng)事務(wù),并提供一些系統(tǒng)服務(wù),而應(yīng)用程序運行在用戶空間,并且不能直接訪問內(nèi)核空間的內(nèi)容。當(dāng)用戶空間的應(yīng)用程序需要訪問內(nèi)核空間的系統(tǒng)服務(wù)時,就需要通過系統(tǒng)調(diào)用來實現(xiàn)。
系統(tǒng)調(diào)用是一種特殊的函數(shù),允許應(yīng)用程序訪問內(nèi)核空間。應(yīng)用程序通過調(diào)用系統(tǒng)調(diào)用函數(shù),將參數(shù)傳遞給內(nèi)核空間,內(nèi)核空間處理該請求并返回結(jié)果。
在 Windows 中,系統(tǒng)調(diào)用是通過向特定的內(nèi)核空間地址發(fā)送特殊的中斷來實現(xiàn)的。具體實現(xiàn)細節(jié)取決于 Windows 版本。例如,在 Windows 10 中,系統(tǒng)調(diào)用可以通過快速系統(tǒng)調(diào)用 (syscall) 指令或者傳統(tǒng)的中斷方式來實現(xiàn)。
綜上,系統(tǒng)調(diào)用是一種能夠讓用戶空間應(yīng)用程序訪問內(nèi)核空間系統(tǒng)服務(wù)的機制。通過系統(tǒng)調(diào)用,用戶空間應(yīng)用程序可以獲得更多的系統(tǒng)功能,例如讀寫磁盤、訪問網(wǎng)絡(luò)等。在 Windows 中,系統(tǒng)調(diào)用的實現(xiàn)方式為直接系統(tǒng)調(diào)用,它是通過執(zhí)行特殊的匯編指令來直接調(diào)用內(nèi)核空間的系統(tǒng)服務(wù)的。對于直接系統(tǒng)調(diào)用,與其它系統(tǒng)調(diào)用不同的是,它需要在編譯時預(yù)知調(diào)用的系統(tǒng)服務(wù)。換句話說,在編譯時就需要確定具體的系統(tǒng)服務(wù)的調(diào)用號,并在匯編代碼中顯式地使用它。例如在 NtCreateThreadEx 函數(shù)的匯編代碼中,我們可以看到如下代碼(win11):
在這段代碼中,mov 指令將 0C7h 的值存儲到了 eax 寄存器中,這個值就是調(diào)用號。然后 syscall 指令會使用 eax 寄存器中的值作為系統(tǒng)服務(wù)的調(diào)用號,從而調(diào)用內(nèi)核空間的系統(tǒng)服務(wù)。
總之,系統(tǒng)調(diào)用是用戶空間應(yīng)用程序訪問內(nèi)核空間系統(tǒng)服務(wù)的機制,而直接系統(tǒng)調(diào)用是 Windows 中系統(tǒng)調(diào)用的實現(xiàn)方式。
現(xiàn)如今,AV/EDR 在用戶層下通常通過掛鉤 Native API 用以監(jiān)控程序的執(zhí)行流程,進而來判斷執(zhí)行的代碼是否是惡意的。為了繞過這些安全產(chǎn)品在用戶層的 API 掛鉤,可以采用直接系統(tǒng)調(diào)用,因為直接系統(tǒng)調(diào)用是指直接通過匯編指令調(diào)用內(nèi)核空間的系統(tǒng)服務(wù),而不會經(jīng)過用戶層的 API 函數(shù),所以能夠繞過一些在用戶層的 API 掛鉤的安全產(chǎn)品。
那如何實現(xiàn)直接系統(tǒng)調(diào)用呢?這里使用 Visual Studio 進行直接系統(tǒng)調(diào)用的測試。
編寫的測試代碼:
首先新建匯編文件(.asm),并在其中編寫需要進行直接系統(tǒng)調(diào)用的匯編代碼。
.code NtCreateThreadEx proc ????mov r10, rcx ????mov eax, 0C7h ????syscall ????ret NtCreateThreadEx endp end
如果想要 .asm 文件參與編譯,需要設(shè)置一下項目屬性,在 Build Customizations 中勾選 masm 的支持。
之后在 asm 文件 上右鍵設(shè)置其屬性 --- General --- Item Type --- Mircrosoft Macro Assembler
設(shè)置完畢后,開始編寫主程序的調(diào)用代碼。
#include?#include? EXTERN_C NTSTATUS NtCreateThreadEx( ????OUT PHANDLE ThreadHandle, ????IN ACCESS_MASK DesiredAccess, ????IN LPVOID ObjectAttributes OPTIONAL, ????IN HANDLE ProcessHandle, ????IN PVOID StartRoutine, ????IN PVOID Argument OPTIONAL, ????IN ULONG CreateFlags, ????IN SIZE_T ZeroBits, ????IN SIZE_T StackSize, ????IN SIZE_T MaximumStackSize, ????IN LPVOID AttributeList OPTIONAL); DWORD WINAPI ThreadProc(LPVOID prarm) { ????std::cout?<< "thead id:"?<< GetCurrentThreadId() << std::endl; ????return?0; } int?main() { ????HANDLE hproc = GetCurrentProcess(); ????HANDLE hthread = nullptr; ????// hthread = CreateThread(nullptr, 0, ThreadProc, nullptr, 0, nullptr); ????NtCreateThreadEx(&hthread, GENERIC_EXECUTE, nullptr, hproc, ThreadProc, nullptr, FALSE, 0, 0, 0, nullptr); ????WaitForSingleObject(hthread, INFINITE); ????CloseHandle(hthread); ????system("pause"); ????return?0; }
在匯編文件中下斷點,調(diào)試運行可以發(fā)現(xiàn),程序能夠走到我們自己編寫的直接系統(tǒng)調(diào)用中,并沒有走原始的 Native API。
執(zhí)行完畢后,程序運行的結(jié)果如下。
使用重寫的 NtCreateThreadEx 創(chuàng)建的線程代碼被正常執(zhí)行了。
如果將上述代碼中 NtCreateThreadEx 函數(shù)換成 CreateThread ,通過 Process Monitor 工具對該程序 Thread Create 操作進行監(jiān)控查看其調(diào)用堆棧。
可以看到,最終還是會轉(zhuǎn)到 NtCreateThreadEx 函數(shù)。
查看 NtCreateThreadEx 創(chuàng)建線程下的調(diào)用堆棧。
在這個調(diào)用堆棧中,并沒有發(fā)現(xiàn) NtCreateThreadEx 被調(diào)用,這是因為此時調(diào)用的是程序中實現(xiàn)的 NtCreateThreadEx。
上述代碼通過對 NtCreateThreadEx 進行直接系統(tǒng)調(diào)用,從而繞過了 AV/EDR 在用戶模式下對 Native API (這里是 NtCreateThreadEx )的掛鉤。
但需要說明的是在不同的 Windows 操作系統(tǒng)版本之間,系統(tǒng)調(diào)用號可能會有所不同。
例如上述代碼是在 win11 中實現(xiàn)的,自己查看 NtCreateThreadEx 的調(diào)用號為 0C7h,當(dāng)將其放到其他操作系統(tǒng)中,可能存在不同,比如在 win10 中測試發(fā)現(xiàn),NtCreateThreadEx 的調(diào)用號為 0C1h。
下圖為 win11(版本號22621.819) 中 NtCreateThreadEx 的調(diào)用號。
下圖為 win10(版本號19043.1110) 中 NtCreateThreadEx 的調(diào)用號。
不同操作系統(tǒng)間調(diào)用號的不同詳情可參考:
https://j00ru.vexillium.org/syscalls/nt/32/
https://j00ru.vexillium.org/syscalls/nt/64/
通過上面自己編寫個 API 的直接系統(tǒng)調(diào)用可以看到,這個過程還是比較繁瑣,需要區(qū)分不同操作系統(tǒng)之間的 API 的調(diào)用號的不同,并且還需要去獲取 API 的函數(shù)原型。于是網(wǎng)上出現(xiàn)了各種方便用戶使用系統(tǒng)調(diào)用的優(yōu)秀項目,從本質(zhì)來說,要想使用直接系統(tǒng)調(diào)用,就是想辦法獲取相關(guān) Native API 的 stubs,我根據(jù)其實現(xiàn)方式的不同進行了分類:
動態(tài) SSN 號獲取
二次加載 ntdll 獲取 stubs(Dual-load ntdll)
讀取內(nèi)存中的 KnownDlls
從磁盤讀取 ntdll
下面介紹幾個比較優(yōu)秀的項目,并對其進行簡單分析。
SysWhispers
SysWhispers 項目可以通過 python 腳本自動生成 x64 版本系統(tǒng)調(diào)用 stubs。
項目地址:https://github.com/jthuraisamy/SysWhispers
使用介紹:根據(jù)項目說明文檔進行測試,該項目最終會生成兩個文件 xxx.h 和 xxx.asm ,將其拷貝到項目中即可使用,具體在項目中使用這些函數(shù)參見上文 API 的調(diào)用過程。
這里以 NtCreateFile 作為演示。
py?.syswhispers.py?--functions NtCreateFile -o?syscalls
運行之后發(fā)現(xiàn),同級目錄下發(fā)現(xiàn):
查看頭文件,發(fā)現(xiàn)其中聲明了調(diào)用相關(guān) Native API 使用到的數(shù)據(jù)結(jié)構(gòu)。
查看其生成 asm 文件內(nèi)容,可以發(fā)現(xiàn)其中是具體 API 的直接系統(tǒng)調(diào)用代碼 stubs,生成的匯編代碼首先會判斷系統(tǒng)版本進而去選擇內(nèi)置的函數(shù)系統(tǒng)調(diào)用號,之后在進行系統(tǒng)調(diào)用。
這段匯編代碼通過 PEB 檢查系統(tǒng)的主要版本、次要版本和構(gòu)建號,之后根據(jù)這些信息獲取正確的系統(tǒng)調(diào)用,如下圖所示。
Syswhispers2
在使用 Syswhispers 過程中可以發(fā)現(xiàn),該項目需要提前知道相關(guān)函數(shù)系統(tǒng)版本調(diào)用號進行編寫,一般未將所有系統(tǒng)版本情況包含進去,就會調(diào)用失敗,不具有通用性,所以原作者對其進行改進,形成了 Syswhispers2 。
項目地址:https://github.com/jthuraisamy/SysWhispers2
作者在項目介紹文檔中指出了與 Syswhispers 項目的不同,其中最大的區(qū)別是不需要指定要支持哪個版本的 Windows 了。
Syswhispers2 項目的用法與 Syswhispers 一致,以生成 NtCreateUserProcess 的系統(tǒng) stubs 來說明。
py .syswhispers.py --functions NtCreateThreadEx -o syscalls -a x64 -l masm
下面對生成的關(guān)鍵代碼進行分析。
生成的 syscallsstubs.std.x64.asm 中的代碼如下圖所示。
上述的代碼片段的主要功能是通過內(nèi)置預(yù)定義的 hash 值來獲取系統(tǒng)調(diào)用號,進而進行相關(guān)函數(shù)的系統(tǒng)調(diào)用。
其中關(guān)鍵的函數(shù)為 SW2_GetSyscallNumber ,該函數(shù)在 syscalls.c 文件中被定義與實現(xiàn)。
該函數(shù)中又調(diào)用了另一個關(guān)鍵函數(shù) SW2_PopulateSyscallList ,該函數(shù)將 Zw 開頭 Native API 名稱的 hash 值按照升序排序保存到 SW2_SyscallList.Entries 這個全局數(shù)組中。其中獲取 Zw 開頭的 API 是通過 peb 的到 ntdll 基址后,遍歷其導(dǎo)出表得到的,排序算法使用的冒泡排序。
通過 peb 獲取已加載的 ntdll 在內(nèi)存中的基址。
遍歷內(nèi)存中 ntdll 的導(dǎo)出表,獲取 Zw 開頭的函數(shù)名稱,存儲到全局數(shù)組 SW2_SyscallList.Entries 中。
對 SW2_SyscallList.Entries 這個全局數(shù)組進行冒泡升序排序。
生成的文件中還有一個匯編文件(syscallsstubs.rnd.x64.asm),其代碼片段如下。
上述代碼片段與 syscallsstubs.std.x64.asm 中代碼不同點在于,該段匯編代碼隱藏了 syscall 指令的出現(xiàn),Syswhispers2 項目使用 SW2_GetRandomSyscallAddress 函數(shù)生成了隨機的 syscall 指令地址,用于防止 syscall 指令在匯編代碼片段中出現(xiàn)。
在使用這個文件時需要注意,需要 #define RANDSYSCALL 聲明宏,以開啟 SW2_GetRandomSyscallAddress 。
其中 SW2_GetRandomSyscallAddress 函數(shù)在 syscalls.c 文件中定義與實現(xiàn)。
該段代碼通過特征碼定位的形式來獲取隨機一個 Native API 的 syscall 指令地址,之后通過 call qword ptr [syscallAddress] 替換代碼中的 syscall 指令,從而繞過了 AV/EDR 對 syscall 指令的標記。
SysWhispers3
SysWhispers3 項目在項目說明文檔中解釋了與 Syswhispers2 項目的不同之處。
下面對 SysWhispers3 項目生成的文件進行簡單分析。
主要分析生成的匯編代碼文件和 syscalls.c 文件。
分析使用的生成腳本命令
py .syswhispers.py --functions NtCreateThreadEx -o syscalls -a x64 -c msvc -m jumper_randomized
對生成的代碼文件分析,一共生成了 3 個文件。
生成的 syscalls-asm.x64.asm 文件如下。
該段匯編代碼主要是隱藏了 syscall 指令的出現(xiàn),并隨機獲取其他 API stubs 中的 syscall 指令,之后通過使用 jmp syscall 地址對其進行轉(zhuǎn)換,從而繞過了部分 AV/EDR 對 syscall 指令的標記。
其中函數(shù)的調(diào)用地址是 SW3_GetRandomSyscallAddress 函數(shù)實現(xiàn)的,該函數(shù)在 syscalls.c 文件中定義并實現(xiàn)。
SW3_GetRandomSyscallAddress 函數(shù)的主要作用是得到一個隨機的 Native API 的 ????syscall 指令地址。剩下的幾個函數(shù)與 Syswhispers2 項目中大體類似,便不在這里分析了。
HellsGate
項目地址:https://github.com/am0nsec/HellsGate
HellsGate 主要思路是通過 PEB 獲取已加載的 ntdll 在內(nèi)存中的基地址,隨后通過解析其導(dǎo)出表,來定位 API 地址,之后通過特征碼來獲取函數(shù)的系統(tǒng)調(diào)用號,進而實現(xiàn)系統(tǒng)調(diào)用。
下面分析一些關(guān)鍵代碼。
這段代碼片段就是 HellsGate 用來定位系統(tǒng)調(diào)用號的特征碼。
mov r10,rcx // 0x4c 0x8b 0xd1 mov eax,// 0xb8 xx xx 0x00 0x00
雖然 HellsGate 通過特征碼能夠準確定位獲取函數(shù)的系統(tǒng)調(diào)用號,但也存在一定的局限性,使用它的前提是內(nèi)存中的 ntdll 必須是“干凈”的 ntdll ,也就是不能被 AV/EDR 掛鉤,一旦被掛鉤,比如 mov r10,rcx 被掛鉤了,那么其字節(jié)碼就變了,就找不到想要的函數(shù)系統(tǒng)調(diào)用號了。
Halo’s Gate
后續(xù)也出現(xiàn)了一些改進方案,比如 Halo’s Gate,詳情可參考:https://blog.sektor7.net/#!res/2021/halosgate.md ,基本思路就是由于相鄰的系統(tǒng)調(diào)用有一定規(guī)律性(如下圖所示),所以只要定位相鄰的系統(tǒng)調(diào)用,就可以推導(dǎo)出想要函數(shù)的系統(tǒng)調(diào)用號。
HellsGatePoC
從 HellsGate 之門項目可以看到,它的局限性在于需要有一塊“干凈”的 ntdll ,不然,它無法獲取到想要的系統(tǒng)調(diào)用號。所以,又一個思路誕生了,該項目的主要思路是從磁盤中中獲取干凈的 ntdll,并將其映射到內(nèi)存中,進而獲取到系統(tǒng)調(diào)用號,從而使用系統(tǒng)調(diào)用。
項目地址:https://github.com/N4kedTurtle/HellsGatePoC
更多的項目
當(dāng)然有關(guān)使用直接系統(tǒng)調(diào)用繞過用戶層掛鉤的項目遠遠不止這些,由于篇幅有限,本文并沒有將其全部都介紹一遍,下面我推薦2個我認為優(yōu)秀的項目。
https://github.com/JustasMasiulis/inline_syscall
https://github.com/crummie5/FreshyCalls
有興趣的讀者可以去了解下。
經(jīng)過前文的分析,可以知道使用直接系統(tǒng)調(diào)用大致就是獲取 Native API 的 stubs 或者動態(tài)的獲取系統(tǒng)調(diào)用號去構(gòu)造 stubs,雖然使用直接系統(tǒng)調(diào)用能夠有效的避免 AV/EDR 在用戶模式下的掛鉤,但是去獲取這個 stubs 或者調(diào)用號的過程還是會被 AV/EDR 監(jiān)控的,并且在 Window Vista 之后,在內(nèi)核模式中,安全廠商的研發(fā)人員可以利用微軟提供的現(xiàn)成的內(nèi)核通知回調(diào)很輕松的監(jiān)控用戶模式下程序的各種動作,所以在進行防御規(guī)避的過程中,需要不斷去發(fā)掘出新的思路方法,或者將已知的各種規(guī)避技術(shù)相互結(jié)合來進行欺騙和繞過。
并且隨著 Windows 版本的提升,微軟也已經(jīng)開始也有趨勢去處理調(diào)用號的問題,從下圖(x86)可以發(fā)現(xiàn),系統(tǒng)調(diào)用號并不一定是穩(wěn)定遞增的,所以從這個方面想,那種基于排序動態(tài)獲取系統(tǒng)調(diào)用號的方法是否在未來還可用?以及是否有對應(yīng)的緩解措施去應(yīng)對這些變化,是作為一個安全研究員需要去不斷探索的事。
參考文獻
https://j00ru.vexillium.org/syscalls/nt/32/
https://j00ru.vexillium.org/syscalls/nt/64/
https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams/
https://blog.sektor7.net/#!res/2021/halosgate.md
https://teamhydra.blog/2020/09/18/implementing-direct-syscalls-using-hells-gate/
編輯:黃飛
?
評論
查看更多