Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
LoadLibrary()
и GetProcAddress()
, но проблема в том, что сами строки, содержащие имена функций, никак не спрятаны. Поэтому любой уважающий себя антивирусный продукт все равно сможет понять, что мы задумали.Чтобы предотвратить это, добрые хакеры придумали технику API Hashing, с которой сейчас и познакомимся.
Раньше мы получали адрес нужной функции через кастомный GetProcAddress(
. Вот как это выглядит.
FARPROC myGetProcAddress(HMODULE hModule, LPCSTR lpProcName) {
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + dosHeader->e_lfanew);
PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hModule +
ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD* addressOfFunctions = (DWORD*)((BYTE*)hModule + exportDirectory->AddressOfFunctions);
WORD* addressOfNameOrdinals = (WORD*)((BYTE*)hModule + exportDirectory->AddressOfNameOrdinals);
DWORD* addressOfNames = (DWORD*)((BYTE*)hModule + exportDirectory->AddressOfNames);
for (DWORD i = 0; i < exportDirectory->NumberOfNames; ++i) {
if (strcmp(lpProcName, (const char*)hModule + addressOfNames[i]) == 0) {
return (FARPROC)((BYTE*)hModule + addressOfFunctions[addressOfNameOrdinals[i]]);
}
}
return NULL;
}
API Hashing заключается в том, что мы вместо имени функции (lpProcName
) будем передавать хеш от нее. Фактически изменится лишь следующая строка.
if (strcmp(lpProcName, (const char*)hModule + addressOfNames[i]) == 0)
И станет вот такой:
if (strcmp(lpProcName, HASH((const char*)hModule + addressOfNames[i])) == 0)
Аналогичным образом все происходит и с GetModuleHandle(
.
Но что это вообще за хеш, откуда его взять? Хеш генерируется с помощью конкретного алгоритма хеширования, можешь выбрать любой на свой вкус. Приведу несколько примеров.
Итак, берем алгоритм Djb2. Это очень быстрый алгоритм хеширования.
hash = ((hash << 5) + hash) + c
hash
— текущее значение хеша;<<
— сдвиг вправо;c
— текущее значение символа в строке.Вот функция, которая позволяет хешировать строку.
#include <Windows.h>
#include <iostream>
#define INITIAL_HASH 3731 // Для рандомизации
#define INITIAL_SEED 7
// ASCII
DWORD HashStringDjb2A(_In_ PCHAR String)
{
ULONG Hash = INITIAL_HASH;
INT c;
while (c = *String++)
Hash = ((Hash << INITIAL_SEED) + Hash) + c;
return Hash;
}
// Unicode
DWORD HashStringDjb2W(_In_ PWCHAR String)
{
ULONG Hash = INITIAL_HASH;
INT c;
while (c = *String++)
Hash = ((Hash << INITIAL_SEED) + Hash) + c;
return Hash;
}
int main() {
LPWSTR str = (LPWSTR)L"HelloWorld";
std::cout << HashStringDjb2W(str);
return 0;
}
Теперь давай попробуем алгоритм JenkinsOneAtATime32Bit. Он перебирает символы входной строки и постепенно обновляет хеш‑значение в соответствии с каждым полученным символом.
hash += (hash << 10);hash ^= (hash 6);
hash
— текущее значение хеша;c
— текущее значение символа в строке.Код с функцией.
#include <iostream>#define INITIAL_SEED
7// ASCIIUINT32 HashStringJenkinsOneAtATime32BitA(_In_ PCHAR String){
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenA(String);
while (Index != Length)
{Hash += String[Index++];Hash += Hash << INITIAL_SEED;Hash ^= Hash 6;
}
Hash += Hash << 3;
Hash ^= Hash 11;
Hash += Hash << 15;
return Hash;}// UnicodeUINT32 HashStringJenkinsOneAtATime32BitW(_In_ PWCHAR String){
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenW(String);
while (Index != Length)
{Hash += String[Index++];Hash += Hash << INITIAL_SEED;Hash ^= Hash 6;
}
Hash += Hash << 3;
Hash ^= Hash 11;
Hash += Hash << 15;
return Hash;}int main() {
LPWSTR str = (LPWSTR)L"HelloWorld";
std::cout << HashStringJenkinsOneAtATime32BitW(str);
return 0;}
Не утомился? Давай посмотрим еще один пример. В каком‑то проекте, чуть ли не в Hell’s Gate, я встречал алгоритм LoseLose. Он вычисляет хеш‑значение входной строки, перебирая каждый символ в ней и суммируя значения ASCII каждого символа.
hash = 0;
hash += c; // Для каждого символа в строке
Хеш‑значение, полученное в результате алгоритма LoseLose, представляет собой целое число, уникальное для входной строки. Но все равно есть вероятность коллизии. Чтобы решить эту проблему, формула алгоритма была обновлена, как показано ниже.
hash = 0;
hash += c;
hash *= c + 2;
Вот пример функции, которая генерирует подобный хеш.
#include <Windows.h>
#include <iostream>
#define INITIAL_SEED 2
// ASCII
DWORD HashStringLoseLoseA(_In_ PCHAR String)
{
ULONG Hash = 0;
INT c;
while (c = *String++) {
Hash += c;
Hash *= c + INITIAL_SEED;
}
return Hash;
}
// Unicode
DWORD HashStringLoseLoseW(_In_ PWCHAR String)
{
ULONG Hash = 0;
INT c;
while (c = *String++) {
Hash += c;
Hash *= c + INITIAL_SEED;
}
return Hash;
}
int main() {
LPWSTR str = (LPWSTR)L"HelloWorld";
std::cout << HashStringLoseLoseW(str);
return 0;
}
И, думаю, последний вариант — Rotr32(
.
#include <Windows.h>
#include <iostream>
#define INITIAL_SEED 5
UINT32 HashStringRotr32SubA(UINT32 Value, UINT Count)
{
DWORD Mask = (CHAR_BIT * sizeof(Value) - 1);
Count &= Mask;
#pragma warning( push )
#pragma warning( disable : 4146)
return (Value >> Count) | (Value << ((-Count) & Mask));
#pragma warning( pop )
}
INT HashStringRotr32A(_In_ LPCSTR String)
{
INT Value = 0;
for (INT Index = 0; Index < strlen(String); Index++)
Value = String[Index] + HashStringRotr32SubA(Value, 7);
return Value;
}
UINT32 HashStringRotr32SubW(UINT32 Value, UINT Count)
{
DWORD Mask = (CHAR_BIT * sizeof(Value) - 1);
Count &= Mask;
#pragma warning( push )
#pragma warning( disable : 4146)
return (Value >> Count) | (Value << ((-Count) & Mask));
#pragma warning( pop )
}
INT HashStringRotr32W(_In_ LPCWSTR String)
{
INT Value = 0;
for (INT Index = 0; Index < wcslen(String); Index++)
Value = String[Index] + HashStringRotr32SubW(Value, 7);
return Value;
}
int main() {
LPWSTR str = (LPWSTR)L"HelloWorld";
std::cout << HashStringRotr32W(str);
return 0;
}
Для API Hashing ты просто инициализируешь строку с именем функции, а затем загоняешь в любой из описанных выше алгоритмов. После получения хеша ты должен добавить его в свою программу и, используя кастомный GetProcAddress(
, искать адрес нужной функции.
Переходим к демонстрации.
|
|