How to Fix Signature Verification Failures Caused by Invalid PointerToRawData Field Values

本文介绍了解决因 IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 标志导致的 PE 文件加载失败问题的方法。文中提供了两种解决方案:一是手动使用二进制编辑器修改 PointerToRawData 字段;二是提供了一个 C 语言示例程序,该程序能够自动检测并修正 PointerToRawData 字段。

转自:social.technet.microsoft.com

Beginning with Windows Server 2008 and Windows Vista, there are components, such as the Windows Security Center, that will only execute code from a Portable Executable (PE) image if theIMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag is set in the PE header. When the flag is set, Windows verifies the digital signature of the image prior to loading the image. The PE image will fail to load if the signature cannot be verified. This can occur if the image contains an uninitialized data section, and the PointerToRawData field in the section header for this section is set to a nonzero value.

This failure may occur when using a third-party linker that does not follow the Microsoft Portable Executable and Common Object File Format Specification.

This article describes how to identify this issue and provides both a procedure and example code that update PointerToRawData field values.

Identifying the Issue

To confirm that the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag is present in the PE header
  1. Install the Windows SDK.
  2. Open a Microsoft Windows Software Development Kit (SDK) CMD 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 IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag is set.
To view an event

When the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag is set, Windows verifies the digital signature of the PE image prior to loading it.

If there is an error generated from verifying the PE image signature, Windows fails to load the image and logs an event.

  1. Run the Windows Event Viewer.
  2. In the tree view, expand Applications and Services logsMicrosoftWindows, and CodeIntegrity.
  3. Select Operational to view the Operational log.
  4. In the upper-right pane, click Event ID to sort the events in the Operational log.
  5. For each event with Event ID 3002, check the file path in the event description to determine whether it matches the file path of your PE image.

Work around #1: Using a binary editor to set the PointerToRawData field

Developers can set the PointerToRawData field to zero by using a binary editor as follows. A Binary Editor is available in Visual Studio.

  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 as follows:
    1. Find the PE Header which is located at the beginning of the file, after the MS-DOS section. For more information about the PE Header, see "2. Overview" in the PE/COFF specification.
    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. For more information about the Section Table of the Section Header, see "4. Section Table (Section headers)" in the PE/COFF specification.
  3. Find the PointerToRawData field, which starts at a 20-byte offset within the section header and is 4 bytes long.
  4. Set all bytes in the PointerToRawData field to zero.
  5. 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.

Work around #2: Programmatically setting thePointerToRawData field

The following example shows how to detect nonzero PointerToRawData fields for uninitialized data sections and set these fields to zero.

// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Description:
//
// This example shows how to load a PE image, find the
// section headers for all sections containing only uninitialized
// data, set nonzero values in the section header PointerToRawData
// 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;
    errno_t err;
    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};
            
            err = _wfopen_s(&f,argv[i] + 1,L"r");
            
            if(err != 0 || 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]);
        }
    }
}


前言 上一次发布过的程序:【首发】检测文件的占用,具有学习和商业价值(By超级用户),可以使用,仿电脑管家 正文 对于怎么枚举文件句柄 ,上一帖子对此有介绍,核心代码大概如下:如果 (ZwQueryObject (handle, #ObjectTypeInformation, unicode, 0, size) ≠ #STATUS_INVALID_HANDLE )' 只要不是无效的,为什么,详细看下面的注释 ' 参数 ' Handle ' 对象的一个句柄来获取信息。 ' ObjectInformationClass ' 指定一个OBJECT_INFORMATION_CLASS返回值的类型决定了信息在ObjectInformation缓冲区。 ' ObjectInformation ' 一个指向caller-allocated缓冲接收请求的信息。 ' ObjectInformationLength ' 指定的大小,以字节为单位,ObjectInformation缓冲区。 ' ReturnLength ' 一个指向变量的指针,接收的大小,以字节为单位,请求的关键信息。如果NtQueryObject STATUS_SUCCESS返回,返回的变量包含的数据量。如果NtQueryObject返回STATUS_BUFFER_OVERFLOW或STATUS_BUFFER_TOO_SMALL,您可以使用变量的值来确定所需的缓冲区大小。 ' 返回值 ' NtQueryObject返回STATUS_SUCCESS或适当的错误状态。可能的错误状态码包括以下: ' 返回代码 描述 ' STATUS_ACCESS_DENIED ' 有足够的权限来执行该cha询。 ' STATUS_INVALID_HANDLE ' 提供对象句柄无效。 ' STATUS_INFO_LENGTH_MISMATCH ' 信息长度不足以容纳数据。 unicode = 取空白字节集 (size) ZwQueryObject (handle, #ObjectTypeInformation, unicode, size, 0)' 读取信息的unicode文本 RtlUnicodeStringToAnsiString (ansi, unicode, 真)' 编码转换 ' RtlUnicodeStringToAnsiString例程将给定Unicode字符串转换成一个ANSI字符串。 str = 指针到文本 (ansi.Buffer) ' RtlFreeAnsiString常规版本存储由RtlUnicodeStringToAnsiString分配。 ' 参数 ' AnsiString ' 指针ANSI字符串缓冲区由RtlUnicodeStringToAnsiString以前分配的。 RtlFreeAnsiString (ansi) str = “无法获取”' 无效的怎么获取…… 返回 (str) 这一次呢更新了一个RemoteCloseHandle ,大概的原理是什么呢? 同时也采用了一些比较骚的方法,这种方法的限制较多,但是对于32位进程就很有效果。 NtClose在MSDN的大概介绍 1. NtClose is a generic routine that operates on any type of object. 2. Closing an open object handle causes that handle to become invalid. The system also decrements the handle count for the object and checks whether the object can be deleted. The system does not actually delete the object until all of the object's handles are closed and no referenced pointers remain. 3. A driver must close every handle that it opens as soon as the handle is no longer required. Kernel handles, which are those that are opened by a system thread or by specifying the OBJ_KERNEL_HANDLE flag, can be closed only when the previous processor mo
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值