How to prevent “check integrity” load failures

This article will demonstrate how to prevent check integrity load failures by setting all Portable Execution (PE) image PointerToRawData fields in the PE header to zero for uninitialized data sections.

Symptoms

Windows Vista®, Windows Server® 2008 contain a set of components, such as the Windows Security Center, that will only execute code from a PE image if the check integrity flag is set in the PE Header. When this flag set, Windows verifies the digital signature of the image prior to loading the image.

Cause

The PE image will fail to load if the signature cannot be verified. This may occur if the image contains an uninitialized data section, and the PointerToRawData field in the section header is set to a non-zero value.

This failure may occur when using a 3rd party linker that does not follow the Microsoft Portable Executable and Common Object File Format Specification (PE/COFF). See the References section below for a link to the PE/COFF specification.

Solution

This article provides both manual steps and sample code describing how to identify this issue and update invalid PointerToRawData values.

Manual steps to identify and update invalid PointerToRawData values

Determining if the check integrity flag is present in the PE Header using Link.exe

  1. Install the Windows SDK. See the References section below for the link to the SDK.

  2. Open a Windows SDK command shell and run the following command: Link.exe /dump /headers targetfile.exe

  3. In the output text, look under OPTIONAL HEADER VALUES for DLL characteristics. If the check integrity string is present under DLL characteristics, the check integrity flag is set.

When the check integrity flag is set, Windows verifies the digital signature of the PE image prior to loading the PE image. If there is an error verifying the PE image signature, Windows fails to load the image and logs an event.

Viewing the even log

  1. Run Windows Event Viewer. To open Event Viewer, click Start, click Control Panel, click Performance Information and Tools, click Advanced Tools, and then select View performance details in Event log.

  2. In the tree view, expand: Applications and Services logs, Microsoft, Windows, CodeIntegrity. Select Operational to view the Operational log.

  3. Using the right top pane, sort events in the Operational log by Event ID.

  4. For each event with Event ID 3002, check if the file path in the event description matches the file path of your PE image.

Event ID 3002 failures may be due to an incorrect value in the PointerToRawData field in the section header of an uninitialized data section.

Developers can check for the presence of uninitialized data section and the value of the corresponding PointerToRawData field using Link.exe.

  1. Open a Windows SDK command shell and run the following command: Link.exe /dump /headers targetfile.exe

  2. Look under each SECTION HEADER entry. If the size of raw data output is zero, then this section contains only uninitialized data.

  3. If the following file pointer to raw data entry contains a non-zero value, then the PointerToRawData field in the PE Section header is invalid.

Note

Developers can work around this issue by setting the PointerToRawData field to zero.

A developer can manually set the PointerToRawData field to zero using a binary editor following the procedure below. A binary editor is available in Visual Studio; see the References section below.

Setting the PointerToRawData field to zero

  1. Find the name value under the SECTION HEADER entry of the uninitialized data section in the link.exe output of the previous step.

  2. Open the image in a binary editor and set the PointerToRawData field to zero for the uninitialized data section:

    Opening the image in the editor to set the PointerToRawData field to zero

    1. Find the PE Header which is located at the beginning of the file, after the MS-DOS section.

Note

Refer to 2. Overview in the PE/COFF specification for details in the References section below.

2.  Find the **section header table** which starts at the end of the PE Header. The **section header table** contains all the **section headers**.

3.  Find the section header with the **name** of the uninitialized data section. The section header **name** field is located in the first 8 bytes of each section header.
    

Note

Refer to 4. Section Table (Sections headers) in the PE/COFF specification for details in the References section below.

4.  Find the **PointerToRawData** field, which starts at a 20 byte offset within the section header and is 4 bytes long.

5.  Set all bytes in the **PointerToRawData** field to zero.
  1. Save the file and close the binary editor.

Note

After updating the PointerToRawData field, the PE Header checksum will no longer be valid. However, signing the file will set the PE Header checksum to a valid state.

Sample code

The following sample code demonstrates how to detect non-zero PointToRawData fields for uninitialized data sections, and set these fields to zero.

//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Description:
//
// This samples demonstrates how to load a PE image, find the 
// section headers for all sections containing only uninitialized 
// data, set non-zero values in the section header PointToRawData  
// field to zero, and recalculate the PE header checksum. 
//
// Build Instructions:
//
// Link with imagehlp.lib
//

#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <imagehlp.h>

