《程序員》5月文章。申明。文章僅代表個人觀點,與所在公司無任何聯系。
- 概述
在前面的安全編碼實踐的文章里,我們討論了GS編譯選項和<wbr>數據執行保護</wbr>DEP功能。 結論是GS和DEP可以有效的緩解緩存溢出類型的安全漏洞的危害<wbr>。關于這個結論,有兩個大家需要值得注意的地方。</wbr>
第一:GS和DEP是緩解(mitigation)措施。<wbr>也就是說,<b>代碼本身仍然存在著安全漏洞</b>,只是由于</wbr>GS和DEP降<wbr>低了其危害程度。</wbr>
第二:GS和DEP存在其自身的局限性。例如,GS不是對<wbr>每一個函數都適用,而</wbr>DEP則需要一定的硬件支持。
那么,一個很自然的問題就是,<wbr>有什么工具可以幫助我們在開發過程中,<wbr>及早發現并修補代碼中存在的安全漏洞?</wbr></wbr>
答案之一是靜態代碼分析工具。本文會著重介紹微軟提供的C<wbr>/C++</wbr>的靜態代碼分析工具:Prefast。對于托管代碼(m<wbr>anaged code</wbr>),微軟提供的靜態代碼分析工具是FxCop。關于Fx<wbr>Cop</wbr>,我們會在以后的安全編碼實踐的文章中專門介紹。
- Prefast 介紹
2.1 歷史
Prefast是微軟研究院提出的靜態代碼分析工具。<wbr>主要目的是通過分析代碼的數據和控制信息來檢測程序中的缺陷。<wbr>需要強調的是,</wbr></wbr>Prefast檢測的缺項不僅僅是安全缺陷,<wbr>但是安全缺陷類型是其檢測的最為重要的部分。</wbr>Prefast推出<wbr>后在微軟內部得到了廣泛的使用,并經歷了若干格版本的升級。<wbr>現在,微軟將這個內部工具商業化,以提供給外部的開發人員使用。</wbr></wbr>
2.2 如何獲得 Prefast
目前有兩個辦法可以獲得Prefast工具。
- Prefast包括在Visual Studio 2005 /2008的團隊版本(team edition)中。
- Prefast包括在Windows驅動程序開發包(Micro<wbr>soft Windows Driver Kits</wbr>)的開發環境中。
需要指出的是,Visual Studio的團隊版本的價格要高于Visual Studio個人版本,而Windows驅動程序開發包是免費下<wbr>載的,那它們提供的</wbr>Prefast版本有什么區別?在Visua<wbr>l Studio</wbr>的團隊版本中,Prefast是直接和代碼的開發過<wbr>程集成的,使用非常方便。并且可以直接根據</wbr>Prefast的輸出<wbr>結果創建相應的開發任務。而在</wbr>Windows驅動程序開發包中,<wbr>Prefast</wbr>是作為一個單獨的工具提供,沒有像Visual Studio團隊版本中一樣與開發環境集成。
下載Windows驅動程序開發包可以通過
http://<wbr>connect.microsoft.com/</wbr>
, 注冊Microsoft Connect后選擇Windows Logo Kit (WLK), Windows Driver Kit (WDK) and Windows Driver Framework (WDF)即可。具體的步驟這里就不詳細敘述了。
安裝好WDK后Prefast就已經直接在其開發環境下使<wbr>用了。</wbr>
2.3 使用 Prefast
在Visual Studio的團隊版本中,使用Prefast,打開Proje<wbr>ct Properties --> Configuration Properties --> Code Analysis -->Enable Code Analysis For C/C++ on build</wbr>。選擇 Yes(/analyze)即可。具體可參見圖1。
如果直接使用CL.exe命令行編譯器,采用/<wbr>analyze</wbr>編譯選項即可。例如:cl test.cpp -W4 /EHsc /analyze
圖1:VSTS中使用Prefast(Code Analysis)
使用在WDK中的Prefast,有以下幾個重要命令。
- 運行Prefast:prefast build -cZ
-
查看Prefast輸出結果:
- 命令行:prefast list
- GUI:prefast view
有關在WDK下使用Prefast的詳細步驟,<wbr>可以參見微軟的</wbr>PREfast Step-by-Step文檔【1】。
- Prefast 輸出的警告( warning )信息
上面的介紹可以看出Prefast的使用操作并不難。<wbr>成功應用</wbr>Prefast的關鍵是要理解其輸出的各類警告信息,<wbr>并作出正確的評估和代碼修改。文章前面我們提到過,</wbr>Prefas<wbr>t</wbr>檢測的不僅僅是安全缺陷,<wbr>但是安全缺陷類型是其檢測的最為重要的部分。下面,<wbr>我們就介紹一些和安全漏洞緊密相關的警告信息。<wbr>以下給出的代碼例子均來自于微軟的文檔</wbr></wbr></wbr>Code Analysis for C/C++ Warnings【2】。
警告 C6001 : using uninitialized memory <variable>,使用未初始化的變量。
例子:
#include "stdafx.h"
int f( bool b )
{
int i;
if ( b )
{
i = 0;
}
return i; // 當b為假時,變量i未初始化
}
大家也許會問,使用未初始化的變量會導致安全漏洞嗎?<wbr>這里我想特別強調的一點是,<wbr>我們前面經常提到的安全漏洞是緩存溢出導致的,<wbr>但是大家千萬不要認為緩存溢出是導致安全漏洞的唯一原因。<wbr>有各種各樣的代碼錯誤可以導致嚴重的安全漏洞,<wbr>使用未初始化的變量也是其中的一種。<wbr>關于未初始化的變量如何導致安全漏洞的詳細信息超出了本文的范疇<wbr>,有興趣的讀者可以參見微軟</wbr></wbr></wbr></wbr></wbr></wbr></wbr>SWI組的博客文章:MS08-<wbr>014 : The Case of the Uninitialized Stack Variable Vulnerability</wbr>【3】
修補:初始化變量。
警告 C6029 : possible buffer overrun in call to <function>: use of unchecked value,使用未驗證的參數可能導致緩存溢出。
例子:
#include "windows.h"
void f(char *buff, DWORD cbLen, DWORD cbRead, HANDLE hFile)
{
if (!ReadFile (hFile, &cbLen, sizeof (cbLen), &cbRead, NULL))
{
// code ...
if (!ReadFile (hFile, buff, cbLen, &cbRead, NULL)) // warning 6029
{
// code ...
}
}
}
在上面這個例子中,第一次的ReadFile得到的cbL<wbr>en</wbr>值,直接作為最大讀取的長度傳遞給第二個ReadFile。<wbr>如果</wbr>cbLen過大的話,就會導致寫入buff過多的數據。
修補:驗證參數。在第二個ReadFile之前驗證cbL<wbr>en</wbr>。
if (cbLen <= sizeof (buff)) // check length
警告 C6057 : buffer overrun due to number of characters/number of bytes mismatch in call to <function>,字符(characters)<wbr>數目和字節(</wbr>bytes)數目的不匹配導致緩存溢出。
對于ANSI字符串類型,字符數目和字節數目是一致的。<wbr>但是如果字符串的類型是</wbr>UNICODE,<wbr>字節數目就是字符數目的兩倍。<wbr>如果在應該傳遞字符數目的地方傳遞了字節數目,<wbr>就可能導致緩存溢出。</wbr></wbr></wbr>
例子:
#include<tchar.h>
#include<windows.h>
void f( HINSTANCE hInst, UINT uID )
{
TCHAR buff[128];
if ( LoadString ( hInst, uID, buff, sizeof buff ) ) // warning C6057
{
// code...
}
}
LoadString期望的參數是字符串緩存的字符數目。
修補:正確計算數組中元素的個數。
LoadString ( hInst, uID, buff, (sizeof buff)/(sizeof buff[0]) )
警告
C6201
: buffer overrun for <variable>, which is possibly stack allocated: index <name> is out of valid index range <min> to <max>,數組索引的越界可能導致緩存溢出。
例子:
void f()
{
int buff[25];
for (int i=0; i <= 25; i++) // i exceeds array bound
{
buff[i]=0; // initialize i
// code ...
}
}
修補:確保數組索引不越界。
警告 C6202 : buffer overrun for <variable>, which is possibly stack allocated, in call to <function>: length <size> exceeds buffer size <max>,使用緩存區時,給出的長度超出緩存區長度的最大值。<wbr><br></wbr>
例子:
#include <memory.h>
void f( )
{
int intArray[5];
char charArray[5];
memset ((void *)charArray, 0, sizeof intArray);
// code ...
}
修補:正確的緩存長度。這里sizeof intArray應該是sizeof charArray。
警告
C6204
: possible buffer overrun in call to <function>: use of unchecked parameter <variable>,使用緩存區時,<wbr>直接使用未經檢查的參數可能導致緩存溢出。</wbr>
例子:
#include<string.h>
void f(char *pCh)
{
char buff[10];
strcpy(buff, pCh);
}
pCh的值沒有驗證就直接使用了。
修補:驗證傳入的參數。加入檢查:if (strlen(pCh) >= sizeof buff) return;
限於篇幅,<wbr>我們這里無法一一列舉所有和安全缺項緊密相關的重要警告信息,<wbr>例如</wbr></wbr>C6327,C6383,C6386等等。有關Prefas<wbr>t</wbr>輸出警告信息的詳細列表,可以參見Code Analysis for C/C++ Warnings【2】。
- 靜態代碼分析工具的局限
靜態代碼分析工具是SDL(安全軟件開發周期)<wbr>提出的編碼實踐中非常重要的一環。但是,<wbr>現實中不存在任何一種工具可以一下子解決所有的安全問題。靜態</wbr></wbr>C<wbr>/C++</wbr>代碼分析工具是不可能發現代碼中的所有安全漏洞的。就像<wbr>GS</wbr>和DEP一樣,它也存在自身的局限性。
由于程序控制流和數據流的復雜程度,<wbr>靜態代碼分析工具不可避免的存在:</wbr>
- 漏報:我們上面舉出的代碼例子都是非常簡單的。<wbr>隨著代碼復雜度的增大,尤其是跨函數之間邏輯和數據的交互關系,<wbr>往往給靜態代碼分析帶來極大的困難。</wbr></wbr>
- 誤報:分析信息的不完備,會導致誤報警告信息。
- 總結
Prefast是微軟提供的針對C/C++程序的靜態代碼<wbr>分析工具,可以有效的檢測代碼中存在的安全缺陷。<wbr>強烈建議在開發過程中,采用</wbr></wbr>Prefast或其他類型的靜態代碼<wbr>分析工具,以降低程序中引入(安全)缺陷的可能。</wbr>
- 參考文獻
- PREfast Step-by-Step , http://www.<wbr>microsoft.com/whdc/DevTools/<wbr>tools/PREfast_steps.mspx</wbr></wbr> , Micro<wbr>soft </wbr> http://msdn2.microsoft.com/en-<wbr>us/library/ms680339(VS.85).<wbr>aspx</wbr></wbr>
- Code Analysis for C/C++ Warnings , http://msdn2.<wbr>microsoft.com/en-us/library/<wbr>a5b9aa09.aspx</wbr></wbr> , Microsoft
- MS08-014 : The Case of the Uninitialized Stack Variable Vulnerability, http://blogs.technet.com/swi/<wbr>archive/2008/03/11/the-case-<wbr>of-the-uninitialized-stack-<wbr>variable-vulnerability.aspx</wbr></wbr></wbr> , SWI, Microsoft
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
