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