面對惡意程式碼,我們告別以前處理的方法(轉)

RegisterForBlog發表於2007-09-19
面對惡意程式碼,我們告別以前處理的方法(轉)[@more@]

  無論是居室還是程式碼,我們總是有必要不時對其進行認真的清理。不幸的是,在我們開始清理時,總會產生這樣的疑惑,那就是這些東西到底來自何方?為什麼我們從未注意到它的存在?儘管能夠清理部分內容,但總有一些會保留下來。如果您在某些方面與我相像,那麼可能還會導致出現更明顯、更新奇的問題。

  我們看一下問題出在什麼地方。C 執行庫亟待進行有效的改進,這裡的改進不是指一般意義上的完善,而是使它具有穩固的結構,使它完全脫胎換骨!

  請認真考慮一下此問題。請問人們在什麼情況下寫出了諸如 strcpy 和 strcat 之類的函式?是很久以前 Kernighan 和 Ritchie 剛剛開發出 C 語言的那段美好時光,那時程式碼面臨的威脅遠沒有現在嚴重,網路的互連也遠沒有現在普及。而現在的情況則讓我摸不清頭腦,那就是您仍然能夠使用諸如 strcpy 的函式寫出安全的程式碼。這就是資料在起作用。但諸如 strcpy 之類的函式本身無法幫助您寫出安全的程式碼,並且在呼叫這類函式時可能出現災難性的錯誤。正如 'Nuff 所說,是的,gets 只是一般的錯誤!

  那麼,針對這種情況,我們能夠採取什麼措施呢?您可能聽說過 strsafe.h 檔案,該檔案中包含了一組一致的、更安全的字串處理函式,於 2002 年 Windows Security Push 活動期間開發,適用於 Visual Studio® .NET 2003 和 Platform SDK。可以查閱 Strsafe.h:Safer String Handling in C,瞭解有關 strsafe 的更多資訊。

  也可以在許多開放原始碼的作業系統中使用其他函式(比如,strlcpy 和 strlcat)。可以在 David Wheeler 撰寫的 Secure Programming for Linux and Unix HOWTO 一文中查閱這兩個函式的有關資訊。

  儘管開發 strl* 和 strsafe 是一種有效的措施,我們仍然需要在核心的 C 執行庫中建立更加可靠的功能,這正是經過更新的 CRT 發揮作用的地方。Microsoft Visual C++® 庫開發小組決定跟蹤並認真檢查 CRT 中的每一個函式,以確定其中是否存在安全性方面的缺陷,找出可能的解決方法。眾所周知,為了提高安全性,也為了有助於使用者寫出更加安全的程式碼,已經重新編寫了許多函式(大約有 400 個左右)。

  新版 CRT 的新增功能

  首先,本文所介紹的新增加的 CRT 函式將出現在 Visual Studio 2005 中,但最終發行的版本可能與目前的版本有所不同。其次要指出的是,僅僅透過改變編譯器的某個引數,新的庫不會奇蹟般地使得不安全的程式碼變為安全的程式碼,但肯定有助於增加程式碼的安全性。

  更安全的可供選擇的方法不會取代已有的功能。換言之, strcpy 仍將是 strcpy。它的更加安全的版本具有一個新名稱,即 strcpy_s。但是,如果在編譯時使用新的庫,那麼舊版本的函式將失效。所以需要說明的是,編譯器會立即向您發出警告資訊。也就是說,與修復安全性缺陷相比,改正編譯器警告資訊所指出的錯誤要更加容易。請認真考慮我就此問題提出的建議!

  某些函式(比如,calloc)僅僅加強了檢查引數的工作,其功能與以前的版本完全相同,所以不存在 calloc_s 函式。稍後將介紹有關 calloc 函式的更多資訊。

  我最贊同的更改是使用 strncat_s 函式代替了 strncat 函式。strncat 函式的問題在於它的最後一個引數不表示目標緩衝區的總的大小,它指示目標緩衝區中剩餘的最小緩衝區的大小,以及需要複製的數目。這可能導致各種型別的 off-by-one(差 1)錯誤,甚至導致更嚴重的 off-by-lots(差多) 錯誤。請看下面的例子:

  if (szURL != NULL) {

  char

  szTmp[MAX_PATH];

  char

  *szExtSrc, *szExtDst;

  strncpy(szTmp, szURL, MAX_PATH);

  szExtSrc = strchr(szURL, '.');

  szExtDst = strchr(szTmp, '.');

  if(szExtDst) {

  szExtDst[0] = 0;

  if (fValid)

  strncat(szTmp, szExtSrc, MAX_PATH);

  }

  }

  呼叫 strncat 函式時出現錯誤-嚴重錯誤。實際上,這時將發生緩衝區溢位。無法將 MAX_PATH 字串安全地複製到 szTemp 中,因為在呼叫 strncpy 函式時,已經將 szURL 新增至該字串,這實際上減少了 szTmp 中剩餘的空間。

  以下是一個較為簡單的例子:

  char szTarget[12];

  char *s = "Hello, World";

  strncpy(szTarget,s,sizeof(szTarget));

  strncat(szTarget,s,sizeof(szTarget));

  如果在 Visual C++ 2003 中編譯此程式,將出現一個錯誤,指示 szTarget 附近的資料已被破壞。這是因為編譯器引數 /GS 在起作用。它檢測到一個基於堆疊的緩衝區溢位,並中止了應用程式。

  可以使用以下程式碼來解決這個問題:

  char szTarget[12];

  char *s = "Hello, World";

  strncpy(szTarget,s,sizeof(szTarget));

  strncat(szTarget,s,strlen(szTarget) - strlen(s));

  但程式中仍然存在一個頑固的錯誤。如果目標緩衝區的長度正好等於源緩衝區的長度,那麼許多 n 版本的函式不會使目標緩衝區以空字元結束,這使得 strlen(szTarget) 有可能返回一個大於目標緩衝區長度的值,因為沒有末尾的“”字元。這樣的話,程式會變得混亂不堪!

  以下是一個以更加靈活的方式使用新執行庫的程式:

  char szTarget[12];

  char *s = "Hello, World";

  size_t cSource = strlen_s(s,20);

  strncpy_s(temp,sizeof(szTarget),s,cSource);

  strncat_s(temp,sizeof(szTarget),s,cSource);

  其中的兩個新增加的函式 strncpy_s 和 strncat_s 具有類似的特徵:

  • 它們都返回錯誤程式碼 (errno_t),而不返回指標。

  • 目標緩衝區 (char *)。

  • 目標緩衝區的總的字元計數 (size_t)。

  • 源緩衝區 (const char *)。

  • 源緩衝區總的字元計數 (size_t)。

  記錄兩個緩衝區的計數,分別用於每個緩衝區。沒有必要跟蹤處於變化狀態的目標緩衝區計數,雖然這一任務肯定較容易完成。此外還有其他引人入勝的特性。這兩個函式都是以空字元來結束字串,但以下功能是我特別看重的。請檢視一下我在前面“發現安全漏洞”部分中所提供的程式碼示例:

  void noOverflow(char *str)

  {

  char buffer[10];

  strncpy(buffer,str,(sizeof(buffer)-1));

  buffer[(sizeof(buffer)-1)]=0;

  /* 上面兩行程式碼用於避免緩衝區溢位 */

  }

  我在 2003 年 12 月釋出的一篇文件中發現了這些程式碼,這篇文件來自一個大型的跨國軟體公司(但不是 Microsoft),它用來向開發人員說明編寫安全程式碼的優點。這段程式碼的問題是,它存在一個很明顯的安全漏洞。如果 *str 指向 NULL,那麼 strncpy 在複製 NULL 指標時將出現錯誤!在各種開放原始碼的軟體中所使用的 strlcat 存在同樣的問題,但 strncat_s 不是這樣。

  strncat_s 不會出現錯誤的原因在於,所有更新的執行庫函式都會對輸入的引數執行更為嚴格的檢查。以下是 strncat_s 函式中引數有效性驗證部分的內容:

  /* 驗證部分 */

  _VALIDATE_RETURN_ERRCODE(front != NULL, EINVAL);

  _VALIDATE_RETURN_ERRCODE(sizeInTChars > 0, EINVAL);

  _VALIDATE_RETURN_ERRCODE(back != NULL || count == 0, EINVAL);

  驗證宏語句為:

  #define _VALIDATE_RETURN_ERRCODE( expr, errorcode

  {

  

  _ASSERTE( ( expr ) );

  

  if ( !( expr ) )

  {

  errno = errorcode;

  

  _INVALID_PARAMETER(expr);

  

  return ( errorcode );

  

  }

  

  }

  _INVALID_PARAMETER 用於在出錯後進行的除錯中提供檔案的有關資訊,以幫助使用者除錯程式碼。

  在學校,老師總是教導我們要檢查函式引數。現在,這項工作將由 CRT 來完成。實現這一飛躍僅僅用了二十年。

  您應該清楚的一點是,strsafe 函式(比如,StringCchCopy 和 StringCchCat)所執行的操作與 strncpy_s 和 strncat_s 函式是不同的。strncat_s 函式在檢測到錯誤之後,會將字串設定為 NULL。但是在預設情況下, strsafe 函式將向目標填充儘可能多的資料,然後以 NULL 結束此字串。可以在 strsafe 函式中加入以下程式碼來模仿這一操作:

  StringCchCatEx(dst,sizeof(dst)/sizeof(dst[0]),src,NULL,NULL,STRSAFE_NULL_ON_FAILURE)

  其他用於操作緩衝區的函式也具有同樣的行為,這些函式包括各種 printf 和 scanf 函式、mbstowcs、strerror、_strdate 和 _strtime、asctime 以及 ctime 函式等。對於緩衝區操作函式以外的函式也進行了更新,這些函式包括 _makepath、_splitpath、getenv、rand 以及許多其他函式。

  calloc 函式也是一個有趣的函式。如果 size * num 超出了 2^32,很容易導致出現整數溢位的錯誤,更新後的 calloc 函式驗證計算是否未溢位:

  /* 確保 (size * num) 未

  

·上一篇:

·下一篇:
 
     最新更新
·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·


| | | | | | |

Copyright © 2004 - 2007 All Rights Reserved

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10763080/viewspace-970286/,如需轉載,請註明出處,否則將追究法律責任。

面對惡意程式碼,我們告別以前處理的方法(轉)
請登入後發表評論 登入
全部評論

相關文章