Blog. Just Blog

Расчет хеша SHA1 на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Алгоритм криптографического хеширования SHA1 (Secure Hash Algorithm 1) используется во многих криптографических приложениях и протоколах. В его основе лежат методы, очень похожие на MD5. В Интернете есть много реализаций этого алгоритма на разных языках программирования, но я не нашел ни одного нормального решения на Ассемблере. Пришлось разбираться самому и в результате у меня получилась достаточно быстрая функция расчета SHA1 участка памяти произвольной длины. Для работы в сегменте данных потребуются некоторые дополнительные переменные и массивы, они вынесены в глобальную область видимости, чтобы не загромождать код.
  1. section '.data' data readable writeable
  2.  
  3. SHA1_h0         dd ?  ; Переменные, в которых будет создан хеш SHA1
  4. SHA1_h1         dd ?
  5. SHA1_h2         dd ?
  6. SHA1_h3         dd ?
  7. SHA1_h4         dd ?
  8.  
  9. SHA1_a          dd ?  ; Вспомогательные переменные для промежуточных
  10. SHA1_b          dd ?  ; вычислений
  11. SHA1_c          dd ?
  12. SHA1_d          dd ?
  13. SHA1_e          dd ?
  14.  
  15. SHA1_W          rd 80  ; Массивы для промежуточных данных
  16. SHA1_Buff       rb 64
Я постарался сохранить оригинальные названия переменных, которые используются в описании алгоритма, так что с этим вопросов возникнуть не должно.

