在开源工程中找到一段校验PE文件签名的代码, 因为以前的工程没有用到过PE文件签名校验, 拿来做个实验.

这段代码比MS给的签名验证的Demo好,对MS程序和第三方程序做了区分.

/// @file       srcVerifyFileSignature.cpp
/// @brief      测试 校验文件内建签名

#include "stdafx.h"
#include <windows.h>
#include <Wintrust.h>
#include <Mscat.h>
#include <SoftPub.h>
#include <tchar.h>
#include <Strsafe.h>

#pragma comment(lib, "Wintrust.lib")

/// 被测试校验签名的PE文件全路径

/// 有签名的系统程序, 校验签名通过
#define G_FILE_TOBE_VERIFY_SIGN L"C:\\Windows\\System32\\notepad.exe"

/// 没有加签名的第三方程序, 校验签名失败
// #define G_FILE_TOBE_VERIFY_SIGN  L"d:\\LsTemp\\srcVerifyFileSignature_X64.exe"

/// 第三方程序带签名, 从360目录里找了个软件管家的PE文件
// #define G_FILE_TOBE_VERIFY_SIGN  L"d:\\LsTemp\\SoftManager.exe"

/// @fn     VerifyFileSignatureBuildIn
/// @brief  调用MS接口, 校验文件内建签名是否正确
/// @param  wchar_t * pFilePathName, 被校验的文件全路径名称
/// @return BOOL
/// @retval TRUE
///         校验成功
/// @retval 其他
///         校验失败
BOOL VerifyFileSignatureBuildIn(wchar_t * pFilePathName);

int _tmain(int argc, _TCHAR* argv[])
{
    BOOL    bRc =   TRUE;

    bRc = VerifyFileSignatureBuildIn(G_FILE_TOBE_VERIFY_SIGN);
    _tprintf(L"%s : VerifyFileSignatureBuildIn(%s)\r\n",
        bRc ? L"TRUE" : L"FALSE",
        G_FILE_TOBE_VERIFY_SIGN);

    /// run result
    /**
    x86和x64程序也可以调用此接口校验目标程序

    已经测试了X86版PE输出校验X64版目标程序的情况
    TRUE : VerifyFileSignatureBuildIn(C:\Windows\System32\notepad.exe)

    /// 带签名的非MS程序
    TRUE : VerifyFileSignatureBuildIn(d:\LsTemp\SoftManager.exe)

    /// 对于没有签名的程序, 校验的结果是FALSE
    FALSE : VerifyFileSignatureBuildIn(d:\LsTemp\
        srcVerifyFileSignature_X64.exe)

    /// @todo 没有测试有签名的程序被篡改的情况
    */

    _tprintf(L"END, press any key to quit\r\n");
    getwchar();
    return 0;
}

