Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Алгоритм хеширования 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).
  1. ;---------------------------------------------
  2. ; Функция вычисления хеша FNV-1 (32-bit)
  3. ; Copyright (C) ManHunter / PCL
  4. ; https://www.manhunter.ru
  5. ;---------------------------------------------
  6. ; Параметры:
  7. ;       lpData - указатель на строку
  8. ;       dSize  - длина строки
  9. ; На выходе:
  10. ;       EAX = полученный хеш
  11. ;---------------------------------------------
  12. proc    FNV1 lpData:DWORD, dSize:DWORD
  13.         push    ebx ecx esi
  14.  
  15.         ; Начальное значение FNV-1
  16.         mov     eax,0x811C9DC5
  17.         mov     esi,[lpData]
  18.  
  19.         ; Длина строки
  20.         mov     ecx,[dSize]
  21.         or      ecx,ecx
  22.         je      .loc_ret
  23. @@:
  24.         ; hash *= FNV_prime
  25.         imul    eax,eax,0x01000193
  26.         ; hash XOR= текущий байт (только младший байт EAX)
  27.         xor     al,[esi]
  28.         ; Следующий символ
  29.         inc     esi
  30.         loop    @b
  31. .loc_ret:
  32.         pop     esi ecx ebx
  33.         ret
  34. endp
  1. ;---------------------------------------------
  2. ; Функция вычисления хеша FNV-1a (32-bit)
  3. ; Copyright (C) ManHunter / PCL
  4. ; https://www.manhunter.ru
  5. ;---------------------------------------------
  6. ; Параметры:
  7. ;       lpData - указатель на строку
  8. ;       dSize  - длина строки
  9. ; На выходе:
  10. ;       EAX = полученный хеш
  11. ;---------------------------------------------
  12. proc    FNV1a lpData:DWORD, dSize:DWORD
  13.         push    ebx ecx esi
  14.  
  15.         ; Начальное значение FNV-1
  16.         mov     eax,0x811C9DC5
  17.         mov     esi,[lpData]
  18.  
  19.         ; Длина строки
  20.         mov     ecx,[dSize]
  21.         or      ecx,ecx
  22.         je      .loc_ret
  23. @@:
  24.         ; hash XOR= текущий байт (только младший байт EAX)
  25.         xor     al,[esi]
  26.         ; hash *= FNV_prime
  27.         imul    eax,eax,0x01000193
  28.         ; Следующий символ
  29.         inc     esi
  30.         loop    @b
  31. .loc_ret:
  32.         pop     esi ecx ebx
  33.         ret
  34. endp
32-битный вариант функции принимает два параметра: lpData - указатель на входные данные (значение типа DWORD), и dSize - длина этих данных в байтах. Результат вычисления возвращается в регистре EAX в виде 32-битного хеш-значения.
  1. ;---------------------------------------------
  2. ; Функция вычисления хеша FNV-1 (64-bit)
  3. ; Copyright (C) ManHunter / PCL
  4. ; https://www.manhunter.ru
  5. ;---------------------------------------------
  6. ; Параметры:
  7. ;       lpData - указатель на строку
  8. ;       dSize  - длина строки
  9. ; На выходе:
  10. ;       EDX:EAX = полученный хеш
  11. ;---------------------------------------------
  12. proc    FNV1 lpData:DWORD, dSize:DWORD
  13.         push    ebx ecx esi edi
  14.  
  15.         ; Константы FNV-1 64-bit
  16.         FNV_OFFSET_LOW  = 0x84222325
  17.         FNV_OFFSET_HIGH = 0xCBF29CE4
  18.         FNV_PRIME_LOW   = 0x000001B3
  19.         FNV_PRIME_HIGH  = 0x00000100
  20.  
  21.         mov     esi,[lpData]
  22.  
  23.         ; Длина строки
  24.         mov     ecx,[dSize]
  25.         or      ecx,ecx
  26.         je      .loc_ret
  27.  
  28.         ; Инициализация: edi (low), ebx (high)
  29.         mov     edi,FNV_OFFSET_LOW
  30.         mov     ebx,FNV_OFFSET_HIGH
  31. .loc_loop:
  32.         ; high
  33.         push    ebx
  34.         ; low
  35.         push    edi
  36.  
  37.         mov     eax,edi
  38.         mov     edx,FNV_PRIME_LOW
  39.         mul     edx
  40.         ; Новый low
  41.         mov     edi,eax
  42.         ; Часть нового high
  43.         mov     ebx,edx
  44.  
  45.         ; Старый low
  46.         pop     eax
  47.         imul    eax,FNV_PRIME_HIGH
  48.         add     ebx,eax
  49.  
  50.         ; Старый high
  51.         pop     eax
  52.         imul    eax, FNV_PRIME_LOW
  53.         add     ebx, eax
  54.  
  55.         ; XOR с текущим байтом
  56.         movzx   eax,byte [esi]
  57.         ; XOR младшей части
  58.         xor     edi,eax
  59.  
  60.         inc     esi
  61.         dec     ecx
  62.         jnz     .loc_loop
  63.  
  64.         ; Возврат low в eax
  65.         mov     eax,edi
  66.         ; Возврат high в edx
  67.         mov     edx,ebx
  68.  
  69. .loc_ret:
  70.         pop     edi esi ecx ebx
  71.         ret
  72. endp
  1. ;---------------------------------------------
  2. ; Функция вычисления хеша FNV-1a (64-bit)
  3. ; Copyright (C) ManHunter / PCL
  4. ; https://www.manhunter.ru
  5. ;---------------------------------------------
  6. ; Параметры:
  7. ;       lpData - указатель на строку
  8. ;       dSize  - длина строки
  9. ; На выходе:
  10. ;       EDX:EAX = полученный хеш
  11. ;---------------------------------------------
  12. proc    FNV1 lpData:DWORD, dSize:DWORD
  13.         push    ebx ecx esi edi
  14.  
  15.         ; Константы FNV-1 64-bit
  16.         FNV_OFFSET_LOW  = 0x84222325
  17.         FNV_OFFSET_HIGH = 0xCBF29CE4
  18.         FNV_PRIME_LOW   = 0x000001B3
  19.         FNV_PRIME_HIGH  = 0x00000100
  20.  
  21.         mov     esi,[lpData]
  22.  
  23.         ; Длина строки
  24.         mov     ecx,[dSize]
  25.         or      ecx,ecx
  26.         je      .loc_ret
  27.  
  28.         ; Инициализация: edi (low), ebx (high)
  29.         mov     edi,FNV_OFFSET_LOW
  30.         mov     ebx,FNV_OFFSET_HIGH
  31.  
  32. .loc_loop:
  33.         ; XOR с текущим байтом ---
  34.         movzx   eax,byte [esi]
  35.         ; XOR младшей части
  36.         xor     edi,eax
  37.  
  38.         ; high
  39.         push    ebx
  40.         ; low
  41.         push    edi
  42.  
  43.         mov     eax,edi
  44.         mov     edx,FNV_PRIME_LOW
  45.         mul     edx
  46.         ; Новый low
  47.         mov     edi,eax
  48.         ; Часть нового high
  49.         mov     ebx,edx
  50.  
  51.         ; Старый low
  52.         pop     eax
  53.         imul    eax,FNV_PRIME_HIGH
  54.         add     ebx,eax
  55.  
  56.         ; Старый high
  57.         pop     eax
  58.         imul    eax,FNV_PRIME_LOW
  59.         add     ebx,eax
  60.  
  61.         inc     esi
  62.         dec     ecx
  63.         jnz     .loc_loop
  64.  
  65.         ; Возврат low в eax
  66.         mov     eax, edi
  67.         ; Возврат high в edx
  68.         mov     edx, ebx
  69.  
  70. .loc_ret:
  71.         pop     edi esi ecx ebx
  72.         ret
  73. endp
