Удаление цифровой подписи PE-файла

    Для подписи PE-файлов (exe, dll, sys и другие) в большинстве случаев используется утилита signtool.exe, но какую утилиту использовать, если цифровую подпись нужно удалить из файла? А такой утилиты официально нет. Можно только подписать или переподписать (поставить свою подпись поверх существующей), но не удалить. Как же быть, если файлик нужно подправить в Hex или PE-редакторе и не хочется оставлять файл с заведомо повреждённой цифровой подписью?

    Для чего это нужно

    • Если нужно изменить подписанный файл, например исправить в нём ошибки или локализовать. После любого изменения файла цифровая подпись становится повреждённой и недействительной, поэтому лучше её убрать совсем, чем оставлять в таком виде.
    • Больше узнать о механизме подписания файлов и разобраться в его работе.
    В статье описан способ ручного удаления цифровой подписи, достаточно простой, чтобы в нём мог разобраться каждый, кто хоть раз в жизни использовал Hex-редактор.

    Инструменты


    Для работы нам понадобятся следующие утилиты:
    1. Hex-редактор. Я предпочитаю WinHex.
    2. Утилита для исправления контрольной суммы PE-файла ModifyPE.
    3. Шестнадцатеричный калькулятор, есть в системе.

    Пример


    Для примера будем удалять цифровую подпись с дистрибутива замечательной открытой и свободной программы для шифрования дисковых разделов DiskCryptor (объект выбран рандомно). Этим примером, кстати, будет видно, что удаление цифровой подписи никак не влияет на работоспособность PE-файлов.

    Открываем dcrypt_setup.exe в Hex-редакторе и ищем 4-х байтную последовательность 50450000h (в тексте видно как PE с последующими двумя нулевыми байтами). Эта сигнатура идентифицирует файл как файл PE-формата и идёт сразу за заголовком MS-DOS. В данном случае начало сигнатуры находится по смещению 100h:



    Следующее, что понадобится исправить после удаления цифровой подписи — это контрольная сумма файла. Она находится через 58h байт после сигнатуры PE-формата, то есть 100h + 58h = 158h, следовательно текущая контрольная сумма (тип dword, то есть занимает 4 байта) этого файла — 9F36Ch (байты переворачиваются):



    Последующие два значения относятся непосредственно к цифровой подписи. Если они состоят из нулей — подписи нет. Первое находится через 40h байт после начала контрольной суммы, или 98h байт после начала сигнатуры — 100h + 98h = 198h:



    Это 4-байтное значение означает смещение, по которому находится начало цифровой подписи. Сейчас оно равно 8E438h:



    Второе, опять же 4-байтное значение, находится сразу же за первым:



    Оно означает размер цифровой подписи, что в данном случае равно 1500h или 5376 байт. Значит конец подписи будет по смещению 8E438h + 1500h = 8F938h. Как правило цифровая подпись идёт до конца файла, проверяем:



    Всё совпадает, поэтому этот блок можно смело удалить, после чего концом файла будет 8E437h:



    Осталась самая малость — затереть нулями указатели смещения и размера цифровой подписи:



    И скорректировать контрольную сумму с помощью утилиты ModifyPE:



    Проверяем:



    Готово! Теперь пакет установки программы именно такой, каким он был до подписи, байт в байт.

    P. S. Не судите строго, это первый раз, в будущем качество будет расти. Конструктивная критика приветствуется.

    P.P.S. Автор топика начинающий хабраюзер systracer, который попросил меня опубликовать его текст. Плюсы следует адресовать ему, минусы оставьте мне.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 18

      +3
      Было бы здорово вкратце про механизм проверки подписи, etc.

      PS:
      Невероятно, но systracer мог и сам написать пост, сообщите ему об этой приятной возможности. ;-)
        +2
        Описание механизма проверки очень легко найти поисковиком, например здесь — эта информация вообще не дефицит.

        P. S. Раньше не мог написать пост, кое-чего не хватало.
        +3
        >А такой утилиты официально нет.
        Так надо такую утилиту срочно официально написать! )
        +2
        «Если нужно изменить подписанный файл, например исправить в нём ошибки или локализовать.» — или сделать crack, господи прости… :-)
          0
          Статья — хороша. Не каждый разбирается в таких вещах.
          +5
          То, что я хотел читать на Хабре ) Отличный пост )
            +1
            hack the planet :)
              +4
              >Последующие два значения относятся непосредственно к цифровой подписи. Если они состоят из нулей — подписи нет.
              последующие два занчения за полем CheckSum это поля Subsystem и DllCharacteristics.
              вариант по удалению подписи еще проще:
              -качаем CFF Explorer
              -открываем в нем наш exe
              — далее в Data Directories находим Security Directory и удаляем
                0
                Спасибо за замечание, имелись в виду нужные нам поля. CFF Explorer просто заполняет их нулями — это тоже допустимо, но тогда останется хвост из подписи, в данном случае лишних 5376 байт.
                +1
                байты не переворачиваются, это little-endian
                  –1
                  Имелось в виду следующее: если читать слева направо или выделять и копировать, то чтобы посчитать числа в обычном калькуляторе их нужно переворачивать.
                  –4
                  О стартапе не думали?
                    +1
                    Половина указанных операций в Hiew делается автоматически, например очистка директории и изменение контрольной суммы.
                      0
                      неплохо бы и саму утилиту написать :)
                      Конечно, такое объяснение весьма ценно и само по себе, но слегка трудоёмко, если делать часто.
                        –1
                        На хабре снова появились торты.
                        Я так стану сладкоежкой.
                          0
                          Прилагаю свой код для нахождения полного размера PE образа. В случае, если полученное значение (возвращается через переданный в параметре image_size указатель) не совпадает (меньше) с размером EXE/DLL/SYS файла — значит начиная с данного смещения в файле следует цифровая подпись или иные данные.
                          Определения структур _IMAGE_* и констант есть в winnt.h (составляющая часть windows.h), включены просто для удобства и портабельности.

                          #include <stdio.h>
                          
                          typedef unsigned char BYTE;
                          typedef unsigned short WORD;
                          typedef unsigned long DWORD;
                          typedef long LONG;
                          
                          #define IMAGE_DOS_SIGNATURE 0x5A4D
                          #define IMAGE_NT_SIGNATURE 0x4550
                          #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
                          #define IMAGE_SIZEOF_SHORT_NAME 8
                          #define IMAGE_DIRECTORY_ENTRY_SECURITY 4
                          
                          typedef struct _IMAGE_DOS_HEADER {
                          	WORD e_magic;
                          	WORD e_cblp;
                          	WORD e_cp;
                          	WORD e_crlc;
                          	WORD e_cparhdr;
                          	WORD e_minalloc;
                          	WORD e_maxalloc;
                          	WORD e_ss;
                          	WORD e_sp;
                          	WORD e_csum;
                          	WORD e_ip;
                          	WORD e_cs;
                          	WORD e_lfarlc;
                          	WORD e_ovno;
                          	WORD e_res[4];
                          	WORD e_oemid;
                          	WORD e_oeminfo;
                          	WORD e_res2[10];
                          	LONG e_lfanew;
                          } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
                          
                          typedef struct _IMAGE_FILE_HEADER {
                          	WORD Machine;
                          	WORD NumberOfSections;
                          	DWORD TimeDateStamp;
                          	DWORD PointerToSymbolTable;
                          	DWORD NumberOfSymbols;
                          	WORD SizeOfOptionalHeader;
                          	WORD Characteristics;
                          } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
                          
                          typedef struct _IMAGE_DATA_DIRECTORY {
                          	DWORD VirtualAddress;
                          	DWORD Size;
                          } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
                          
                          typedef struct _IMAGE_OPTIONAL_HEADER {
                          	WORD Magic;
                          	BYTE MajorLinkerVersion;
                          	BYTE MinorLinkerVersion;
                          	DWORD SizeOfCode;
                          	DWORD SizeOfInitializedData;
                          	DWORD SizeOfUninitializedData;
                          	DWORD AddressOfEntryPoint;
                          	DWORD BaseOfCode;
                          	DWORD BaseOfData;
                          	DWORD ImageBase;
                          	DWORD SectionAlignment;
                          	DWORD FileAlignment;
                          	WORD MajorOperatingSystemVersion;
                          	WORD MinorOperatingSystemVersion;
                          	WORD MajorImageVersion;
                          	WORD MinorImageVersion;
                          	WORD MajorSubsystemVersion;
                          	WORD MinorSubsystemVersion;
                          	DWORD Win32VersionValue;
                          	DWORD SizeOfImage;
                          	DWORD SizeOfHeaders;
                          	DWORD CheckSum;
                          	WORD Subsystem;
                          	WORD DllCharacteristics;
                          	DWORD SizeOfStackReserve;
                          	DWORD SizeOfStackCommit;
                          	DWORD SizeOfHeapReserve;
                          	DWORD SizeOfHeapCommit;
                          	DWORD LoaderFlags;
                          	DWORD NumberOfRvaAndSizes;
                          	IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
                          } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
                          
                          typedef struct _IMAGE_NT_HEADERS {
                          	DWORD Signature;
                          	IMAGE_FILE_HEADER FileHeader;
                          	IMAGE_OPTIONAL_HEADER OptionalHeader;
                          } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
                          
                          typedef IMAGE_NT_HEADERS IMAGE_NT_HEADERS32;
                          
                          typedef struct _IMAGE_SECTION_HEADER {
                          	BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
                          	union {
                          		DWORD PhysicalAddress;
                          		DWORD VirtualSize;
                          	} Misc;
                          	DWORD VirtualAddress;
                          	DWORD SizeOfRawData;
                          	DWORD PointerToRawData;
                          	DWORD PointerToRelocations;
                          	DWORD PointerToLinenumbers;
                          	WORD NumberOfRelocations;
                          	WORD NumberOfLinenumbers;
                          	DWORD Characteristics;
                          } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
                          
                          int GetPESize(FILE *fEXE, DWORD *image_size, DWORD *sd_addr, DWORD *sd_size)
                          {
                          	BYTE buff[4096];
                          	if (fread(buff, sizeof(buff), 1, fEXE) != 1)
                          		return 1;
                          	IMAGE_DOS_HEADER *dosheader = (IMAGE_DOS_HEADER *) buff;
                          	IMAGE_NT_HEADERS32 *pe_header = (IMAGE_NT_HEADERS32 *)(buff + dosheader->e_lfanew);
                          	if (dosheader->e_magic != IMAGE_DOS_SIGNATURE || pe_header->Signature != IMAGE_NT_SIGNATURE)
                          		return 1;
                          	IMAGE_SECTION_HEADER *sectiontable = (IMAGE_SECTION_HEADER *)((BYTE *) pe_header + sizeof(IMAGE_NT_HEADERS32));
                          	DWORD maxpointer = 0, exesize = 0;
                          	unsigned int i;
                          	for (i = 0; i < pe_header->FileHeader.NumberOfSections; i++)
                          	{
                          		if (sectiontable->PointerToRawData > maxpointer)
                          		{
                          			maxpointer = sectiontable->PointerToRawData;
                          			exesize = sectiontable->PointerToRawData + sectiontable->SizeOfRawData;
                          		}
                          		sectiontable++;
                          	}
                          	*image_size = exesize;
                          	IMAGE_DATA_DIRECTORY *sd = &pe_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
                          	*sd_addr = sd->VirtualAddress;
                          	*sd_size = sd->Size;
                          	return 0;
                          }
                            0
                            Пробовали утилитку imageremcert?

                            Only users with full accounts can post comments. Log in, please.