程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 深入C/C++之基於Cookie的安全檢查(VS2005)

深入C/C++之基於Cookie的安全檢查(VS2005)

編輯:關於C語言

 昨天在試著逆向一個有時間期限的LIB時,發現一些特別的檢查函數,在之前的VC2003中是沒有的,這些函數可謂是重量級函數。由於個人比較看不慣自己不懂的東西,出於不憤之情緒研究了下這些函數。首先在這裡介紹個人認為較之其他幾個更為重要的一種安全檢查方式——基於Cookie的緩沖區溢出安全檢查!

 

為了在發布版本中也能檢測到緩沖區溢出,防止程序因緩沖區而受到攻擊,VS2005VC8)便增加了基於Cookie的安全檢查。

在計算機領域,Cookie一詞最早出現在網站開發中,是指網站的服務程序通過浏覽器保存到客戶端的少量數據,這些數據都是二進制的,或者是經過加密的,通常用來記錄用戶身份和登錄情況等信息。後來這個詞被泛指一方簽發給另一方的認證或者標志信息。

 

在之前的博文中,都提到過堆棧調用及EBP,RET返回地址,還有局部變量在棧幀中存放的各種微妙關系。相信大家也有清晰的了解,如果不清晰呢,可以參見Shell Code,HOOK API這兩篇。嘿嘿!

 

VC8編譯器在編譯可能發生緩沖區溢出的函數時,會定義一個很特別局部變量,如果不加分析的話,這個局部變量還真不知道代表什麼意思。而且它的值又是怎麼算出來的?,它有什麼作用? 可能有的朋友不是沒有注意到這個細節就是覺得沒有必要追究它。不過個人認為熟悉了堆棧調用裡面的原理會對我們的調試能力和差錯能力有很明顯的提高。好了,轉入正題。編譯器增加的這個局部變量時緊挨著EBP的存放地址的。順序就是:VAR COOKIE EBP RET ARGS。這個Cookie的變量位於函數體內的局部變量和EBP的存放地址之間,具體表示就是:[EBP-4]。這個專門用於保存Cookie的變量被稱為Cookie變量。是一個32位的無符號整數。它的值是從全局變量__security_cookie得到的。我們可以看看這個全局變量的定義:

#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);

由於我測試的機子是32位的,這裡只看32位的。有朋友是64位的可以測試下。這裡個定義位於gs_cookie.c文件中。文件位於編譯器目錄的VC/crt/src下。這裡可以看到,最開始的時候這個全局變量被初始化成了0xBB40E64E。為什麼是這個值,這裡不是討論的重點,還有待研究!嘿嘿。

 

上面說到最開始的時候被初始化為了0xBB40E64E。言外之意後面還會對它進行處理?答案是肯定的!下一次初始化是在我們程序進入主函數之前的:__tmainCRTStartup函數裡。

拋開預處理和機器的條件判斷,這個函數的原型如下:

int mainCRTStartup( void )
{
        /*
         * The /GS security cookie must be initialized before any exception
         * handling targetting the current image is registered.  No function
         * using exception handling can be called in the current image until
         * after __security_init_cookie has been called.
         */
        __security_init_cookie();

        return __tmainCRTStartup();
}

我的機子拋開後是調用的這個函數。現在的硬件環境大多數也是這個。這裡可以看到在這裡會調用__security_init_cookie()這個函數對__security_cookie變量再次初始化。這個函數也是能看到原型的,它位於gs_support.c文件中。進去看看,拋開一編譯時的判斷條件其原型主體部分為:

void __cdecl __security_init_cookie()

{

    UINT_PTR cookie;
    FT systime={0};
    LARGE_INTEGER perfctr;

 

    GetSystemTimeAsFileTime(&systime.ft_struct);

    cookie = systime.ft_struct.dwLowDateTime;
    cookie ^= systime.ft_struct.dwHighDateTime;

    cookie ^= GetCurrentProcessId();
    cookie ^= GetCurrentThreadId();
    cookie ^= GetTickCount();

    QueryPerformanceCounter(&perfctr);

    cookie ^= perfctr.LowPart;
    cookie ^= perfctr.HighPart;

 

    __security_cookie = cookie;
    __security_cookie_complement = ~cookie;

}

這裡可以看出來,為了取得好的隨機性,先是取出時間,異或之,然後是分別跟其他一些列具有隨機性的數據進程ID,線程ID,TickCount和性能計數器)進行異或運算。這個變量因為是全局的,在這裡 mainCRTStartup啟動函數)初始化後在進程過程中將不會再改變。如果想要查看這個cookie變量的值。可以再調試的時候拉出“即時窗口Immediate)”,在裡面輸入__security_cookie回車就能看到了。

 

好了,在上面介紹完了Cookie變量的產生、初始化和作用後,下面來看看使用。

寫個最簡單的測試:

int main( void )

{

    char a[ 20 ];

    strcpy( a, "masefee" );

    return 0;

}

上面說過,編譯器會在可能發生緩沖區溢出的函數插入Cookie變量和安全檢查。這樣一個小例子足以讓它檢查了。他已經發現可能存在危險了。是不是很智能? - -

