在开源工程中找到一段校验PE文件签名的代码, 因为以前的工程没有用到过PE文件签名校验, 拿来做个实验.
这段代码比MS给的签名验证的Demo好,对MS程序和第三方程序做了区分.
发帖之前,在论坛内搜索了一次。
发现 http://bbs.pediy.com/showthread.php?t=65492 已经有pediyer给出了代码.
和我整理之前的那段签名校验是一样的~~, 大家都是Copy来Copy去~
那段代码有处理不细腻的地方, e.g. 签名值空间的分配, 成功后的返回值判断.
还是发一下整理后的这个签名校验接口, 对刚接触PE签名校验的同学,应该有帮助~
实验中做了些注释, 对几种签名校验(MS签名, 非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;
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课