4 * Copyright 1998 Patrik Stridvall
5 * Copyright 2003 Mike McCormack
6 * Copyright 2009 Owen Rudge for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(imagehlp);
36 * These functions are partially documented at:
37 * http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
44 /***********************************************************************
45 * IMAGEHLP_GetNTHeaders (INTERNAL)
47 * Return the IMAGE_NT_HEADERS for a PE file, after validating magic
48 * numbers and distinguishing between 32-bit and 64-bit files.
50 static int IMAGEHLP_GetNTHeaders(HANDLE handle, DWORD *pe_offset, IMAGE_NT_HEADERS32 *nt32, IMAGE_NT_HEADERS64 *nt64)
52 IMAGE_DOS_HEADER dos_hdr;
56 TRACE("handle %p\n", handle);
58 if ((!nt32) || (!nt64))
61 /* read the DOS header */
62 count = SetFilePointer(handle, 0, NULL, FILE_BEGIN);
64 if (count == INVALID_SET_FILE_POINTER)
69 r = ReadFile(handle, &dos_hdr, sizeof dos_hdr, &count, NULL);
74 if (count != sizeof dos_hdr)
77 /* verify magic number of 'MZ' */
78 if (dos_hdr.e_magic != 0x5A4D)
81 if (pe_offset != NULL)
82 *pe_offset = dos_hdr.e_lfanew;
84 /* read the PE header */
85 count = SetFilePointer(handle, dos_hdr.e_lfanew, NULL, FILE_BEGIN);
87 if (count == INVALID_SET_FILE_POINTER)
92 r = ReadFile(handle, nt32, sizeof(IMAGE_NT_HEADERS32), &count, NULL);
97 if (count != sizeof(IMAGE_NT_HEADERS32))
100 /* verify NT signature */
101 if (nt32->Signature != IMAGE_NT_SIGNATURE)
104 /* check if we have a 32-bit or 64-bit executable */
105 switch (nt32->OptionalHeader.Magic)
107 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
110 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
111 /* Re-read as 64-bit */
113 count = SetFilePointer(handle, dos_hdr.e_lfanew, NULL, FILE_BEGIN);
115 if (count == INVALID_SET_FILE_POINTER)
120 r = ReadFile(handle, nt64, sizeof(IMAGE_NT_HEADERS64), &count, NULL);
125 if (count != sizeof(IMAGE_NT_HEADERS64))
128 /* verify NT signature */
129 if (nt64->Signature != IMAGE_NT_SIGNATURE)
138 /***********************************************************************
139 * IMAGEHLP_GetSecurityDirOffset (INTERNAL)
141 * Read a file's PE header, and return the offset and size of the
142 * security directory.
144 static BOOL IMAGEHLP_GetSecurityDirOffset( HANDLE handle,
145 DWORD *pdwOfs, DWORD *pdwSize )
147 IMAGE_NT_HEADERS32 nt_hdr32;
148 IMAGE_NT_HEADERS64 nt_hdr64;
149 IMAGE_DATA_DIRECTORY *sd;
152 ret = IMAGEHLP_GetNTHeaders(handle, NULL, &nt_hdr32, &nt_hdr64);
155 sd = &nt_hdr32.OptionalHeader.DataDirectory[IMAGE_FILE_SECURITY_DIRECTORY];
156 else if (ret == HDR_NT64)
157 sd = &nt_hdr64.OptionalHeader.DataDirectory[IMAGE_FILE_SECURITY_DIRECTORY];
161 TRACE("ret = %d size = %x addr = %x\n", ret, sd->Size, sd->VirtualAddress);
164 *pdwOfs = sd->VirtualAddress;
169 /***********************************************************************
170 * IMAGEHLP_SetSecurityDirOffset (INTERNAL)
172 * Read a file's PE header, and update the offset and size of the
173 * security directory.
175 static BOOL IMAGEHLP_SetSecurityDirOffset(HANDLE handle,
176 DWORD dwOfs, DWORD dwSize)
178 IMAGE_NT_HEADERS32 nt_hdr32;
179 IMAGE_NT_HEADERS64 nt_hdr64;
180 IMAGE_DATA_DIRECTORY *sd;
181 int ret, nt_hdr_size = 0;
187 ret = IMAGEHLP_GetNTHeaders(handle, &pe_offset, &nt_hdr32, &nt_hdr64);
191 sd = &nt_hdr32.OptionalHeader.DataDirectory[IMAGE_FILE_SECURITY_DIRECTORY];
194 nt_hdr_size = sizeof(IMAGE_NT_HEADERS32);
196 else if (ret == HDR_NT64)
198 sd = &nt_hdr64.OptionalHeader.DataDirectory[IMAGE_FILE_SECURITY_DIRECTORY];
201 nt_hdr_size = sizeof(IMAGE_NT_HEADERS64);
207 sd->VirtualAddress = dwOfs;
209 TRACE("size = %x addr = %x\n", sd->Size, sd->VirtualAddress);
211 /* write the header back again */
212 count = SetFilePointer(handle, pe_offset, NULL, FILE_BEGIN);
214 if (count == INVALID_SET_FILE_POINTER)
219 r = WriteFile(handle, nt_hdr, nt_hdr_size, &count, NULL);
224 if (count != nt_hdr_size)
230 /***********************************************************************
231 * IMAGEHLP_GetCertificateOffset (INTERNAL)
233 * Read a file's PE header, and return the offset and size of the
234 * security directory.
236 static BOOL IMAGEHLP_GetCertificateOffset( HANDLE handle, DWORD num,
237 DWORD *pdwOfs, DWORD *pdwSize )
239 DWORD size, count, offset, len, sd_VirtualAddr;
242 r = IMAGEHLP_GetSecurityDirOffset( handle, &sd_VirtualAddr, &size );
247 /* take the n'th certificate */
250 /* read the length of the current certificate */
251 count = SetFilePointer( handle, sd_VirtualAddr + offset,
253 if( count == INVALID_SET_FILE_POINTER )
255 r = ReadFile( handle, &len, sizeof len, &count, NULL );
258 if( count != sizeof len )
261 /* check the certificate is not too big or too small */
262 if( len < sizeof len )
264 if( len > (size-offset) )
269 /* calculate the offset of the next certificate */
272 /* padded out to the nearest 8-byte boundary */
274 offset += 8 - (len % 8);
280 *pdwOfs = sd_VirtualAddr + offset;
283 TRACE("len = %x addr = %x\n", len, sd_VirtualAddr + offset);
288 /***********************************************************************
289 * IMAGEHLP_RecalculateChecksum (INTERNAL)
291 * Update the NT header checksum for the specified file.
293 static BOOL IMAGEHLP_RecalculateChecksum(HANDLE handle)
295 DWORD FileLength, count, HeaderSum, pe_offset, nt_hdr_size;
296 IMAGE_NT_HEADERS32 nt_hdr32;
297 IMAGE_NT_HEADERS64 nt_hdr64;
305 TRACE("handle %p\n", handle);
307 ret = IMAGEHLP_GetNTHeaders(handle, &pe_offset, &nt_hdr32, &nt_hdr64);
311 CheckSum = &nt_hdr32.OptionalHeader.CheckSum;
314 nt_hdr_size = sizeof(IMAGE_NT_HEADERS32);
316 else if (ret == HDR_NT64)
318 CheckSum = &nt_hdr64.OptionalHeader.CheckSum;
321 nt_hdr_size = sizeof(IMAGE_NT_HEADERS64);
326 hMapping = CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL);
331 BaseAddress = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
335 CloseHandle(hMapping);
339 FileLength = GetFileSize(handle, NULL);
342 CheckSumMappedFile(BaseAddress, FileLength, &HeaderSum, CheckSum);
344 UnmapViewOfFile(BaseAddress);
345 CloseHandle(hMapping);
349 /* write the header back again */
350 count = SetFilePointer(handle, pe_offset, NULL, FILE_BEGIN);
352 if (count == INVALID_SET_FILE_POINTER)
357 r = WriteFile(handle, nt_hdr, nt_hdr_size, &count, NULL);
362 if (count != nt_hdr_size)
371 /***********************************************************************
372 * ImageAddCertificate (IMAGEHLP.@)
374 * Adds the specified certificate to the security directory of
378 BOOL WINAPI ImageAddCertificate(
379 HANDLE FileHandle, LPWIN_CERTIFICATE Certificate, PDWORD Index)
381 DWORD size = 0, count = 0, offset = 0, sd_VirtualAddr = 0, index = 0;
383 const size_t cert_hdr_size = sizeof hdr - sizeof hdr.bCertificate;
386 TRACE("(%p, %p, %p)\n", FileHandle, Certificate, Index);
388 r = IMAGEHLP_GetSecurityDirOffset(FileHandle, &sd_VirtualAddr, &size);
390 /* If we've already got a security directory, find the end of it */
391 if ((r) && (sd_VirtualAddr != 0))
397 /* Check if the security directory is at the end of the file.
398 If not, we should probably relocate it. */
399 if (GetFileSize(FileHandle, NULL) != sd_VirtualAddr + size)
401 FIXME("Security directory already present but not located at EOF, not adding certificate\n");
403 SetLastError(ERROR_NOT_SUPPORTED);
407 while (offset < size)
409 /* read the length of the current certificate */
410 count = SetFilePointer (FileHandle, sd_VirtualAddr + offset,
413 if (count == INVALID_SET_FILE_POINTER)
416 r = ReadFile(FileHandle, &hdr, cert_hdr_size, &count, NULL);
421 if (count != cert_hdr_size)
424 /* check the certificate is not too big or too small */
425 if (hdr.dwLength < cert_hdr_size)
428 if (hdr.dwLength > (size-offset))
431 /* next certificate */
432 offset += hdr.dwLength;
434 /* padded out to the nearest 8-byte boundary */
435 if (hdr.dwLength % 8)
436 offset += 8 - (hdr.dwLength % 8);
441 count = SetFilePointer (FileHandle, sd_VirtualAddr + offset, NULL, FILE_BEGIN);
443 if (count == INVALID_SET_FILE_POINTER)
448 sd_VirtualAddr = SetFilePointer(FileHandle, 0, NULL, FILE_END);
450 if (sd_VirtualAddr == INVALID_SET_FILE_POINTER)
454 /* Write the certificate to the file */
455 r = WriteFile(FileHandle, Certificate, Certificate->dwLength, &count, NULL);
460 /* Pad out if necessary */
461 if (Certificate->dwLength % 8)
466 WriteFile(FileHandle, null, 8 - (Certificate->dwLength % 8), NULL, NULL);
468 size += 8 - (Certificate->dwLength % 8);
471 size += Certificate->dwLength;
473 /* Update the security directory offset and size */
474 if (!IMAGEHLP_SetSecurityDirOffset(FileHandle, sd_VirtualAddr, size))
477 if (!IMAGEHLP_RecalculateChecksum(FileHandle))
483 /***********************************************************************
484 * ImageEnumerateCertificates (IMAGEHLP.@)
486 BOOL WINAPI ImageEnumerateCertificates(
487 HANDLE handle, WORD TypeFilter, PDWORD CertificateCount,
488 PDWORD Indices, DWORD IndexCount)
490 DWORD size, count, offset, sd_VirtualAddr, index;
492 const size_t cert_hdr_size = sizeof hdr - sizeof hdr.bCertificate;
495 TRACE("%p %hd %p %p %d\n",
496 handle, TypeFilter, CertificateCount, Indices, IndexCount);
498 r = IMAGEHLP_GetSecurityDirOffset( handle, &sd_VirtualAddr, &size );
504 *CertificateCount = 0;
505 while( offset < size )
507 /* read the length of the current certificate */
508 count = SetFilePointer( handle, sd_VirtualAddr + offset,
510 if( count == INVALID_SET_FILE_POINTER )
512 r = ReadFile( handle, &hdr, cert_hdr_size, &count, NULL );
515 if( count != cert_hdr_size )
518 TRACE("Size = %08x id = %08hx\n",
519 hdr.dwLength, hdr.wCertificateType );
521 /* check the certificate is not too big or too small */
522 if( hdr.dwLength < cert_hdr_size )
524 if( hdr.dwLength > (size-offset) )
527 if( (TypeFilter == CERT_SECTION_TYPE_ANY) ||
528 (TypeFilter == hdr.wCertificateType) )
530 (*CertificateCount)++;
531 if(Indices && *CertificateCount <= IndexCount)
535 /* next certificate */
536 offset += hdr.dwLength;
538 /* padded out to the nearest 8-byte boundary */
539 if (hdr.dwLength % 8)
540 offset += 8 - (hdr.dwLength % 8);
548 /***********************************************************************
549 * ImageGetCertificateData (IMAGEHLP.@)
551 * FIXME: not sure that I'm dealing with the Index the right way
553 BOOL WINAPI ImageGetCertificateData(
554 HANDLE handle, DWORD Index,
555 LPWIN_CERTIFICATE Certificate, PDWORD RequiredLength)
557 DWORD r, offset, ofs, size, count;
559 TRACE("%p %d %p %p\n", handle, Index, Certificate, RequiredLength);
563 SetLastError( ERROR_INVALID_PARAMETER );
567 if( !IMAGEHLP_GetCertificateOffset( handle, Index, &ofs, &size ) )
570 if( *RequiredLength < size )
572 *RequiredLength = size;
573 SetLastError( ERROR_INSUFFICIENT_BUFFER );
579 SetLastError( ERROR_INVALID_PARAMETER );
583 *RequiredLength = size;
585 offset = SetFilePointer( handle, ofs, NULL, FILE_BEGIN );
586 if( offset == INVALID_SET_FILE_POINTER )
589 r = ReadFile( handle, Certificate, size, &count, NULL );
596 SetLastError( NO_ERROR );
601 /***********************************************************************
602 * ImageGetCertificateHeader (IMAGEHLP.@)
604 BOOL WINAPI ImageGetCertificateHeader(
605 HANDLE handle, DWORD index, LPWIN_CERTIFICATE pCert)
607 DWORD r, offset, ofs, size, count;
608 const size_t cert_hdr_size = sizeof *pCert - sizeof pCert->bCertificate;
610 TRACE("%p %d %p\n", handle, index, pCert);
612 if( !IMAGEHLP_GetCertificateOffset( handle, index, &ofs, &size ) )
615 if( size < cert_hdr_size )
618 offset = SetFilePointer( handle, ofs, NULL, FILE_BEGIN );
619 if( offset == INVALID_SET_FILE_POINTER )
622 r = ReadFile( handle, pCert, cert_hdr_size, &count, NULL );
625 if( count != cert_hdr_size )
633 /***********************************************************************
634 * ImageGetDigestStream (IMAGEHLP.@)
636 BOOL WINAPI ImageGetDigestStream(
637 HANDLE FileHandle, DWORD DigestLevel,
638 DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle)
640 FIXME("(%p, %d, %p, %p): stub\n",
641 FileHandle, DigestLevel, DigestFunction, DigestHandle
643 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
647 /***********************************************************************
648 * ImageRemoveCertificate (IMAGEHLP.@)
650 BOOL WINAPI ImageRemoveCertificate(HANDLE FileHandle, DWORD Index)
652 DWORD size = 0, count = 0, sd_VirtualAddr = 0, offset = 0;
653 DWORD data_size = 0, cert_size = 0, cert_size_padded = 0, ret = 0;
657 TRACE("(%p, %d)\n", FileHandle, Index);
659 r = ImageEnumerateCertificates(FileHandle, CERT_SECTION_TYPE_ANY, &count, NULL, 0);
661 if ((!r) || (count == 0))
664 if ((!IMAGEHLP_GetSecurityDirOffset(FileHandle, &sd_VirtualAddr, &size)) ||
665 (!IMAGEHLP_GetCertificateOffset(FileHandle, Index, &offset, &cert_size)))
668 /* Ignore any padding we have, too */
670 cert_size_padded = cert_size + (8 - (cert_size % 8));
672 cert_size_padded = cert_size;
674 data_size = size - (offset - sd_VirtualAddr) - cert_size_padded;
678 ret = SetFilePointer(FileHandle, sd_VirtualAddr, NULL, FILE_BEGIN);
680 if (ret == INVALID_SET_FILE_POINTER)
685 cert_data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, data_size);
690 ret = SetFilePointer(FileHandle, offset + cert_size_padded, NULL, FILE_BEGIN);
692 if (ret == INVALID_SET_FILE_POINTER)
695 /* Read any subsequent certificates */
696 r = ReadFile(FileHandle, cert_data, data_size, &count, NULL);
698 if ((!r) || (count != data_size))
701 SetFilePointer(FileHandle, offset, NULL, FILE_BEGIN);
703 /* Write them one index back */
704 r = WriteFile(FileHandle, cert_data, data_size, &count, NULL);
706 if ((!r) || (count != data_size))
709 HeapFree(GetProcessHeap(), 0, cert_data);
712 /* If security directory is at end of file, trim the file */
713 if (GetFileSize(FileHandle, NULL) == sd_VirtualAddr + size)
714 SetEndOfFile(FileHandle);
717 r = IMAGEHLP_SetSecurityDirOffset(FileHandle, 0, 0);
719 r = IMAGEHLP_SetSecurityDirOffset(FileHandle, sd_VirtualAddr, size - cert_size_padded);
724 if (!IMAGEHLP_RecalculateChecksum(FileHandle))
730 HeapFree(GetProcessHeap(), 0, cert_data);