使用 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 FieldMike GrimmSue HotellingAriel NetzRichard Ward 和 Roy Williams


原文地址:https://msdn.microsoft.com/en-us/magazine/mt795185


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/326378.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php 输出01,php基础01_thinkphp输出Hello World-Go语言中文社区

用phpstorm和mamp搭建好php开发环境后,就来体验一下thinkPHP的输出过程1.下载thinkphp3.2.3版本框架,将其放入本地主机MyPhp文件夹中thinkphp3.2.3所包含的目录内容如下:目录对应文件为:composer.json:PHP组件的依赖管理器index.p…

人脸识别活体检测之眨眨眼和张张嘴

【这段时间有点忙,终于截止今天2018.06.22完成了人脸识别的最后一道程序——活体检测之眨眨眼和张张嘴】 关于人脸识别的内容我之前也写过好几篇博文,其中有: {java实现人脸识别源码} {C#winforms实现windows窗体人脸识别} {人脸识别活体检测…

工作效益问题

图解 代码实现 package com.kuang.study.lanqiao;public class Main {public static void main(String[] args) {int n8;//机器人个数int[] pre{0,0,0,0,1,0,2,3,5};//选择这个工作后的其那句最近工作下标int[] profit {5,1,8,4,6,3,2,4}; //工作收益int opt[]new int[n1];op…

amazon php 空间,(四)Amazon Lightsail 部署LAMP应用程序之扩展PHP前端

扩展PHP前端既然PHP前端和数据库是分开的,您将为Web层添加可伸缩性和容错性: 在以下步骤,您将获取Web前端实例的快照,并从该快照部署另外2个Web层实例。最终,您将在三个Web实例前面添加一个负载均衡器。至此,您将拥有一…

人脸识别活体检测之张张嘴和眨眨眼——Landmark

/** * Title: Landmark.java * Package org.entity * Description: TODO该方法的主要作用: * author A18ccms A18ccms_gmail_com * date 2018-6-8 上午10:39:01 * blog https://blog.csdn.net/qq_34137397 * version V1.0 */ package org.entity;import java.…

相邻数字+(正月点灯笼的动态规划2)(递归+DP)---JAVA

思路分析 代码实现 递归实现 package com.kuang.study.lanqiao;public class Main {public static void main(String[] args) {int arr[]{1,2,4,1,7,8,3};int max d(arr,arr.length-1);System.out.println(max);}public static int d(int[] arr,int n){if(n0){return arr[0]…

.NET Core Tools 1.0 版本

与上周发布的Visual Studio 2017一起,微软还发布了.NET Core Tools 1.0。这些工具对所有.NET核心开发人员都非常有用,无论他们使用VS2017、Visual Studio for Mac、Visual Studio Code,还是基于他们自己选择的非微软IDE。 对于想要使用.NET C…

MySQL day()函数

转载自 MySQL day()函数 MySQL DAY函数简介 DAY函数返回给定日期的月份的日期部分。下面显示DAY函数的语法: DAY(date);DAY函数接受一个参数,它是您要获取月份日期的日期值。如果date参数为零,例如0000-00-00,则DAY函数返回0&…

matlab 的excel 对象,MATLAB 中的 COM 对象

COM concepts and an overview of COM support in MATLAB.Examples that show how to use COM interface with MATLAB.Before using COM objects, you must register their servers.如何创建 Microsoft 控件和 COM 服务器对象Pass data to and handle data from a COM object.L…

人脸识别活体检测之张张嘴和眨眨眼——Json_Parsing

/** * Title: Json_Parsing.java * Package org.util * Description: TODO该方法的主要作用: * author A18ccms A18ccms_gmail_com * date 2018-6-9 上午10:33:48 * blog https://blog.csdn.net/qq_34137397 * version V1.0 */ package org.util;import java.…

MySQL date_add()函数​​​​​​​

转载自 MySQL date_add()函数 MySQL DATE_ADD函数简介 DATE_ADD函数将间隔时间添加到DATE或DATETIME值。 下面说明了DATE_ADD函数的语法: DATE_ADD(start_date, INTERVAL expr unit);DATE_ADD函数有两个参数: start_date是DATE或DATETIME的起始值。…

交换数组中的两个元素

方法一 public static void swap(int[] arr,int i,int j){arr[i]arr[i]^arr[j];arr[j]arr[i]^arr[j];arr[i]arr[i]^arr[j];}方法二 public static void swap(int[] arr,int i,int j){int temparr[i];arr[i]arr[j];arr[j]temp;}

塑造Visual Studio的未来

类似于Windows Insider计划,Microsoft为Visual Studio也启动了一项提前体验计划。该计划被命名为Visual Studio预览,它允许任何对Visual Studio未来感兴趣的用户都能参与其中。Microsoft已经为Visual Studio的三个主要版本(社区版、专业版、企…

php划分年龄段,儿童时期是怎样划分年龄阶段的

儿童时期是人生开始的一个重要年龄阶段,一般是指从出生到15周岁。儿童时期生理、心理发育很快,不同的月龄和不同的年龄具有不同的生理与心理特征。因此,根据儿童各年龄时期的胜利与心理特征和不同的发展规律,进一步将儿童时期划分…

MySQL date_sub()函数

转载自 MySQL date_sub()函数 MySQL DATE_SUB函数简介 DATE_SUB()函数从DATE或DATETIME值中减去时间值(或间隔)。 下面说明了DATE_SUB()函数的语法: DATE_SUB(start_date,INTERVAL expr unit);DATE_SUB()函数接受两个参数: start_date是DATE或DATET…

php无限极菜单,无限极菜单的实现

摘要&#xff1a;<?php /** * Created by PhpStorm. * User: 12757 * Date: 2019/4/16 * Time: 18:32 */namespace app\admins\controller;use think\Controller;use Util\SysDb;class Menu extends Base{ public func/*** Created by PhpStorm.* User: 12757* Date: 20…

2016蓝桥杯省赛---java---C---8(冰雹数)

题目描述 任意给定一个正整数N&#xff0c; 如果是偶数&#xff0c;执行&#xff1a; N / 2 如果是奇数&#xff0c;执行&#xff1a; N * 3 1生成的新的数字再执行同样的动作&#xff0c;循环往复。通过观察发现&#xff0c;这个数字会一会儿上升到很高&#xff0c; 一会儿又…

临时表的软更新

当今绝大多数开发者都使用经典关系数据库来存储数据。尽管取而代之的无架构数据存储&#xff08;统称为“NoSQL 存储”&#xff09;在各种业务方案中都证明相当有效&#xff0c;但经典关系数据库是沿用了几十年且目前仍适用的方法。每次更新现有表记录时&#xff0c;都不会自动…

人脸识别活体检测之张张嘴和眨眨眼——readme

人脸检测返回参数详情 ———————————————————————————————————————————————————————————————————————————————————————————————— 字段 必选 类型 说明 face_num 是 int 检测…

MySQL date_format()函数

转载自 MySQL date_format()函数 MySQL DATE_FORMAT函数简介 要将日期值格式化为特定格式&#xff0c;请使用DATE_FORMAT函数。 DATE_FORMAT函数的语法如下&#xff1a; DATE_FORMAT(date,format);DATE_FORMAT()函数接受两个参数&#xff1a; date&#xff1a;是要格式化…