xref: /reactos/dll/win32/imagehlp/integrity.c (revision 15dda9eb)
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  */
IMAGEHLP_GetNTHeaders(HANDLE handle,DWORD * pe_offset,IMAGE_NT_HEADERS32 * nt32,IMAGE_NT_HEADERS64 * nt64)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 != IMAGE_DOS_SIGNATURE)
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  */
IMAGEHLP_GetSecurityDirOffset(HANDLE handle,DWORD * pdwOfs,DWORD * pdwSize)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  */
IMAGEHLP_SetSecurityDirOffset(HANDLE handle,DWORD dwOfs,DWORD dwSize)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  */
IMAGEHLP_GetCertificateOffset(HANDLE handle,DWORD num,DWORD * pdwOfs,DWORD * pdwSize)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  */
IMAGEHLP_RecalculateChecksum(HANDLE handle)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 
ImageAddCertificate(HANDLE FileHandle,LPWIN_CERTIFICATE Certificate,PDWORD Index)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         /* Check if the security directory is at the end of the file.
396            If not, we should probably relocate it. */
397         if (GetFileSize(FileHandle, NULL) != sd_VirtualAddr + size)
398         {
399             FIXME("Security directory already present but not located at EOF, not adding certificate\n");
400 
401             SetLastError(ERROR_NOT_SUPPORTED);
402             return FALSE;
403         }
404 
405         while (offset < size)
406         {
407             /* read the length of the current certificate */
408             count = SetFilePointer (FileHandle, sd_VirtualAddr + offset,
409                                      NULL, FILE_BEGIN);
410 
411             if (count == INVALID_SET_FILE_POINTER)
412                 return FALSE;
413 
414             r = ReadFile(FileHandle, &hdr, cert_hdr_size, &count, NULL);
415 
416             if (!r)
417                 return FALSE;
418 
419             if (count != cert_hdr_size)
420                 return FALSE;
421 
422             /* check the certificate is not too big or too small */
423             if (hdr.dwLength < cert_hdr_size)
424                 return FALSE;
425 
426             if (hdr.dwLength > (size-offset))
427                 return FALSE;
428 
429             /* next certificate */
430             offset += hdr.dwLength;
431 
432             /* padded out to the nearest 8-byte boundary */
433             if (hdr.dwLength % 8)
434                 offset += 8 - (hdr.dwLength % 8);
435 
436             index++;
437         }
438 
439         count = SetFilePointer (FileHandle, sd_VirtualAddr + offset, NULL, FILE_BEGIN);
440 
441         if (count == INVALID_SET_FILE_POINTER)
442             return FALSE;
443     }
444     else
445     {
446         sd_VirtualAddr = SetFilePointer(FileHandle, 0, NULL, FILE_END);
447 
448         if (sd_VirtualAddr == INVALID_SET_FILE_POINTER)
449             return FALSE;
450     }
451 
452     /* Write the certificate to the file */
453     r = WriteFile(FileHandle, Certificate, Certificate->dwLength, &count, NULL);
454 
455     if (!r)
456         return FALSE;
457 
458     /* Pad out if necessary */
459     if (Certificate->dwLength % 8)
460     {
461         char null[8];
462 
463         ZeroMemory(null, 8);
464         WriteFile(FileHandle, null, 8 - (Certificate->dwLength % 8), &count, NULL);
465 
466         size += 8 - (Certificate->dwLength % 8);
467     }
468 
469     size += Certificate->dwLength;
470 
471     /* Update the security directory offset and size */
472     if (!IMAGEHLP_SetSecurityDirOffset(FileHandle, sd_VirtualAddr, size))
473         return FALSE;
474 
475     if (!IMAGEHLP_RecalculateChecksum(FileHandle))
476         return FALSE;
477 
478     if(Index)
479         *Index = index;
480     return TRUE;
481 }
482 
483 /***********************************************************************
484  *		ImageEnumerateCertificates (IMAGEHLP.@)
485  */
ImageEnumerateCertificates(HANDLE handle,WORD TypeFilter,PDWORD CertificateCount,PDWORD Indices,DWORD IndexCount)486 BOOL WINAPI ImageEnumerateCertificates(
487     HANDLE handle, WORD TypeFilter, PDWORD CertificateCount,
488     PDWORD Indices, DWORD IndexCount)
489 {
490     DWORD size, count, offset, sd_VirtualAddr, index;
491     WIN_CERTIFICATE hdr;
492     const size_t cert_hdr_size = sizeof hdr - sizeof hdr.bCertificate;
493     BOOL r;
494 
495     TRACE("%p %hd %p %p %d\n",
496            handle, TypeFilter, CertificateCount, Indices, IndexCount);
497 
498     r = IMAGEHLP_GetSecurityDirOffset( handle, &sd_VirtualAddr, &size );
499     if( !r )
500         return FALSE;
501 
502     offset = 0;
503     index = 0;
504     *CertificateCount = 0;
505     while( offset < size )
506     {
507         /* read the length of the current certificate */
508         count = SetFilePointer( handle, sd_VirtualAddr + offset,
509                                  NULL, FILE_BEGIN );
510         if( count == INVALID_SET_FILE_POINTER )
511             return FALSE;
512         r = ReadFile( handle, &hdr, cert_hdr_size, &count, NULL );
513         if( !r )
514             return FALSE;
515         if( count != cert_hdr_size )
516             return FALSE;
517 
518         TRACE("Size = %08x  id = %08hx\n",
519                hdr.dwLength, hdr.wCertificateType );
520 
521         /* check the certificate is not too big or too small */
522         if( hdr.dwLength < cert_hdr_size )
523             return FALSE;
524         if( hdr.dwLength > (size-offset) )
525             return FALSE;
526 
527         if( (TypeFilter == CERT_SECTION_TYPE_ANY) ||
528             (TypeFilter == hdr.wCertificateType) )
529         {
530             (*CertificateCount)++;
531             if(Indices && *CertificateCount <= IndexCount)
532                 *Indices++ = index;
533         }
534 
535         /* next certificate */
536         offset += hdr.dwLength;
537 
538         /* padded out to the nearest 8-byte boundary */
539         if (hdr.dwLength % 8)
540             offset += 8 - (hdr.dwLength % 8);
541 
542         index++;
543     }
544 
545     return TRUE;
546 }
547 
548 /***********************************************************************
549  *		ImageGetCertificateData (IMAGEHLP.@)
550  *
551  *  FIXME: not sure that I'm dealing with the Index the right way
552  */
ImageGetCertificateData(HANDLE handle,DWORD Index,LPWIN_CERTIFICATE Certificate,PDWORD RequiredLength)553 BOOL WINAPI ImageGetCertificateData(
554                 HANDLE handle, DWORD Index,
555                 LPWIN_CERTIFICATE Certificate, PDWORD RequiredLength)
556 {
557     DWORD r, offset, ofs, size, count;
558 
559     TRACE("%p %d %p %p\n", handle, Index, Certificate, RequiredLength);
560 
561     if( !RequiredLength)
562     {
563         SetLastError( ERROR_INVALID_PARAMETER );
564         return FALSE;
565     }
566 
567     if( !IMAGEHLP_GetCertificateOffset( handle, Index, &ofs, &size ) )
568         return FALSE;
569 
570     if( *RequiredLength < size )
571     {
572         *RequiredLength = size;
573         SetLastError( ERROR_INSUFFICIENT_BUFFER );
574         return FALSE;
575     }
576 
577     if( !Certificate )
578     {
579         SetLastError( ERROR_INVALID_PARAMETER );
580         return FALSE;
581     }
582 
583     *RequiredLength = size;
584 
585     offset = SetFilePointer( handle, ofs, NULL, FILE_BEGIN );
586     if( offset == INVALID_SET_FILE_POINTER )
587         return FALSE;
588 
589     r = ReadFile( handle, Certificate, size, &count, NULL );
590     if( !r )
591         return FALSE;
592     if( count != size )
593         return FALSE;
594 
595     TRACE("OK\n");
596     SetLastError( NO_ERROR );
597 
598     return TRUE;
599 }
600 
601 /***********************************************************************
602  *		ImageGetCertificateHeader (IMAGEHLP.@)
603  */
ImageGetCertificateHeader(HANDLE handle,DWORD index,LPWIN_CERTIFICATE pCert)604 BOOL WINAPI ImageGetCertificateHeader(
605     HANDLE handle, DWORD index, LPWIN_CERTIFICATE pCert)
606 {
607     DWORD r, offset, ofs, size, count;
608     const size_t cert_hdr_size = sizeof *pCert - sizeof pCert->bCertificate;
609 
610     TRACE("%p %d %p\n", handle, index, pCert);
611 
612     if( !IMAGEHLP_GetCertificateOffset( handle, index, &ofs, &size ) )
613         return FALSE;
614 
615     if( size < cert_hdr_size )
616         return FALSE;
617 
618     offset = SetFilePointer( handle, ofs, NULL, FILE_BEGIN );
619     if( offset == INVALID_SET_FILE_POINTER )
620         return FALSE;
621 
622     r = ReadFile( handle, pCert, cert_hdr_size, &count, NULL );
623     if( !r )
624         return FALSE;
625     if( count != cert_hdr_size )
626         return FALSE;
627 
628     TRACE("OK\n");
629 
630     return TRUE;
631 }
632 
633 /* Finds the section named section in the array of IMAGE_SECTION_HEADERs hdr.  If
634  * found, returns the offset to the section.  Otherwise returns 0.  If the section
635  * is found, optionally returns the size of the section (in size) and the base
636  * address of the section (in base.)
637  */
IMAGEHLP_GetSectionOffset(IMAGE_SECTION_HEADER * hdr,DWORD num_sections,LPCSTR section,PDWORD size,PDWORD base)638 static DWORD IMAGEHLP_GetSectionOffset( IMAGE_SECTION_HEADER *hdr,
639     DWORD num_sections, LPCSTR section, PDWORD size, PDWORD base )
640 {
641     DWORD i, offset = 0;
642 
643     for( i = 0; !offset && i < num_sections; i++, hdr++ )
644     {
645         if( !memcmp( hdr->Name, section, strlen(section) ) )
646         {
647             offset = hdr->PointerToRawData;
648             if( size )
649                 *size = hdr->SizeOfRawData;
650             if( base )
651                 *base = hdr->VirtualAddress;
652         }
653     }
654     return offset;
655 }
656 
657 /* Calls DigestFunction e bytes at offset offset from the file mapped at map.
658  * Returns the return value of DigestFunction, or FALSE if the data is not available.
659  */
IMAGEHLP_ReportSectionFromOffset(DWORD offset,DWORD size,BYTE * map,DWORD fileSize,DIGEST_FUNCTION DigestFunction,DIGEST_HANDLE DigestHandle)660 static BOOL IMAGEHLP_ReportSectionFromOffset( DWORD offset, DWORD size,
661     BYTE *map, DWORD fileSize, DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle )
662 {
663     if( offset + size > fileSize )
664     {
665         SetLastError(ERROR_INVALID_PARAMETER);
666         return FALSE;
667     }
668     return DigestFunction( DigestHandle, map + offset, size );
669 }
670 
671 /* Finds the section named section among the IMAGE_SECTION_HEADERs in
672  * section_headers and calls DigestFunction for this section.  Returns
673  * the return value from DigestFunction, or FALSE if the data could not be read.
674  */
IMAGEHLP_ReportSection(IMAGE_SECTION_HEADER * section_headers,DWORD num_sections,LPCSTR section,BYTE * map,DWORD fileSize,DIGEST_FUNCTION DigestFunction,DIGEST_HANDLE DigestHandle)675 static BOOL IMAGEHLP_ReportSection( IMAGE_SECTION_HEADER *section_headers,
676     DWORD num_sections, LPCSTR section, BYTE *map, DWORD fileSize,
677     DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle )
678 {
679     DWORD offset, size = 0;
680 
681     offset = IMAGEHLP_GetSectionOffset( section_headers, num_sections, section,
682         &size, NULL );
683     if( !offset )
684         return FALSE;
685     return IMAGEHLP_ReportSectionFromOffset( offset, size, map, fileSize,
686             DigestFunction, DigestHandle );
687 }
688 
689 /* Calls DigestFunction for all sections with the IMAGE_SCN_CNT_CODE flag set.
690  * Returns the return value from * DigestFunction, or FALSE if a section could not be read.
691  */
IMAGEHLP_ReportCodeSections(IMAGE_SECTION_HEADER * hdr,DWORD num_sections,BYTE * map,DWORD fileSize,DIGEST_FUNCTION DigestFunction,DIGEST_HANDLE DigestHandle)692 static BOOL IMAGEHLP_ReportCodeSections( IMAGE_SECTION_HEADER *hdr, DWORD num_sections,
693     BYTE *map, DWORD fileSize, DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle )
694 {
695     DWORD i;
696     BOOL ret = TRUE;
697 
698     for( i = 0; ret && i < num_sections; i++, hdr++ )
699     {
700         if( hdr->Characteristics & IMAGE_SCN_CNT_CODE )
701             ret = IMAGEHLP_ReportSectionFromOffset( hdr->PointerToRawData,
702                 hdr->SizeOfRawData, map, fileSize, DigestFunction, DigestHandle );
703     }
704     return ret;
705 }
706 
707 /* Reports the import section from the file FileHandle.  If
708  * CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO is set in DigestLevel, reports the entire
709  * import section.
710  * FIXME: if it's not set, the function currently fails.
711  */
IMAGEHLP_ReportImportSection(IMAGE_SECTION_HEADER * hdr,DWORD num_sections,BYTE * map,DWORD fileSize,DWORD DigestLevel,DIGEST_FUNCTION DigestFunction,DIGEST_HANDLE DigestHandle)712 static BOOL IMAGEHLP_ReportImportSection( IMAGE_SECTION_HEADER *hdr,
713     DWORD num_sections, BYTE *map, DWORD fileSize, DWORD DigestLevel,
714     DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle )
715 {
716     BOOL ret = FALSE;
717     DWORD offset, size, base;
718 
719     /* Get import data */
720     offset = IMAGEHLP_GetSectionOffset( hdr, num_sections, ".idata", &size,
721         &base );
722     if( !offset )
723         return FALSE;
724 
725     /* If CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO is set, the entire
726      * section is reported.  Otherwise, the debug info section is
727      * decoded and reported piecemeal.  See tests.  However, I haven't been
728      * able to figure out how the native implementation decides which values
729      * to report.  Either it's buggy or my understanding is flawed.
730      */
731     if( DigestLevel & CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO )
732         ret = IMAGEHLP_ReportSectionFromOffset( offset, size, map, fileSize,
733                 DigestFunction, DigestHandle );
734     else
735     {
736         FIXME("not supported except for CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO\n");
737         SetLastError(ERROR_INVALID_PARAMETER);
738         ret = FALSE;
739     }
740 
741     return ret;
742 }
743 
744 /***********************************************************************
745  *		ImageGetDigestStream (IMAGEHLP.@)
746  *
747  * Gets a stream of bytes from a PE file over which a hash might be computed to
748  * verify that the image has not changed.  Useful for creating a certificate to
749  * be added to the file with ImageAddCertificate.
750  *
751  * PARAMS
752  *  FileHandle     [In] File for which to return a stream.
753  *  DigestLevel    [In] Flags to control which portions of the file to return.
754  *                      0 is allowed, as is any combination of:
755  *                       CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO: reports the entire
756  *                        import section rather than selected portions of it.
757  *                       CERT_PE_IMAGE_DIGEST_DEBUG_INFO: reports the debug section.
758  *                       CERT_PE_IMAGE_DIGEST_RESOURCES: reports the resources
759                           section.
760  *  DigestFunction [In] Callback function.
761  *  DigestHandle   [In] Handle passed as first parameter to DigestFunction.
762  *
763  * RETURNS
764  *  TRUE if successful.
765  *  FALSE if unsuccessful.  GetLastError returns more about the error.
766  *
767  * NOTES
768  *  Only supports 32-bit PE files, not tested with any other format.
769  *  Reports data in the following order:
770  *  1. The file headers are reported first
771  *  2. Any code sections are reported next.
772  *  3. The data (".data" and ".rdata") sections are reported next.
773  *  4. The import section is reported next.
774  *  5. If CERT_PE_IMAGE_DIGEST_DEBUG_INFO is set in DigestLevel, the debug section is
775  *     reported next.
776  *  6. If CERT_PE_IMAGE_DIGEST_RESOURCES is set in DigestLevel, the resources section
777  *     is reported next.
778  *
779  * BUGS
780  *  CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO must be specified, returns an error if not.
781  */
ImageGetDigestStream(HANDLE FileHandle,DWORD DigestLevel,DIGEST_FUNCTION DigestFunction,DIGEST_HANDLE DigestHandle)782 BOOL WINAPI ImageGetDigestStream(
783   HANDLE FileHandle, DWORD DigestLevel,
784   DIGEST_FUNCTION DigestFunction, DIGEST_HANDLE DigestHandle)
785 {
786     DWORD error = 0;
787     BOOL ret = FALSE;
788     DWORD offset, size, num_sections, fileSize;
789     HANDLE hMap = INVALID_HANDLE_VALUE;
790     BYTE *map = NULL;
791     IMAGE_DOS_HEADER *dos_hdr;
792     IMAGE_NT_HEADERS *nt_hdr;
793     IMAGE_SECTION_HEADER *section_headers;
794 
795     TRACE("(%p, %d, %p, %p)\n", FileHandle, DigestLevel, DigestFunction,
796         DigestHandle);
797 
798     /* Get the file size */
799     if( !FileHandle )
800         goto invalid_parameter;
801     fileSize = GetFileSize( FileHandle, NULL );
802     if(fileSize == INVALID_FILE_SIZE )
803         goto invalid_parameter;
804 
805     /* map file */
806     hMap = CreateFileMappingW( FileHandle, NULL, PAGE_READONLY, 0, 0, NULL );
807     if( hMap == INVALID_HANDLE_VALUE )
808         goto invalid_parameter;
809     map = MapViewOfFile( hMap, FILE_MAP_COPY, 0, 0, 0 );
810     if( !map )
811         goto invalid_parameter;
812 
813     /* Read the file header */
814     if( fileSize < sizeof(IMAGE_DOS_HEADER) )
815         goto invalid_parameter;
816     dos_hdr = (IMAGE_DOS_HEADER *)map;
817 
818     if( dos_hdr->e_magic != IMAGE_DOS_SIGNATURE )
819         goto invalid_parameter;
820     offset = dos_hdr->e_lfanew;
821     if( !offset || offset > fileSize )
822         goto invalid_parameter;
823     ret = DigestFunction( DigestHandle, map, offset );
824     if( !ret )
825         goto end;
826 
827     /* Read the NT header */
828     if( offset + sizeof(IMAGE_NT_HEADERS) > fileSize )
829         goto invalid_parameter;
830     nt_hdr = (IMAGE_NT_HEADERS *)(map + offset);
831     if( nt_hdr->Signature != IMAGE_NT_SIGNATURE )
832         goto invalid_parameter;
833     /* It's clear why the checksum is cleared, but why only these size headers?
834      */
835     nt_hdr->OptionalHeader.SizeOfInitializedData = 0;
836     nt_hdr->OptionalHeader.SizeOfImage = 0;
837     nt_hdr->OptionalHeader.CheckSum = 0;
838     size = sizeof(nt_hdr->Signature) + sizeof(nt_hdr->FileHeader) +
839         nt_hdr->FileHeader.SizeOfOptionalHeader;
840     ret = DigestFunction( DigestHandle, map + offset, size );
841     if( !ret )
842         goto end;
843 
844     /* Read the section headers */
845     offset += size;
846     num_sections = nt_hdr->FileHeader.NumberOfSections;
847     size = num_sections * sizeof(IMAGE_SECTION_HEADER);
848     if( offset + size > fileSize )
849         goto invalid_parameter;
850     ret = DigestFunction( DigestHandle, map + offset, size );
851     if( !ret )
852         goto end;
853 
854     section_headers = (IMAGE_SECTION_HEADER *)(map + offset);
855     IMAGEHLP_ReportCodeSections( section_headers, num_sections,
856         map, fileSize, DigestFunction, DigestHandle );
857     IMAGEHLP_ReportSection( section_headers, num_sections, ".data",
858         map, fileSize, DigestFunction, DigestHandle );
859     IMAGEHLP_ReportSection( section_headers, num_sections, ".rdata",
860         map, fileSize, DigestFunction, DigestHandle );
861     IMAGEHLP_ReportImportSection( section_headers, num_sections,
862         map, fileSize, DigestLevel, DigestFunction, DigestHandle );
863     if( DigestLevel & CERT_PE_IMAGE_DIGEST_DEBUG_INFO )
864         IMAGEHLP_ReportSection( section_headers, num_sections, ".debug",
865             map, fileSize, DigestFunction, DigestHandle );
866     if( DigestLevel & CERT_PE_IMAGE_DIGEST_RESOURCES )
867         IMAGEHLP_ReportSection( section_headers, num_sections, ".rsrc",
868             map, fileSize, DigestFunction, DigestHandle );
869 
870 end:
871     if( map )
872         UnmapViewOfFile( map );
873     if( hMap != INVALID_HANDLE_VALUE )
874         CloseHandle( hMap );
875     if( error )
876         SetLastError(error);
877     return ret;
878 
879 invalid_parameter:
880     error = ERROR_INVALID_PARAMETER;
881     goto end;
882 }
883 
884 /***********************************************************************
885  *		ImageRemoveCertificate (IMAGEHLP.@)
886  */
ImageRemoveCertificate(HANDLE FileHandle,DWORD Index)887 BOOL WINAPI ImageRemoveCertificate(HANDLE FileHandle, DWORD Index)
888 {
889     DWORD size = 0, count = 0, sd_VirtualAddr = 0, offset = 0;
890     DWORD data_size = 0, cert_size = 0, cert_size_padded = 0, ret = 0;
891     LPVOID cert_data;
892     BOOL r;
893 
894     TRACE("(%p, %d)\n", FileHandle, Index);
895 
896     r = ImageEnumerateCertificates(FileHandle, CERT_SECTION_TYPE_ANY, &count, NULL, 0);
897 
898     if ((!r) || (count == 0))
899         return FALSE;
900 
901     if ((!IMAGEHLP_GetSecurityDirOffset(FileHandle, &sd_VirtualAddr, &size)) ||
902         (!IMAGEHLP_GetCertificateOffset(FileHandle, Index, &offset, &cert_size)))
903         return FALSE;
904 
905     /* Ignore any padding we have, too */
906     if (cert_size % 8)
907         cert_size_padded = cert_size + (8 - (cert_size % 8));
908     else
909         cert_size_padded = cert_size;
910 
911     data_size = size - (offset - sd_VirtualAddr) - cert_size_padded;
912 
913     if (data_size == 0)
914     {
915         ret = SetFilePointer(FileHandle, sd_VirtualAddr, NULL, FILE_BEGIN);
916 
917         if (ret == INVALID_SET_FILE_POINTER)
918             return FALSE;
919     }
920     else
921     {
922         cert_data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, data_size);
923 
924         if (!cert_data)
925             return FALSE;
926 
927         ret = SetFilePointer(FileHandle, offset + cert_size_padded, NULL, FILE_BEGIN);
928 
929         if (ret == INVALID_SET_FILE_POINTER)
930             goto error;
931 
932         /* Read any subsequent certificates */
933         r = ReadFile(FileHandle, cert_data, data_size, &count, NULL);
934 
935         if ((!r) || (count != data_size))
936             goto error;
937 
938         SetFilePointer(FileHandle, offset, NULL, FILE_BEGIN);
939 
940         /* Write them one index back */
941         r = WriteFile(FileHandle, cert_data, data_size, &count, NULL);
942 
943         if ((!r) || (count != data_size))
944             goto error;
945 
946         HeapFree(GetProcessHeap(), 0, cert_data);
947     }
948 
949     /* If security directory is at end of file, trim the file */
950     if (GetFileSize(FileHandle, NULL) == sd_VirtualAddr + size)
951         SetEndOfFile(FileHandle);
952 
953     if (count == 1)
954         r = IMAGEHLP_SetSecurityDirOffset(FileHandle, 0, 0);
955     else
956         r = IMAGEHLP_SetSecurityDirOffset(FileHandle, sd_VirtualAddr, size - cert_size_padded);
957 
958     if (!r)
959         return FALSE;
960 
961     if (!IMAGEHLP_RecalculateChecksum(FileHandle))
962         return FALSE;
963 
964     return TRUE;
965 
966 error:
967     HeapFree(GetProcessHeap(), 0, cert_data);
968     return FALSE;
969 }
970