И, собственно, сама функция расчета SHA1. Параметры вызова: lpData - указатель на участок памяти, хеш от которого надо получить, и dSize - размер этого участка. Кроме основной функции расчета здесь используются еще две вспомогательные функции - SHA1_BE для преобразования строки к формату Big-endian и SHA1_Calc для цикла трансформации хеша.
  1. ;-----------------------------------------------------------------------
  2. ; Функция расчета хэша SHA1
  3. ; Copyright (C) ManHunter / PCL
  4. ; http://www.manhunter.ru
  5. ;-----------------------------------------------------------------------
  6. ; Параметры:
  7. ;      lpData - указатель на блок данных
  8. ;      dSize  - размер блока
  9. ; На выходе: заполненные переменные SHA1_h0 - SHA1_h4
  10. ;-----------------------------------------------------------------------
  11. proc    SHA1 lpData:DWORD, dSize:DWORD
  12.         locals
  13.                 len     dd ?
  14.                 padding dd ?
  15.                 index   dd ?
  16.         endl
  17.  
  18.         pusha
  19.  
  20.         ; Инициализация
  21.         mov     [SHA1_h0],0x67452301
  22.         mov     [SHA1_h1],0xEFCDAB89
  23.         mov     [SHA1_h2],0x98BADCFE
  24.         mov     [SHA1_h3],0x10325476
  25.         mov     [SHA1_h4],0xC3D2E1F0
  26.  
  27.         ; Получить количество 64-байтных блоков
  28.         mov     eax,[dSize]
  29.         push    eax
  30.         shr     eax,6
  31.         mov     [len],eax
  32.         pop     eax
  33.  
  34.         ; Получить остаток от деления на 64
  35.         and     eax,3Fh
  36.         ; Расчет количества добавляемых байт
  37.         mov     [padding],eax
  38.         ; Подсчет SHA1 основных данных
  39.         mov     [index],0
  40. .main_loop:
  41.         mov     eax,[index]
  42.         cmp     eax,[len]
  43.         je      .main_done
  44.  
  45.         ; Указатель * 64
  46.         shl     eax,6
  47.         mov     esi,[lpData]
  48.         add     esi,eax
  49.         mov     edi,SHA1_Buff
  50.         mov     ecx,16
  51.         rep     movsd
  52.  
  53.         ; Привести строку к Big-endian виду
  54.         stdcall SHA1_BE
  55.         stdcall SHA1_Calc
  56.  
  57.         inc     [index]
  58.         jmp     .main_loop
  59. .main_done:
  60.         ; Подсчет SHA1 остаточных данных
  61.         xor     eax,eax
  62.         mov     edi,SHA1_Buff
  63.         mov     ecx,16
  64.         rep     stosd
  65.  
  66.         ; Скопировать остаточные данные в буфер
  67.         mov     eax,[index]
  68.         shl     eax,6
  69.         mov     esi,[lpData]
  70.         add     esi,eax
  71.         mov     edi,SHA1_Buff
  72.         mov     ecx,[padding]
  73.         rep     movsb
  74.         ; Дописать финальный бит
  75.         mov     al,80h
  76.         stosb
  77.  
  78.         ; Привести строку к Big-endian виду
  79.         stdcall SHA1_BE
  80.  
  81.         ; Проверка на пограничные случаи, когда остаток
  82.         ; строки меньше, чем требуется для записи финального
  83.         ; бита и длины строки
  84.         cmp     [padding],56
  85.         jae     @f
  86.  
  87.         ; В конец буфера записать длину строки
  88.         mov     eax,[dSize]
  89.         shl     eax,3
  90.         mov     dword [SHA1_Buff+60],eax
  91.  
  92.         stdcall SHA1_Calc
  93.         jmp     .sha1_done
  94. @@:
  95.         stdcall SHA1_Calc
  96.  
  97.         ; Очистка буфера
  98.         mov     edi,SHA1_Buff
  99.         xor     eax,eax
  100.         mov     ecx,15
  101.         rep     stosd
  102.  
  103.         ; В конец буфера записать длину строки
  104.         mov     eax,[dSize]
  105.         shl     eax,3
  106.         stosd
  107.  
  108.         stdcall SHA1_Calc
  109.  
  110. .sha1_done:
  111.         popa
  112.         ret
  113. endp
  1. ;-----------------------------------------------------------------------
  2. ; Вспомогательная функция
  3. ; Приведение строки к Big-endian виду
  4. ;-----------------------------------------------------------------------
  5. proc    SHA1_BE
  6.         pusha
  7.  
  8.         mov     ecx,16
  9.         mov     esi,SHA1_Buff
  10.         mov     edi,esi
  11. @@:
  12.         lodsd
  13.         bswap   eax
  14.         stosd
  15.         loop    @b
  16.  
  17.         popa
  18.         ret
  19. endp
  1. ;-----------------------------------------------------------------------
  2. ; Вспомогательная функция для расчета SHA1
  3. ;-----------------------------------------------------------------------
  4. proc    SHA1_Calc
  5.         pusha
  6.  
  7.         mov     eax,[SHA1_h0]
  8.         mov     [SHA1_a],eax
  9.         mov     eax,[SHA1_h1]
  10.         mov     [SHA1_b],eax
  11.         mov     eax,[SHA1_h2]
  12.         mov     [SHA1_c],eax
  13.         mov     eax,[SHA1_h3]
  14.         mov     [SHA1_d],eax
  15.         mov     eax,[SHA1_h4]
  16.         mov     [SHA1_e],eax
  17.  
  18.         xor     ecx,ecx ; i
  19. .cycle_loop:
  20.         mov     eax,ecx
  21.         shl     eax,2
  22.  
  23.         cmp     ecx,16
  24.         jae     @f
  25.  
  26.         mov     ebx,dword [SHA1_Buff+eax]
  27.         mov     dword [SHA1_W+eax],ebx
  28.         jmp     .@1
  29. @@:
  30.         ; rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1)
  31.         mov     eax,ecx
  32.         sub     eax,3
  33.         shl     eax,2
  34.         mov     ebx,dword [SHA1_W+eax]
  35.  
  36.         mov     eax,ecx
  37.         sub     eax,8
  38.         shl     eax,2
  39.         xor     ebx,dword [SHA1_W+eax]
  40.  
  41.         mov     eax,ecx
  42.         sub     eax,14
  43.         shl     eax,2
  44.         xor     ebx,dword [SHA1_W+eax]
  45.  
  46.         mov     eax,ecx
  47.         sub     eax,16
  48.         shl     eax,2
  49.         xor     ebx,dword [SHA1_W+eax]
  50.  
  51.         rol     ebx,1
  52.  
  53.         mov     eax,ecx
  54.         shl     eax,2
  55.         mov     dword [SHA1_W+eax],ebx
  56. .@1:
  57.         mov     edx,[SHA1_a]
  58.         rol     edx,5
  59.  
  60.         ; Расчет t и k
  61.         cmp     ecx,20
  62.         jae     @f
  63.         ; (b & c) | ((~b) & d)
  64.         mov     eax,[SHA1_b]
  65.         and     eax,[SHA1_c]
  66.         mov     ebx,[SHA1_b]
  67.         not     ebx
  68.         and     ebx,[SHA1_d]
  69.         or      eax,ebx
  70.         add     edx,0x5A827999
  71.         jmp     .@2
  72. @@:
  73.         cmp     ecx,40
  74.         jae     @f
  75.         ; b ^ c ^ d
  76.         mov     eax,[SHA1_b]
  77.         xor     eax,[SHA1_c]
  78.         xor     eax,[SHA1_d]
  79.         add     edx,0x6ED9EBA1
  80.         jmp     .@2
  81. @@:
  82.         cmp     ecx,60
  83.         jae     @f
  84.         ; (b & c) | (b & d) | (c & d)
  85.         mov     eax,[SHA1_b]
  86.         and     eax,[SHA1_c]
  87.         mov     ebx,[SHA1_b]
  88.         and     ebx,[SHA1_d]
  89.         or      eax,ebx
  90.         mov     ebx,[SHA1_c]
  91.         and     ebx,[SHA1_d]
  92.         or      eax,ebx
  93.         add     edx,0x8F1BBCDC
  94.         jmp     .@2
  95. @@:
  96.         mov     eax,[SHA1_b]
  97.         xor     eax,[SHA1_c]
  98.         xor     eax,[SHA1_d]
  99.         add     edx,0xCA62C1D6
  100. .@2:
  101.         add     edx,eax
  102.         add     edx,[SHA1_e]
  103.  
  104.         mov     eax,ecx
  105.         shl     eax,2
  106.         add     edx,[SHA1_W+eax]
  107.  
  108.         mov     eax,[SHA1_d]         ; e = d
  109.         mov     [SHA1_e],eax
  110.  
  111.         mov     eax,[SHA1_c]         ; d = c
  112.         mov     [SHA1_d],eax
  113.  
  114.         mov     eax,[SHA1_b]         ; c = rol(b,30)
  115.         rol     eax,30
  116.         mov     [SHA1_c],eax
  117.  
  118.         mov     eax,[SHA1_a]         ; b = a
  119.         mov     [SHA1_b],eax
  120.  
  121.         mov     [SHA1_a],edx         ; a = t
  122.  
  123.         inc     ecx
  124.         cmp     ecx,80
  125.         jne     .cycle_loop
  126.  
  127.         mov     eax,[SHA1_a]
  128.         add     [SHA1_h0],eax
  129.         mov     eax,[SHA1_b]
  130.         add     [SHA1_h1],eax
  131.         mov     eax,[SHA1_c]
  132.         add     [SHA1_h2],eax
  133.         mov     eax,[SHA1_d]
  134.         add     [SHA1_h3],eax
  135.         mov     eax,[SHA1_e]
  136.         add     [SHA1_h4],eax
  137.  
  138.         popa
  139.         ret
  140. endp