要看這個函數一開始怎麼寫入Cookie變量的,可以打斷點在紅色的括號處或者F11單步。這裡又得在反匯編裡面進行了,這裡不厭其煩的從匯編裡面去看問題,包括以前的文章基本跟匯編有聯系。這裡不是別的,只是個人認為還是很有必要從匯編的角度去了解高級語言的原理。很多是很必要的。當然這裡也只能從匯編去分析這個Cookie變量的寫入過程及檢查過程!忍耐一下! - -

就這裡這個簡單的例子,DEBUG模式下反匯編如下:

 
0043BEF0  push        ebp  
0043BEF1  mov         ebp,esp 
0043BEF3  sub         esp,0E0h 
0043BEF9  push        ebx  
0043BEFA  push        esi  
0043BEFB  push        edi  
0043BEFC  lea         edi,[ebp-0E0h] 
0043BF02  mov         ecx,38h 
0043BF07  mov         eax,0CCCCCCCCh 
0043BF0C  rep stos    dword ptr es:[edi] 
0043BF0E  mov         eax,dword ptr [___security_cookie (4B7A74h)] 
0043BF13  xor         eax,ebp 
0043BF15  mov         dword ptr [ebp-4],eax 
0043BF18  push        offset string "masefee" (4AA938h) 
0043BF1D  lea         eax,[ebp-1Ch] 
0043BF20  push        eax  
0043BF21  call        @ILT+3880(_strcpy) (437F2Dh) 
0043BF26  add         esp,8 
0043BF29  xor         eax,eax 
0043BF2B  push        edx  
0043BF2C  mov         ecx,ebp 
0043BF2E  push        eax  
0043BF2F  lea         edx,[ (43BF5Ch)] 
0043BF35  call        @ILT+3115(@_RTC_CheckStackVars@8) (437C30h) 
0043BF3A  pop         eax  
0043BF3B  pop         edx  
0043BF3C  pop         edi  
0043BF3D  pop         esi  
0043BF3E  pop         ebx  
0043BF3F  mov         ecx,dword ptr [ebp-4] 
0043BF42  xor         ecx,ebp 
0043BF44  call        @ILT+920(@__security_check_cookie@4) (43739Dh) 
0043BF49  add         esp,0E0h 
0043BF4F  cmp         ebp,esp 
0043BF51  call        @ILT+7205(__RTC_CheckEsp) (438C2Ah) 
0043BF56  mov         esp,ebp 
0043BF58  pop         ebp  
0043BF59  ret             

這就是main函數的所有反匯編代碼。首先看紅色的一句指令。是將___security_cookie變量的值給取出來。然後藍色的指令就是將取出來的Cookie全局變量與當前EBP的值進行異或運算。與EBP異或當然有好處。

1. 可以增加隨機性,盡可能使不同函數的安全Cookie都不同。

2. 可以檢查EBP是否被破壞,因為在函數結束檢查Cookie時,還會將Cookie變量值再次與EBP異或,如果EBP的值沒有變化,那麼就能恢  復成原來的___security_cookie值。

這些細節地方不得不佩服微軟的設計師們的缜密和擴展的思維!

 

上面綠色的指令便是關鍵的一步,它是將異或後的值存入[ebp-4]中。當前的EBP就是最開始壓入EBP後ESP的值,也就是壓入的EBP的地址。減4就剛好挨著一進函數時壓入的EBP的地址減4。好了!Cookie變量已經在棧幀中了。

下面一步就看最後的檢查部分,粉色的部分前兩句指令是將Cookie變量的值重新取出來並異或還原並保存到ECX中。保存到ECX是有目的的。之後再討論。第三句粉色的CALL就是調用__security_check_cookie 函數了。其原型非常簡單:

void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie)
{
    /* x86 version written in asm to preserve all regs */
    __asm

    {
        cmp ecx, __security_cookie
        jne failure
        rep ret /* REP to avoid AMD branch prediction penalty */
    failure:
        jmp __report_gsfailure
    }
}

這個函數位於編譯器目錄下的VC/crt/src/intel/secchk.c中。為了降低對可執行文件大小和運行性能的影響,這個函數直接用匯編寫的。而且只有4條指令,不存在任何變量和寄存器標志寄存器除外)的改變。因為是使用的快速調用協定。因為唯一的一個參數都是存放在ECX中,直接進行CMP比較的。紅色的部分就是與全局的Cookie變量進行比較。如果相同就正常,如果不同就jmp failure報錯。跳轉到__report_gsfailure函數。這樣既檢查了EBP是否合法,又檢查了Cookie變量的合法性。

 

好了,基本上寫完了。上面留了兩個點,一是mian函數裡面我有標志了一句橙色的語句,這個也是一個檢查。將在後面的博文中提到。二是__report_gsfailure函數的整個過程。也將在後面的博文中深入闡述。上面有什麼不對的地方還望大家批評。我很希望得到大家的指點!

 

思考:

      1. 這種模式的安全檢查能夠移植到我們平常的項目中的哪些地方,在我們用戶代碼上顯示進行檢查?

      2. 這種產生盡可能隨機的方式用於類似於生成對象的全局唯一ID或者更多的需要唯一性的數據上?

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved