
Алгоритм хеширования FNV (Fowler-Noll-Vo) на Ассемблере

Алгоритм хеширования FNV (Fowler-Noll-Vo) на Ассемблере
FNV (Fowler-Noll-Vo) - это семейство некриптографических хеш-функций, разработанное в 1990-х годах программистами Glenn Fowler, Landon Curt Noll и Kiem-Phong Vo. Изначально алгоритм создавался для быстрого и эффективного хеширования строк в системных утилитах и языках программирования, например, в Perl для реализации хеш-таблиц. Основные достоинства FNV - крайняя простота реализации, высокая производительность даже на слабых процессорах и хорошее распределение хешей для типичных данных: строк, идентификаторов, путей к файлам и подобных структур. При этом FNV не предназначен для криптографического применения: он подвержен коллизиям, предсказуем и не обеспечивает свойств, необходимых для защиты информации.
Алгоритм FNV работает исключительно с двумя простыми операциями - умножением на фиксированную константу (FNV prime) и побитовым исключающим ИЛИ (XOR). Благодаря такой минималистичной арифметике он отличается высокой скоростью и легко реализуется даже на архитектурах с ограниченными ресурсами. При этом FNV является потоковым: входные данные обрабатываются по одному байту за раз, без необходимости загружать весь объем в память, что делает его особенно удобным для обработки больших массивов или непрерывных потоков.
Исторически первой была версия FNV-0, в которой хеш инициализировался нулем. Однако этот подход оказался неудачным: для пустой строки и ряда коротких входов результат оказывался нулевым или легко предсказуемым. В ответ на это была введена версия FNV-1, использующая ненулевое начальное значение, известное как "offset basis". Сегодня в практическом применении доминируют две модификации: FNV-1 и FNV-1a. В FNV-1 на каждой итерации сначала происходит умножение текущего хеша на константу "FNV prime", а затем - XOR с очередным байтом входных данных. В FNV-1a порядок операций обратный: сначала выполняется XOR с байтом, затем - умножение. Несмотря на кажущуюся незначительность этого различия, FNV-1a обеспечивает значительно лучшее перемешивание хеш-значений, особенно для коротких ключей и строк с общими префиксами, что и объясняет его повсеместное предпочтение в современных системах.
Для 32-битной реализации начальное значение (offset basis) равно 0x811C9DC5, а константа (FNV prime) - 0x01000193. В 64-битной версии используются соответственно 0xCBF29CE484222325 и 0x100000001B3. Во всех случаях арифметика выполняется с естественным целочисленным переполнением: результат автоматически берётся по модулю 232 или 264 в зависимости от разрядности. Это поведение аппаратно поддерживается большинством процессоров и не требует дополнительных операций маскирования. FNV-1a использует те же константы и начальные значения, что и FNV-1, но благодаря измененному порядку операций демонстрирует более равномерное распределение хешей, что особенно важно при работе с типичными данными, такими как идентификаторы, пути к файлам или короткие текстовые ключи.
Функция обрабатывает входной буфер побайтово, применяя операции умножения на FNV-простое число и XOR с каждым байтом в зависимости от выбранного варианта алгоритма (FNV-1 или FNV-1a).
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; Функция вычисления хеша FNV-1 (32-bit)
- ; Copyright (C) ManHunter / PCL
- ; https://www.manhunter.ru
- ;---------------------------------------------
- ; Параметры:
- ; lpData - указатель на строку
- ; dSize - длина строки
- ; На выходе:
- ; EAX = полученный хеш
- ;---------------------------------------------
- proc FNV1 lpData:DWORD, dSize:DWORD
- push ebx ecx esi
- ; Начальное значение FNV-1
- mov eax,0x811C9DC5
- mov esi,[lpData]
- ; Длина строки
- mov ecx,[dSize]
- or ecx,ecx
- je .loc_ret
- @@:
- ; hash *= FNV_prime
- imul eax,eax,0x01000193
- ; hash XOR= текущий байт (только младший байт EAX)
- xor al,[esi]
- ; Следующий символ
- inc esi
- loop @b
- .loc_ret:
- pop esi ecx ebx
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; Функция вычисления хеша FNV-1a (32-bit)
- ; Copyright (C) ManHunter / PCL
- ; https://www.manhunter.ru
- ;---------------------------------------------
- ; Параметры:
- ; lpData - указатель на строку
- ; dSize - длина строки
- ; На выходе:
- ; EAX = полученный хеш
- ;---------------------------------------------
- proc FNV1a lpData:DWORD, dSize:DWORD
- push ebx ecx esi
- ; Начальное значение FNV-1
- mov eax,0x811C9DC5
- mov esi,[lpData]
- ; Длина строки
- mov ecx,[dSize]
- or ecx,ecx
- je .loc_ret
- @@:
- ; hash XOR= текущий байт (только младший байт EAX)
- xor al,[esi]
- ; hash *= FNV_prime
- imul eax,eax,0x01000193
- ; Следующий символ
- inc esi
- loop @b
- .loc_ret:
- pop esi ecx ebx
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; Функция вычисления хеша FNV-1 (64-bit)
- ; Copyright (C) ManHunter / PCL
- ; https://www.manhunter.ru
- ;---------------------------------------------
- ; Параметры:
- ; lpData - указатель на строку
- ; dSize - длина строки
- ; На выходе:
- ; EDX:EAX = полученный хеш
- ;---------------------------------------------
- proc FNV1 lpData:DWORD, dSize:DWORD
- push ebx ecx esi edi
- ; Константы FNV-1 64-bit
- FNV_OFFSET_LOW = 0x84222325
- FNV_OFFSET_HIGH = 0xCBF29CE4
- FNV_PRIME_LOW = 0x000001B3
- FNV_PRIME_HIGH = 0x00000100
- mov esi,[lpData]
- ; Длина строки
- mov ecx,[dSize]
- or ecx,ecx
- je .loc_ret
- ; Инициализация: edi (low), ebx (high)
- mov edi,FNV_OFFSET_LOW
- mov ebx,FNV_OFFSET_HIGH
- .loc_loop:
- ; high
- push ebx
- ; low
- push edi
- mov eax,edi
- mov edx,FNV_PRIME_LOW
- mul edx
- ; Новый low
- mov edi,eax
- ; Часть нового high
- mov ebx,edx
- ; Старый low
- pop eax
- imul eax,FNV_PRIME_HIGH
- add ebx,eax
- ; Старый high
- pop eax
- imul eax, FNV_PRIME_LOW
- add ebx, eax
- ; XOR с текущим байтом
- movzx eax,byte [esi]
- ; XOR младшей части
- xor edi,eax
- inc esi
- dec ecx
- jnz .loc_loop
- ; Возврат low в eax
- mov eax,edi
- ; Возврат high в edx
- mov edx,ebx
- .loc_ret:
- pop edi esi ecx ebx
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; Функция вычисления хеша FNV-1a (64-bit)
- ; Copyright (C) ManHunter / PCL
- ; https://www.manhunter.ru
- ;---------------------------------------------
- ; Параметры:
- ; lpData - указатель на строку
- ; dSize - длина строки
- ; На выходе:
- ; EDX:EAX = полученный хеш
- ;---------------------------------------------
- proc FNV1 lpData:DWORD, dSize:DWORD
- push ebx ecx esi edi
- ; Константы FNV-1 64-bit
- FNV_OFFSET_LOW = 0x84222325
- FNV_OFFSET_HIGH = 0xCBF29CE4
- FNV_PRIME_LOW = 0x000001B3
- FNV_PRIME_HIGH = 0x00000100
- mov esi,[lpData]
- ; Длина строки
- mov ecx,[dSize]
- or ecx,ecx
- je .loc_ret
- ; Инициализация: edi (low), ebx (high)
- mov edi,FNV_OFFSET_LOW
- mov ebx,FNV_OFFSET_HIGH
- .loc_loop:
- ; XOR с текущим байтом ---
- movzx eax,byte [esi]
- ; XOR младшей части
- xor edi,eax
- ; high
- push ebx
- ; low
- push edi
- mov eax,edi
- mov edx,FNV_PRIME_LOW
- mul edx
- ; Новый low
- mov edi,eax
- ; Часть нового high
- mov ebx,edx
- ; Старый low
- pop eax
- imul eax,FNV_PRIME_HIGH
- add ebx,eax
- ; Старый high
- pop eax
- imul eax,FNV_PRIME_LOW
- add ebx,eax
- inc esi
- dec ecx
- jnz .loc_loop
- ; Возврат low в eax
- mov eax, edi
- ; Возврат high в edx
- mov edx, ebx
- .loc_ret:
- pop edi esi ecx ebx
- ret
- endp
Поскольку официальной 128-битной спецификации FNV не существует, реализация основана на неофициальных эвристических константах.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; Функция вычисления хеша FNV-1 (128-bit)
- ; Copyright (C) ManHunter / PCL
- ; https://www.manhunter.ru
- ;---------------------------------------------
- ; Параметры:
- ; lpData - указатель на строку
- ; dSize - длина строки
- ; На выходе:
- ; EAX:EBX:ECX:EDX = полученный хеш
- ;---------------------------------------------
- proc FNV1a lpData:DWORD, dSize:DWORD
- locals
- ; Инициализация FNV_offset_basis (128-bit)
- val0 dd 0x6295C58D
- val1 dd 0x62B82175
- val2 dd 0x07BB0142
- val3 dd 0x6C62272E
- ; Временное хранилище
- tmp0 dd 0,0
- tmp1 dd 0,0
- tmp2 dd 0,0
- tmp3 dd 0,0
- endl
- FNV_128_PRIME_LOW = 0x13B
- FNV_128_PRIME_SHIFT = 24
- push edi esi
- mov esi,[lpData]
- ; Длина строки
- mov ecx,[dSize]
- or ecx,ecx
- je .loc_ret
- .loop:
- push ecx
- ; tmp[0] = val[0] * FNV_128_PRIME_LOW
- xor edx,edx
- mov eax,[val0]
- mov ebx,FNV_128_PRIME_LOW
- mul ebx
- mov [tmp0],eax
- mov [tmp0+4],edx
- ; tmp[1] = val[1] * FNV_128_PRIME_LOW
- xor edx,edx
- mov eax,[val1]
- mov ebx,FNV_128_PRIME_LOW
- mul ebx
- mov [tmp1],eax
- mov [tmp1+4],edx
- ; tmp[2] = val[2] * FNV_128_PRIME_LOW
- ; + (val[0] << FNV_128_PRIME_SHIFT)
- mov ecx,[val2]
- mov eax,ecx
- mov edx,0
- shld edx,eax,2
- shl eax,2
- add eax,ecx
- adc edx,0
- mov ecx,eax
- mov ebx,edx
- shld ebx,ecx,6
- shl ecx,6
- sub ecx,eax
- sbb ebx,edx
- mov eax,[val0]
- mov edx,0
- shld edx,eax,FNV_128_PRIME_SHIFT
- shl eax,FNV_128_PRIME_SHIFT
- add eax,ecx
- adc edx,ebx
- mov [tmp2],eax
- mov [tmp2+4],edx
- ; tmp[3] = val[3] * FNV_128_PRIME_LOW
- ; + (val[1] << FNV_128_PRIME_SHIFT)
- mov ecx,[val3]
- mov eax,ecx
- mov edx,0
- shld edx,eax,2
- shl eax,2
- add eax,ecx
- adc edx,0
- mov ecx,eax
- mov ebx,edx
- shld ebx,ecx,6
- shl ecx,6
- sub ecx,eax
- sbb ebx,edx
- mov eax,[val1]
- mov edx,0
- shld edx,eax,FNV_128_PRIME_SHIFT
- shl eax,FNV_128_PRIME_SHIFT
- add eax,ecx
- adc edx,ebx
- mov [tmp3],eax
- mov [tmp3+4],edx
- ; tmp[0]
- mov eax,[tmp0]
- mov [val0],eax
- ; tmp[1] += (tmp[0] >> 32)
- mov eax,[tmp0+4]
- add eax,[tmp1]
- mov [val1],eax
- ; tmp[2] += (tmp[1] >> 32)
- mov eax,[tmp1+4]
- add eax,[tmp2]
- mov [val2],eax
- ; tmp[3] += (tmp[2] >> 32)
- mov eax,[tmp2+4]
- add eax,[tmp3]
- mov [val3],eax
- ; XOR текущего байта с младшим байтом
- lodsb
- xor byte [val0],al
- pop ecx
- dec ecx
- jnz .loop
- .loc_ret:
- mov eax,[val3]
- mov ebx,[val2]
- mov ecx,[val1]
- mov edx,[val0]
- pop esi edi
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; Функция вычисления хеша FNV-1a (128-bit)
- ; Copyright (C) ManHunter / PCL
- ; https://www.manhunter.ru
- ;---------------------------------------------
- ; Параметры:
- ; lpData - указатель на строку
- ; dSize - длина строки
- ; На выходе:
- ; EAX:EBX:ECX:EDX = полученный хеш
- ;---------------------------------------------
- proc FNV1a lpData:DWORD, dSize:DWORD
- locals
- ; Инициализация FNV_offset_basis (128-bit)
- val0 dd 0x6295C58D
- val1 dd 0x62B82175
- val2 dd 0x07BB0142
- val3 dd 0x6C62272E
- ; Временное хранилище
- tmp0 dd 0,0
- tmp1 dd 0,0
- tmp2 dd 0,0
- tmp3 dd 0,0
- endl
- FNV_128_PRIME_LOW = 0x13B
- FNV_128_PRIME_SHIFT = 24
- push edi esi
- mov esi,[lpData]
- ; Длина строки
- mov ecx,[dSize]
- or ecx,ecx
- je .loc_ret
- .loop:
- push ecx
- ; XOR текущего байта с младшим байтом
- lodsb
- xor byte [val0],al
- ; tmp[0] = val[0] * FNV_128_PRIME_LOW
- xor edx,edx
- mov eax,[val0]
- mov ebx,FNV_128_PRIME_LOW
- mul ebx
- mov [tmp0],eax
- mov [tmp0+4],edx
- ; tmp[1] = val[1] * FNV_128_PRIME_LOW
- xor edx,edx
- mov eax,[val1]
- mov ebx,FNV_128_PRIME_LOW
- mul ebx
- mov [tmp1],eax
- mov [tmp1+4],edx
- ; tmp[2] = val[2] * FNV_128_PRIME_LOW
- ; + (val[0] << FNV_128_PRIME_SHIFT)
- mov ecx,[val2]
- mov eax,ecx
- mov edx,0
- shld edx,eax,2
- shl eax,2
- add eax,ecx
- adc edx,0
- mov ecx,eax
- mov ebx,edx
- shld ebx,ecx,6
- shl ecx,6
- sub ecx,eax
- sbb ebx,edx
- mov eax,[val0]
- mov edx,0
- shld edx,eax,FNV_128_PRIME_SHIFT
- shl eax,FNV_128_PRIME_SHIFT
- add eax,ecx
- adc edx,ebx
- mov [tmp2],eax
- mov [tmp2+4],edx
- ; tmp[3] = val[3] * FNV_128_PRIME_LOW
- ; + (val[1] << FNV_128_PRIME_SHIFT)
- mov ecx,[val3]
- mov eax,ecx
- mov edx,0
- shld edx,eax,2
- shl eax,2
- add eax,ecx
- adc edx,0
- mov ecx,eax
- mov ebx,edx
- shld ebx,ecx,6
- shl ecx,6
- sub ecx,eax
- sbb ebx,edx
- mov eax,[val1]
- mov edx,0
- shld edx,eax,FNV_128_PRIME_SHIFT
- shl eax,FNV_128_PRIME_SHIFT
- add eax,ecx
- adc edx,ebx
- mov [tmp3],eax
- mov [tmp3+4],edx
- ; tmp[0]
- mov eax,[tmp0]
- mov [val0],eax
- ; tmp[1] += (tmp[0] >> 32)
- mov eax,[tmp0+4]
- add eax,[tmp1]
- mov [val1],eax
- ; tmp[2] += (tmp[1] >> 32)
- mov eax,[tmp1+4]
- add eax,[tmp2]
- mov [val2],eax
- ; tmp[3] += (tmp[2] >> 32)
- mov eax,[tmp2+4]
- add eax,[tmp3]
- mov [val3],eax
- pop ecx
- dec ecx
- jnz .loop
- .loc_ret:
- mov eax,[val3]
- mov ebx,[val2]
- mov ecx,[val1]
- mov edx,[val0]
- pop esi edi
- ret
- endp
Таким образом, FNV остается одним из самых элегантных и практичных некриптографических хеш-алгоритмов, его ценят там, где критичны скорость, минимальные вычислительные затраты и хорошее статистическое распределение хешей, но не требуется защита от преднамеренных атак или криптографическая стойкость. Благодаря предельной простоте - всего две операции на байт - его реализация даже на чистом Ассемблере занимает буквально десятки строк кода, что делает FNV идеальным кандидатом для встраивания в ресурсоограниченные среды: микроконтроллеры, встроенные системы, загрузчики, ядра операционных систем и другие низкоуровневые компоненты, где важны компактность и производительность.
В приложении примеры программ с исходными текстами, демонстрирующие вычисление различных вариантов хешей FNV для введенной строки.
Просмотров: 283 | Комментариев: 0
Комментарии
Отзывы посетителей сайта о статье
Комментариeв нет
Добавить комментарий
Заполните форму для добавления комментария
Примеры программ с исходными текстами (FASM)