BOOL VerifyFileSignatureBuildIn(wchar_t * pFilePathName)
{
    BOOL        bRc         =   FALSE;
    HANDLE      hFile       =   INVALID_HANDLE_VALUE;
    HCATADMIN   hCatAdmin   =   NULL;
    GUID        GuidAction  =   WINTRUST_ACTION_GENERIC_VERIFY_V2;
    long        lRc         =   NULL;

    WINTRUST_DATA           wd          =   {0};
    WINTRUST_FILE_INFO      wfi         =   {0};
    WINTRUST_CATALOG_INFO   wci         =   {0};
    CATALOG_INFO            ci          =   {0};
    HCATINFO                hCatInfo    =   NULL;

    BYTE *      pbHash          =   NULL;   ///< 容纳Hash值
    DWORD       dwLenHash       =   0;
    DWORD       dwIndex         =   0;
    DWORD       dwPosCur        =   0;
    wchar_t *   pcwszMemberTag  =   NULL;   ///< wfi.pcwszMemberTag

    if (!CryptCATAdminAcquireContext(&hCatAdmin, NULL, 0))
        return FALSE;

    hFile = CreateFileW(
                    pFilePathName,
                    GENERIC_READ,
                    FILE_SHARE_READ,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL );
    if (INVALID_HANDLE_VALUE == hFile)
    {
        bRc = FALSE;
        goto END_VerifyFileSignatureBuildIn;
    }

    /// 得到返回的hash值缓冲区大小
    bRc = CryptCATAdminCalcHashFromFileHandle(hFile, &dwLenHash, pbHash, 0);
    if (!bRc)
    {
        bRc = FALSE;
        goto END_VerifyFileSignatureBuildIn;
    }

    /// 构造字节型的HASH值缓冲区
    pbHash = new BYTE[dwLenHash + 1];
    if (NULL == pbHash)
    {
        bRc = FALSE;
        goto END_VerifyFileSignatureBuildIn;
    }

    ::ZeroMemory(pbHash, dwLenHash + 1);

    /// 计算文件HASH值
    bRc = CryptCATAdminCalcHashFromFileHandle(hFile, &dwLenHash, pbHash, 0);
    if (!bRc)
    {
        bRc = FALSE;
        goto END_VerifyFileSignatureBuildIn;
    }

    /// 从HASH值中枚举目录
    /// @note 这个目录(Catalog)的含义?
    /// 经测试, MS签名的程序都可以按照目录找到
    /// 那目录的含义就是带MS签名的HASH本地库
    /// MS在本地已经存储了该系统文件的HASH值
    hCatInfo = CryptCATAdminEnumCatalogFromHash(
                    hCatAdmin,
                    pbHash,
                    dwLenHash,
                    0,
                    NULL);

    /// vs2010 WinVerifyTrust 例子中, 给出的签名校验方式
    /// 是按照文件方式 WTD_CHOICE_FILE 方式来校验签名的
    /// 如果将被校验的文件, 分为MS签名和非MS签名, 可能会提高效率

    if (NULL == hCatInfo)
    {
        /// 按照文件来校验HASH

        /// 未加签名的程序会进这里
        /// 第三方程序(非MS出品)加签名, 也会进这里

        wfi.cbStruct       = sizeof(WINTRUST_FILE_INFO);
        wfi.pcwszFilePath  = pFilePathName;
        wfi.hFile          = NULL;
        wfi.pgKnownSubject = NULL;

        wd.cbStruct            = sizeof(WINTRUST_DATA);
        wd.dwUnionChoice       = WTD_CHOICE_FILE;
        wd.pFile               = &wfi;
        wd.dwUIChoice          = WTD_UI_NONE;
        wd.fdwRevocationChecks = WTD_REVOKE_NONE;
        wd.dwStateAction       = WTD_STATEACTION_IGNORE;
        wd.dwProvFlags         = WTD_SAFER_FLAG;
        wd.hWVTStateData       = NULL;
        wd.pwszURLReference    = NULL;
    }
    else
    {
        /// 按照目录来校验签名

        /// 已经签名未篡改的PE文件会到这里
        /// 在文件中能找到计算出的HASH值
        CryptCATCatalogInfoFromContext(hCatInfo, &ci, 0);
        wci.cbStruct             = sizeof(WINTRUST_CATALOG_INFO);
        wci.pcwszCatalogFilePath = ci.wszCatalogFile;
        wci.pcwszMemberFilePath  = pFilePathName;

        /// 一个字节的HASH值 => 2个wchar_t的HASH字符串
        /// e.g. 0x11 => L"11"
        /// 缓冲区尾部留2个L'\0'的位置, 留一个L'\0'会报错的
        pcwszMemberTag = new wchar_t[(dwLenHash + 1) * 2];
        if (NULL == pcwszMemberTag)
        {
            bRc = FALSE;
            goto END_VerifyFileSignatureBuildIn;
        }

        ::ZeroMemory(pcwszMemberTag, sizeof(wchar_t) * (dwLenHash + 1) * 2);
        for (dwIndex = 0; 
            dwIndex < dwLenHash; 
            dwIndex++, dwPosCur += sizeof(wchar_t))
        {
            swprintf_s( &pcwszMemberTag[dwIndex * 2],
                        sizeof(wchar_t) * 2, 
                        L"%02X", 
                        pbHash[dwIndex]);
        }

        wci.pcwszMemberTag       = pcwszMemberTag;

        wd.cbStruct            = sizeof(WINTRUST_DATA);
        wd.dwUnionChoice       = WTD_CHOICE_CATALOG;
        wd.pCatalog            = &wci;
        wd.dwUIChoice          = WTD_UI_NONE;
        wd.fdwRevocationChecks = WTD_STATEACTION_VERIFY;
        wd.dwProvFlags         = 0;
        wd.hWVTStateData       = NULL;
        wd.pwszURLReference    = NULL;
    }

    /**
    Note  The return value is a LONG, not an HRESULT as previously 
    documented. Do not use HRESULT macros such as SUCCEEDED to determine 
    whether the function succeeded. Instead, check the return value for 
    equality to zero.

    TRUST_E_SUBJECT_NOT_TRUSTED The subject failed the specified verification 
    action. Most trust providers return a more detailed error code that 
    describes the reason for the failure.

    TRUST_E_PROVIDER_UNKNOWN The trust provider is not recognized on this 
    system.

    TRUST_E_ACTION_UNKNOWN The trust provider does not support the specified 
    action.

    TRUST_E_SUBJECT_FORM_UNKNOWN The trust provider does not support the form 
    specified for the subject.
    */
    lRc = WinVerifyTrust(NULL, &GuidAction, &wd);
    bRc = (0 == lRc);

END_VerifyFileSignatureBuildIn:

    if (NULL != hCatInfo)
        CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0);

    if (NULL != hCatAdmin)
        CryptCATAdminReleaseContext(hCatAdmin, 0);

    if ((NULL != hFile)
        && (INVALID_HANDLE_VALUE != hFile))
    {
        CloseHandle(hFile);
        hFile = INVALID_HANDLE_VALUE;
    }

    if (NULL != pbHash)
    {
        delete []pbHash;
        pbHash = NULL;
    }

    if (NULL != pcwszMemberTag)
    {
        delete []pcwszMemberTag;
        pcwszMemberTag = NULL;
    }

    return bRc;
}


Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