xref: /reactos/dll/win32/imagehlp/integrity.c (revision c2c66aff)
1 /*
2  *	IMAGEHLP library
3  *
4  *	Copyright 1998	Patrik Stridvall
5  *	Copyright 2003	Mike McCormack
6  *	Copyright 2009  Owen Rudge for CodeWeavers
7  *	Copyright 2010  Juan Lang
8  *	Copyright 2010  Andrey Turkin
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include "precomp.h"
26 
27 /*
28  * These functions are partially documented at:
29  *   http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
30  */
31 
32 #define HDR_FAIL   -1
33 #define HDR_NT32    0
34 #define HDR_NT64    1
35 
36 /***********************************************************************
37  * IMAGEHLP_GetNTHeaders (INTERNAL)
38  *
39  * Return the IMAGE_NT_HEADERS for a PE file, after validating magic
40  * numbers and distinguishing between 32-bit and 64-bit files.
41  */
42 static int IMAGEHLP_GetNTHeaders(HANDLE handle, DWORD *pe_offset, IMAGE_NT_HEADERS32 *nt32, IMAGE_NT_HEADERS64 *nt64)
43 {
44     IMAGE_DOS_HEADER dos_hdr;
45     DWORD count;
46     BOOL r;
47 
48     TRACE("handle %p\n", handle);
49 
50     if ((!nt32) || (!nt64))
51         return HDR_FAIL;
52 
53     /* read the DOS header */
54     count = SetFilePointer(handle, 0, NULL, FILE_BEGIN);
55 
56     if (count == INVALID_SET_FILE_POINTER)
57         return HDR_FAIL;
58 
59     count = 0;
60 
61     r = ReadFile(handle, &dos_hdr, sizeof dos_hdr, &count, NULL);
62 
63     if (!r)
64         return HDR_FAIL;
65 
66     if (count != sizeof dos_hdr)
67         return HDR_FAIL;
68 
69     /* verify magic number of 'MZ' */
70     if (dos_hdr.e_magic != IMAGE_DOS_SIGNATURE)
71         return HDR_FAIL;
72 
73     if (pe_offset != NULL)
74         *pe_offset = dos_hdr.e_lfanew;
75 
76     /* read the PE header */
77     count = SetFilePointer(handle, dos_hdr.e_lfanew, NULL, FILE_BEGIN);
78 
79     if (count == INVALID_SET_FILE_POINTER)
80         return HDR_FAIL;
81 
82     count = 0;
83 
84     r = ReadFile(handle, nt32, sizeof(IMAGE_NT_HEADERS32), &count, NULL);
85 
86     if (!r)
87         return HDR_FAIL;
88 
89     if (count != sizeof(IMAGE_NT_HEADERS32))
90         return HDR_FAIL;
91 
92     /* verify NT signature */
93     if (nt32->Signature != IMAGE_NT_SIGNATURE)
94         return HDR_FAIL;
95 
96     /* check if we have a 32-bit or 64-bit executable */
97     switch (nt32->OptionalHeader.Magic)
98     {
99         case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
100             return HDR_NT32;
101 
102         case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
103             /* Re-read as 64-bit */
104 
105             count = SetFilePointer(handle, dos_hdr.e_lfanew, NULL, FILE_BEGIN);
106 
107             if (count == INVALID_SET_FILE_POINTER)
108                 return HDR_FAIL;
109 
110             count = 0;
111 
112             r = ReadFile(handle, nt64, sizeof(IMAGE_NT_HEADERS64), &count, NULL);
113 
114             if (!r)
115                 return HDR_FAIL;
116 
117             if (count != sizeof(IMAGE_NT_HEADERS64))
118                 return HDR_FAIL;
119 
120             /* verify NT signature */
121             if (nt64->Signature != IMAGE_NT_SIGNATURE)
122                 return HDR_FAIL;
123 
124             return HDR_NT64;
125     }
126 
127     return HDR_FAIL;
128 }
129 
130 /***********************************************************************
131  * IMAGEHLP_GetSecurityDirOffset (INTERNAL)
132  *
133  * Read a file's PE header, and return the offset and size of the
134  *  security directory.
135  */
136 static BOOL IMAGEHLP_GetSecurityDirOffset( HANDLE handle,
137                                            DWORD *pdwOfs, DWORD *pdwSize )
138 {
139     IMAGE_NT_HEADERS32 nt_hdr32;
140     IMAGE_NT_HEADERS64 nt_hdr64;
141     IMAGE_DATA_DIRECTORY *sd;
142     int ret;
143 
144     ret = IMAGEHLP_GetNTHeaders(handle, NULL, &nt_hdr32, &nt_hdr64);
145 
146     if (ret == HDR_NT32)
147         sd = &nt_hdr32.OptionalHeader.DataDirectory[IMAGE_FILE_SECURITY_DIRECTORY];
148     else if (ret == HDR_NT64)
149         sd = &nt_hdr64.OptionalHeader.DataDirectory[IMAGE_FILE_SECURITY_DIRECTORY];
150     else
151         return FALSE;
152 
153     TRACE("ret = %d size = %x addr = %x\n", ret, sd->Size, sd->VirtualAddress);
154 
155     *pdwSize = sd->Size;
156     *pdwOfs = sd->VirtualAddress;
157 
158     return TRUE;
159 }
160 
161 /***********************************************************************
162  * IMAGEHLP_SetSecurityDirOffset (INTERNAL)
163  *
164  * Read a file's PE header, and update the offset and size of the
165  *  security directory.
166  */
167 static BOOL IMAGEHLP_SetSecurityDirOffset(HANDLE handle,
168                                           DWORD dwOfs, DWORD dwSize)
169 {
170     IMAGE_NT_HEADERS32 nt_hdr32;
171     IMAGE_NT_HEADERS64 nt_hdr64;
172     IMAGE_DATA_DIRECTORY *sd;
173     int ret, nt_hdr_size = 0;
174     DWORD pe_offset;
175     void *nt_hdr;
176     DWORD count;
177     BOOL r;
178 
179     ret = IMAGEHLP_GetNTHeaders(handle, &pe_offset, &nt_hdr32, &nt_hdr64);
180 
181     if (ret == HDR_NT32)
182     {
183         sd = &nt_hdr32.OptionalHeader.DataDirectory[IMAGE_FILE_SECURITY_DIRECTORY];
184 
185         nt_hdr = &nt_hdr32;
186         nt_hdr_size = sizeof(IMAGE_NT_HEADERS32);
187     }
188     else if (ret == HDR_NT64)
189     {
190         sd = &nt_hdr64.OptionalHeader.DataDirectory[IMAGE_FILE_SECURITY_DIRECTORY];
191 
192         nt_hdr = &nt_hdr64;
193         nt_hdr_size = sizeof(IMAGE_NT_HEADERS64);
194     }
195     else
196         return FALSE;
197 
198     sd->Size = dwSize;
199     sd->VirtualAddress = dwOfs;
200 
201     TRACE("size = %x addr = %x\n", sd->Size, sd->VirtualAddress);
202 
203     /* write the header back again */
204     count = SetFilePointer(handle, pe_offset, NULL, FILE_BEGIN);
205 
206     if (count == INVALID_SET_FILE_POINTER)
207         return FALSE;
208 
209     count = 0;
210 
211     r = WriteFile(handle, nt_hdr, nt_hdr_size, &count, NULL);
212 
213     if (!r)
214         return FALSE;
215 
216     if (count != nt_hdr_size)
217         return FALSE;
218 
219     return TRUE;
220 }
221 
222 /***********************************************************************
223  * IMAGEHLP_GetCertificateOffset (INTERNAL)
224  *
225  * Read a file's PE header, and return the offset and size of the
226  *  security directory.
227  */
228 static BOOL IMAGEHLP_GetCertificateOffset( HANDLE handle, DWORD num,
229                                            DWORD *pdwOfs, DWORD *pdwSize )
230 {
231     DWORD size, count, offset, len, sd_VirtualAddr;
232     BOOL r;
233 
234     r = IMAGEHLP_GetSecurityDirOffset( handle, &sd_VirtualAddr, &size );
235     if( !r )
236         return FALSE;
237 
238     offset = 0;
239     /* take the n'th certificate */
240     while( 1 )
241     {
242         /* read the length of the current certificate */
243         count = SetFilePointer( handle, sd_VirtualAddr + offset,
244                                  NULL, FILE_BEGIN );
245         if( count == INVALID_SET_FILE_POINTER )
246             return FALSE;
247         r = ReadFile( handle, &len, sizeof len, &count, NULL );
248         if( !r )
249             return FALSE;
250         if( count != sizeof len )
251             return FALSE;
252 
253         /* check the certificate is not too big or too small */
254         if( len < sizeof len )
255             return FALSE;
256         if( len > (size-offset) )
257             return FALSE;
258         if( !num-- )
259             break;
260 
261         /* calculate the offset of the next certificate */
262         offset += len;
263 
264         /* padded out to the nearest 8-byte boundary */
265         if( len % 8 )
266             offset += 8 - (len % 8);
267 
268         if( offset >= size )
269             return FALSE;
270     }
271 
272     *pdwOfs = sd_VirtualAddr + offset;
273     *pdwSize = len;
274 
275     TRACE("len = %x addr = %x\n", len, sd_VirtualAddr + offset);
276 
277     return TRUE;
278 }
279 
280 /***********************************************************************
281  * IMAGEHLP_RecalculateChecksum (INTERNAL)
282  *
283  * Update the NT header checksum for the specified file.
284  */
285 static BOOL IMAGEHLP_RecalculateChecksum(HANDLE handle)
286 {
287     DWORD FileLength, count, HeaderSum, pe_offset, nt_hdr_size;
288     IMAGE_NT_HEADERS32 nt_hdr32;
289     IMAGE_NT_HEADERS64 nt_hdr64;
290     LPVOID BaseAddress;
291     HANDLE hMapping;
292     DWORD *CheckSum;
293     void *nt_hdr;
294     int ret;
295     BOOL r;
296 
297     TRACE("handle %p\n", handle);
298 
299     ret = IMAGEHLP_GetNTHeaders(handle, &pe_offset, &nt_hdr32, &nt_hdr64);
300 
301     if (ret == HDR_NT32)
302     {
303         CheckSum = &nt_hdr32.OptionalHeader.CheckSum;
304 
305         nt_hdr = &nt_hdr32;
306         nt_hdr_size = sizeof(IMAGE_NT_HEADERS32);
307     }
308     else if (ret == HDR_NT64)
309     {
310         CheckSum = &nt_hdr64.OptionalHeader.CheckSum;
311 
312         nt_hdr = &nt_hdr64;
313         nt_hdr_size = sizeof(IMAGE_NT_HEADERS64);
314     }
315     else
316         return FALSE;
317 
318     hMapping = CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL);
319 
320     if (!hMapping)
321         return FALSE;
322 
323     BaseAddress = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
324 
325     if (!BaseAddress)
326     {
327         CloseHandle(hMapping);
328         return FALSE;
329     }
330 
331     FileLength = GetFileSize(handle, NULL);
332 
333     *CheckSum = 0;
334     CheckSumMappedFile(BaseAddress, FileLength, &HeaderSum, CheckSum);
335 
336     UnmapViewOfFile(BaseAddress);
337     CloseHandle(hMapping);
338 
339     if (*CheckSum)
340     {
341         /* write the header back again */
342         count = SetFilePointer(handle, pe_offset, NULL, FILE_BEGIN);
343 
344         if (count == INVALID_SET_FILE_POINTER)
345             return FALSE;
346 
347         count = 0;
348 
349         r = WriteFile(handle, nt_hdr, nt_hdr_size, &count, NULL);
350 
351         if (!r)
352             return FALSE;
353 
354         if (count != nt_hdr_size)
355             return FALSE;
356 
357         return TRUE;
358     }
359 
360     return FALSE;
361 }
362 
363 /***********************************************************************
364  *		ImageAddCertificate (IMAGEHLP.@)
365  *
366  * Adds the specified certificate to the security directory of
367  * open PE file.
368  */
369 
370 BOOL WINAPI ImageAddCertificate(
371   HANDLE FileHandle, LPWIN_CERTIFICATE Certificate, PDWORD Index)
372 {
373     DWORD size = 0, count = 0, offset = 0, sd_VirtualAddr = 0, index = 0;
374     WIN_CERTIFICATE hdr;
375     const size_t cert_hdr_size = sizeof hdr - sizeof hdr.bCertificate;
376     BOOL r;
377 
378     TRACE("(%p, %p, %p)\n", FileHandle, Certificate, Index);
379 
380     r = IMAGEHLP_GetSecurityDirOffset(FileHandle, &sd_VirtualAddr, &size);
381 
382     /* If we've already got a security directory, find the end of it */
383     if ((r) && (sd_VirtualAddr != 0))
384     {
385         /* Check if the security directory is at the end of the file.
386            If not, we should probably relocate it. */
387         if (GetFileSize(FileHandle, NULL) != sd_VirtualAddr + size)
388         {
389             FIXME("Security directory already present but not located at EOF, not adding certificate\n");
390 
391             SetLastError(ERROR_NOT_SUPPORTED);
392             return FALSE;
393         }
394 
395         while (offset < size)
396         {
397             /* read the length of the current certificate */
398             count = SetFilePointer (FileHandle, sd_VirtualAddr + offset,
399                                      NULL, FILE_BEGIN);
400 
401             if (count == INVALID_SET_FILE_POINTER)
402                 return FALSE;
403 
404             r = ReadFile(FileHandle, &hdr, cert_hdr_size, &count, NULL);
405 
406             if (!r)
407                 return FALSE;
408 
409             if (count != cert_hdr_size)
410                 return FALSE;
411 
412             /* check the certificate is not too big or too small */
413             if (hdr.dwLength < cert_hdr_size)
414                 return FALSE;
415 
416             if (hdr.dwLength > (size-offset))
417                 return FALSE;
418 
419             /* next certificate */
420             offset += hdr.dwLength;
421 
422             /* padded out to the nearest 8-byte boundary */
423             if (hdr.dwLength % 8)
424                 offset += 8 - (hdr.dwLength % 8);
425 
426             index++;
427         }
428 
429         count = SetFilePointer (FileHandle, sd_VirtualAddr + offset, NULL, FILE_BEGIN);
430 
431         if (count == INVALID_SET_FILE_POINTER)
432             return FALSE;
433     }
434     else
435     {
436         sd_VirtualAddr = SetFilePointer(FileHandle, 0, NULL, FILE_END);
437 
438         if (sd_VirtualAddr == INVALID_SET_FILE_POINTER)
439             return FALSE;
440     }
441 
442     /* Write the certificate to the file */
443     r = WriteFile(FileHandle, Certificate, Certificate->dwLength, &count, NULL);
444 
445     if (!r)
446         return FALSE;
447 
448     /* Pad out if necessary */
449     if (Certificate->dwLength % 8)
450     {
451         char null[8];
452 
453         ZeroMemory(null, 8);
454         WriteFile(FileHandle, null, 8 - (Certificate->dwLength % 8), &count, NULL);
455 
456         size += 8 - (Certificate->dwLength % 8);
457     }
458 
459     size += Certificate->dwLength;
460 
461     /* Update the security directory offset and size */
462     if (!IMAGEHLP_SetSecurityDirOffset(FileHandle, sd_VirtualAddr, size))
463         return FALSE;
464 
465     if (!IMAGEHLP_RecalculateChecksum(FileHandle))
466         return FALSE;
467 
468     if(Index)
469         *Index = index;
470     return TRUE;
471 }
472 
473 /***********************************************************************
474  *		ImageEnumerateCertificates (IMAGEHLP.@)
475  */
476 BOOL WINAPI ImageEnumerateCertificates(
477     HANDLE handle, WORD TypeFilter, PDWORD CertificateCount,
478     PDWORD Indices, DWORD IndexCount)
479 {
480     DWORD size, count, offset, sd_VirtualAddr, index;
481     WIN_CERTIFICATE hdr;
482     const size_t cert_hdr_size = sizeof hdr - sizeof hdr.bCertificate;
483     BOOL r;
484 
485     TRACE("%p %hd %p %p %d\n",
486            handle, TypeFilter, CertificateCount, Indices, IndexCount);
487 
488     r = IMAGEHLP_GetSecurityDirOffset( handle, &sd_VirtualAddr, &size );
489     if( !r )
490         return FALSE;
491 
492     offset = 0;
493     index = 0;
494     *CertificateCount = 0;
495     while( offset < size )
496     {
497         /* read the length of the current certificate */
498         count = SetFilePointer( handle, sd_VirtualAddr + offset,
499                                  NULL, FILE_BEGIN );
500         if( count == INVALID_SET_FILE_POINTER )
501             return FALSE;
502         r = ReadFile( handle, &hdr, cert_hdr_size, &count, NULL );
503         if( !r )
504             return FALSE;
505         if( count != cert_hdr_size )
506             return FALSE;
507 
508         TRACE("Size = %08x  id = %08hx\n",
509                hdr.dwLength, hdr.wCertificateType );
510 
511         /* check the certificate is not too big or too small */
512         if( hdr.dwLength < cert_hdr_size )
513             return FALSE;
514         if( hdr.dwLength > (size-offset) )
515             return FALSE;
516 
517         if( (TypeFilter == CERT_SECTION_TYPE_ANY) ||
518             (TypeFilter == hdr.wCertificateType) )
519         {
520             (*CertificateCount)++;
521             if(Indices && *CertificateCount <= IndexCount)
522                 *Indices++ = index;
523         }
524 
525         /* next certificate */
526         offset += hdr.dwLength;
527 
528         /* padded out to the nearest 8-byte boundary */
529         if (hdr.dwLength % 8)
530             offset += 8 - (hdr.dwLength % 8);
531 
532         index++;
533     }
534 
535     return TRUE;
536 }
537 
538 /***********************************************************************
539  *		ImageGetCertificateData (IMAGEHLP.@)
540  *
541  *  FIXME: not sure that I'm dealing with the Index the right way
542  */
543 BOOL WINAPI ImageGetCertificateData(
544                 HANDLE handle, DWORD Index,
545                 LPWIN_CERTIFICATE Certificate, PDWORD RequiredLength)
546 {
547     DWORD r, offset, ofs, size, count;
548 
549     TRACE("%p %d %p %p\n", handle, Index, Certificate, RequiredLength);
550 
551     if( !RequiredLength)
552     {
553         SetLastError( ERROR_INVALID_PARAMETER );
554         return FALSE;
555     }
556 
557     if( !IMAGEHLP_GetCertificateOffset( handle, Index, &ofs, &size ) )
558         return FALSE;
559 
560     if( *RequiredLength < size )
561     {
562         *RequiredLength = size;
563         SetLastError( ERROR_INSUFFICIENT_BUFFER );
564         return FALSE;
565     }
566 
567     if( !Certificate )
568     {
569         SetLastError( ERROR_INVALID_PARAMETER );
570         return FALSE;
571     }
572 
573     *RequiredLength = size;
574 
575     offset = SetFilePointer( handle, ofs, NULL, FILE_BEGIN );
576     if( offset == INVALID_SET_FILE_POINTER )
577         return FALSE;
578 
579     r = ReadFile( handle, Certificate, size, &count, NULL );
580     if( !r )
581         return FALSE;
582     if( count != size )
583         return FALSE;
584 
585     TRACE("OK\n");
586     SetLastError( NO_ERROR );
587 
588     return TRUE;
589 }
590 
591 /***********************************************************************
592  *		ImageGetCertificateHeader (IMAGEHLP.@)
593  */
594 BOOL WINAPI ImageGetCertificateHeader(
595     HANDLE handle, DWORD index, LPWIN_CERTIFICATE pCert)
596 {
597     DWORD r, offset, ofs, size, count;
598     const size_t cert_hdr_size = sizeof *pCert - sizeof pCert->bCertificate;
599 
600     TRACE("%p %d %p\n", handle, index, pCert);
601 
602     if( !IMAGEHLP_GetCertificateOffset( handle, index, &ofs, &size ) )
603         return FALSE;
604 
605     if( size < cert_hdr_size )
606         return FALSE;
607 
608     offset = SetFilePointer( handle, ofs, NULL, FILE_BEGIN );
609     if( offset == INVALID_SET_FILE_POINTER )
610         return FALSE;
611 
612     r = ReadFile( handle, pCert, cert_hdr_size, &count, NULL );
613     if( !r )
614         return FALSE;
615     if( count != cert_hdr_size )
616         return FALSE;
617 
618     TRACE("OK\n");
619 
620     return TRUE;
621 }
622 
623 /* Finds the section named section in the array of IMAGE_SECTION_HEADERs hdr.  If
624  * found, returns the offset to the section.  Otherwise returns 0.  If the section
625  * is found, optionally returns the size of the section (in size) and the base
626  * address of the section (in base.)
627  */
628 static DWORD IMAGEHLP_GetSectionOffset( IMAGE_SECTION_HEADER *hdr,
629     DWORD num_sections, LPCSTR section, PDWORD size, PDWORD base )
630 {
631     DWORD i, offset = 0;
632 
633     for( i = 0; !offset && i < num_sections; i++, hdr++ )
634     {
635         if( !memcmp( hdr->Name, section, strlen(section) ) )
636         {
637             offset = hdr->PointerToRawData;
638             if( size )
639                 *size = hdr->SizeOfRawData;
640             if( base )
641                 *base = hdr->VirtualAddress;
642         }
643     }
644     return offset;
645 }
646 
647 /* Calls DigestFunction e bytes at offset offset from the file mapped at map.
648  * Returns the return value of DigestFunction, or FALSE if the data is not available.
649  */
650 static BOOL IMAGEHLP_ReportSectionFromOffset( DWORD offset, DWORD size,
651     BYTE *map, DWORD fileSize, DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle )
652 {
653     if( offset + size > fileSize )
654     {
655         SetLastError(ERROR_INVALID_PARAMETER);
656         return FALSE;
657     }
658     return DigestFunction( DigestHandle, map + offset, size );
659 }
660 
661 /* Finds the section named section among the IMAGE_SECTION_HEADERs in
662  * section_headers and calls DigestFunction for this section.  Returns
663  * the return value from DigestFunction, or FALSE if the data could not be read.
664  */
665 static BOOL IMAGEHLP_ReportSection( IMAGE_SECTION_HEADER *section_headers,
666     DWORD num_sections, LPCSTR section, BYTE *map, DWORD fileSize,
667     DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle )
668 {
669     DWORD offset, size = 0;
670 
671     offset = IMAGEHLP_GetSectionOffset( section_headers, num_sections, section,
672         &size, NULL );
673     if( !offset )
674         return FALSE;
675     return IMAGEHLP_ReportSectionFromOffset( offset, size, map, fileSize,
676             DigestFunction, DigestHandle );
677 }
678 
679 /* Calls DigestFunction for all sections with the IMAGE_SCN_CNT_CODE flag set.
680  * Returns the return value from * DigestFunction, or FALSE if a section could not be read.
681  */
682 static BOOL IMAGEHLP_ReportCodeSections( IMAGE_SECTION_HEADER *hdr, DWORD num_sections,
683     BYTE *map, DWORD fileSize, DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle )
684 {
685     DWORD i;
686     BOOL ret = TRUE;
687 
688     for( i = 0; ret && i < num_sections; i++, hdr++ )
689     {
690         if( hdr->Characteristics & IMAGE_SCN_CNT_CODE )
691             ret = IMAGEHLP_ReportSectionFromOffset( hdr->PointerToRawData,
692                 hdr->SizeOfRawData, map, fileSize, DigestFunction, DigestHandle );
693     }
694     return ret;
695 }
696 
697 /* Reports the import section from the file FileHandle.  If
698  * CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO is set in DigestLevel, reports the entire
699  * import section.
700  * FIXME: if it's not set, the function currently fails.
701  */
702 static BOOL IMAGEHLP_ReportImportSection( IMAGE_SECTION_HEADER *hdr,
703     DWORD num_sections, BYTE *map, DWORD fileSize, DWORD DigestLevel,
704     DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle )
705 {
706     BOOL ret = FALSE;
707     DWORD offset, size, base;
708 
709     /* Get import data */
710     offset = IMAGEHLP_GetSectionOffset( hdr, num_sections, ".idata", &size,
711         &base );
712     if( !offset )
713         return FALSE;
714 
715     /* If CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO is set, the entire
716      * section is reported.  Otherwise, the debug info section is
717      * decoded and reported piecemeal.  See tests.  However, I haven't been
718      * able to figure out how the native implementation decides which values
719      * to report.  Either it's buggy or my understanding is flawed.
720      */
721     if( DigestLevel & CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO )
722         ret = IMAGEHLP_ReportSectionFromOffset( offset, size, map, fileSize,
723                 DigestFunction, DigestHandle );
724     else
725     {
726         FIXME("not supported except for CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO\n");
727         SetLastError(ERROR_INVALID_PARAMETER);
728         ret = FALSE;
729     }
730 
731     return ret;
732 }
733 
734 /***********************************************************************
735  *		ImageGetDigestStream (IMAGEHLP.@)
736  *
737  * Gets a stream of bytes from a PE file over which a hash might be computed to
738  * verify that the image has not changed.  Useful for creating a certificate to
739  * be added to the file with ImageAddCertificate.
740  *
741  * PARAMS
742  *  FileHandle     [In] File for which to return a stream.
743  *  DigestLevel    [In] Flags to control which portions of the file to return.
744  *                      0 is allowed, as is any combination of:
745  *                       CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO: reports the entire
746  *                        import section rather than selected portions of it.
747  *                       CERT_PE_IMAGE_DIGEST_DEBUG_INFO: reports the debug section.
748  *                       CERT_PE_IMAGE_DIGEST_RESOURCES: reports the resources
749                           section.
750  *  DigestFunction [In] Callback function.
751  *  DigestHandle   [In] Handle passed as first parameter to DigestFunction.
752  *
753  * RETURNS
754  *  TRUE if successful.
755  *  FALSE if unsuccessful.  GetLastError returns more about the error.
756  *
757  * NOTES
758  *  Only supports 32-bit PE files, not tested with any other format.
759  *  Reports data in the following order:
760  *  1. The file headers are reported first
761  *  2. Any code sections are reported next.
762  *  3. The data (".data" and ".rdata") sections are reported next.
763  *  4. The import section is reported next.
764  *  5. If CERT_PE_IMAGE_DIGEST_DEBUG_INFO is set in DigestLevel, the debug section is
765  *     reported next.
766  *  6. If CERT_PE_IMAGE_DIGEST_RESOURCES is set in DigestLevel, the resources section
767  *     is reported next.
768  *
769  * BUGS
770  *  CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO must be specified, returns an error if not.
771  */
772 BOOL WINAPI ImageGetDigestStream(
773   HANDLE FileHandle, DWORD DigestLevel,
774   DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle)
775 {
776     DWORD error = 0;
777     BOOL ret = FALSE;
778     DWORD offset, size, num_sections, fileSize;
779     HANDLE hMap = INVALID_HANDLE_VALUE;
780     BYTE *map = NULL;
781     IMAGE_DOS_HEADER *dos_hdr;
782     IMAGE_NT_HEADERS *nt_hdr;
783     IMAGE_SECTION_HEADER *section_headers;
784 
785     TRACE("(%p, %d, %p, %p)\n", FileHandle, DigestLevel, DigestFunction,
786         DigestHandle);
787 
788     /* Get the file size */
789     if( !FileHandle )
790         goto invalid_parameter;
791     fileSize = GetFileSize( FileHandle, NULL );
792     if(fileSize == INVALID_FILE_SIZE )
793         goto invalid_parameter;
794 
795     /* map file */
796     hMap = CreateFileMappingW( FileHandle, NULL, PAGE_READONLY, 0, 0, NULL );
797     if( hMap == INVALID_HANDLE_VALUE )
798         goto invalid_parameter;
799     map = MapViewOfFile( hMap, FILE_MAP_COPY, 0, 0, 0 );
800     if( !map )
801         goto invalid_parameter;
802 
803     /* Read the file header */
804     if( fileSize < sizeof(IMAGE_DOS_HEADER) )
805         goto invalid_parameter;
806     dos_hdr = (IMAGE_DOS_HEADER *)map;
807 
808     if( dos_hdr->e_magic != IMAGE_DOS_SIGNATURE )
809         goto invalid_parameter;
810     offset = dos_hdr->e_lfanew;
811     if( !offset || offset > fileSize )
812         goto invalid_parameter;
813     ret = DigestFunction( DigestHandle, map, offset );
814     if( !ret )
815         goto end;
816 
817     /* Read the NT header */
818     if( offset + sizeof(IMAGE_NT_HEADERS) > fileSize )
819         goto invalid_parameter;
820     nt_hdr = (IMAGE_NT_HEADERS *)(map + offset);
821     if( nt_hdr->Signature != IMAGE_NT_SIGNATURE )
822         goto invalid_parameter;
823     /* It's clear why the checksum is cleared, but why only these size headers?
824      */
825     nt_hdr->OptionalHeader.SizeOfInitializedData = 0;
826     nt_hdr->OptionalHeader.SizeOfImage = 0;
827     nt_hdr->OptionalHeader.CheckSum = 0;
828     size = sizeof(nt_hdr->Signature) + sizeof(nt_hdr->FileHeader) +
829         nt_hdr->FileHeader.SizeOfOptionalHeader;
830     ret = DigestFunction( DigestHandle, map + offset, size );
831     if( !ret )
832         goto end;
833 
834     /* Read the section headers */
835     offset += size;
836     num_sections = nt_hdr->FileHeader.NumberOfSections;
837     size = num_sections * sizeof(IMAGE_SECTION_HEADER);
838     if( offset + size > fileSize )
839         goto invalid_parameter;
840     ret = DigestFunction( DigestHandle, map + offset, size );
841     if( !ret )
842         goto end;
843 
844     section_headers = (IMAGE_SECTION_HEADER *)(map + offset);
845     IMAGEHLP_ReportCodeSections( section_headers, num_sections,
846         map, fileSize, DigestFunction, DigestHandle );
847     IMAGEHLP_ReportSection( section_headers, num_sections, ".data",
848         map, fileSize, DigestFunction, DigestHandle );
849     IMAGEHLP_ReportSection( section_headers, num_sections, ".rdata",
850         map, fileSize, DigestFunction, DigestHandle );
851     IMAGEHLP_ReportImportSection( section_headers, num_sections,
852         map, fileSize, DigestLevel, DigestFunction, DigestHandle );
853     if( DigestLevel & CERT_PE_IMAGE_DIGEST_DEBUG_INFO )
854         IMAGEHLP_ReportSection( section_headers, num_sections, ".debug",
855             map, fileSize, DigestFunction, DigestHandle );
856     if( DigestLevel & CERT_PE_IMAGE_DIGEST_RESOURCES )
857         IMAGEHLP_ReportSection( section_headers, num_sections, ".rsrc",
858             map, fileSize, DigestFunction, DigestHandle );
859 
860 end:
861     if( map )
862         UnmapViewOfFile( map );
863     if( hMap != INVALID_HANDLE_VALUE )
864         CloseHandle( hMap );
865     if( error )
866         SetLastError(error);
867     return ret;
868 
869 invalid_parameter:
870     error = ERROR_INVALID_PARAMETER;
871     goto end;
872 }
873 
874 /***********************************************************************
875  *		ImageRemoveCertificate (IMAGEHLP.@)
876  */
877 BOOL WINAPI ImageRemoveCertificate(HANDLE FileHandle, DWORD Index)
878 {
879     DWORD size = 0, count = 0, sd_VirtualAddr = 0, offset = 0;
880     DWORD data_size = 0, cert_size = 0, cert_size_padded = 0, ret = 0;
881     LPVOID cert_data;
882     BOOL r;
883 
884     TRACE("(%p, %d)\n", FileHandle, Index);
885 
886     r = ImageEnumerateCertificates(FileHandle, CERT_SECTION_TYPE_ANY, &count, NULL, 0);
887 
888     if ((!r) || (count == 0))
889         return FALSE;
890 
891     if ((!IMAGEHLP_GetSecurityDirOffset(FileHandle, &sd_VirtualAddr, &size)) ||
892         (!IMAGEHLP_GetCertificateOffset(FileHandle, Index, &offset, &cert_size)))
893         return FALSE;
894 
895     /* Ignore any padding we have, too */
896     if (cert_size % 8)
897         cert_size_padded = cert_size + (8 - (cert_size % 8));
898     else
899         cert_size_padded = cert_size;
900 
901     data_size = size - (offset - sd_VirtualAddr) - cert_size_padded;
902 
903     if (data_size == 0)
904     {
905         ret = SetFilePointer(FileHandle, sd_VirtualAddr, NULL, FILE_BEGIN);
906 
907         if (ret == INVALID_SET_FILE_POINTER)
908             return FALSE;
909     }
910     else
911     {
912         cert_data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, data_size);
913 
914         if (!cert_data)
915             return FALSE;
916 
917         ret = SetFilePointer(FileHandle, offset + cert_size_padded, NULL, FILE_BEGIN);
918 
919         if (ret == INVALID_SET_FILE_POINTER)
920             goto error;
921 
922         /* Read any subsequent certificates */
923         r = ReadFile(FileHandle, cert_data, data_size, &count, NULL);
924 
925         if ((!r) || (count != data_size))
926             goto error;
927 
928         SetFilePointer(FileHandle, offset, NULL, FILE_BEGIN);
929 
930         /* Write them one index back */
931         r = WriteFile(FileHandle, cert_data, data_size, &count, NULL);
932 
933         if ((!r) || (count != data_size))
934             goto error;
935 
936         HeapFree(GetProcessHeap(), 0, cert_data);
937     }
938 
939     /* If security directory is at end of file, trim the file */
940     if (GetFileSize(FileHandle, NULL) == sd_VirtualAddr + size)
941         SetEndOfFile(FileHandle);
942 
943     if (count == 1)
944         r = IMAGEHLP_SetSecurityDirOffset(FileHandle, 0, 0);
945     else
946         r = IMAGEHLP_SetSecurityDirOffset(FileHandle, sd_VirtualAddr, size - cert_size_padded);
947 
948     if (!r)
949         return FALSE;
950 
951     if (!IMAGEHLP_RecalculateChecksum(FileHandle))
952         return FALSE;
953 
954     return TRUE;
955 
956 error:
957     HeapFree(GetProcessHeap(), 0, cert_data);
958     return FALSE;
959 }
960