/*++ Copyright (c) 1995 Microsoft Corporation Module Name: dice.cxx Abstract: This file implements the Image Integrity API's. Author: Bryan Tuttle (bryant) 7-Dec-1995 Environment: User Mode --*/ #include BOOL FindCertificate( IN PLOADED_IMAGE LoadedImage, IN DWORD Index, LPWIN_CERTIFICATE * Certificate ) { PIMAGE_DATA_DIRECTORY pDataDir; DWORD_PTR CurrentCert; BOOL rc; if (LoadedImage->fDOSImage) { // No way this could have a certificate; return(FALSE); } rc = FALSE; __try { if (LoadedImage->FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { pDataDir = &((PIMAGE_NT_HEADERS32)(LoadedImage->FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } else if (LoadedImage->FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { pDataDir = &((PIMAGE_NT_HEADERS64)(LoadedImage->FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } else { __leave; // Not an interesting file type. } // Check if the cert pointer is at least reasonable. if (!pDataDir->VirtualAddress || !pDataDir->Size || (pDataDir->VirtualAddress + pDataDir->Size > LoadedImage->SizeOfImage)) { __leave; } // We're not looking at an empty security slot or an invalid (past the image boundary) value. // Let's see if we can find it. DWORD CurrentIdx = 0; DWORD_PTR LastCert; CurrentCert = (DWORD_PTR)(LoadedImage->MappedAddress) + pDataDir->VirtualAddress; LastCert = CurrentCert + pDataDir->Size; while (CurrentCert < LastCert ) { if (CurrentIdx == Index) { rc = TRUE; __leave; } CurrentIdx++; CurrentCert += ((LPWIN_CERTIFICATE)CurrentCert)->dwLength; CurrentCert = (CurrentCert + 7) & ~7; // align it. } } __except(EXCEPTION_EXECUTE_HANDLER) { } if (rc == TRUE) { *Certificate = (LPWIN_CERTIFICATE)CurrentCert; } return(rc); } typedef struct _EXCLUDE_RANGE { PBYTE Offset; DWORD Size; struct _EXCLUDE_RANGE *Next; } EXCLUDE_RANGE; typedef enum { Raw, Virtual } ADDRTYPE; class EXCLUDE_LIST { public: EXCLUDE_LIST() { m_Image = NULL; m_ExRange = (EXCLUDE_RANGE *)MemAlloc(sizeof(EXCLUDE_RANGE)); } ~EXCLUDE_LIST() { EXCLUDE_RANGE *pTmp; pTmp = m_ExRange->Next; while (pTmp) { MemFree(m_ExRange); m_ExRange = pTmp; pTmp = m_ExRange->Next; } MemFree(m_ExRange); } void Init(LOADED_IMAGE * Image, DIGEST_FUNCTION pFunc, DIGEST_HANDLE dh) { m_Image = Image; m_ExRange->Offset = NULL; m_ExRange->Size = 0; m_pFunc = pFunc; m_dh = dh; return; } void Add(DWORD_PTR Offset, DWORD Size, ADDRTYPE AddrType); BOOL Emit(PBYTE Offset, DWORD Size); private: LOADED_IMAGE * m_Image; EXCLUDE_RANGE * m_ExRange; DIGEST_FUNCTION m_pFunc; DIGEST_HANDLE m_dh; }; void EXCLUDE_LIST::Add( DWORD_PTR Offset, DWORD Size, ADDRTYPE AddrType ) { if (AddrType == Virtual) { // Always save raw offsets DWORD_PTR RawOffset; // Note: it's O.K. to cast down to a dword here. Offset is really a Rva from the start // of the image (always limited to 4G). RawOffset = (DWORD_PTR)ImageRvaToVa((PIMAGE_NT_HEADERS)m_Image->FileHeader, m_Image->MappedAddress, (DWORD)Offset, NULL); Offset = RawOffset; } EXCLUDE_RANGE *pTmp, *pExRange; pExRange = m_ExRange; while (pExRange->Next && (pExRange->Next->Offset < (PBYTE)Offset)) { pExRange = pExRange->Next; } pTmp = (EXCLUDE_RANGE *) MemAlloc(sizeof(EXCLUDE_RANGE)); pTmp->Next = pExRange->Next; pTmp->Offset = (PBYTE)Offset; pTmp->Size = Size; pExRange->Next = pTmp; return; } BOOL EXCLUDE_LIST::Emit( PBYTE Offset, DWORD Size ) { BOOL rc; EXCLUDE_RANGE *pExRange; DWORD EmitSize, ExcludeSize; pExRange = m_ExRange->Next; while (pExRange && (Size > 0)) { if (pExRange->Offset >= Offset) { // Emit what's before the exclude list. EmitSize = __min((DWORD)(pExRange->Offset - Offset), Size); if (EmitSize) { rc = (*m_pFunc)(m_dh, Offset, EmitSize); Size -= EmitSize; Offset += EmitSize; } } if (Size) { if (pExRange->Offset + pExRange->Size >= Offset) { // Skip over what's in the exclude list. ExcludeSize = __min(Size, (DWORD)(pExRange->Offset + pExRange->Size - Offset)); Size -= ExcludeSize; Offset += ExcludeSize; } } pExRange = pExRange->Next; } // Emit what's left. if (Size) { rc = (*m_pFunc)(m_dh, Offset, Size); } return rc; } BOOL IMAGEAPI ImageGetDigestStream( IN HANDLE FileHandle, IN DWORD DigestLevel, IN DIGEST_FUNCTION DigestFunction, IN DIGEST_HANDLE DigestHandle ) /*++ Routine Description: Given an image, return the bytes necessary to construct a certificate. Only PE images are supported at this time. Arguments: FileHandle - Handle to the file in question. The file should be opened with at least GENERIC_READ access. DigestLevel - Indicates what data will be included in the returned buffer. Valid values are: CERT_PE_IMAGE_DIGEST_DEBUG_INFO - Include Debug symbolic (if mapped) CERT_PE_IMAGE_DIGEST_RESOURCES - Include Resource info CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO - Include ALL the import information By default, neither Debug Symbolic, Resources, nor import information affected by binding are returned. DigestFunction - User supplied routine that will process the data. DigestHandle - User supplied handle to identify the digest. Passed as the first argument to the DigestFunction. Return Value: TRUE - Success. FALSE - There was some error. Call GetLastError for more information. Possible values are ERROR_INVALID_PARAMETER or ERROR_OPERATION_ABORTED. --*/ { LOADED_IMAGE LoadedImage; BOOL rc, fAddThisSection, fDebugAdded; DWORD i; EXCLUDE_LIST ExList; PIMAGE_SECTION_HEADER SectionHeaders; ULONG ResourceOffset, ResourceSize, DebugOffset, DebugSize, RelocOffset, RelocSize, SectionHeaderSize; PIMAGE_FILE_HEADER FileHeader; PIMAGE_DATA_DIRECTORY pDataDir; INT RelocHdr; union { IMAGE_NT_HEADERS32 PE32; IMAGE_NT_HEADERS64 PE64; }Hdr; BOOL f32; if (MapIt(FileHandle, &LoadedImage, MAP_READONLY) == FALSE) { // Unable to map the image. SetLastError(ERROR_INVALID_PARAMETER); return(FALSE); } rc = ERROR_INVALID_PARAMETER; __try { if (LoadedImage.fDOSImage) { __leave; } if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { f32 = TRUE; } else if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { f32 = FALSE; } else { __leave; } ExList.Init(&LoadedImage, DigestFunction, DigestHandle); // Return all the interesting stuff from the image. First, the common stuff. // 1. Add the DOS stub (if it exists). if ((ULONG_PTR)LoadedImage.FileHeader - (ULONG_PTR) LoadedImage.MappedAddress) { if (!ExList.Emit((PBYTE) LoadedImage.MappedAddress, (DWORD)((ULONG_PTR) LoadedImage.FileHeader - (ULONG_PTR) LoadedImage.MappedAddress))) { rc = ERROR_OPERATION_ABORTED; __leave; } } // Add the headers, but not the checksum and not the security Data directory entry. if (f32) { Hdr.PE32 = *((PIMAGE_NT_HEADERS32)LoadedImage.FileHeader); pDataDir = &Hdr.PE32.OptionalHeader.DataDirectory[0]; Hdr.PE32.OptionalHeader.CheckSum = 0; } else { Hdr.PE64 = *((PIMAGE_NT_HEADERS64)LoadedImage.FileHeader); pDataDir = &Hdr.PE64.OptionalHeader.DataDirectory[0]; Hdr.PE64.OptionalHeader.CheckSum = 0; } pDataDir[IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0; pDataDir[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0; SectionHeaderSize = sizeof(IMAGE_SECTION_HEADER) * LoadedImage.NumberOfSections; SectionHeaders = (PIMAGE_SECTION_HEADER) MemAlloc(SectionHeaderSize); if (SectionHeaders == NULL) { rc = ERROR_OPERATION_ABORTED; __leave; } ResourceOffset = pDataDir[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; ResourceSize = pDataDir[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size; RelocOffset = pDataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; RelocSize = pDataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; fDebugAdded = TRUE; DebugOffset = 0xFFFFFFFF; RelocHdr = -1; for (i = 0; i < LoadedImage.NumberOfSections; i++) { SectionHeaders[i] = LoadedImage.Sections[i]; // Keep track of the reloc section header. We may need to adjust it later. if (RelocSize && ((LoadedImage.Sections[i].VirtualAddress <= RelocOffset) && (LoadedImage.Sections[i].VirtualAddress + LoadedImage.Sections[i].Misc.VirtualSize >= RelocOffset + RelocSize)) ) { RelocHdr = i; } // If resources aren't in the digest, we need to clear the resource section header if (ResourceSize && !(DigestLevel & CERT_PE_IMAGE_DIGEST_RESOURCES)) { if (((LoadedImage.Sections[i].VirtualAddress <= ResourceOffset) && (LoadedImage.Sections[i].VirtualAddress + LoadedImage.Sections[i].Misc.VirtualSize >= ResourceOffset + ResourceSize)) ) { // Found the resource section header. Zero it out. SectionHeaders[i].Misc.VirtualSize = 0; SectionHeaders[i].VirtualAddress = 0; SectionHeaders[i].SizeOfRawData = 0; SectionHeaders[i].PointerToRawData = 0; } } if (!(DigestLevel & CERT_PE_IMAGE_DIGEST_DEBUG_INFO)) { // Same with mapped debug info. if (!strncmp((char *)LoadedImage.Sections[i].Name, ".debug", sizeof(".debug"))) { DebugOffset = SectionHeaders[i].VirtualAddress; DebugSize = SectionHeaders[i].SizeOfRawData; ExList.Add(SectionHeaders[i].PointerToRawData + (DWORD_PTR) LoadedImage.MappedAddress, DebugSize, Raw); SectionHeaders[i].Misc.VirtualSize = 0; SectionHeaders[i].VirtualAddress = 0; SectionHeaders[i].SizeOfRawData = 0; SectionHeaders[i].PointerToRawData = 0; fDebugAdded = FALSE; } } } // The first pass on the section headers is finished. See it we need to adjust the // reloc dir or the image headers. if (!(DigestLevel & CERT_PE_IMAGE_DIGEST_RESOURCES)) { // If the resources aren't in the digest, don't add the base reloc address or the // resource address/size to the digest. This allows subsequent tools to add/subtract // resource info w/o effecting the digest. if ((ResourceOffset < RelocOffset) && (RelocHdr != -1)) { pDataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; SectionHeaders[RelocHdr].PointerToRawData = 0; SectionHeaders[RelocHdr].VirtualAddress = 0; } pDataDir[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = 0; pDataDir[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = 0; if (f32) { Hdr.PE32.OptionalHeader.SizeOfImage = 0; Hdr.PE32.OptionalHeader.SizeOfInitializedData = 0; } else { Hdr.PE64.OptionalHeader.SizeOfImage = 0; Hdr.PE64.OptionalHeader.SizeOfInitializedData = 0; } ExList.Add(ResourceOffset, ResourceSize, Virtual); } if (!(DigestLevel & CERT_PE_IMAGE_DIGEST_DEBUG_INFO) && (fDebugAdded == FALSE)) { // Debug wasn't added to the image and IS mapped in. Allow these to grow also. if (f32) { Hdr.PE32.OptionalHeader.SizeOfImage = 0; Hdr.PE32.OptionalHeader.SizeOfInitializedData = 0; } else { Hdr.PE64.OptionalHeader.SizeOfImage = 0; Hdr.PE64.OptionalHeader.SizeOfInitializedData = 0; } if ((DebugOffset < RelocOffset) && (RelocHdr != -1)) { pDataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; SectionHeaders[RelocHdr].PointerToRawData = 0; SectionHeaders[RelocHdr].VirtualAddress = 0; } } // Looks good. Send the headers to the digest function. if (f32) { if (!ExList.Emit((PBYTE) &Hdr.PE32, sizeof(Hdr.PE32))) { rc = ERROR_OPERATION_ABORTED; __leave; } } else { if (!ExList.Emit((PBYTE) &Hdr.PE64, sizeof(Hdr.PE64))) { rc = ERROR_OPERATION_ABORTED; __leave; } } // Then the section headers. if (!ExList.Emit((PBYTE) SectionHeaders, SectionHeaderSize)) { rc = ERROR_OPERATION_ABORTED; __leave; } MemFree(SectionHeaders); // The headers are done. Now let's see what we need to do with the import information. if (!(DigestLevel & CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO)) { // The user didn't explicitly ask for all import info. // Add the info modified by bind to the exclude list. PIMAGE_IMPORT_DESCRIPTOR ImportDesc; DWORD ImportDescSize, IATSize; PVOID IAT; ImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData( LoadedImage.MappedAddress, FALSE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ImportDescSize); if (ImportDescSize) { IAT = ImageDirectoryEntryToData(LoadedImage.MappedAddress, FALSE, IMAGE_DIRECTORY_ENTRY_IAT, &IATSize); if (IAT) { // Easy case. All the IATs are grouped together. ExList.Add((DWORD_PTR) IAT, IATSize, Raw); // Add the TimeDateStamp and ForwarderChain fields in the Import Descriptors while (ImportDesc->Characteristics) { ExList.Add((DWORD_PTR) &ImportDesc->TimeDateStamp, 8, Raw); ImportDesc++; } } else { // Not so easy. Need to walk each Import descriptor to find the bounds of the IAT // (note, there's no requirement that all the IAT's for all descriptors be contiguous). while (ImportDesc->Characteristics) { PIMAGE_THUNK_DATA ThunkStart; ExList.Add((DWORD_PTR)&ImportDesc->TimeDateStamp, 8, Raw); ThunkStart = (PIMAGE_THUNK_DATA) ImageRvaToVa((PIMAGE_NT_HEADERS)LoadedImage.FileHeader, LoadedImage.MappedAddress, (ULONG) ImportDesc->OriginalFirstThunk, NULL); if (f32) { PIMAGE_THUNK_DATA32 Thunk = (PIMAGE_THUNK_DATA32)ThunkStart; while (Thunk->u1.AddressOfData) { Thunk++; } ExList.Add( (DWORD)ImportDesc->FirstThunk, (DWORD)((DWORD_PTR)Thunk - (DWORD_PTR) ThunkStart + sizeof(IMAGE_THUNK_DATA32)), Virtual); } else { PIMAGE_THUNK_DATA64 Thunk = (PIMAGE_THUNK_DATA64)ThunkStart; while (Thunk->u1.AddressOfData) { Thunk++; } ExList.Add( (DWORD)ImportDesc->FirstThunk, (DWORD)((DWORD_PTR)Thunk - (DWORD_PTR) ThunkStart + sizeof(IMAGE_THUNK_DATA64)), Virtual); } ImportDesc++; } } } } // Add each section header followed by the data from that section. for (i = 0; i < LoadedImage.NumberOfSections; i++) { if (!ExList.Emit((PBYTE) (LoadedImage.MappedAddress + LoadedImage.Sections[i].PointerToRawData), LoadedImage.Sections[i].SizeOfRawData)) { rc = ERROR_OPERATION_ABORTED; __leave; } } rc = ERROR_SUCCESS; } __except(EXCEPTION_EXECUTE_HANDLER) { } UnMapIt(&LoadedImage); SetLastError(rc); return(rc == ERROR_SUCCESS ? TRUE : FALSE); } BOOL IMAGEAPI ImageAddCertificate( IN HANDLE FileHandle, IN LPWIN_CERTIFICATE Certificate, OUT PDWORD Index ) /*++ Routine Description: Add a certificate to the image. There is no checking to ensure there are no duplicate types. Arguments: FileHandle - Handle to the file in question. The file should be opened with at least GENERIC_WRITE access. Certificate - Pointer to a WIN_CERTIFICATE structure. Index - After adding the Certificate to the image, this is the index you can use for later references to that certificate. Return Value: TRUE - Success FALSE - There was some error. Call GetLastError() for more information. --*/ { LOADED_IMAGE LoadedImage; DWORD rc; LPWIN_CERTIFICATE pCert; DWORD OnDiskCertLength; DWORD_PTR NewCertLocation; DWORD OriginalImageSize; PIMAGE_DATA_DIRECTORY pDataDir; BOOL f32, fSkipUnMap; if (MapIt(FileHandle, &LoadedImage, MAP_READWRITE) == FALSE) { // Unable to map the image. SetLastError(ERROR_INVALID_PARAMETER); return(FALSE); } rc = ERROR_INVALID_PARAMETER; fSkipUnMap = FALSE; __try { if (LoadedImage.fDOSImage) { __leave; } if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { f32 = TRUE; pDataDir = &((PIMAGE_NT_HEADERS32)(LoadedImage.FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } else if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { f32 = FALSE; pDataDir = &((PIMAGE_NT_HEADERS64)(LoadedImage.FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } else { __leave; } pCert = (LPWIN_CERTIFICATE) Certificate; // Test the output parameter and the the cert. *Index = (DWORD) -1; OnDiskCertLength = pCert->dwLength; OnDiskCertLength = (OnDiskCertLength + 7) & ~7; // Round the size of cert. // Grow the image. OriginalImageSize = LoadedImage.SizeOfImage; OriginalImageSize = (OriginalImageSize + 7) & ~7; // Round the size of Image. // Check if the cert pointer is at least reasonable. if (pDataDir->VirtualAddress && (pDataDir->VirtualAddress + pDataDir->Size) > LoadedImage.SizeOfImage) { __leave; } // Looks good now. *Index = 0; if (pDataDir->VirtualAddress == 0) { pDataDir->VirtualAddress = OriginalImageSize; pDataDir->Size = 0; NewCertLocation = OriginalImageSize; } else { LPWIN_CERTIFICATE CurrentCert; NewCertLocation = pDataDir->VirtualAddress + pDataDir->Size + (DWORD_PTR) LoadedImage.MappedAddress; CurrentCert = (LPWIN_CERTIFICATE) (LoadedImage.MappedAddress + pDataDir->VirtualAddress); while (((DWORD_PTR)CurrentCert) < NewCertLocation) { if (CurrentCert->dwLength == 0) { __leave; } CurrentCert = (LPWIN_CERTIFICATE)(((DWORD_PTR)CurrentCert + CurrentCert->dwLength + 7) & ~7); (*Index)++; } NewCertLocation -= (DWORD_PTR) LoadedImage.MappedAddress; } if (!GrowMap (&LoadedImage, OnDiskCertLength + (OriginalImageSize - LoadedImage.SizeOfImage))) { fSkipUnMap = TRUE; __leave; } if (NewCertLocation < OriginalImageSize) { // There's data after the current security data. Move it down. memmove(LoadedImage.MappedAddress + NewCertLocation + pCert->dwLength, LoadedImage.MappedAddress + NewCertLocation, (unsigned) (OriginalImageSize - NewCertLocation)); } memmove(LoadedImage.MappedAddress + NewCertLocation, pCert, pCert->dwLength); // GrowMap may have moved the dirs. if (f32) { pDataDir = &((PIMAGE_NT_HEADERS32)(LoadedImage.FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } else { pDataDir = &((PIMAGE_NT_HEADERS64)(LoadedImage.FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } pDataDir->Size += OnDiskCertLength; rc = ERROR_SUCCESS; } __except(EXCEPTION_EXECUTE_HANDLER) { } if (!fSkipUnMap) UnMapIt(&LoadedImage); SetLastError(rc); return(rc == ERROR_SUCCESS ? TRUE : FALSE); } BOOL IMAGEAPI ImageRemoveCertificate( IN HANDLE FileHandle, IN DWORD Index ) /*++ Routine Description: Remove a certificate from an image. Arguments: FileHandle - Handle to the file in question. The file should be opened with at least GENERIC_WRITE access. Index - The index to remove from the image. Return Value: TRUE - Successful FALSE - There was some error. Call GetLastError() for more information. --*/ { LOADED_IMAGE LoadedImage; LPWIN_CERTIFICATE CurrentCert; DWORD rc; DWORD OldCertLength; if (MapIt(FileHandle, &LoadedImage, MAP_READWRITE) == FALSE) { // Unable to map the image. SetLastError(ERROR_INVALID_PARAMETER); return(FALSE); } rc = ERROR_INVALID_PARAMETER; __try { if (FindCertificate(&LoadedImage, Index, &CurrentCert) == FALSE) { __leave; } OldCertLength = CurrentCert->dwLength; OldCertLength = (OldCertLength + 7) & ~7; // The disk size is actually a multiple of 8 memmove(CurrentCert, ((PCHAR)CurrentCert) + OldCertLength, (size_t)(LoadedImage.SizeOfImage - (((DWORD_PTR)CurrentCert) - (DWORD_PTR)LoadedImage.MappedAddress) - OldCertLength)); if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { ((PIMAGE_NT_HEADERS32)LoadedImage.FileHeader)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size -= OldCertLength; if (!((PIMAGE_NT_HEADERS32)LoadedImage.FileHeader)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size) { // Last one removed. Clear the pointer ((PIMAGE_NT_HEADERS32)LoadedImage.FileHeader)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0; } } else { ((PIMAGE_NT_HEADERS64)LoadedImage.FileHeader)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size -= OldCertLength; if (!((PIMAGE_NT_HEADERS64)LoadedImage.FileHeader)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size) { // Last one removed. Clear the pointer ((PIMAGE_NT_HEADERS64)LoadedImage.FileHeader)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0; } } LoadedImage.SizeOfImage -= OldCertLength; rc = ERROR_SUCCESS; } __except(EXCEPTION_EXECUTE_HANDLER) { } UnMapIt(&LoadedImage); SetLastError(rc); return(rc == ERROR_SUCCESS ? TRUE : FALSE); } BOOL IMAGEAPI ImageEnumerateCertificates( IN HANDLE FileHandle, IN WORD TypeFilter, OUT PDWORD CertificateCount, IN OUT PDWORD Indices OPTIONAL, IN DWORD IndexCount OPTIONAL ) /*++ Routine Description: Enumerate the certificates in an image. Arguments: FileHandle - Handle to the file in question. The file should be opened with at least GENERIC_READ access. TypeFilter - The filter to apply when enumertating the certificates. Valid values are: CERT_SECTION_TYPE_ANY - Enumerate all certificate types in the image. CertificateCount - How many certificates are in the image. Indices - An array of indexes that match the filter type. IndexCount - The number of indexes in the indices array. Return Value: TRUE - Successful FALSE - There was some error. Call GetLastError() for more information. --*/ { LOADED_IMAGE LoadedImage; BOOL rc; PIMAGE_DATA_DIRECTORY pDataDir; LPWIN_CERTIFICATE CurrentCert, LastCert; PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL; PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL; if (MapIt(FileHandle, &LoadedImage, MAP_READONLY) == FALSE) { // Unable to map the image. SetLastError(ERROR_INVALID_PARAMETER); return(FALSE); } rc = ERROR_INVALID_PARAMETER; __try { if (LoadedImage.fDOSImage) { __leave; } if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { pDataDir = &((PIMAGE_NT_HEADERS32)(LoadedImage.FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } else if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { pDataDir = &((PIMAGE_NT_HEADERS64)(LoadedImage.FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } else { __leave; } if ((pDataDir->VirtualAddress + pDataDir->Size) > LoadedImage.SizeOfImage) { *CertificateCount = 0; __leave; } if (!pDataDir->VirtualAddress || !pDataDir->Size) { *CertificateCount = 0; } else { DWORD MatchedIndex = 0; DWORD ActualIndex = 0; CurrentCert = (LPWIN_CERTIFICATE)((DWORD_PTR)LoadedImage.MappedAddress + pDataDir->VirtualAddress); LastCert = (LPWIN_CERTIFICATE)((DWORD_PTR)CurrentCert + pDataDir->Size); while (CurrentCert < LastCert ) { if ((TypeFilter == CERT_SECTION_TYPE_ANY) || (TypeFilter == CurrentCert->wCertificateType)) { if (Indices && (MatchedIndex < IndexCount)) { Indices[MatchedIndex] = ActualIndex; } MatchedIndex++; } ActualIndex++; CurrentCert = (LPWIN_CERTIFICATE)((((DWORD_PTR)CurrentCert + CurrentCert->dwLength) +7) & ~7); } *CertificateCount = MatchedIndex; } rc = ERROR_SUCCESS; } __except(EXCEPTION_EXECUTE_HANDLER) { } UnMapIt(&LoadedImage); SetLastError(rc); return(rc == ERROR_SUCCESS ? TRUE : FALSE); } BOOL IMAGEAPI ImageGetCertificateData( IN HANDLE FileHandle, IN DWORD CertificateIndex, OUT LPWIN_CERTIFICATE Certificate, IN OUT PDWORD RequiredLength ) /*++ Routine Description: Given a specific certificate index, retrieve the certificate data. Arguments: FileHandle - Handle to the file in question. The file should be opened with at least GENERIC_READ access. CertificateIndex - Index to retrieve Certificate - Output buffer where the certificate is to be stored. RequiredLength - Size of the certificate buffer (input). On return, is set to the actual certificate length. NULL can be used to determine the size of a certificate. Return Value: TRUE - Successful FALSE - There was some error. Call GetLastError() for more information. --*/ { LOADED_IMAGE LoadedImage; DWORD ErrorCode; LPWIN_CERTIFICATE ImageCert; if (MapIt(FileHandle, &LoadedImage, MAP_READONLY) == FALSE) { // Unable to map the image. SetLastError(ERROR_INVALID_PARAMETER); return(FALSE); } ErrorCode = ERROR_INVALID_PARAMETER; __try { if (FindCertificate(&LoadedImage, CertificateIndex, &ImageCert) == FALSE) { __leave; } if (*RequiredLength < ImageCert->dwLength) { *RequiredLength = ImageCert->dwLength; ErrorCode = ERROR_INSUFFICIENT_BUFFER; } else { memcpy(Certificate, (PUCHAR)ImageCert, ImageCert->dwLength); ErrorCode = ERROR_SUCCESS; } } __except(EXCEPTION_EXECUTE_HANDLER) { } UnMapIt(&LoadedImage); SetLastError(ErrorCode); return(ErrorCode == ERROR_SUCCESS ? TRUE: FALSE); } BOOL IMAGEAPI ImageGetCertificateHeader( IN HANDLE FileHandle, IN DWORD CertificateIndex, IN OUT LPWIN_CERTIFICATE CertificateHeader ) /*++ Routine Description: Given a specific certificate index, retrieve the certificate data. Arguments: FileHandle - Handle to the file in question. The file should be opened with at least GENERIC_READ access. CertificateIndex - Index to retrieve. CertificateHeader - Pointer to a WIN_CERTIFICATE to fill in. Return Value: TRUE - Success FALSE - There was some error. Call GetLastError() for more information. --*/ { LOADED_IMAGE LoadedImage; LPWIN_CERTIFICATE ImageCert; BOOL rc; if (MapIt(FileHandle, &LoadedImage, MAP_READONLY) == FALSE) { // Unable to map the image. SetLastError(ERROR_INVALID_PARAMETER); return(FALSE); } if (FindCertificate(&LoadedImage, CertificateIndex, &ImageCert) == FALSE) { rc = FALSE; goto Exit; } __try { memcpy(CertificateHeader, ImageCert, sizeof(WIN_CERTIFICATE)); rc = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { rc = FALSE; } Exit: UnMapIt(&LoadedImage); if (rc == FALSE) { SetLastError(ERROR_INVALID_PARAMETER); } return(rc); }