Пример использования:
  1. ; Сегмент данных
  2. section '.data' data readable writeable  
  3. ...
  4. somedata db 'Yeah! I like Flat Assembler!',0 ; Исходные данные для хеширования
  5. mask     db '%.8x%.8x%.8x%.8x%.8x',0         ; Маска для вывода строки
  6.  
  7. ; Сегмент кода
  8. section '.code' code readable executable
  9.         ...
  10.         ; Расчет длины строки. Для бинарных данных lstrlen лучше не использовать
  11.         invoke  lstrlen,somedata
  12.         ; Расчет SHA1
  13.         stdcall SHA1,somedata,eax
  14.         ; Перевод в строку, буквы строчные
  15.         invoke  wsprintf, buff, mask, [SHA1_h0], [SHA1_h1], [SHA1_h2],\
  16.                 [SHA1_h3], [SHA1_h4]
  17.         ...
Функция, конечно, не идеальная, часть кода можно было оптимизировать. Еще один недостаток в том, что она считает хеш сразу за один проход, а не накопительно, то есть, например, с ее помощью не получится посчитать SHA1 от большого файла, подгружая его по частям. Но такая задача и не ставилась, мне нужен был именно однопроходный вариант. Вы же можете доработать функцию SHA1 как вам угодно.

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

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