BOOL FixPeHeaderOnFile(const wchar_t *szFile)
{
    HANDLE hFile = INVALID_HANDLE_VALUE;
    HANDLE hMapping = NULL;
    PVOID pvMap = NULL;
    PIMAGE_NT_HEADERS pHeader = NULL;
    ULONG NumberOfSections;
    ULONG OffsetToSectionTable;
    PIMAGE_SECTION_HEADER SectionTableEntry;
    DWORD cbFileSize;
    DWORD dwPriorCheckSum;
    DWORD dwNewCheckSum;

    DWORD dwLastError = ERROR_SUCCESS;
    BOOL fSuccess = FALSE;
      
    hFile = CreateFile(szFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        dwLastError = GetLastError();
        goto cleanup;
    }
          
    hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
    if(hMapping == NULL)
    {
        dwLastError = GetLastError();
        goto cleanup;
    }
            
    pvMap = MapViewOfFile(hMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
    if(pvMap == NULL)
    {
        dwLastError = GetLastError();
        goto cleanup;
    }

    pHeader = ImageNtHeader( pvMap );

    if(pHeader == NULL)
    {
        dwLastError = GetLastError();
        goto cleanup;
    }

    // 
    // Sections where the section header SizeOfRawData field is zero contain 
    // only uninitialized data.  
    //
    // Look for sections that have SizeOfRawData == 0, and PointerToRawData !=0.
    // 

    NumberOfSections = pHeader->FileHeader.NumberOfSections;

    OffsetToSectionTable = FIELD_OFFSET (IMAGE_NT_HEADERS, OptionalHeader) +
                           pHeader->FileHeader.SizeOfOptionalHeader;

    SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)pHeader + OffsetToSectionTable);

    while (NumberOfSections > 0)
    {
        //
        // Where the SizeOfRawData is zero, but the PointerToRawData is not 
        // zero, set PointerToRawData to zero.
        //

        if ((SectionTableEntry->SizeOfRawData == 0) &&
            (SectionTableEntry->PointerToRawData != 0))
        {
            printf("Fixing up a section\n");
            SectionTableEntry->PointerToRawData = 0;
        }

        SectionTableEntry += 1;
        NumberOfSections -= 1;
    };


    //
    // Update the OptionalHeader.CheckSum field.
    //

    cbFileSize = GetFileSize(hFile, NULL);
    if(cbFileSize == INVALID_FILE_SIZE)
    {
        dwLastError = GetLastError();
        goto cleanup;
    }

    if(CheckSumMappedFile(pvMap, cbFileSize, &dwPriorCheckSum, &dwNewCheckSum) == NULL)
    {
        dwLastError = GetLastError();
        goto cleanup;
    }

    pHeader->OptionalHeader.CheckSum = dwNewCheckSum;

    fSuccess = TRUE;

cleanup:
      
    if(pvMap)
    {
        UnmapViewOfFile(pvMap);
    }
    
    if(hMapping)
    {
        CloseHandle(hMapping);
    }

    if(hFile != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hFile);
    }

    if(!fSuccess)
    {
        char *szErrText = NULL;
        BOOL fFreeError = TRUE;

        if (FormatMessageA(
                    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                    NULL,
                    dwLastError,
                    0,
                    (char *)&szErrText,
                    16,
                    NULL
                    ) == 0)
        {
            szErrText = "Unknown error";
            fFreeError = FALSE;
        }

        printf("%s: Can't open file error %x: %s\n", szFile, dwLastError, szErrText);
        
        if(fFreeError)
        {
            LocalFree(szErrText);
        }

        SetLastError(dwLastError);
    }


    return fSuccess;
}

int
__cdecl
wmain(int argc, wchar_t **argv)
{
    int i;
    if (argc == 1)
    {
        printf("pefix [files... | @filelist]\n");
        return 1;
    }

    for(i=1;i<argc;i++)
    {
        if (argv[i][0] == '@')
        {
            FILE *f;
              
            wchar_t name[MAX_PATH+1] = {0};
            
            f = _wfopen(argv[i] + 1,L"r");
            
            if(f == NULL )
            {
                printf("Unable to open filelist %s\n", argv[i]+1);
            } else {
                while(fgetws(name,MAX_PATH-1,f))
                {
                    name[wcslen(name)-1]=0;
                    
                    if (wcslen(name) > 0)
                    {
                        FixPeHeaderOnFile(name);
                    }
                }

                fclose(f);
            }
        } else {
              FixPeHeaderOnFile(argv[i]);
        }
    }
}

References

  1. Microsoft Portable Executable and Common Object File Format (PE/COFF) specification: https://go.microsoft.com/fwlink/?LinkId=177580

  2. Windows SDK Web Download: https://go.microsoft.com/fwlink/?LinkId=177581

  3. MSDN description of Link.exe: https://go.microsoft.com/fwlink/?LinkId=177582

  4. Visual Studio Binary Editor: https://go.microsoft.com/fwlink/?LinkId=177583