【编程】使用wintrust API获取程序签名信息 && 检查签名是否有效

在编写安全工具的过程中,有时要验证程序的数字签名,微软提供了一套API可供调用wintrust.h标头

WinVerifyTrust

https://learn.microsoft.com/zh-cn/windows/win32/api/wintrust/nf-wintrust-winverifytrust

1
2
3
4
5
LONG WinVerifyTrust(
[in] HWND hwnd,
[in] GUID *pgActionID,
[in] LPVOID pWVTData
);

hwnd:调用方窗口的句柄,一般为NULL

pgActionID:指向GUID结构的指针,该结构指定信任提供程序。验证程序数字签名则传入WINTRUST_ACTION_GENERIC_VERIFY_V2

pWVTData:指向WINTRUST_DATA结构的指针,结构包括处理所需的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
typedef struct _WINTRUST_DATA {
DWORD cbStruct;
LPVOID pPolicyCallbackData;
LPVOID pSIPClientData;
DWORD dwUIChoice;
DWORD fdwRevocationChecks;
DWORD dwUnionChoice;
union {
#if ...
WINTRUST_FILE_INFO_ *pFile;
#else
struct WINTRUST_FILE_INFO_ *pFile;
#endif
#if ...
WINTRUST_CATALOG_INFO_ *pCatalog;
#else
struct WINTRUST_CATALOG_INFO_ *pCatalog;
#endif
#if ...
WINTRUST_BLOB_INFO_ *pBlob;
#else
struct WINTRUST_BLOB_INFO_ *pBlob;
#endif
#if ...
WINTRUST_SGNR_INFO_ *pSgnr;
#else
struct WINTRUST_SGNR_INFO_ *pSgnr;
#endif
#if ...
WINTRUST_CERT_INFO_ *pCert;
#else
struct WINTRUST_CERT_INFO_ *pCert;
#endif
#if ...
WINTRUST_DETACHED_SIG_INFO_ *pDetachedSig;
#else
struct WINTRUST_DETACHED_SIG_INFO_ *pDetachedSig;
#endif
};
DWORD dwStateAction;
HANDLE hWVTStateData;
WCHAR *pwszURLReference;
DWORD dwProvFlags;
DWORD dwUIContext;
struct WINTRUST_SIGNATURE_SETTINGS_ *pSignatureSettings;
} WINTRUST_DATA, *PWINTRUST_DATA;

cbStruct:结构大小,即sizeof(WINTRUST_DATA)

dwUIChoice:指定要使用的UI的类型,一般为WTD_UI_NONE(不显示UI)

fdwRevocationChecks:证书吊销检查选项,可以是以下两个值

WTD_REVOKE_NONE:不检查证书是否被吊销,哪怕证书已经被吊销,也会将其视为有效,可以提高验证的速度(安全换效率)
WTD_REVOKE_WHOLECHAIN:检查整个证书链中的所有证书是否被吊销,任何一个证书被吊销,则整个证书链失效(效率换安全)

dwUnionChoice:指定要使用的联合成员。验证文件则设置为WTD_CHOICE_FILE

pFile:指向WINTRUST_FILE_INFO结构的指针,包含要验证的文件信息

dwStateAction:指定要执行的操作。如果不需要hWVTStateData成员则设置为WTD_STATEACTION_IGNORE,否则设置为WTD_STATEACTION_VERIFYhWVTStateData成员在下文用于获取签名者姓名,所以我这里设置为WTD_STATEACTION_VERIFY)(如果设置为WTD_STATEACTION_VERIFY,则需要使用WTD_STATEACTION_CLOSE关闭句柄以免造成资源泄露)

1
2
3
4
5
6
typedef struct WINTRUST_FILE_INFO_ {
DWORD cbStruct;
LPCWSTR pcwszFilePath;
HANDLE hFile;
GUID *pgKnownSubject;
} WINTRUST_FILE_INFO, *PWINTRUST_FILE_INFO;

cbStruct:结构大小,即sizeof(WINTRUST_FILE_INFO)

pcwszFilePath:要验证文件的完整路径

返回值

以下给出几个常用返回值,完整返回值列表可从 https://learn.microsoft.com/zh-cn/windows/win32/seccrypto/certificate-and-trust-return-values 获取

TRUST_E_NOSIGNATURE:程序无签名

ERROR_SUCCESS:签名有效

TRUST_E_BAD_DIGEST:签名无效

CERT_E_EXPIRED:签名过期

CERT_E_REVOKED:签名被吊销

TRUST_E_EXPLICIT_DISTRUST:签名不被信任

CertGetNameStringW

https://learn.microsoft.com/zh-cn/windows/win32/api/wincrypt/nf-wincrypt-certgetnamestringw

1
2
3
4
5
6
7
8
DWORD CertGetNameStringW(
[in] PCCERT_CONTEXT pCertContext,
[in] DWORD dwType,
[in] DWORD dwFlags,
[in] void *pvTypePara,
[out] LPWSTR pszNameString,
[in] DWORD cchNameString
);

