首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

深入 C/C++ 安全機制:Visual Studio 2005 (VC8) 基於 Cookie 的緩衝區溢位防護 (/GS) 原理剖析

     編輯:關於C語言

VC8 引入 /GS (Buffer Security Check) 編譯選項,在函數序言/結語插入 Security Cookie 機制,於運行時偵測堆疊緩衝區溢位導致的返回位址/框架指標竄改。本文從組合語言層面完整還原 Cookie 生成、植入、驗證流程,並展示 `__security_check_cookie` 內部實作。

威脅模型:堆疊佈局與溢位後果

高位址
+------------------+
| 函數參數         |
+------------------+
| 返回位址 (RET)   | ← 攻擊目標:覆蓋控制流
+------------------+
| 儲存的 EBP       | ← 攻擊目標:破壞堆疊幀鏈
+------------------+
| Security Cookie  | ← /GS 插入的金絲雀值
+------------------+
| 區域變數/緩衝區  | ← 溢位來源
+------------------+
低位址

Cookie 置於 區域變數與舊 EBP 之間,溢位寫入必須先覆蓋 Cookie 才能觸及 EBP/RET。

Cookie 生命週期:兩階段初始化

階段 1:編譯期預設值


// gs_cookie.c
#ifdef _WIN64
#define DEFAULT_SECURITY_COOKIE 0x00002B992DDFA232
#else
#define DEFAULT_SECURITY_COOKIE 0xBB40E64E
#endif

DECLSPEC_SELECTANY UINT_PTR __security_cookie = DEFAULT_SECURITY_COOKIE;
DECLSPEC_SELECTANY UINT_PTR __security_cookie_complement = ~DEFAULT_SECURITY_COOKIE;

階段 2:進程啟動時重新播種 (真正隨機化)

在 `mainCRTStartup` → `__security_init_cookie()` 中執行:


void __cdecl __security_init_cookie() {
    UINT_PTR cookie;
    FILETIME systime;
    LARGE_INTEGER perfctr;

    GetSystemTimeAsFileTime(&systime);
    cookie = systime.dwLowDateTime;
    cookie ^= systime.dwHighDateTime;
    cookie ^= GetCurrentProcessId();
    cookie ^= GetCurrentThreadId();
    cookie ^= GetTickCount();
    QueryPerformanceCounter(&perfctr);
    cookie ^= perfctr.LowPart;
    cookie ^= perfctr.HighPart;

    __security_cookie = cookie;
    __security_cookie_complement = ~cookie;
}

綜合系統時間、進程/執行緒 ID、TickCount、高效能計數器多重熵源,確保每次執行 Cookie 值不可預測。

函數序言:植入 Cookie

反組譯範例 (Debug 模式):


main:
    push    ebp
    mov     ebp, esp
    sub     esp, 0E0h
    ; ... 省略暫存器保存 ...
    mov     eax, dword ptr [__security_cookie]  ; 取得全域 Cookie
    xor     eax, ebp                            ; 與 EBP 異或
    mov     dword ptr [ebp-4], eax              ; 存入 [EBP-4] (Cookie 變數)

異或 EBP 的雙重目的:

  1. 增加隨機性:不同函數 EBP 不同,衍生出不同 Cookie 值。
  2. 偵測 EBP 竄改:驗證時再異或一次 EBP,若 EBP 被改寫,還原值將不匹配。

函數結語:驗證 Cookie


    ; ... 函數本體結束 ...
    mov     ecx, dword ptr [ebp-4]   ; 取出 Cookie 變數
    xor     ecx, ebp                 ; 還原原始 Cookie
    call    @__security_check_cookie@4  ; 快速呼叫慣例,參數在 ECX
    ; ... 恢復堆疊、返回 ...

核心驗證函式 `__security_check_cookie` (純組語實作)


// secchk.c (Intel 目錄)
void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) {
    __asm {
        cmp     ecx, __security_cookie   ; 比較還原值與全域 Cookie
        jne     failure                  ; 不等 → 跳轉失敗處理
        rep     ret                      ; REP 防 AMD 分支預測罰分
failure:
        jmp     __report_gsfailure       ; 回報緩衝區溢位失敗
    }
}
  • __fastcall:唯一參數放 `ECX`,無堆疊操作極致高效。
  • naked:編譯器不產生序言/結語,完全手工控制。
  • 僅 4 條指令,零額外暫存器/旗標副作用。

失敗處理 `__report_gsfailure`

終止進程、呼叫錯誤回報機制 (WER)、可選擇啟動偵錯器。細節留待後續文章探討。

偵錯技巧:即時視窗觀察 Cookie

中斷點停下時,在「即時運算視窗」輸入 `__security_cookie` 即可查看當前進程的全域 Cookie 值。

思考延伸

  1. 此模式能否移植至使用者層專案,保護關鍵回呼函式、插件介面?
  2. 多重熵源異或生成隨機數,是否適用於分散式系統的全域唯一 ID 生成?

本文為逆向工程視角剖析 VC8 /GS 機制,歡迎指正補充。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved