-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpeutils.pas
256 lines (232 loc) · 9.89 KB
/
peutils.pas
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
unit peutils;
interface
uses
Windows;
const
{$EXTERNALSYM WIN_CERT_REVISION_1_0}
WIN_CERT_REVISION_1_0 = $0100;
function IsCodeSigned(const Filename: string): Boolean;
function IsCompanySigningCertificate(const Filename, CompanyName :string): Boolean;
function GetCertCompanyName(const Filename: string): String;
implementation
// IsCodeSigned, which verifies that the exe hasn't been modified, uses
// WinVerifyTrust, so it's NT only. IsCompanySigningCertificate works on Win9x,
// but it only checks that the signing certificate hasn't been replaced, which
// keeps someone from re-signing a modified executable.
// Imagehlp.dll
const
CERT_SECTION_TYPE_ANY = $FF; // Any Certificate type
function ImageEnumerateCertificates(FileHandle: THandle; TypeFilter: WORD;
out CertificateCount: DWORD; Indicies: PDWORD; IndexCount: Integer): BOOL; stdcall; external 'Imagehlp.dll';
function ImageGetCertificateHeader(FileHandle: THandle; CertificateIndex: Integer;
var CertificateHeader: TWinCertificate): BOOL; stdcall; external 'Imagehlp.dll';
function ImageGetCertificateData(FileHandle: THandle; CertificateIndex: Integer;
Certificate: PWinCertificate; var RequiredLength: DWORD): BOOL; stdcall; external 'Imagehlp.dll';
// Crypt32.dll
const
CERT_NAME_SIMPLE_DISPLAY_TYPE = 4;
PKCS_7_ASN_ENCODING = $00010000;
X509_ASN_ENCODING = $00000001;
type
PCCERT_CONTEXT = type Pointer;
HCRYPTPROV_LEGACY = type Pointer;
PFN_CRYPT_GET_SIGNER_CERTIFICATE = type Pointer;
CRYPT_VERIFY_MESSAGE_PARA = record
cbSize: DWORD;
dwMsgAndCertEncodingType: DWORD;
hCryptProv: HCRYPTPROV_LEGACY;
pfnGetSignerCertificate: PFN_CRYPT_GET_SIGNER_CERTIFICATE;
pvGetArg: Pointer;
end;
function CryptVerifyMessageSignature(const pVerifyPara: CRYPT_VERIFY_MESSAGE_PARA;
dwSignerIndex: DWORD; pbSignedBlob: PByte; cbSignedBlob: DWORD; pbDecoded: PBYTE;
pcbDecoded: PDWORD; ppSignerCert: PCCERT_CONTEXT): BOOL; stdcall; external 'Crypt32.dll';
function CertGetNameStringA(pCertContext: PCCERT_CONTEXT; dwType: DWORD; dwFlags: DWORD; pvTypePara: Pointer;
pszNameString: PAnsiChar; cchNameString: DWORD): DWORD; stdcall; external 'Crypt32.dll';
function CertFreeCertificateContext(pCertContext: PCCERT_CONTEXT): BOOL; stdcall; external 'Crypt32.dll';
function CertCreateCertificateContext(dwCertEncodingType: DWORD;
pbCertEncoded: PBYTE; cbCertEncoded: DWORD): PCCERT_CONTEXT; stdcall; external 'Crypt32.dll';
// WinTrust.dll
const
WINTRUST_ACTION_GENERIC_VERIFY_V2: TGUID = '{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}';
WTD_CHOICE_FILE = 1;
WTD_REVOKE_NONE = 0;
WTD_UI_NONE = 2;
type
PWinTrustFileInfo = ^TWinTrustFileInfo;
TWinTrustFileInfo = record
cbStruct: DWORD; // = sizeof(WINTRUST_FILE_INFO)
pcwszFilePath: PWideChar; // required, file name to be verified
hFile: THandle; // optional, open handle to pcwszFilePath
pgKnownSubject: PGUID; // optional: fill if the subject type is known
end;
PWinTrustData = ^TWinTrustData;
TWinTrustData = record
cbStruct: DWORD;
pPolicyCallbackData: Pointer;
pSIPClientData: Pointer;
dwUIChoice: DWORD;
fdwRevocationChecks: DWORD;
dwUnionChoice: DWORD;
pFile: PWinTrustFileInfo;
dwStateAction: DWORD;
hWVTStateData: THandle;
pwszURLReference: PWideChar;
dwProvFlags: DWORD;
dwUIContext: DWORD;
end;
function WinVerifyTrust(hwnd: HWND; const ActionID: TGUID; ActionData: Pointer): Longint; stdcall; external wintrust;
{-----------------------------------------------}
function IsCodeSigned(const Filename: string): Boolean;
var
file_info: TWinTrustFileInfo;
trust_data: TWinTrustData;
begin
// Verify that the exe is signed and the checksum matches
FillChar(file_info, SizeOf(file_info), 0);
file_info.cbStruct := sizeof(file_info);
file_info.pcwszFilePath := PWideChar(WideString(Filename));
FillChar(trust_data, SizeOf(trust_data), 0);
trust_data.cbStruct := sizeof(trust_data);
trust_data.dwUIChoice := WTD_UI_NONE;
trust_data.fdwRevocationChecks := WTD_REVOKE_NONE;
trust_data.dwUnionChoice := WTD_CHOICE_FILE;
trust_data.pFile := @file_info;
Result := WinVerifyTrust(INVALID_HANDLE_VALUE, WINTRUST_ACTION_GENERIC_VERIFY_V2,
@trust_data) = ERROR_SUCCESS
end;
{-----------------------------------------------}
function IsCompanySigningCertificate(const Filename, CompanyName :string): Boolean;
var
hExe: HMODULE;
Cert: PWinCertificate;
CertContext: PCCERT_CONTEXT;
CertCount: DWORD;
CertName: AnsiString;
CertNameLen: DWORD;
VerifyParams: CRYPT_VERIFY_MESSAGE_PARA;
begin
// Returns TRUE if the SubjectName on the certificate used to sign the exe is
// "Company Name". Should prevent a cracker from modifying the file and
// re-signing it with their own certificate.
//
// Microsoft has an example that does this using CryptQueryObject and
// CertFindCertificateInStore instead of CryptVerifyMessageSignature, but
// CryptQueryObject is NT-only. Using CertCreateCertificateContext doesn't work
// either, though I don't know why.
Result := False;
// Verify that the exe was signed by our private key
hExe := CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, 0);
if hExe = INVALID_HANDLE_VALUE then
Exit;
try
// There should only be one certificate associated with the exe
if (not ImageEnumerateCertificates(hExe, CERT_SECTION_TYPE_ANY, CertCount, nil, 0)) or
(CertCount <> 1) then
Exit;
// Read the certificate header so we can get the size needed for the full cert
GetMem(Cert, SizeOf(TWinCertificate) + 3); // ImageGetCertificateHeader writes an DWORD at bCertificate for some reason
try
Cert.dwLength := 0;
Cert.wRevision := WIN_CERT_REVISION_1_0;
if not ImageGetCertificateHeader(hExe, 0, Cert^) then
Exit;
// Read the full certificate
ReallocMem(Cert, SizeOf(TWinCertificate) + Cert.dwLength);
if not ImageGetCertificateData(hExe, 0, Cert, Cert.dwLength) then
Exit;
// Get the certificate context. CryptVerifyMessageSignature has the
// side effect of creating a context for the signing certificate.
FillChar(VerifyParams, SizeOf(VerifyParams), 0);
VerifyParams.cbSize := SizeOf(VerifyParams);
VerifyParams.dwMsgAndCertEncodingType := X509_ASN_ENCODING or PKCS_7_ASN_ENCODING;
if not CryptVerifyMessageSignature(VerifyParams, 0, @Cert.bCertificate,
Cert.dwLength, nil, nil, @CertContext) then
Exit;
try
// Extract and compare the certificate's subject names. Don't
// compare the entire certificate or the public key as those will
// change when the certificate is renewed.
CertNameLen := CertGetNameStringA(CertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nil, nil, 0);
SetLength(CertName, CertNameLen - 1);
CertGetNameStringA(CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
nil, PAnsiChar(CertName), CertNameLen);
if CertName <> CompanyName then
Exit;
finally
CertFreeCertificateContext(CertContext)
end;
finally
FreeMem(Cert);
end;
finally
CloseHandle(hExe);
end;
Result := True;
end;
function GetCertCompanyName(const Filename: string): String;
var
hExe: HMODULE;
Cert: PWinCertificate;
CertContext: PCCERT_CONTEXT;
CertCount: DWORD;
CertName: AnsiString;
CertNameLen: DWORD;
VerifyParams: CRYPT_VERIFY_MESSAGE_PARA;
begin
Result := '';
// Verify that the exe was signed by our private key
hExe := CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, 0);
if hExe = INVALID_HANDLE_VALUE then
Exit;
try
// There should only be one certificate associated with the exe
if (not ImageEnumerateCertificates(hExe, CERT_SECTION_TYPE_ANY, CertCount, nil, 0)) or
(CertCount <> 1) then
Exit;
// Read the certificate header so we can get the size needed for the full cert
GetMem(Cert, SizeOf(TWinCertificate) + 3); // ImageGetCertificateHeader writes an DWORD at bCertificate for some reason
try
Cert.dwLength := 0;
Cert.wRevision := WIN_CERT_REVISION_1_0;
if not ImageGetCertificateHeader(hExe, 0, Cert^) then
Exit;
// Read the full certificate
ReallocMem(Cert, SizeOf(TWinCertificate) + Cert.dwLength);
if not ImageGetCertificateData(hExe, 0, Cert, Cert.dwLength) then
Exit;
// Get the certificate context. CryptVerifyMessageSignature has the
// side effect of creating a context for the signing certificate.
FillChar(VerifyParams, SizeOf(VerifyParams), 0);
VerifyParams.cbSize := SizeOf(VerifyParams);
VerifyParams.dwMsgAndCertEncodingType := X509_ASN_ENCODING or PKCS_7_ASN_ENCODING;
if not CryptVerifyMessageSignature(VerifyParams, 0, @Cert.bCertificate,
Cert.dwLength, nil, nil, @CertContext) then
Exit;
try
// Extract and compare the certificate's subject names. Don't
// compare the entire certificate or the public key as those will
// change when the certificate is renewed.
CertNameLen := CertGetNameStringA(CertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nil, nil, 0);
SetLength(CertName, CertNameLen - 1);
CertGetNameStringA(CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
nil, PAnsiChar(CertName), CertNameLen);
Result := CertName;
if CertName <> '' then
Exit;
finally
CertFreeCertificateContext(CertContext)
end;
finally
FreeMem(Cert);
end;
finally
CloseHandle(hExe);
end;
//Result := True;
end;
end.