dwType:这里只是获取程序的签名者姓名,传入CERT_NAME_SIMPLE_DISPLAY_TYPE即可

pszNameString:指针,指向用于接收返回值的缓冲区

cchNameString:缓冲区大小,防止返回数据时缓冲区溢出。传入0则获取预期大小

pCertContext:指向CERT_CONTEXT结构的指针,CERT_CONTEXT结构从CRYPT_PROVIDER_SGNR结构中获取

1
2
3
CRYPT_PROVIDER_DATA * WTHelperProvDataFromStateData(
[in] HANDLE hStateData
);

hStateData:就是上文的hWVTStateData成员

1
2
3
4
5
6
CRYPT_PROVIDER_SGNR * WTHelperGetProvSignerFromChain(
[in] CRYPT_PROVIDER_DATA *pProvData,
[in] DWORD idxSigner,
[in] BOOL fCounterSigner,
[in] DWORD idxCounterSigner
);

pProvDataWTHelperProvDataFromStateData函数的返回值,其中包含签名者和反签名者信息

idxSigner:签名者索引,从0开始

fCounterSigner:这里传入FALSE,检索idxSigner指定的签名者

idxCounterSigner:反签名器的索引,从0开始

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct _CRYPT_PROVIDER_SGNR {
DWORD cbStruct;
FILETIME sftVerifyAsOf;
DWORD csCertChain;
struct _CRYPT_PROVIDER_CERT *pasCertChain;
DWORD dwSignerType;
CMSG_SIGNER_INFO *psSigner;
DWORD dwError;
DWORD csCounterSigners;
struct _CRYPT_PROVIDER_SGNR *pasCounterSigners;
PCCERT_CHAIN_CONTEXT pChainContext;
} CRYPT_PROVIDER_SGNR, *PCRYPT_PROVIDER_SGNR;

唯一需要注意的是pasCertChain成员,可通过pasCertChain[0].pCert获取签名上下文(就是上文提到的CERT_CONTEXT

示例代码

以下为示例代码,获取签名者姓名的同时验证签名是否有效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <windows.h>
#include <wintrust.h>
#include <softpub.h>
#include <stdio.h>
#pragma comment(lib, "wintrust")
#pragma comment(lib, "crypt32.lib")

int main() {
//设置WINTRUST_FILE_INFO结构
WINTRUST_FILE_INFO fileData = { 0 };
fileData.cbStruct = sizeof(WINTRUST_FILE_INFO);
fileData.pcwszFilePath = L"C:\\Users\\ADMIN\\Desktop\\test.exe";
fileData.hFile = NULL;
fileData.pgKnownSubject = NULL;

//设置WINTRUST_DATA结构
GUID guidAction = WINTRUST_ACTION_GENERIC_VERIFY_V2;
WINTRUST_DATA wintrustData = { 0 };
wintrustData.cbStruct = sizeof(WINTRUST_DATA);
wintrustData.dwUIChoice = WTD_UI_NONE;
wintrustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN;
wintrustData.dwUnionChoice = WTD_CHOICE_FILE;
wintrustData.pFile = &fileData;
wintrustData.dwStateAction = WTD_STATEACTION_VERIFY;

//调用WinVerifyTrust
LONG result = WinVerifyTrust(NULL, &guidAction, &wintrustData);

//获取CRYPT_PROVIDER_SGNR
CRYPT_PROVIDER_DATA* providerData = WTHelperProvDataFromStateData(wintrustData.hWVTStateData);
if (providerData == NULL) {
return 1;
}

CRYPT_PROVIDER_SGNR* signer = WTHelperGetProvSignerFromChain(providerData, 0, FALSE, 0);
if (signer == NULL) {
return 1;
}

//从CRYPT_PROVIDER_SGNR中获取CERT_CONTEXT
PCCERT_CONTEXT certContext = signer->pasCertChain[0].pCert;

//获取缓冲区预期大小
DWORD nameSize = CertGetNameStringA(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0);

//分配缓冲区,获取签名者姓名
char* nameBuffer = new char[nameSize];
CertGetNameStringA(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nameBuffer, nameSize);

printf("签名者姓名:%s\n",nameBuffer);

//关闭句柄,删除缓冲区
wintrustData.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust(nullptr, &guidAction, &wintrustData);
delete[]nameBuffer;

switch (result)
{
case ERROR_SUCCESS:
printf("签名有效");
break;
case TRUST_E_NOSIGNATURE:
printf("无签名");
break;
case CERT_E_EXPIRED:
printf("签名过期");
break;
case CERT_E_REVOKED:
printf("签名被吊销");
break;
case TRUST_E_EXPLICIT_DISTRUST:
printf("签名不被信任");
break;
case TRUST_E_BAD_DIGEST:
printf("签名无效");
break;
default:
printf("0x%x", result);
break;
}
}

【编程】使用wintrust API获取程序签名信息 && 检查签名是否有效
https://crackme.net/articles/wintrust_example/
作者
Brassinolide
发布于
2024年9月28日
许可协议