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