___security_cookie

标簽: ,

用IDA Pro反彙編某個程序時發現很多函數都有___security_cookie這個東西,Google了一下,原來是GS編譯選項搞的鬼。

有關security cookie在棧保護上的研究
其實不是什麼新東西了,也不是多高深的技術,在此隻是寫寫我的一點研究心得而已。
這裡主要讨論棧,不是堆。
首先,security cookie并不是windows系統自帶的保護機制,并不是說一個确實存在溢出漏洞的程序,放到帶security cookie保護的環境中,就不能正常溢出了。
那麼,到底是什麼是security cookie呢?
我覺得從廣義上講,它應該是一種保護棧的機制,提供這種保護的,是程序本身,編譯進程序本身的代碼提供的,而不是系統中某個運行在黑暗角落中的線程。
所以,既然是程序自身就帶上的,為了不給程序員帶來額外的負擔,這份工作就交給編譯器來完成了。
vc6.0的cl.exe是不帶這個功能的,隻有vc.net以後面版本的cl.exe才帶這個功能,就所謂的/GS選項。
即用vc.net的cl編譯器時,/GS選項默認就打開了。
現在,我們知道了這個機制的提供方,那麼,這個機制到底是怎麼一回事呢?
熟悉函數調用及返回前後的彙編指令的人肯定很清楚,在win32平台,對于stdcall類型的函數調用,
當call指令運行完畢,當前的堆棧結構基本上是這樣的:
局變2        ebp-8            低地址
局變1        ebp-4
ebp        ebp
返回地址    ebp+4
參數1        ebp+8
參數2        ebp+c
參數3        ebp+10
參數4        ebp+14            高地址
第一列是堆棧中存放的dword的内容,第二列是用ebp作為棧地址的索引時,它對應的應該用ebp表示的值,說得形象一點,ebp中存放着棧的一個地址 (棧其實也是一片内存,ebp隻是指向其中一個對當前函數内部比較重要的地址,其實是相當重要),棧的其它位置都是通過這個ebp來尋址的,即我們給函數的第一個形參的地址,就是ebp+8,第二個就是ebp+c,我們定義的局部變量的地址,第一個局部變量是ebp-4,第二局部變量的地址就是ebp- 8,依此類推。但這也不是一定的,上面說的是理想情況,如果我們在函數裡定了一個數組,如 char buf[8];并且是定義的第一個局部變量,那麼它的地址肯定就不是ebp-4,還是ebp-8。所以,數組比較特殊,結構體也比較特殊,其根本原因是棧是從高地址向低地址生長的,而我們的數組,結構體,卻是從低向高地址生長的,兩者矛盾的結果就是尋址上的微妙變化。當然,這裡為了方便說明問題,都默認定義的變量,傳入的參數,都是四字節對齊的,并且一個變量一個雙字。你可以把數組理解一個4字節的char,也就是一個雙字了。
話說回來,當call運行完畢,當前的堆棧結構已經給出,如果在函數裡調用strcpy()往局部變量1裡考入東西,對長度沒有進行檢測,那麼ebp- 4,ebp,ebp+4,還有後面的地址,其所在的内容都會被覆蓋掉。這裡溢出就發生了,我們控制住了ret的返回地址,然後…
嗯,為了防止這一切,新的cl編譯器的/GS選項加上入所謂的"security cookie",如何加入的?在哪加入的呢?
先看看加入"security cookie"後的call指令運行完以後,堆棧的變化。
局變2        ebp-c            低地址
局變1        ebp-8
XXXXX        ebp-4
ebp        ebp
返回地址    ebp+4
參數1        ebp+8
參數2        ebp+c
參數3        ebp+10
參數4        ebp+14            高地址
變化很明顯,在ebp上面,第一個局部變量的下面,填入的一個新的值,這個值就是所謂的"security cookie".按照前面說的溢出過程,ebp-4的内容被覆蓋掉,即security cookie的值被修改,在函數返回,即執行ret指令前,會call另一個函數,這個函數就是用來對比ebp-4的值和當時push到棧中的值是不是一樣,不一樣的話,就說明溢出了,然後進程被終止。
那麼,你大概會産生以下幾個問題:
1.這個security cookie是如何計算出來的?
security cookie是一個雙字,也可以說是一個int,其本身是保存在全局變量裡的,其創建是編譯器在編譯階段就創建的,然後寫入到.data段裡,即在PE裡就保存了這個值。
但這個值又是變化的,windows裝載器完成必要的前期準備工作後(如創建進程,為棧分配内存,等待)把EIP設置為PE裡的代碼入口處,第一個執行的指令就是一個call調用,這個call調用就是用來初始化這個
cookie值的,當然,這段代碼也是公開的,但沒有關系,這個算法保證這個cookie值是随機的,hacker也是不能在一個shellcode中可以猜出來的。
具體算法我不打算在此說明,感興趣的讀者可以自己編譯一下再反彙編一下看看。
2.是什麼時候填入到棧裡面的?
我們知道了這個security cookie的計算和初始化過程,那麼,它必須在函數調用時寫入到ebp-4裡面才有用。
所以,過去的不帶這種保護的代碼,在函數入口處一般是這樣的:
push    ebp
mov    ebp,esp
sub    ebp,n   ;這條指令可能不同,不過多數情況下都是這樣來為局部變量分配空間的
然後,後面就開始執行我們的代碼了,
加入這種保護後,會在sub ebp,n後面,加入一條像這樣的指令:
mov     dword ptr [ebp-4],XXX
XXX就是security cookie的值,這個值保存在全局變量裡,通過RVA+PE頭地址,實際上也可以說成是
絕對地址來引用了。
到這裡security cookie的值就寫入棧了,然後在函數返回前檢測一下就行了。
到了這裡,你大概又會産生一個新的問題,
必須為每一個函數調用都寫入security cookie進行保護嗎?
答案是否定的,要不然我們的程序的執行效率會受到一定的影響,并且可能還不小。
那麼,就應該存在一定的規則,什麼時候進行這種保護,什麼時候不需要。
其依據當然也很簡單,有溢出可能的,就加入這種保護,沒有溢出可能的,就不加。
那怎麼樣才算是有溢出可能呢?
這個是編譯器進行判斷的,像函數裡定義了char數組,後面又用字符串操作函數進行了一定的操作,就說明
可能存在溢出。編譯器在編譯這個函數裡的時候就加上security cookie的保護。
當然,這裡還有一些其它的很具體的規則,在msdn裡有更詳細的描述。
還有其它一些問題沒有在這裡說明,可以把這些問題留給大家
1.有對付security cookie的檢測的方法嗎?(答案是有的,但好像都不是很優美)
2.有關security異常的異常處理函數
3./safeSEH對SEH處理的變化

原文鍊接:http://static.byr.cn/#elite/path?v=/groups/sci.faq/Security/hacker/BufferOverflow

随機文章:

  1. bbPress漢化(簡體中文語言包)
  2. 邪惡的eval和new Function
  3. C/C++的64位整型
  4. VBS轉EXE工具:ExeScript
  5. iesetup.exe無法找到入口

留下回複