Статья Как проверить соответствие файла PE-формату без запуска

Тема в разделе "Другие языки программирования", создана пользователем Dragokas, 4 сен 2016.

  1. Dragokas
    Оффлайн

    Dragokas Very kind Developer Команда форума Супер-Модератор Разработчик Клуб переводчиков

    Сообщения:
    4.492
    Симпатии:
    4.309
    Автор: ManHunter

    [​IMG]
    Как проверить соответствие файла PE-формату без запуска

    На форуме EXEL@B встретилась интересная тема по проверке является ли файл корректным PE-файлом, но без его запуска. Статическая проверка по типовым полям PE-заголовка не дает гарантии корректности, так как можно просто приписать к валидному заголовку кусок мусора или испортить его содержимое. Придется как минимум проверить соответствие размера файла суммарному размеру секций, а более тщательные проверки потребуют самостоятельного разбора секций импорта и экспорта, TLS, релоков и еще много чего. Проще всего доверить подобные проверки самой системе.

    Самый простой способ - использовать функцию LoadLibraryEx с двумя флагами в параметрах: LOAD_LIBRARY_AS_DATAFILE и DONT_RESOLVE_DLL_REFERENCES. Первый флаг будет подавлять системное сообщение об ошибке загрузки, если проверяемый файл вообще никак не относится к исполняемым, второй не даст выполниться DllMain при загрузке библиотеки и не будет подгружать дополнительные модули из ее таблицы импорта. Это будет гарантировать, что никакой код не получит несанкционированного управления.

    Код (ASM):

            invoke  LoadLibraryEx,fname,NULL,\
                    LOAD_LIBRARY_AS_DATAFILE+DONT_RESOLVE_DLL_REFERENCES
            or      eax,eax
            ; EAX=0 - файл загрузить не удалось
            ; EAX!=0 - заголовок файла соответствует PE-формату
    Но, как показала практика, использовать для проверки только эту функцию недостаточно. Поэтому на форуме EXEL@B был предложен второй способ - с использованием функции WinAPICreateFileMapping с флагом SEC_IMAGE. Такая комбинация сообщает системе, что проецируемый файл должен являться исполняемым, и, соответственно, память проекции надо подготовить соответствующим образом, с учетом заголовка и секций. Если файл не является корректным, например, повреждена структура секций или не соответствует заголовок, то CreateFileMapping вернет ошибку. На этом и основана следующая функция проверки:

    Код (ASM):

    ;----------------------------------------------------------
    ; Функция проверки корректности PE-файла
    ;----------------------------------------------------------
    ; Параметры:
    ;    lpszFileName - указатель на путь файла
    ; На выходе:
    ;    EAX = 1 - файл корректный
    ;    EAX = 0 - файл не является PE-файлом
    ;----------------------------------------------------------
    proc isPE lpszFileName:DWORD
            locals
            result  dd ?
            endl

            SEC_IMAGE = 0x01000000

            pusha
            mov     [result],0
            invoke  CreateFile,[lpszFileName],GENERIC_READ,FILE_SHARE_READ,\
                    NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
            cmp     eax,-1
            je      .loc_ret
            mov     ebx,eax
            invoke  CreateFileMapping,ebx,NULL,PAGE_READONLY+SEC_IMAGE,0,0,NULL
            push    eax
            invoke  CloseHandle,ebx
            pop     ebx
            or      ebx,ebx
            jz      .loc_ret
            invoke  MapViewOfFile,ebx,FILE_MAP_READ,0,0,0
            or      eax,eax
            jz      @f
            invoke  UnmapViewOfFile,eax

            ; Файл соответствует PE-формату
            mov     [result],1
    @@:
            invoke  CloseHandle,ebx
    .loc_ret:
            popa

            mov     eax,[result]
            ret
    endp
     
    Функция принимает единственный параметр lpszFileName - указатель на имя проверяемого файла. На выходе EAX=1 - файл соответствует PE-формату, EAX=0 - файл не является корректным PE-файлом. Осталось протестировать обе функции. Тестирование проводилось на Windows 7 x86, Windows XP и Windows 8.1 x64, приложение скомпилировано как 32-битное. Для наглядности я собрал в табличку результаты тестирования на различных файлах.
    Тестовый файл LoadLibraryEx Функция isPE
    1 32-битный EXE-файл Correct Correct
    2 32-битный DLL-файл Correct Correct
    3 64-битный EXE-файл на 32-битной системе Correct Invalid
    4 64-битный DLL-файл на 32-битной системе Correct Invalid
    5 64-битный EXE-файл на 64-битной системе Correct Correct
    6 64-битный DLL-файл на 64-битной системе Correct Correct
    7 Поврежденный 32-битный EXE-файл Correct Invalid
    8 Поврежденный 32-битный DLL-файл Correct Invalid
    9 Поврежденный 64-битный EXE-файл Correct Invalid
    10 Поврежденный 64-битный DLL-файл Correct Invalid
    11 Текстовый файл Invalid Invalid

    Поврежденными я назвал изначально корректные исполняемые файлы, от которых я просто отрезал приличный кусок. То есть заголовок остался прежним, а вот внутренняя структура секций была нарушена. При попытке запустить поврежденный файл система выдаст ошибку. Тем не менее, функция LoadLibraryEx бодренько загружает такие файлы в память и возвращает корректный хэндл. Конечно, дальнейшая работа с этим файлом очень сомнительна, например, при попытке загрузить из него какие-нибудь ресурсы, вы почти со стопроцентной вероятностью получите ошибку. Результат "Invalid" LoadLibraryEx дает только на файлах, которые к исполняемым не имеют вообще никакого отношения.

    В то же время, если попытаться проверить корректность 64-битного исполняемого файла на 32-битной системе при помощи функции isPE, она вернет результат "Invalid". Своя правда в этом тоже есть, тем самым функция сообщает, что проверяемый файл не может быть запущен в имеющемся окружении, хотя имеет абсолютно корректную внутреннюю структуру. Поэтому результаты функции isPE можно расценивать как ответ на вопрос "может ли проверяемый файл вообще запуститься на этом компьютере?".

    В приложении пример программы с исходным текстом, которая проверяет соответствие файла "test.exe" PE-формату с помощью функций LoadLibraryEx и isPE.

    // Модератор: файл не прикладывал, т.к. он содержит много ложных срабатываний от антивирусов.
     
    Последнее редактирование: 4 сен 2016

Поделиться этой страницей