SHA1.Demo.zip (4,053 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 4017 | Комментариев: 13

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Андрей (30.03.2017 в 08:35):
Всё понял, спасибо.

На всякий случай
ЦитатаПоправил, спасибо

На сайте поправлено, в исходнике осталось- ; На выходе: заполненные переменные SHA1_h0 - SHA1_h0
ManHunter (29.03.2017 в 12:37):
Конкретно в этом случае - да, избыточный код. Хотел совместить исходник с примером из статьи.
Андрей (29.03.2017 в 12:23):
Возник вопрос по функции invoke lstrlen,buff. Зачем она нужна если GetDlgItemText идущая перед ней и так возвращает число скопированных символов, по сути длину строки?
ManHunter (15.07.2014 в 20:44):
Поправил, спасибо
Женя (15.07.2014 в 18:57):
Опечатка в комментарии. 2-ой листинг.
; На выходе: заполненные переменные SHA1_h0 - SHA1_h0
Наверное, должно быть:
; На выходе: заполненные переменные SHA1_h0 - SHA1_h4
ManHunter (19.04.2012 в 14:28):
Ну бывает же, иногда клинит :) Конечно же надо было поменять на bswap, а я все еще мыслю 16-битным стилем. Спасибо!
Alexey (19.04.2012 в 14:15):
Спасибо! Сам буквально на днях мучался пытался реализовать хеширование методом SHA1, я ведь даже и не подумал, что двойные слова надо разворачивать, блогодоря тебе и моя програмка заработала.
И смотрю на твой код, ты используешь короткие команды: loop, movsd ...
А вот команду bswap eax развернул вон как:
xchg    al,ah
rol     eax,16
xchg    al,ah
ManHunter (27.03.2012 в 17:38):
Открываешь мурзилку по FASM и куришь ее до наступления просветления:
http://flatassembler.net/docs....manual#1.2.3
А конкретно последний абзац раздела "Constants and labels".
im_infamous (27.03.2012 в 17:34):
Не разобрался в метках. Подскажите, как все организовано. Все циклы начинаются с @@, хотя нигде нет на эту метку ни условного ни безусловного перехода, и наоборот, с меток @b, @f программа никуда не переходит.
ManHunter (08.06.2011 в 17:29):
Может получилось и помедленнее каких-нибудь эталонных алгоритмов, зато все компактно и наглядно. Я не замерял. Мне же не надо хешировать гигабайтами, для этого есть совершенно другие многопроходные функции расчета SHA1 с инициализацией, накопительным расчетом и финализацией. А эта функция написана для небольших объемов данных.
Isaev (08.06.2011 в 17:23):
А по скорости сравнивал?
ManHunter (08.06.2011 в 08:34):
Isaev, лежат, но они во-первых на masm, а во-вторых мне не нравятся :)
Isaev (08.06.2011 в 05:06):
"но я не нашел ни одного нормального решения на Ассемблере."
да ладно! у тебя наверняка даже в компе лежит либа cryptohash c реализациями многих хешей и крипто поделёнными каждый по отдельным файлам на асме ;)

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

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

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