64-битный вариант функции принимает два параметра: lpData - указатель на входные данные (значение типа DWORD), и dSize - длина буфера в байтах. Результат вычисления возвращается в паре регистров EDX:EAX, где EAX содержит младшие 32 бита хеша, а EDX - старшие, образуя совместно 64-битное хеш-значение.

Поскольку официальной 128-битной спецификации FNV не существует, реализация основана на неофициальных эвристических константах.
  1. ;---------------------------------------------
  2. ; Функция вычисления хеша FNV-1 (128-bit)
  3. ; Copyright (C) ManHunter / PCL
  4. ; https://www.manhunter.ru
  5. ;---------------------------------------------
  6. ; Параметры:
  7. ;       lpData - указатель на строку
  8. ;       dSize  - длина строки
  9. ; На выходе:
  10. ;       EAX:EBX:ECX:EDX = полученный хеш
  11. ;---------------------------------------------
  12. proc    FNV1a lpData:DWORD, dSize:DWORD
  13.         locals
  14.           ; Инициализация FNV_offset_basis (128-bit)
  15.           val0 dd 0x6295C58D
  16.           val1 dd 0x62B82175
  17.           val2 dd 0x07BB0142
  18.           val3 dd 0x6C62272E
  19.  
  20.           ; Временное хранилище
  21.           tmp0 dd 0,0
  22.           tmp1 dd 0,0
  23.           tmp2 dd 0,0
  24.           tmp3 dd 0,0
  25.         endl
  26.  
  27.         FNV_128_PRIME_LOW   = 0x13B
  28.         FNV_128_PRIME_SHIFT = 24
  29.  
  30.         push    edi esi
  31.  
  32.         mov     esi,[lpData]
  33.  
  34.         ; Длина строки
  35.         mov     ecx,[dSize]
  36.         or      ecx,ecx
  37.         je      .loc_ret
  38. .loop:
  39.         push    ecx
  40.  
  41.         ; tmp[0] = val[0] * FNV_128_PRIME_LOW
  42.         xor     edx,edx
  43.         mov     eax,[val0]
  44.         mov     ebx,FNV_128_PRIME_LOW
  45.         mul     ebx
  46.         mov     [tmp0],eax
  47.         mov     [tmp0+4],edx
  48.  
  49.         ; tmp[1] = val[1] * FNV_128_PRIME_LOW
  50.         xor     edx,edx
  51.         mov     eax,[val1]
  52.         mov     ebx,FNV_128_PRIME_LOW
  53.         mul     ebx
  54.         mov     [tmp1],eax
  55.         mov     [tmp1+4],edx
  56.  
  57.         ; tmp[2] = val[2] * FNV_128_PRIME_LOW
  58.         ;        + (val[0] << FNV_128_PRIME_SHIFT)
  59.         mov     ecx,[val2]
  60.         mov     eax,ecx
  61.         mov     edx,0
  62.         shld    edx,eax,2
  63.         shl     eax,2
  64.         add     eax,ecx
  65.         adc     edx,0
  66.         mov     ecx,eax
  67.         mov     ebx,edx
  68.         shld    ebx,ecx,6
  69.         shl     ecx,6
  70.         sub     ecx,eax
  71.         sbb     ebx,edx
  72.         mov     eax,[val0]
  73.         mov     edx,0
  74.         shld    edx,eax,FNV_128_PRIME_SHIFT
  75.         shl     eax,FNV_128_PRIME_SHIFT
  76.         add     eax,ecx
  77.         adc     edx,ebx
  78.         mov     [tmp2],eax
  79.         mov     [tmp2+4],edx
  80.  
  81.         ; tmp[3] = val[3] * FNV_128_PRIME_LOW
  82.         ;        + (val[1] << FNV_128_PRIME_SHIFT)
  83.         mov     ecx,[val3]
  84.         mov     eax,ecx
  85.         mov     edx,0
  86.         shld    edx,eax,2
  87.         shl     eax,2
  88.         add     eax,ecx
  89.         adc     edx,0
  90.         mov     ecx,eax
  91.         mov     ebx,edx
  92.         shld    ebx,ecx,6
  93.         shl     ecx,6
  94.         sub     ecx,eax
  95.         sbb     ebx,edx
  96.         mov     eax,[val1]
  97.         mov     edx,0
  98.         shld    edx,eax,FNV_128_PRIME_SHIFT
  99.         shl     eax,FNV_128_PRIME_SHIFT
  100.         add     eax,ecx
  101.         adc     edx,ebx
  102.         mov     [tmp3],eax
  103.         mov     [tmp3+4],edx
  104.  
  105.         ; tmp[0]
  106.         mov     eax,[tmp0]
  107.         mov     [val0],eax
  108.         ; tmp[1] += (tmp[0] >> 32)
  109.         mov     eax,[tmp0+4]
  110.         add     eax,[tmp1]
  111.         mov     [val1],eax
  112.         ; tmp[2] += (tmp[1] >> 32)
  113.         mov     eax,[tmp1+4]
  114.         add     eax,[tmp2]
  115.         mov     [val2],eax
  116.         ; tmp[3] += (tmp[2] >> 32)
  117.         mov     eax,[tmp2+4]
  118.         add     eax,[tmp3]
  119.         mov     [val3],eax
  120.  
  121.         ; XOR текущего байта с младшим байтом
  122.         lodsb
  123.         xor     byte [val0],al
  124.  
  125.         pop     ecx
  126.         dec     ecx
  127.         jnz     .loop
  128. .loc_ret:
  129.         mov     eax,[val3]
  130.         mov     ebx,[val2]
  131.         mov     ecx,[val1]
  132.         mov     edx,[val0]
  133.  
  134.         pop     esi edi
  135.         ret
  136. endp
  1. ;---------------------------------------------
  2. ; Функция вычисления хеша FNV-1a (128-bit)
  3. ; Copyright (C) ManHunter / PCL
  4. ; https://www.manhunter.ru
  5. ;---------------------------------------------
  6. ; Параметры:
  7. ;       lpData - указатель на строку
  8. ;       dSize  - длина строки
  9. ; На выходе:
  10. ;       EAX:EBX:ECX:EDX = полученный хеш
  11. ;---------------------------------------------
  12. proc    FNV1a lpData:DWORD, dSize:DWORD
  13.         locals
  14.           ; Инициализация FNV_offset_basis (128-bit)
  15.           val0 dd 0x6295C58D
  16.           val1 dd 0x62B82175
  17.           val2 dd 0x07BB0142
  18.           val3 dd 0x6C62272E
  19.  
  20.           ; Временное хранилище
  21.           tmp0 dd 0,0
  22.           tmp1 dd 0,0
  23.           tmp2 dd 0,0
  24.           tmp3 dd 0,0
  25.         endl
  26.  
  27.         FNV_128_PRIME_LOW   = 0x13B
  28.         FNV_128_PRIME_SHIFT = 24
  29.  
  30.         push    edi esi
  31.  
  32.         mov     esi,[lpData]
  33.  
  34.         ; Длина строки
  35.         mov     ecx,[dSize]
  36.         or      ecx,ecx
  37.         je      .loc_ret
  38. .loop:
  39.         push    ecx
  40.  
  41.         ; XOR текущего байта с младшим байтом
  42.         lodsb
  43.         xor     byte [val0],al
  44.  
  45.         ; tmp[0] = val[0] * FNV_128_PRIME_LOW
  46.         xor     edx,edx
  47.         mov     eax,[val0]
  48.         mov     ebx,FNV_128_PRIME_LOW
  49.         mul     ebx
  50.         mov     [tmp0],eax
  51.         mov     [tmp0+4],edx
  52.  
  53.         ; tmp[1] = val[1] * FNV_128_PRIME_LOW
  54.         xor     edx,edx
  55.         mov     eax,[val1]
  56.         mov     ebx,FNV_128_PRIME_LOW
  57.         mul     ebx
  58.         mov     [tmp1],eax
  59.         mov     [tmp1+4],edx
  60.  
  61.         ; tmp[2] = val[2] * FNV_128_PRIME_LOW
  62.         ;        + (val[0] << FNV_128_PRIME_SHIFT)
  63.         mov     ecx,[val2]
  64.         mov     eax,ecx
  65.         mov     edx,0
  66.         shld    edx,eax,2
  67.         shl     eax,2
  68.         add     eax,ecx
  69.         adc     edx,0
  70.         mov     ecx,eax
  71.         mov     ebx,edx
  72.         shld    ebx,ecx,6
  73.         shl     ecx,6
  74.         sub     ecx,eax
  75.         sbb     ebx,edx
  76.         mov     eax,[val0]
  77.         mov     edx,0
  78.         shld    edx,eax,FNV_128_PRIME_SHIFT
  79.         shl     eax,FNV_128_PRIME_SHIFT
  80.         add     eax,ecx
  81.         adc     edx,ebx
  82.         mov     [tmp2],eax
  83.         mov     [tmp2+4],edx
  84.  
  85.         ; tmp[3] = val[3] * FNV_128_PRIME_LOW
  86.         ;        + (val[1] << FNV_128_PRIME_SHIFT)
  87.         mov     ecx,[val3]
  88.         mov     eax,ecx
  89.         mov     edx,0
  90.         shld    edx,eax,2
  91.         shl     eax,2
  92.         add     eax,ecx
  93.         adc     edx,0
  94.         mov     ecx,eax
  95.         mov     ebx,edx
  96.         shld    ebx,ecx,6
  97.         shl     ecx,6
  98.         sub     ecx,eax
  99.         sbb     ebx,edx
  100.         mov     eax,[val1]
  101.         mov     edx,0
  102.         shld    edx,eax,FNV_128_PRIME_SHIFT
  103.         shl     eax,FNV_128_PRIME_SHIFT
  104.         add     eax,ecx
  105.         adc     edx,ebx
  106.         mov     [tmp3],eax
  107.         mov     [tmp3+4],edx
  108.  
  109.         ; tmp[0]
  110.         mov     eax,[tmp0]
  111.         mov     [val0],eax
  112.         ; tmp[1] += (tmp[0] >> 32)
  113.         mov     eax,[tmp0+4]
  114.         add     eax,[tmp1]
  115.         mov     [val1],eax
  116.         ; tmp[2] += (tmp[1] >> 32)
  117.         mov     eax,[tmp1+4]
  118.         add     eax,[tmp2]
  119.         mov     [val2],eax
  120.         ; tmp[3] += (tmp[2] >> 32)
  121.         mov     eax,[tmp2+4]
  122.         add     eax,[tmp3]
  123.         mov     [val3],eax
  124.  
  125.         pop     ecx
  126.         dec     ecx
  127.         jnz     .loop
  128. .loc_ret:
  129.         mov     eax,[val3]
  130.         mov     ebx,[val2]
  131.         mov     ecx,[val1]
  132.         mov     edx,[val0]
  133.  
  134.         pop     esi edi
  135.         ret
  136. endp
128-битный вариант функции принимает два параметра: lpData - указатель на входные данные (значение типа DWORD), и dSize - длина буфера в байтах. Результат возвращается в виде 128-битного хеш-значения, распределенного по четырем 32-битным регистрам: младшие 32 бита в EAX, следующие в EBX, затем в ECX, а старшие 32 бита в EDX (представление в порядке little-endian по регистрам).

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

В приложении примеры программ с исходными текстами, демонстрирующие вычисление различных вариантов хешей FNV для введенной строки.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

FNV1.Hash.Demo.zip (16,358 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 283 | Комментариев: 0

Метки: Assembler, хеши

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2026
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.17 сек. / MySQL: 2 (0.0093 сек.) / Память: 4.5 Mb
Наверх