Однажды была поставлена задача написать некую небольшую программу, она должна была что-то делать, но при это требовалось максимально усложнить процесс анализа кода при дизассемблировании. Один из методов это убрать из exe все упоминания об используемых WinApi функциях, и было принято решение хранить не названия WinApi функций, а некие хэш-коды, вычисляемые каким-нить несложным алгоритмом. Потом при перечислений всех функций dll библиотеки находить нужные по этим хэш-кодам. Один из часто применяемых способов, это написать небольшую утилитку, подать ей на вход список названий нужных функций, она вычисляет их хэш-коды и на выходе выдает исходник в котором находится готовая таблица с кодами. Потом этот исходник подключается к проекту и дальше во всю используется. Но как всегда вмешалась лень. Лень было писать такую утилитку, да и еще прописывать необходимые действия в make файле. Хотелось чтобы все вычислялось во время компиляции. Вот тогда был брошен взгляд на шаблоны С++ …
Шаблоны С++ не позволяют работать со строками типа char*, в них нельзя писать, с них нельзя читать, можно использовать только указатель для передачи его куда-то дальше. Но лень упертая штука, а что если в параметрах шаблона указывать не саму строку, а отдельно символы? Это уже очень хорошо, да и шаблоны могут иметь параметры по умолчанию, а это как раз то что нам нужно. Теперь достаточно написать шаблонный класс параметрами которого будут символы (тип char) и функцию для расчета хэша на основе значений параметров, а вот и сам класс:
Как видите все не так сложно. Единственное ограничение для данного класса, так это возможность использования строк длиною не более 32-х символов, но если нужно больше, то легко расширить.
Как теперь использовать этот класс? Все достаточно просто, берем название функции и прописываем его посимвольно в параметрах шаблона:
Как вам?
А чтобы получить сам хэш:
и все! Теперь достаточно объявить нужное количество типов-функций и использовать хэши в где вам нужно.
Если вы думаете, что компилятор сгенерирует кучу ненужн��го кода, то ошибаетесь, будет только одно число и больше ничего (попробуйте сами скомпилить в ассемблерный вид).
Но это еще не предел, таким способом еще можно шифровать строки прямо налету, во время компиляции.
Шифрование строк
Для шифрования строк будем использовать простой алгоритм: будем менять каждую пару символов местами, в зависимости от некоего параметра code. Для этого добавим в наш класс дополнительно буфер (а где еще хранить шифровку?) и несколько специальных функций и вуаля:
Как видите тут немного сложнее, но смысл тот же. Использовать нужно несколько по другому:
тут создается объект, а не как выше – тип. Ну и применять можно так:
на экране будет зашифрованная строка, или
на экране будет расшифрованная строка. Функция расшифровки (decode), естественно должна быть где-то описана.
При создании объекта, а не типа, есть одно но, полученные данные не помещаются таким способом в секцию .data (как обычные строки), компилятор формирует код который сохраняет вычисленные данные посредством инструкций mov. Это так называемый инициализируемый код, и запускается перед вызовом функции main(), то есть для нас все прозрачно и мы можем пользоваться результатом как обычными строками. Также возможно придется поработать над опциями оптимизации компилятора, так как такие огромные инлайн функции могут остаться в вашем конечном exe, т. е. нужно добиться того чтобы остался только инициализируемый код (переброс данных) и больше ничего.
Закуска
Ну и на закуску еще такой примерчик. С++ не имеет встроенных средств написания чисел в двоичном виде. Но мы можем это обойти используя данный метод. Ниже показан класс который может преобразовывать 32-х разрядные числа:
Использовать так:
Я использую в одном своем проекте данный метод для формирования хэшей и закодированных строк и пока все замечательно, но вам особо не рекомендую, лучше пишите отдельные утилитки, хотя … кто знает Вас и вашу лень )
Шаблоны С++ не позволяют работать со строками типа char*, в них нельзя писать, с них нельзя читать, можно использовать только указатель для передачи его куда-то дальше. Но лень упертая штука, а что если в параметрах шаблона указывать не саму строку, а отдельно символы? Это уже очень хорошо, да и шаблоны могут иметь параметры по умолчанию, а это как раз то что нам нужно. Теперь достаточно написать шаблонный класс параметрами которого будут символы (тип char) и функцию для расчета хэша на основе значений параметров, а вот и сам класс:
template<char c01 = 0, char c02 = 0, char c03 = 0, char c04 = 0, char c05 = 0, char c06 = 0, char c07 = 0, char c08 = 0, char c09 = 0, char c10 = 0, char c11 = 0, char c12 = 0, char c13 = 0, char c14 = 0, char c15 = 0, char c16 = 0, char c17 = 0, char c18 = 0, char c19 = 0, char c20 = 0, char c21 = 0, char c22 = 0, char c23 = 0, char c24 = 0, char c25 = 0, char c26 = 0, char c27 = 0, char c28 = 0, char c29 = 0, char c30 = 0, char c31 = 0, char c32 = 0 > class hash_text { public: //функция вычисления хэша static unsigned int get_hash() { unsigned int hash = c01; if( c02 ) { hash ^= (hash << 8) | c02; } if( c03 ) { hash ^= (hash << 8) | c03; } if( c04 ) { hash ^= (hash << 8) | c04; } if( c05 ) { hash ^= (hash << 8) | c05; } if( c06 ) { hash ^= (hash << 8) | c06; } if( c07 ) { hash ^= (hash << 8) | c07; } if( c08 ) { hash ^= (hash << 8) | c08; } if( c09 ) { hash ^= (hash << 8) | c09; } if( c10 ) { hash ^= (hash << 8) | c10; } if( c11 ) { hash ^= (hash << 8) | c11; } if( c12 ) { hash ^= (hash << 8) | c12; } if( c13 ) { hash ^= (hash << 8) | c13; } if( c14 ) { hash ^= (hash << 8) | c14; } if( c15 ) { hash ^= (hash << 8) | c15; } if( c16 ) { hash ^= (hash << 8) | c16; } if( c17 ) { hash ^= (hash << 8) | c17; } if( c18 ) { hash ^= (hash << 8) | c18; } if( c19 ) { hash ^= (hash << 8) | c19; } if( c20 ) { hash ^= (hash << 8) | c20; } if( c21 ) { hash ^= (hash << 8) | c21; } if( c22 ) { hash ^= (hash << 8) | c22; } if( c23 ) { hash ^= (hash << 8) | c23; } if( c24 ) { hash ^= (hash << 8) | c24; } if( c25 ) { hash ^= (hash << 8) | c25; } if( c26 ) { hash ^= (hash << 8) | c26; } if( c27 ) { hash ^= (hash << 8) | c27; } if( c28 ) { hash ^= (hash << 8) | c28; } if( c29 ) { hash ^= (hash << 8) | c29; } if( c30 ) { hash ^= (hash << 8) | c30; } if( c31 ) { hash ^= (hash << 8) | c31; } if( c32 ) { hash ^= (hash << 8) | c32; } return hash; } };
Как видите все не так сложно. Единственное ограничение для данного класса, так это возможность использования строк длиною не более 32-х символов, но если нужно больше, то легко расширить.
Как теперь использовать этот класс? Все достаточно просто, берем название функции и прописываем его посимвольно в параметрах шаблона:
typedef hash_text<'C','r','e','a','t','e','F','i','l','e'> hashCreateFile; //объявляем новый тип
Как вам?
А чтобы получить сам хэш:
hashCreateFile::get_hash();
и все! Теперь достаточно объявить нужное количество типов-функций и использовать хэши в где вам нужно.
Если вы думаете, что компилятор сгенерирует кучу ненужн��го кода, то ошибаетесь, будет только одно число и больше ничего (попробуйте сами скомпилить в ассемблерный вид).
Но это еще не предел, таким способом еще можно шифровать строки прямо налету, во время компиляции.
Шифрование строк
Для шифрования строк будем использовать простой алгоритм: будем менять каждую пару символов местами, в зависимости от некоего параметра code. Для этого добавим в наш класс дополнительно буфер (а где еще хранить шифровку?) и несколько специальных функций и вуаля:
template<unsigned int code, char c01 = 0, char c02 = 0, char c03 = 0, char c04 = 0, char c05 = 0, char c06 = 0, char c07 = 0, char c08 = 0, char c09 = 0, char c10 = 0, char c11 = 0, char c12 = 0, char c13 = 0, char c14 = 0, char c15 = 0, char c16 = 0, char c17 = 0, char c18 = 0, char c19 = 0, char c20 = 0, char c21 = 0, char c22 = 0, char c23 = 0, char c24 = 0, char c25 = 0, char c26 = 0, char c27 = 0, char c28 = 0, char c29 = 0, char c30 = 0, char c31 = 0, char c32 = 0 > class code_text { //вычисляем количество символов в строке static const char count = (c01 ? 1 : 0) + (c02 ? 1 : 0) + (c03 ? 1 : 0) + (c04 ? 1 : 0) + (c05 ? 1 : 0) + (c06 ? 1 : 0) + (c07 ? 1 : 0) + (c08 ? 1 : 0) + (c09 ? 1 : 0) + (c10 ? 1 : 0) + (c11 ? 1 : 0) + (c12 ? 1 : 0) + (c13 ? 1 : 0) + (c14 ? 1 : 0) + (c15 ? 1 : 0) + (c16 ? 1 : 0) + (c17 ? 1 : 0) + (c18 ? 1 : 0) + (c19 ? 1 : 0) + (c20 ? 1 : 0) + (c21 ? 1 : 0) + (c22 ? 1 : 0) + (c23 ? 1 : 0) + (c24 ? 1 : 0) + (c25 ? 1 : 0) + (c26 ? 1 : 0) + (c27 ? 1 : 0) + (c28 ? 1 : 0) + (c29 ? 1 : 0) + (c30 ? 1 : 0) + (c31 ? 1 : 0) + (c32 ? 1 : 0); char buf[count + 1]; //выделяем место под нашу строку char encode_char( bool first, int bit, char b1, char b2 ) { if( b2 == 0 ) //конец строки return b1; unsigned int change = code & (1 << bit); //делать обмен или нет if( first ) return change ? b2 : b1; //кодирование 1-го символа else return change ? b1 : b2; //кодирование 2-го символа } //помещает кодированный символ в буфер, n - начальный индекс для двух символов bool put_char( int n, char c1, char c2 ) { if( c1 == 0 ) return false; //конец строки buf[n] = encode_char( true, n / 2, c1, c2 ); if( c2 == 0 ) return false; //конец строки buf[n + 1] = encode_char( false, n / 2, c1, c2 ); return true; } //кодирование строки целиком void encode() { int v = 0; //символы ложим в буфер парами, конструкции if не делал лесенкой, так как в столбик красивее ) if( put_char( 0, c01, c02 ) ) if( put_char( 2, c03, c04 ) ) if( put_char( 4, c05, c06 ) ) if( put_char( 6, c07, c08 ) ) if( put_char( 8, c09, c10 ) ) if( put_char( 10, c11, c12 ) ) if( put_char( 12, c13, c14 ) ) if( put_char( 14, c15, c16 ) ) if( put_char( 16, c17, c18 ) ) if( put_char( 18, c19, c20 ) ) if( put_char( 20, c21, c22 ) ) if( put_char( 22, c23, c24 ) ) if( put_char( 24, c25, c26 ) ) if( put_char( 26, c27, c28 ) ) if( put_char( 28, c29, c30 ) ) if( put_char( 30, c31, c32 ) ) v = 0; //в последней конструкции if должен быть какой-нить оператор buf[count] = 0; } public: code_text() { encode(); } const char* ptr() const //указатель на кодированную строку { return buf; } int len() { return count; } char* str( char* to ) { return decode( buf, count, to ); //декодирование в to } };
Как видите тут немного сложнее, но смысл тот же. Использовать нужно несколько по другому:
code_text<44717397, 'E','n','c','o','d','e','S','t','r','i','n','g'> EncodeString;
тут создается объект, а не как выше – тип. Ну и применять можно так:
printf( "%s\n", EncodeString.ptr() );
на экране будет зашифрованная строка, или
char to[33]; printf( "%s\n", EncodeString.str(to) );
на экране будет расшифрованная строка. Функция расшифровки (decode), естественно должна быть где-то описана.
При создании объекта, а не типа, есть одно но, полученные данные не помещаются таким способом в секцию .data (как обычные строки), компилятор формирует код который сохраняет вычисленные данные посредством инструкций mov. Это так называемый инициализируемый код, и запускается перед вызовом функции main(), то есть для нас все прозрачно и мы можем пользоваться результатом как обычными строками. Также возможно придется поработать над опциями оптимизации компилятора, так как такие огромные инлайн функции могут остаться в вашем конечном exe, т. е. нужно добиться того чтобы остался только инициализируемый код (переброс данных) и больше ничего.
Закуска
Ну и на закуску еще такой примерчик. С++ не имеет встроенных средств написания чисел в двоичном виде. Но мы можем это обойти используя данный метод. Ниже показан класс который может преобразовывать 32-х разрядные числа:
template<int c01 = -1, int c02 = -1, int c03 = -1, int c04 = -1, int c05 = -1, int c06 = -1, int c07 = -1, int c08 = -1, int c09 = -1, int c10 = -1, int c11 = -1, int c12 = -1, int c13 = -1, int c14 = -1, int c15 = -1, int c16 = -1, int c17 = -1, int c18 = -1, int c19 = -1, int c20 = -1, int c21 = -1, int c22 = -1, int c23 = -1, int c24 = -1, int c25 = -1, int c26 = -1, int c27 = -1, int c28 = -1, int c29 = -1, int c30 = -1, int c31 = -1, int c32 = -1 > class bin_to_dec { static unsigned int get_bit( int res, int c ) { return (res << 1) | (c ? 1 : 0); //c не обязательно может быть равен 0 или 1 } public: static unsigned int dec() { unsigned int res = 0; if( c01 >= 0 ) res = get_bit( res, c01 ); if( c02 >= 0 ) res = get_bit( res, c02 ); if( c03 >= 0 ) res = get_bit( res, c03 ); if( c04 >= 0 ) res = get_bit( res, c04 ); if( c05 >= 0 ) res = get_bit( res, c05 ); if( c06 >= 0 ) res = get_bit( res, c06 ); if( c07 >= 0 ) res = get_bit( res, c07 ); if( c08 >= 0 ) res = get_bit( res, c08 ); if( c09 >= 0 ) res = get_bit( res, c09 ); if( c10 >= 0 ) res = get_bit( res, c10 ); if( c11 >= 0 ) res = get_bit( res, c11 ); if( c12 >= 0 ) res = get_bit( res, c12 ); if( c13 >= 0 ) res = get_bit( res, c13 ); if( c14 >= 0 ) res = get_bit( res, c14 ); if( c15 >= 0 ) res = get_bit( res, c15 ); if( c16 >= 0 ) res = get_bit( res, c16 ); if( c17 >= 0 ) res = get_bit( res, c17 ); if( c18 >= 0 ) res = get_bit( res, c18 ); if( c19 >= 0 ) res = get_bit( res, c19 ); if( c20 >= 0 ) res = get_bit( res, c20 ); if( c21 >= 0 ) res = get_bit( res, c21 ); if( c22 >= 0 ) res = get_bit( res, c22 ); if( c23 >= 0 ) res = get_bit( res, c23 ); if( c24 >= 0 ) res = get_bit( res, c24 ); if( c25 >= 0 ) res = get_bit( res, c25 ); if( c26 >= 0 ) res = get_bit( res, c26 ); if( c27 >= 0 ) res = get_bit( res, c27 ); if( c28 >= 0 ) res = get_bit( res, c28 ); if( c29 >= 0 ) res = get_bit( res, c29 ); if( c30 >= 0 ) res = get_bit( res, c30 ); if( c31 >= 0 ) res = get_bit( res, c31 ); if( c32 >= 0 ) res = get_bit( res, c32 ); return res; } };
Использовать так:
typedef bin_to_dec<1,1,1,0,0,0,1,1,1> flags; printf( "%u", flags::dec() );
Послесловие
Я использую в одном своем проекте данный метод для формирования хэшей и закодированных строк и пока все замечательно, но вам особо не рекомендую, лучше пишите отдельные утилитки, хотя … кто знает Вас и вашу лень )
