You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在C#中不加载DLL/EXE验证其是否由微软签名?

在C#中无需加载文件验证微软签名的DLL/EXE

为什么你的现有方法返回null?

首先得明确:你之前用的程序集签名检查方法(比如Assembly.GetName().GetPublicKeyToken()这类)只适用于**.NET托管程序集**,但微软发布的大部分系统文件(像kernel32.dlluser32.dll)都是原生PE文件,根本不是.NET代码。用针对托管程序集的方法去读取原生文件,自然会返回null,这是完全正常的行为。

要验证这类原生Windows二进制文件的签名,必须用Windows系统自带的数字签名验证API,而不是.NET程序集专属的方法。

解决方案:用Windows Wintrust API实现无加载验证

Windows提供了Wintrust API来验证PE文件的数字签名,而且不需要把文件加载到内存里。我们可以通过C#的P/Invoke直接调用这些API完成验证。

核心步骤

  1. 调用WinVerifyTrust函数检查文件的签名链是否完整有效
  2. 验证签名证书是否属于微软(通过证书的颁发者、主题信息判断)

C#代码实现

先定义需要的P/Invoke结构体和函数(和Windows API类型一一对应):

using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;

public static class SignatureValidator
{
    // Wintrust API所需的常量
    private const int WTD_CHOICE_FILE = 1;
    private const int WTD_STATEACTION_VERIFY = 0;
    private const int WTD_REVOKE_NONE = 0;
    private const int WTD_UI_NONE = 2;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct WINTRUST_FILE_INFO
    {
        public uint cbStruct;
        public string pcwszFilePath;
        public IntPtr hFile;
        public IntPtr pgKnownSubject;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct WINTRUST_DATA
    {
        public uint cbStruct;
        public IntPtr pPolicyCallbackData;
        public IntPtr pSIPClientData;
        public int dwUIChoice;
        public int fdwRevocationChecks;
        public int dwUnionChoice;
        public IntPtr pFile;
        public int dwStateAction;
        public IntPtr hWVTStateData;
        public string pwszURLReference;
        public uint dwProvFlags;
        public uint dwUIContext;
        public IntPtr pSignatureSettings;
    }

    [DllImport("wintrust.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int WinVerifyTrust(IntPtr hWnd, IntPtr pgActionID, ref WINTRUST_DATA pWinTrustData);

    /// <summary>
    /// 检查文件是否有有效的数字签名
    /// </summary>
    /// <param name="filePath">目标DLL/EXE的路径</param>
    /// <returns>签名有效返回true</returns>
    public static bool HasValidSignature(string filePath)
    {
        var fileInfo = new WINTRUST_FILE_INFO
        {
            cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)),
            pcwszFilePath = filePath
        };

        var trustData = new WINTRUST_DATA
        {
            cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA)),
            dwUIChoice = WTD_UI_NONE, // 不弹出任何UI提示
            fdwRevocationChecks = WTD_REVOKE_NONE, // 跳过吊销检查(可按需调整)
            dwUnionChoice = WTD_CHOICE_FILE,
            pFile = Marshal.AllocHGlobal(Marshal.SizeOf(fileInfo)),
            dwStateAction = WTD_STATEACTION_VERIFY
        };

        Marshal.StructureToPtr(fileInfo, trustData.pFile, false);

        try
        {
            // WinVerifyTrust返回0表示签名有效
            int result = WinVerifyTrust(IntPtr.Zero, IntPtr.Zero, ref trustData);
            return result == 0;
        }
        finally
        {
            Marshal.FreeHGlobal(trustData.pFile);
        }
    }

    /// <summary>
    /// 检查文件是否由微软签名
    /// </summary>
    /// <param name="filePath">目标DLL/EXE的路径</param>
    /// <returns>是微软签名且链有效返回true</returns>
    public static bool IsSignedByMicrosoft(string filePath)
    {
        if (!HasValidSignature(filePath))
            return false;

        try
        {
            // 从文件中提取签名证书
            var cert = new X509Certificate2(X509Certificate2.CreateFromSignedFile(filePath));
            
            // 检查证书颁发者和主题是否包含微软标识,也可以用证书指纹做更严格匹配
            bool isMicrosoftCert = cert.Issuer.Contains("Microsoft Corporation") && 
                                   cert.Subject.Contains("Microsoft Corporation");
            
            // 验证证书链(可选但推荐)
            var chain = new X509Chain();
            chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // 为了速度关闭吊销检查,严格场景可改为Online
            bool isChainValid = chain.Build(cert);

            return isMicrosoftCert && isChainValid;
        }
        catch
        {
            // 任何异常都返回false,也可以根据需求添加日志
            return false;
        }
    }
}

使用示例

string targetFile = @"C:\Windows\System32\kernel32.dll";
bool isMicrosoftSigned = SignatureValidator.IsSignedByMicrosoft(targetFile);
Console.WriteLine($"该文件是否由微软签名:{isMicrosoftSigned}");

关键说明

  • 无需加载文件WinVerifyTrustX509Certificate2.CreateFromSignedFile都是直接读取磁盘文件内容验证,不会把PE文件加载到内存,避免了恶意代码执行风险。
  • 证书验证灵活性:示例中用"Microsoft Corporation"做模糊匹配,如果你需要更严格的验证,可以改用微软根证书的指纹来匹配。
  • 吊销检查:代码默认关闭了吊销检查以提升速度,如果需要确保证书未被吊销,可以修改常量为WTD_REVOKE_WHOLECHAIN,同时把证书链的吊销模式改为X509RevocationMode.Online(需要网络支持)。
  • 兼容.NET程序集:如果需要同时支持原生和托管文件,可以做个兼容逻辑:先尝试用Assembly相关方法检查,若返回null,再用上述Wintrust方法验证。

内容的提问来源于stack exchange,提问作者vishal

火山引擎 最新活动