使用 Visual Studio 对源代码文件进行哈希处理以确保文件完整性
對所有編譯的軟件語言來說,將人類可讀代碼轉換成計算機可讀代碼都是一項軟件保障挑戰: 用戶如何有信心相信在其計算機上運行的軟件程序是根據開發者創建的同一源代碼文件生成的呢? 這不一定,即使源代碼文件經過行業專家評審,也不例外,因為可能出現開放源代碼軟件的情況。軟件保障的核心是信任經過評審的源代碼文件是生成可執行文件的相同源代碼文件。
?
在編譯和鏈接過程中,使用特定編程語言(C#、C++、Objective C、Java 等)編寫的一組源代碼文件被轉換成二進制可執行文件,以供在特定體系結構(如 x86、x64、ARM)的計算機上運行。但這種轉換可能不具有決定性作用。兩組不同的源代碼文件可能被轉換成兩組位完全相同的可執行文件。有時,這是有意而為之。源代碼文件內空格或文本注釋不一致不得影響編譯器生成的二進制代碼。另一方面,同一組源代碼文件也可能會因不同的編譯過程而被轉換成不同的可執行文件。無論屬于上述哪種情況,問題都在于確定性,即無法確定擁有的文件是否就是所需的文件。
為了解決這個問題,不妨在編譯過程中使用 Visual Studio 編譯器對源代碼文件進行哈希處理。將編譯器生成的哈希值與經過檢查的源代碼文件生成的哈希值進行匹配,可以驗證可執行代碼是否的確是由特定的源代碼文件生成而來。這顯然會讓用戶很受益(實際上,如果其他編譯器的供應商也采用了類似方法,那么用戶會進一步受益)。本文介紹了用于選擇哈希算法的新 Visual Studio 開關、此類哈希可能適用的應用場景,以及如何使用 Visual Studio 生成源代碼哈希值。
在編譯過程中生成強哈希值
程序數據庫 (PDB) 文件是一個單獨數據文件,存儲用于調試二進制可執行文件的信息。Microsoft 最近將其各種編譯器文件哈希運算(如 PDB 文件中嵌入的源哈希值)更新為使用強加密算法。
???
本機代碼編譯器:Visual Studio 2015 本機 C/C++ 編譯器 cl.exe 隨附一個新開關 /ZH:{MD5|SHA_256},用于為編譯器選擇其他哈希算法,從而對源代碼文件進行哈希處理。默認開關為 MD5,雖然已知其更容易導致沖突,但仍采用默認開關,因為從計算層面來講它的哈希值生成成本更低。使用新的開關,編譯器可以實現密碼強度高于 MD5 的 SHA-256 選項。
如果源代碼文件的 SHA-256 哈希值與二進制可執行文件的 PDB 文件中存儲的 SHA-256 哈希值一致,就可以確定可執行文件是由相同的源代碼文件編譯而成,這樣所有利益干系人便可以對二進制可執行文件有信心。實際上,二進制可執行文件的 PDB 文件中存儲的一組 SHA-256 哈希值全都成為二進制可執行文件的“生成證明”中的標識符,因為這些標識符由“生成”二進制可執行文件的編譯器進行注冊。??
使用調試接口訪問 SDK (bit.ly/2gBqKDo),可以輕松創建簡單的工具,如調試信息轉儲程序 cvdump.exe(可從?bit.ly/2hAUhyy?中獲取此程序及其源代碼)。可以使用 cvdump.exe 的 -sf 開關查看模塊(使用本地生成計算機中的完整路徑名稱)及其 MD5 或 SHA-256 哈希值的列表,如圖 1?中的命令窗口所示。
圖 1:使用 cvdump.exe 查看模塊及其哈希值
使用舊版 cvdump.exe 查看同一 PDB 文件時,我看到的文字是“0x3”,而不是“SHA_256”。“0x3”值是“SHA_256”的枚舉值,更新后的 cvdump.exe 知道如何進行解析。它也是調試接口訪問 SDK 的 IDiaSourceFile::get_checksumType 方法返回的同一枚舉值。
托管代碼編譯器:默認情況下,Visual Studio 2015 托管代碼 C# 編譯器 csc.exe 使用 SHA-1 加密算法計算源文件校驗和哈希值,以存儲在 PDB 文件中。然而,csc.exe 現在支持使用新的可選“/checksumalgorithm”開關來指定 SHA-256 算法。若要切換到 SHA-256 算法,請使用此選項編譯當前目錄中的所有 C# 文件,然后將調試信息(包括源文件列表和 SHA-256 哈希值)放入 PDB文件中:
csc /checksumalgorithm:SHA256 /debug+ *.cs
可從?github.com/dotnet/roslyn?中獲取屬于 .NET 編譯器平臺 (Roslyn) 開放源代碼項目的 csc.exe。有關對文件中 SHA-256 源文件調試校驗和算法命令行選擇器的支持,請訪問?bit.ly/2hd3rF3。
Visual Studio 2015 csc.exe 只與 Microsoft .NET Framework 4 或更高版本的可執行文件兼容。另一個用于生成低于版本 4 的可執行文件的 Visual Studio 2015 .NET Framework 編譯器不支持 /checksumalgorithm 開關。
托管代碼 PDB 文件存儲數據的方式不同于本機代碼 PDB 文件。可使用 Microsoft DiaSymReader 互操作接口和實用工具來讀取托管代碼 PDB 文件,而不是使用調試接口訪問 SDK。可從?bit.ly/2hrLZJb?中以 NuGet 包的形式獲取 Microsoft DiaSymReader。
???
Roslyn 項目包括 pdb2xml.exe 實用工具,可從?bit.ly/2h2h596?中獲取此工具及其源。此實用工具以 XML格式顯示 PDB 的內容。例如,圖 2?中的各段列出了用于編譯托管代碼可執行文件的 C# 源代碼文件。??
圖 2:以 XML 格式顯示托管代碼 PDB
checkSumAlgorithmId 字段中的“8829d00f-11b8-4213-878b-770e8597ac16”GUID 表明,校驗和字段中的值是名稱字段中引用的文件的 SHA-256 哈希值。可移植 PDB 格式規范 v0.1 (bit.ly/2hVYfEX) 中定義了此 GUID。??
編譯器對 SHA-256 的支持
以下 Visual Studio 2015 編譯器支持對源代碼文件進行 SHA-256 哈希處理:?
?????
cl.exe /ZH:SHA_256
ml.exe /ZH:SHA_256
ml64.exe /ZH:SHA_256
armasm.exe -gh:SHA_256
armasm64.exe -gh:SHA_256
csc.exe /checksumalgorithm:SHA256
可在 Visual Studio 2015 的“VS2015 開發者命令提示符”命令窗口中創建這些編譯器。
不面向 Windows 平臺的編譯器通常不使用 PDB 文件存儲其調試信息。這些編譯器通常在編譯期間同時生成兩個可執行文件,一個是未刪除源信息的可執行文件,另一個是已刪除源信息的可執行文件 (bit.ly/2hIfvx6)。所有調試信息都存儲在未刪除源信息的可執行文件中,而已刪除源信息的可執行文件則不包含任何詳細的調試信息。未刪除源信息的可執行文件可能適合存儲可執行文件的已處理源代碼文件的 SHA-256 哈希值。我們正打算聯系其他這些編譯器的創建者,確定最適合其編譯器的方法,以便使用這些編譯器的非 Windows 軟件(如 Office for Android、Office for iOS 或 Office for Mac)可以和 Windows 軟件一樣受益。
用例應用場景???
現在,我們來看一下源文件哈希值可能適用的一些應用場景。
???????
檢索可移植可執行 (PE) 二進制文件的已編入索引源文件:Ssindex.cmd 腳本 (bit.ly/2haI0D6) 是一種實用工具,可用于生成簽入源控件的(已編入索引)源文件列表,以及每個文件的版本信息,以供存儲在 PDB 文件中。如果 PDB 文件包含此版本控制信息,可以結合使用 srctool 實用工具 (bit.ly/2hs3WXY) 及其 -h 選項來顯示信息。由于已編入索引的源文件也將其哈希值嵌入 PDB 文件,因此這些哈希值可用于在檢索期間驗證源文件,如知識庫文章 3195907 (bit.ly/2hs8q0u)“How To Retrieve Indexed Source Files of a Portable Executable Binary File”(如何檢索可移植可執行二進制文件的已編入索引源文件)中所述。 具體來說,如果哈希值不一致,則表明 PE/PDB 對生成期間或源控件系統中的某個環節可能出現了問題。這可能有必要執行進一步調查。相比之下,如果哈希值一致,則充分表明檢索到的已編入索引源文件是用于編譯 PE/PDB 對。??
??????
匹配源文件靜態分析器生成的哈希值:現在,使用自動工具來評估軟件質量是常事,就像 Microsoft 安全開發生命周期 (SDL) 針對實現階段建議的一樣 (bit.ly/-29qEfVd)。具體來說,源文件靜態分析器用于掃描目標源代碼文件,以評估軟件質量的許多不同方面。這些靜態分析器通常在掃描目標源代碼文件后立即生成相應的實時結果。在靜態分析器掃描各個源代碼文件時,也是生成每個在掃描源代碼文件的強哈希值 (SHA-256) 的絕佳機會。實際上,bit.ly/2ibkbwz?中開放源代碼項目提出了靜態分析結果交換格式 (SARIF),這種格式提供了靜態分析結果中的特定位置,以供靜態分析器生成掃描的目標源代碼文件及其 SHA-256 哈希值。?
以 PE 文件為例,假設可獲得以下內容:
1. 由編譯器生成的相應 PDB 文件中的編譯源文件哈希列表。
2. 由靜態分析器生成的相應靜態分析結果中的掃描源文件哈希列表。
在此應用場景中,可以評審并驗證這兩個文件哈希列表是否匹配。如果匹配,表明靜態分析器已掃描源文件來評估某方面的質量,無需重新掃描源文件。以前沒有文件哈希列表,可能就需要重新掃描,以確保靜態分析器進行了正確的評估。??
在軟件更新或修補程序開發過程中更快速地執行健全性檢查:如果需要發布軟件更新來修復源文件靜態分析器在已發布產品中發現的質量問題,靜態分析器應報告待定更新程序的源代碼文件中不存在發現的質量問題。這個報告至少將確認更新程序能否有效解決原始質量問題。也就是說,它將驗證軟件更新的預期用途。如果需要,你或安全評審員可以執行下列步驟來實施快速驗證:?
1. 確認原始靜態分析器報告是否發現相關質量問題。
2. 確認原始靜態分析器報告是否包括存在質量問題的源文件的哈希值。
3. 將原始靜態分析器報告中發現的文件哈希值與已發布產品版本的源文件的哈希值進行匹配。
4. 使用同一靜態分析器掃描更新程序的源代碼文件,生成更新后的靜態分析器報告。
5. 確認更新程序的靜態分析器報告中是否不存在之前發現的質量問題。
6. 將更新后的靜態分析器報告中的文件哈希值與更新程序的源文件的哈希值進行匹配。
在執行這些驗證步驟期間,無需訪問原始發布產品或更新程序的實際源代碼文件。?
構造兩個軟件版本之間的源代碼增量:評審一組完整的源代碼可能需要一些時間。然而,在某些情況下,不一定非要在更改源代碼后評審全部源代碼。因此,可能要求只評審源代碼增量。這樣的要求當然合理,因為重復分析上次評審后沒有變化的所有部分并不合理。
??????
以前沒有源代碼文件的密碼強度高的哈希值,很難精確構造增量子集。即使你有增量子集可以提供,行業專家也可能對你能否準確創建增量子集沒有什么信心。但現在情況已不再如此。借助源代碼文件的密碼強度高的哈希值,可以執行下列步驟來創建增量子集:
1. 獲取原始產品版本的所有源代碼文件的哈希值池(例如:池 X)。
2. 精確復制文件目錄(例如:目錄 A),其中包含后續產品版本的源代碼登記,將根據其構造增量子集。
3. 準備用于僅保留增量文件子集的最終文件目標文件夾(例如:目錄 B)。
4. 整理目錄 A 中的所有文件:
5. ? ? ? ? a.如果文件的哈希值與池 X 中的哈希值一致,什么也不做,匹配下一個文件。
6. ? ? ? ? b.如果文件的哈希值與池 X 中的哈希值不一致,將文件復制到目錄 B 中,然后匹配下一個文件。
7. 確認目錄 B 中所有文件的哈希值與后續產品版本的源文件的相應哈希值一致。??
8. 讓目錄 B 的內容成為后續產品版本的增量源文件子集。
?????
生成哈希值
現在,我們來了解一下如何使用 Visual Studio 編譯器對文件進行哈希處理。為此,我將以聯機 Visual Studio 文檔 (bit.ly/2haPupF) 中的“Hello, World”應用程序創建應用場景為例:
1. 介紹在輸出 PDB 文件中在何處可以找到編譯的源文件的哈希值。
2. 使用 certutil 工具 (bit.ly/2hIrnPR) 計算源文件哈希值,與 PDB 文件中的哈希值進行匹配。
?? ? ??
首先,我在 Visual Studio 2015\Projects 文件夾中新建了一個 Win32HelloWorld 應用程序項目。在這個 Win32HelloWorld 項目中,只有一個 C++ 源文件 Win32HelloWorld.cpp,如圖 3?所示。
圖 3:Win32HelloWorld.cpp
如你所見,Win32HelloWorld.cpp 包含用于顯示“Hello”文字的主函數。
生成 Win32HelloWorld 項目后,我在 Visual Studio 2015\Projects\W32HelloWorld\x64\Debug 文件夾中生成了 W32HelloWorld.exe 和 W32HelloWorld.pdb 文件。
對 W32Hello-World.pdb 文件結合使用 cvdump 工具和 -sf 選項在輸出中顯示 Win32HelloWorld.cpp 文件及其 MD5 哈希值,如圖 4?所示。
圖 4:顯示 Win32HelloWorld.cpp 及其 MD5 哈希值的 cvdump 輸出
此哈希值是 MD5,因為 MD5 是 Visual Studio 2015 編譯器 cl.exe 的默認算法。若要將源文件哈希算法切換成 SHA-256,我需要向 cl.exe 提供 /ZH:SHA_256 選項。為此,我可以在 Win32HelloWorld 項目“屬性”頁上的“其他選項”框中添加“/ZH:SHA_256”,如圖 5?所示。
圖 5:將源文件哈希算法切換成 SHA-256
在 Visual Studio 中重建后,我在 Visual Studio 2015\Projects\W32HelloWorld\x64\Debug 文件夾中生成了 W32HelloWorld.exe 和 W32HelloWorld.pdb 的新 PE/PDB 對。現在,對新的 W32HelloWorld.pdb 文件結合使用 cvdump 工具和 -sf 選項在輸出中顯示 Win32HelloWorld.cpp 文件及其 SHA-256 哈希值,如圖 6?所示。
圖 6:顯示 Win32HelloWorld.cpp 及其 SHA-256 哈希值的 cvdump 輸出
現在,我可以返回到 Visual Studio 2015\Projects\W32HelloWorld\W32HelloWorld 文件夾中的 W32HelloWorld.cpp 文件,查看它的 SHA-256 哈希值。對于 SHA-256,對 Win32HelloWorld.cpp 文件結合使用 certutil 工具和 -hashfile 謂詞生成 SHA-256 哈希值,如圖 7?所示。
圖 7:使用 Certutil 生成 SHA-256 哈希值
很顯然,此哈希值與 W32Hello-World.pdb 文件中記錄的 SHA-256 值一致。這充分表明 W32HelloWorld.exe 應用程序確實是按預期由 Win32HelloWorld.cpp 文件編譯而成。
若要詳細了解適用于本機代碼和托管代碼 PE/PDB 文件對的相關公共工具,請參閱知識庫文章 3195907 (bit.ly/2hs8q0u)“How To Retrieve Indexed Source Files of a Portable Executable Binary File”(如何檢索可移植可執行二進制文件的已編入索引源文件)。
總結
我希望通過本文你可以了解到,更緊密地關聯源代碼文件和用其編譯的 PE 文件可能會帶來的一些好處。可以在編譯過程中使用最強的可用哈希算法 SHA-256 讓編譯器對源代碼文件進行哈希處理,從而更緊密地關聯兩者。實際上,編譯器生成的源代碼文件的實際哈希值成為了用于編譯可執行文件的源代碼文件的唯一標識符。
了解這些唯一標識符的值后,就可以在不同軟件開發生命周期計劃中使用它們跟蹤、處理和控制與特定可執行文件有著緊密聯系的源代碼文件,從而讓最終用戶對可執行文件更有信心。
Mike Lai?剛剛迎來他在 Microsoft 工作的第 20 個年頭。他很感謝 Microsoft 能夠提供各種機會來推動許多產品在功能及工程方面取得進步。他想要感謝可信任計算部門現任管理層能夠耐心等待他的思想變成熟并逐步融入已發布的產品,另外還要感謝他們支持加入信息和通信技術安全標準組織。
衷心感謝以下 Microsoft 技術專家對本文的審閱:?Scott Field、Mike Grimm、Sue Hotelling、Ariel Netz、Richard Ward?和?Roy Williams
原文地址:https://msdn.microsoft.com/en-us/magazine/mt795185
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的使用 Visual Studio 对源代码文件进行哈希处理以确保文件完整性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Docker基础入门及示例
- 下一篇: .NET Core Tools 1.0