company_banner

Intel® Tamper Protection Toolkit — обфусцирующий компилятор и средства проверки целостности кода


    Совсем недавно компания Intel выпустила очень интересный набор инструментов для разработчиков программного обеспечения, позволяющий добавить защиту программного кода от взлома и существенно усложнить жизнь взломщикам программ. Этот набор включает в себя обфусцирующий компилятор, средство для создания файла подписи, используемого для проверки целостности загружаемых динамических библиотек, а также библиотеку функций проверки целостности и дополнительные полезные инструменты. Intel® Tamper Protection Toolkit beta можно совершенно бесплатно скачать на сайте Intel.

    Обфусцирующий компилятор


    Одним из инструментов Tamper Protection Toolkit является обфусцирующий компилятор — iprot. С помощью этого инструмента можно защитить исполняемый код от статического и динамического анализа/изменения. Результатом работы обфусцирующего компилятора является самошифрующийся, самомодифицирующийся код, сопротивляющийся модифицированию, статическому анализу и функционально эквивалентный начальному коду.

    Обфусцирующий компилятор работает с функциями динамических библиотек. Однако очень часто приложения написаны так, что конфиденциальный код располагается внутри приложения. Для того, чтобы этот код защитить, нужно сделать небольшой рефакторинг приложения: выделить конфиденциальные функции в отдельную динамическую библиотеку.



    В результаты работы обфусцирующего компилятора будет создана другая динамическая библиотека, функции которой будут подвергнуты обработке. Эти функции далее и надо использовать для работы.

    Естественно, защита кода не бывает бесплатной. Одной из плат за защиту является замедление работы кода и увеличение размера кода функций. У обфусцирующего компилятора есть пара параметров, с помощью которых можно контролировать соотношение защита/производительность для кода. Падение производительности можно компенсировать рефакторингом исходного кода, например, использовать инлайн функции, оптимизацию по размеру кода. Можно попробовать поменять параметры обфусцирующего компилятора --mutation-distance и --cell-size.

    Параметр --mutation-distance контролирует частоту, с которой происходит само-модификация кода. Меньшая дистанция дает нам лучшую защиту но ухудшает производительность. Можно попробовать несколько различных значений этого параметра, чтобы добиться оптимальной на ваш взгляд производительности.

    Параметр --cell-size контролирует размер декодированного/открытого кода в определенный момент времени. Меньший размер ячейки кода означает лучшую защиту, однако ухудшает производительность. Увеличивая размер ячейки можно существенно улучшить производительность, но и существенно ухудшить защиту кода, т.к. большие участки кода будут доступны (находиться в открытом виде) для анализа. Попробовав различные значения этого параметра, можно добиться оптимальной производительности.

    Таким образом можно сказать, что создание защищенного и в тоже время производительного кода не простая задача. Для ее осуществления могут потребоваться не только варьирование параметров обфусцирующего компилятора, но и так же рефакторинг исходного кода, что бы добиться лучшей производительности и меньшего размера исходного кода.

    Надо заметить, не всякий код может быть обработан обфусцирующим компилятором. Есть ограничения на исходный код, такие как отсутствие релокаций (relocations), косвенных переходов (inderect jumps) или вызовов функций из других библиотек, код которых недоступен на момент обработки обфусцирующим компилятором.

    Далее мы рассмотрим пример, в котором мы постараемся побороться с некоторыми проблемами несовместимости кода, которые могут встретиться в реальной жизни.

    Проверка целостности кода динамической библиотеки


    Для того, что бы убедиться в том, что загружаемый модуль динамической библиотеки не подвергался модификациям, Tamper Protection Toolkit содержит специальный набор средств. Инструмент создания подписи динамической библиотеки – codebind и небольшую библиотеку функций для проверки целостности библиотеки – codeverify.lib.



    Инструмент создания цифровой подписи динамической библиотеки (codebind) принимает на вход имя библиотеки и приватный DSA ключ, который можно сгенерировать, например, с помощью библиотеки OpenSSL*. В результате получается дополнительный файл (secure box), который в дальнейшем будет использоваться для проверки целостности связанной с ним динамической библиотеки.

    Проверка целостности динамической библиотеки осуществляется с помощью нескольких функций, являющихся частью Tamper Protection Toolkit. Приложение нужно модифицировать, добавив вызовы API. Функции этого API позволяют проверить целостность динамической библиотеки еще до загрузки в память (статическая проверка), а также проверять, не подвергался ли модификации код библиотеки в процессе выполнения программы (динамическая проверка).

    Библиотека криптографических функций


    В состав Tamper Protection Toolkit входят несколько базовых криптографических функций:
    • Функции создания однонаправленного хэша: HMAC SHA256
    • Функции шифрования с симметричным ключом: AES (CTR/GCM)
    • И некоторые другие.

    Все эти функции могут быть использованы в конфиденциальном коде и обработаны обфусцирующим компилятором, входящим в состав Tamper Protection Toolkit.

    Таким образом, с помощью инструментов и функций Tamper Protection Toolkit можно создать надежную защиту конфиденциального кода или информации, содержащейся в пользовательском приложении. Обфусцирующий компилятор позволяет создать самошифрующийся, самомодифицирующийся код, сопротивляющийся модифицированию и статическому анализу. Инструмент Codebind поможет в создании файла цифровой подписи, а библиотека функций проверки целостности поможет проверить, не модифицировались ли функции динамической библиотеки как на диске, до загрузки, так и после загрузки кода в память. Функции крипто библиотеки помогут в создании криптографических алгоритмов и защиты их с помощью обфусцирующего комплиятора. 

    Пример обфускации кода


    В этом примере мы покажем как можно защитить код с помощью обфусцирующего компилятора, входящего в состав Intel® Tamper Protection Toolkit. А так же мы покажем, как можно избавиться от некоторых проблем, которые могут возникнуть в процессе обфускации кода.

    Что нам понадобится для этого примера
    • Понимание базовых основ С/С++. Знакомство в общих чертах с релокациями (relocations) и косвенными переходами (indirect jump).
    • С/С++ компилятор, например, Intel®; Compiler или Microsoft Visual Studio*
    • Intel® Tamper Protection Toolkit

    В этом примере используются комманды компилятора Visual Studio, однако аналогичным образом можно собрать код и любым другим компилятором.

    Обфусцирующий компилятор, входящий в набор средств Intel® Tamper Protection Toolkit, принимает на вход путь к динамической библиотеке (dll/so), имя функции, которую надо будет обфусцировать и параметры обфускации. Поэтому для работы нам сначала надо создать динамическую библиотеку. Пример для создания динамической библиотеки можно найти на диске папке tutorials\obfuscation_tutorial.

    Для сборки динамической библиотеки воспользуемся Visual Studio*. Запустите командную строку Visual Studio, наберите следующую команду:

    cl /GS- /GR- src_compatible.c /link /DLL /NOENTRY /OUT:.\src_compatible.dll
    

    В результате выполнения этой комманды должна появиться src_compatible.dll динамическая библиотека. Для компиляции были использованы следующие опции
    • /GR- — эта опция выключает проверку типов объектов в процессе выполнения
    • /GS- — эта опция выключает проверку переполнения.
    • /NOENTRY — эта опция отключает создание main entry в динамической библиотеке.

    Чтобы тест заработал, скомпилируйте тестовое приложение loadutil.c и related.h.

    cl loadutil.c /link /OUT:.\loadutil.exe
    

    После того, как соберется тестовое приложение, можно запустить тест и проверить, что собранная нами динамическая библиотека работает:

    loadutil src_compatible.dll
    

    Приложение должно написать Called get_symbol() function successfully в командном окне.

    Далее, создадим обфускированную версию нашей динамической библиотеки с помощью следующей команды:

    iprot src_compatible.dll -o obfuscated.dll get_symbol
    

    На диске должна появиться библиотека obfuscated.dll, имя которой мы и подадим на вход нашего тестового приложения:

    loadutil obfuscated.dll
    

    Приложение снова должно написать Called get_symbol() function successfully в командном окне. Таким образом, обфусцированная библиотека функционально полностью эквивалентна не обфусцированной библиотеки. Однако, если вы взгляните на динамические библиотеки с помощью HEX редактора, вы обнаружите, что понять код обфусцированной библиотеки практически невозможно.
    Итак, мы построили нашу первую простейшую обфусцированную динамическую библиотеку. Давайте посмотрим, какие проблемы и трудности могут встретится при обфусцировании более сложного С/С++ кода и как с этим бороться.

    Советы, как обойти подводные камни


    Обфусцирующий компилятор, входящий в состав Tamper Protection Toolkit, имеет несколько ограничений на код, который он пытается обработать, а именно:
    • Код, генерирующий релокации (relocations)
    • Код, генерирующий косвенные переходы (inderect jumps)
    • Код, генерирующий PIC глобальные ссылки (специфика Anroid*)

    Язык С содержит довольно много конструкций, которые могут генерировать косвенные переходы и релокации. Далее мы посмотрим на некоторые примеры таких конструкций и способы, как можно обойти генерацию нежелательного кода.

    Рассмотрим пример, содержащий код, который не может обработать обфусцирующий компилятор — src_incompatible.c. Его можно взять в папке tutorials/obfuscation_tutorial.
    Сперва построим динамическую библиотеку из этого файла:

    cl /GS- /GR- src_incompatible.c /link /DLL /NOENTRY /OUT:.\Incompatible.dll
    

    Далее, обфускируем одну из функций этой динамической библиотеки:

    iprot Incompatible.dll -o Obfuincompatible.dll get_symbol
    

    В результате, вы должны увидеть примерно следующее сообщение:

    [parsing_flow-1]: Processing 'get_symbol'…
    [warning-1]: warning: minimal mutations detected at top level loop; adding more
    [scheduling-1]: Setting top level mutation distance: 1
    [analysis-1]:
    [PROC 0:0x10001010:12<-0]

    iprot: unsupported memory reference with relocation in acquired code at 0x1000101c:
    mov al, byte ptr [eax+10002000h]


    Обфусцирующий компилятор встретил релокацию и не смог продолжить обработку. Это произошло потому, что в функции get_symbol() используется обращение к глобальной переменной alphabet. Компилятор генерирует релокацию, которую не может обработать обфусцирующий компилятор. Один из способов избавиться от релокации — передать указатель как параметр при вызове функции:
    Решение №1
      char API get_symbol(char const* alphabet_data, unsigned int alphabet_size, unsigned int s_idx)
      {
          if (s_idx < alphabet_size)
              return alphabet_data[s_idx];
    
          return ' ';
      }
    


    Можно поступить иначе, вместо глобальных данных использовать локальную переменную.
    Решение №2
      char API get_symbol_second(unsigned int s_idx)
      {
          char alphabet_local[26];
          alphabet_local[0] = 'a';
          alphabet_local[1] = 'b';
          alphabet_local[2] = 'c';
          alphabet_local[3] = 'd';
          alphabet_local[4] = 'e';
          alphabet_local[5] = 'f';
          alphabet_local[6] = 'g';
          alphabet_local[7] = 'h';
          alphabet_local[8] = 'i';
          alphabet_local[9] = 'j';
          alphabet_local[10] = 'k';
          alphabet_local[11] = 'l';
          alphabet_local[12] = 'm';
          alphabet_local[13] = 'n';
          alphabet_local[14] = 'o';
          alphabet_local[15] = 'p';
          alphabet_local[16] = 'q';
          alphabet_local[17] = 'r';
          alphabet_local[18] = 's';
          alphabet_local[19] = 't';
          alphabet_local[20] = 'u';
          alphabet_local[21] = 'v';
          alphabet_local[22] = 'w';
          alphabet_local[23] = 'x';
          alphabet_local[24] = 'y';
          alphabet_local[25] = 'z';
          if (s_idx < sizeof(alphabet_local))
              return alphabet_local[s_idx];
          return ' ';
      }
    

    Далее, обфусцируем нашу библиотеку со следующей командой:

    iprot Incompatible.dll -o Obfuincompatible.dll get_next_state
    

    В результате, должно получиться следующее:

    [parsing_flow-1]: Processing 'get_next_state'…
    [warning-1]: warning: minimal mutations detected at top level loop; adding more
    [scheduling-1]: Setting top level mutation distance: 2
    [analysis-1]:
    [PROC 0:0x10001030:18<-0]

    iprot: unsupported indirect jump in acquired code at 0x10001055:
    jmp dword ptr [100010A0h+edx*4]


    Обфусцирующий компилятор встретил косвенный переход, который не смог обработать.
    Если заглянуть в код функции get_next_state() можно увидеть использование switch, который и генерирует косвенный переход. От косвенного перехода можно легко избавиться используя if-else if.
    Решение
      my_state API get_next_state(my_state in_state)
      {
         if(ST_UNINITIALIZED == in_state)
            return ST_CONNECTING;
         if(ST_CONNECTING == in_state)
          return ST_NEGOTIATING;
         if(ST_NEGOTIATING == in_state)
            return ST_INITIALIZING;
         if(ST_INITIALIZING == in_state)
              return ST_PROCESSING;
           if(ST_PROCESSING == in_state)
              return ST_DISCONNECTING;
          if(ST_DISCONNECTING == in_state)
              return ST_FINISHED;
           return ST_UNINITIALIZED;
      }
    


    Для того, чтобы избавиться от генерации PIC кода для Android, можно использовать опцию компиляции -fno-pic.

    Пример проверки целостности кода


    В этом примере мы покажем, как использовать функции программного обеспечения, называющиеся связывание (binding) и проверка целостности (integrity verification) бинарных данных, предоставляемых Tamper Protection Toolkit. Эти функции могут помочь вам существенным образом осложнить жизнь взломщикам вашего программного кода. Функция проверки целостности проверяет данные на диске, а так же бинарный код, загруженный в память программы. Следуя шагам из этого примера, вы научитесь как можно использовать связывание и проверку кода.

    Что нам понадобится для этого примера
    • Понимание базовых основ С/С++. Знакомство в общих чертах с релокациями (relocations) и косвенными переходами (indirect jump).
    • С/С++ компилятор, например, Intel® Compiler или Microsoft Visual Studio*
    • OpenSSL* библиотека для генерации ключей.
    • Intel® Tamper Protection Toolkit

    В этом примере используются команды компилятора Visual Studio, однако аналогичным образом можно собрать код и любым другим компилятором.

    Давайте начнем со сборки первого компонента нашего примера — динамическую библиотеку module.dll. Исходный код можно найти на диске в папке tutorials/code_verification.

    cl module.c  /link  /DLL /OUT:module.dll
    

    Далее соберем приложение, использующее функции из динамической библиотеки.

    cl sample_app_without_verification.cpp /link module.lib
    

    Если запустить собранное приложение, то вы должны увидеть следующие

    >sample_app_without_verification
    sum(3,5) returns 8
    sum(3,5) + global_array[3] returns 12


    Теперь все готово для защиты нашей динамической библиотеки с помощью функций связывания и проверки целостности. С помощью одного из средств Tamper Protection Toolkit мы создадим специальный файл подписи с расширением .sb, называемый «secure box». Этот файл содержит данные, используемые функциями тулкита для проверки целостности динамической библиотеки module.dll.
    Для начала воспользуемся OpenSSL, что бы сгенерировать необходимые ключи

    md keys
    openssl dsaparam -out keys/dsaparam.pem 2048
    openssl gendsa -out keys/prikey.pem keys/dsaparam.pem
    openssl dsa -in keys/prikey.pem -outform der -out keys/pubkey.der -pubout
    

    Для генерации файла подписи воспользуемся программой codebind.exe. Входными данными для функции связывания кода с подписью является динамическая библиотека и приватный ключ, сгенерированный заранее. Результатом процесса связывания является файл подписи — «secure box». Имя файла подписи и его расширение произвольно, на выбор пользователя. В этом примере будет использоваться расширение ".sb". Команда для связывания выглядит так:

    codebind -i <path to the dll/so file> -k <path to private key> -o <output path to secure box file>
    

    Что бы связать нашу динамическую библиотеку с помощью приватного ключа сгенерированного с помощью OpenSSL запустите следующую команду:

    codebind -i module.dll -k keys/prikey.pem -o module.sb
    

    Если ваша платформа не поддерживает “Intel ® Secure Key” вы увидите следующее сообщение:

    codebind: Intel® Secure Key (RDRAND instruction) is not supported.
    Use "--seed" program option


    В этом случае выполните следующую команду используя ключ "--seed" со случайным числом:

    codebind -i module.dll -k keys/prikey.pem -o module.sb --seed 0xabba
    

    В случае успешного связывания, в директории должен появиться файл «module.sb», его мы будем использовать для проверки целостности нашей динамической библиотеки.

    Итак, к настоящему моменту у нас есть динамическая библиотека — module.dll, содержащая функциональность, которую мы хотим защитить. Приложение «sample_app_without_verification», вызывающее функции из нашей динамической библиотеки. Пара ключей: публичный и приватный, последний мы использовали для создания файла подписи — «module.sb».

    Следующим шагом нашего примера будет добавление кода, который будет проверять целостность нашей динамической библиотеки статически и динамически в процессе вызова функций из нее.

    Первый шаг на этом пути — превратить публичный ключ в вид, который можно будет использовать в коде проверки целостности. Для этого можно воспользоваться инструментом под названием «bin2hex», который принимает на входе публичный ключ и генерирует текстовый файл (".h"), который содержит публичный ключ, в виде пригодном для «C» компиляции.
    bin2hex keys/pubkey.der pubkey.h
    


    Последующие шаги помогут нам добавить проверку целостности нашей динамической библиотеки в наше приложение sample_app_without_verification.cpp. После того, как все кусочки кода будут добавлены в исходный файл, должен получиться код, совпадающий с файлом sample_app_with_verification.cpp.
    Итак, начнем с включения нужных нам файлов заголовков:

        #include "codeverify.h"
        #include "pubkey.h"
        #include <fstream>
        #include <memory>
        #if defined(_WIN32)
        #include <windows.h>
        #else
        #include <dlfcn.h>
        #endif
    


    Далее, добавим коды для обработки ошибок и декларацию нужных переменных и функций:

    Коды ошибок и объявления
        enum {
            V_STATUS_OK                      = 0,   /*!< Indicates no error */
            V_STATUS_NULL_PTR                = -1,  /*!< Input argument is null pointer */
            V_STATUS_BAD_ARG                 = -2,  /*!< Bad input argument */
            V_STATUS_KEY_GETSIZE_FAILED      = -3,  /*!< Key get size failed */
            V_STATUS_KEY_INIT_FAILED         = -4,  /*!< Key init failed */
            V_STATUS_VER_GETSIZE_FAILED      = -5,  /*!< Verification get size failed */
            V_STATUS_VER_INIT_FAILED         = -6,  /*!< Verification init failed */
            V_STATUS_VERIFICATION_FAILED     = -7,  /*!< Verification failed */
            V_STATUS_RANGE_SAFE_FAILED       = -8,  /*!< Is Range Safe failed */
            V_STATUS_ERR                     = -9   /*!< Unexpected error */
        };
    
        CodeVerify *c_verifier = 0;
    
        unsigned char * ReadFromFile(const string & file_name, unsigned int &fsize);
        int InitVerification(void *handle, unsigned char *sb, unsigned int sb_size);
    


    Добавим код, для загрузки нашей динамической библиотеки и файла подписи

    Объявление переменных и чтение файла
    #if defined _WIN32
        string dll_name = "module.dll"; //path to dll.
        string sb_name = "module.sb"; //path to sb.
    #else
        string dll_name = "libmodule.so"; //path to shared library.
        string sb_name = "module.sb"; //path to sb.
    #endif
        //Read SB to buffer:
        unique_ptr<unsigned char[]> sb;
        unsigned int sb_size = 0;
        sb.reset(ReadFromFile(sb_name, sb_size));
        if(!sb)
        {
            cout << "SB file reading failed!" << endl;
            return V_STATUS_ERR;
        }
    
    


    В конец файла добавим функцию чтения из файлов, используемую в коде:

    Функция чтения из файла
        unsigned char * ReadFromFile(const string & file_name, unsigned int &fsize)
        {
            ifstream f;
            f.open (file_name, ifstream::in | ifstream::binary);
            if(f)
            {
                f.seekg(0, ios::end);
                unsigned int size = (unsigned int)f.tellg();
                f.seekg(0, ios::beg);
                // allocate memory to contain file data
                unique_ptr<unsigned char[]> res(new unsigned char[size]);
                f.read((char*)res.get(), size);
                if(!f)
                {
                    f.close();
                    return 0;
                }
                f.close();
                fsize = size;
                return res.release();
            }
            return 0;
        }
    


    Инициализация контекста проверки целостности происходит внутри функции InitVerification(), которая принимает указатель на файл и данные из файла подписи в качестве входных параметров.

    Инициализация
        //Get DLL handle:
    #if defined _WIN32
        HMODULE handle = GetModuleHandle(dll_name.c_str());
    #else
        Dl_info dl_info;
        dladdr((void*)sum, &dl_info);
        void *handle = dlopen(dl_info.dli_fname, RTLD_NOW);
    #endif
        if(!handle)
        {
            cout << "Dll handle can't be obtained, dll-name: " << dll_name.c_str() << endl;
            return V_STATUS_ERR;
        }
        int ret = V_STATUS_OK;
        ret = InitVerification(handle, sb.get(), sb_size);
        if(V_STATUS_OK != ret)
        {
            cout << "InitVerification failed!  Error code: " << ret << endl;
    #if defined _WIN32
            if(c_verifier) delete [](char*)c_verifier;
    #else
            if(handle) dlclose(handle);
            if(c_verifier) delete [](char*)c_verifier;
    #endif
            return V_STATUS_ERR;
        }
    


    Добавим реализацию функции InitVerification() в конец файла:

    Функция InitVerification()
    int InitVerification(void *handle, unsigned char *sb, unsigned int sb_size)
    {
        DECLARE_pubkey_der;
        VerificationKey *m_verifier = 0;
        unsigned int size = 0;
        int err = V_STATUS_ERR;
        if(!handle || !sb)
            return V_STATUS_NULL_PTR;
        if(!sb_size)
            return V_STATUS_BAD_ARG;
        DEFINE_pubkey_der;
        //Get size of VerificationKey context:
        if(VK_STATUS_OK != VerificationKey_GetSize(pubkey_der, sizeof(pubkey_der), &size))
        {
            return V_STATUS_KEY_GETSIZE_FAILED;
        }
        //VerificationKey context memory allocation:
        m_verifier = (VerificationKey *)(new char[size]);
        //Init VerificationKey context:
        if(VK_STATUS_OK != VerificationKey_Init(m_verifier, pubkey_der, sizeof(pubkey_der)))
        {
            err = V_STATUS_KEY_INIT_FAILED;
            if(m_verifier)  delete [] (char*)m_verifier;
            return err;
        }
        //Get size of CodeVerify context:
        if(CV_STATUS_OK != CodeVerify_GetSize(sb,sb_size,m_verifier,&size))
        {
            err = V_STATUS_VER_GETSIZE_FAILED;
            if(m_verifier)  delete [] (char*)m_verifier;
            return err;
        }
        //CodeVerify context memory allocation:
        c_verifier = (CodeVerify*)(new unsigned char[size]);
        //Init CodeVerify context:
        if(CV_STATUS_OK != CodeVerify_Init(c_verifier,size,(const void *)handle,sb,sb_size,m_verifier))
        {
            err = V_STATUS_VER_INIT_FAILED;
            if(m_verifier)  delete [] (char*)m_verifier;
            return err;
        }
        err = V_STATUS_OK;
        if(m_verifier)  delete [] (char*)m_verifier;
        return err;
    }
    


    После инициализации контекста проверки целостности все готово для того, чтобы добавить динамическую проверку целостности динамической библиотеки, загруженной в память. Это делается с помощью вызова функции CodeVerify_Verify(). Эту функцию можно вызывать неограниченное количество раз, в разных местах кода, чтобы проверить целостность загруженной библиотеки. У этой функции есть входной параметр -work_factor, с помощью которого можно уменьшить размер проверяемой памяти загруженной библиотеки. Например, при work_factor = 3 полная проверка загруженной динамической библиотеки завершится за три вызова функции. Переменная pass_count, передаваемая каждый раз при вызове функции CodeVerify_Verify(), содержит количество полных проверок динамической библиотеки.
    Если вы используете глобальные переменные и не хотите, чтобы кто-либо их изменял в процессе выполнения кода приложения, можно воспользоваться функцией CodeVerify_IsRangeSafe() чтобы убедиться, что целостность интересующих вас данных проверяется вызовом функции CodeVerify_Verify():

    Проверка переменной и завершение работы
        ret = CodeVerify_IsRangeSafe(c_verifier, global_array, sizeof(global_array));
        if(CV_STATUS_OK != ret)
        {
            cout << "IsRangeSafe failed!" << endl;
    #if defined _WIN32
            if(c_verifier) delete [](char*)c_verifier;
    #else
            if(handle) dlclose(handle);
            if(c_verifier) delete [](char*)c_verifier;
    #endif
            return V_STATUS_RANGE_SAFE_FAILED;
        }
        cout << "Range verification was successfully done!" << endl;
    


    После того, как вы завершите использовать функции библиотеки codeverify, не забудьте высвободить использованную для проверки целостности память и выгрузить динамическую библиотеку из памяти:

    Высвобождение ресурсов
    #if defined _WIN32
            if(c_verifier) delete [](char*)c_verifier;
    #else
            if(handle) dlclose(handle);
            if(c_verifier) delete [](char*)c_verifier;
    #endif
    


    Для сборки программы с включенным кодом проверки целостности можно воспользоваться следующей командой компиляции:

    cl sample_app_without_verification.cpp /I../../inc  /link ../../lib/win-x86/codeverify.lib module.lib
    

    Если компиляция прошла успешно и приложение было успешно создано, можно его запустить и вы должны получить следующее:

    > sample_app_without_verification
    Range verification was successfully done!
    sum(3,5) returns 8
    sum(3,5) + global_array[3] returns 12


    Если попробовать изменить код функций библиотеки module.dll, пересобрать ее и попробовать запустить с тестовым приложением, не пересоздавая файла цифровой подписи, проверка целостности должна заметить подмему и выдать ошибку:

    > sample_app_without_verification
    InitVerification failed! Error code: -6


    Вот пожалуй и все, что нужно сделать с помощью инструментов Intel® Tamper Protection Toolkit, чтобы проверить целостность загружаемой динамической библиотеки.

    Надеюсь данный краткий экскурс в инструмент Intel® Tamper Protection Toolkit был полезен и помог вам понять, что он из себя представляет, что умеет делать и как им можно воспользоваться для защиты своего программного обеспечения. Удачи!

    * Другие наименования и товарные знаки являются собственностью своих законных владельцев.
    Intel
    213,77
    Компания
    Поделиться публикацией

    Похожие публикации

    Комментарии 17

      0
      Очень интересно. Жаль, нет поддержки операционных систем, я бы сразу попробовал в продакшне.
        0
        Какой именно поддержке операционных систем не хватает? Режима ядра?
          +1
          Подозреваю, что это был тонкий троллинг в сторону MS Windows, с намёком на поддержку UNIX-like систем.
            0
            Android поддерживается :) Чем не unix-like?
              0
              Было бы ещё неплохо, если бы софт можно было запустить на чём-то кроме MS Windows. Вот например если бы работала связка ubuntu/rhel+gcc/clang, то можно было бы говорить о поддержке.

              То что через специфический SDK/NDK можно собирать ELF бинарники для Android конечно неплохо, но явно маловато. Я подозреваю, что кучей хаков можно и под десктопный Linux провернуть сборку, раз уж под Android поддержка есть, но всё равно нужно это делать на MS Windows.
                0
                Согласен. Тулы пока увы только на Windows x64…
            0
            Не хватает поддержки Linux. Тема интересная, я бы сразу попробовал в большом продакшне. Но Windows-only девелоперский софт — это странно. Вероятно, есть какой-то business case, для чего это нужно именно в винде, и он для этого продукта основной. Ну ок.
              0
              Вот так сразу в большом продашне? Даже не проверив, сработает ли тулкит как планируется? Нет, правда, неужели у вас нет виндовой машины поставить тулкит и поиграться с ним в приложении к вашему проекту? :)
              Для выпуска тулов на Линух нужен серьезный бизнес ризон, ибо сейчас считается, что Windows хост достаточно…
                0
                Конечно же не проверив. Код я компилирую прямо на тысячах серверов, а баги отлавливают пользователи.

                Кем считается? С моей точки зрения, Linux host достаточно, OS X очень желательно, а вот Windows — опционально.
                  0
                  Егор, простите, что так долго не отвечал, был на каникулах :)
                  Что бы выпустить Linux тулы, нужны кастомеры. Как ни странно, Вы пока один из очень немногих, кому нужны тулы под Linux.
                  Однако чем больше будет таких запросов, тем больше шансов появиться поддержки хоста Linux :)
                    0
                    Просто под линукс их уже немало есть и так.
          0
          Как планируется защитить массив констант? Иногда не так важен алгоритм, как важны эти данные…
            0
            Массив констант, если он находится в рид-онли секции динамической библиотеки, будет «защищен» codebind-ом и целостность этого массива можно будет проверить. С iprot сложнее, да… пока не умеет…
            0
            А есть ли у вас какой-нибудь crackme?
            Просто так, на посмотреть?
            А то захочет кто нибудь вас сравнить например, с Arxan или OLLVM…
              0
              Что такое crackme? Предполагаю это простенькая программка для проверки надежности работы тулов, да?
                0
                crackme (от английского crack me) — это маленькая программа, защищенная с помощью вашего пакета, как правило в виде «введите правильный пароль» с проверкой.
                Очень полезная штука для тех, кто хочет проверить насколько хорошо ваш пакет на самом деле защищает и сравнить с другими решениями.
                Предоставление такой программы позволяет избежать ошибок в использовании в процессе эвалуации.
                  0
                  К сожалению такой програмки пока нету.
                  В качестве замены, можно попробовать собрать примеры, описаные выше…

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое