
Расчет хеша SHA1 на Ассемблере
Алгоритм криптографического хеширования SHA1 (Secure Hash Algorithm 1) используется во многих криптографических приложениях и протоколах. В его основе лежат методы, очень похожие на MD5. В Интернете есть много реализаций этого алгоритма на разных языках программирования, но я не нашел ни одного нормального решения на Ассемблере. Пришлось разбираться самому и в результате у меня получилась достаточно быстрая функция расчета SHA1 участка памяти произвольной длины. Для работы в сегменте данных потребуются некоторые дополнительные переменные и массивы, они вынесены в глобальную область видимости, чтобы не загромождать код.Code (Assembler) : Убрать нумерацию
- section '.data' data readable writeable
- SHA1_h0 dd ? ; Переменные, в которых будет создан хеш SHA1
- SHA1_h1 dd ?
- SHA1_h2 dd ?
- SHA1_h3 dd ?
- SHA1_h4 dd ?
- SHA1_a dd ? ; Вспомогательные переменные для промежуточных
- SHA1_b dd ? ; вычислений
- SHA1_c dd ?
- SHA1_d dd ?
- SHA1_e dd ?
- SHA1_W rd 80 ; Массивы для промежуточных данных
- SHA1_Buff rb 64
И, собственно, сама функция расчета SHA1. Параметры вызова: lpData - указатель на участок памяти, хеш от которого надо получить, и dSize - размер этого участка. Кроме основной функции расчета здесь используются еще две вспомогательные функции - SHA1_BE для преобразования строки к формату Big-endian и SHA1_Calc для цикла трансформации хеша.
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------------------------
- ; Функция расчета хэша SHA1
- ; Copyright (C) ManHunter / PCL
- ; http://www.manhunter.ru
- ;-----------------------------------------------------------------------
- ; Параметры:
- ; lpData - указатель на блок данных
- ; dSize - размер блока
- ; На выходе: заполненные переменные SHA1_h0 - SHA1_h4
- ;-----------------------------------------------------------------------
- proc SHA1 lpData:DWORD, dSize:DWORD
- locals
- len dd ?
- padding dd ?
- index dd ?
- endl
- pusha
- ; Инициализация
- mov [SHA1_h0],0x67452301
- mov [SHA1_h1],0xEFCDAB89
- mov [SHA1_h2],0x98BADCFE
- mov [SHA1_h3],0x10325476
- mov [SHA1_h4],0xC3D2E1F0
- ; Получить количество 64-байтных блоков
- mov eax,[dSize]
- push eax
- shr eax,6
- mov [len],eax
- pop eax
- ; Получить остаток от деления на 64
- and eax,3Fh
- ; Расчет количества добавляемых байт
- mov [padding],eax
- ; Подсчет SHA1 основных данных
- mov [index],0
- .main_loop:
- mov eax,[index]
- cmp eax,[len]
- je .main_done
- ; Указатель * 64
- shl eax,6
- mov esi,[lpData]
- add esi,eax
- mov edi,SHA1_Buff
- mov ecx,16
- rep movsd
- ; Привести строку к Big-endian виду
- stdcall SHA1_BE
- stdcall SHA1_Calc
- inc [index]
- jmp .main_loop
- .main_done:
- ; Подсчет SHA1 остаточных данных
- xor eax,eax
- mov edi,SHA1_Buff
- mov ecx,16
- rep stosd
- ; Скопировать остаточные данные в буфер
- mov eax,[index]
- shl eax,6
- mov esi,[lpData]
- add esi,eax
- mov edi,SHA1_Buff
- mov ecx,[padding]
- rep movsb
- ; Дописать финальный бит
- mov al,80h
- stosb
- ; Привести строку к Big-endian виду
- stdcall SHA1_BE
- ; Проверка на пограничные случаи, когда остаток
- ; строки меньше, чем требуется для записи финального
- ; бита и длины строки
- cmp [padding],56
- jae @f
- ; В конец буфера записать длину строки
- mov eax,[dSize]
- shl eax,3
- mov dword [SHA1_Buff+60],eax
- stdcall SHA1_Calc
- jmp .sha1_done
- @@:
- stdcall SHA1_Calc
- ; Очистка буфера
- mov edi,SHA1_Buff
- xor eax,eax
- mov ecx,15
- rep stosd
- ; В конец буфера записать длину строки
- mov eax,[dSize]
- shl eax,3
- stosd
- stdcall SHA1_Calc
- .sha1_done:
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------------------------
- ; Вспомогательная функция
- ; Приведение строки к Big-endian виду
- ;-----------------------------------------------------------------------
- proc SHA1_BE
- pusha
- mov ecx,16
- mov esi,SHA1_Buff
- mov edi,esi
- @@:
- lodsd
- bswap eax
- stosd
- loop @b
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------------------------
- ; Вспомогательная функция для расчета SHA1
- ;-----------------------------------------------------------------------
- proc SHA1_Calc
- pusha
- mov eax,[SHA1_h0]
- mov [SHA1_a],eax
- mov eax,[SHA1_h1]
- mov [SHA1_b],eax
- mov eax,[SHA1_h2]
- mov [SHA1_c],eax
- mov eax,[SHA1_h3]
- mov [SHA1_d],eax
- mov eax,[SHA1_h4]
- mov [SHA1_e],eax
- xor ecx,ecx ; i
- .cycle_loop:
- mov eax,ecx
- shl eax,2
- cmp ecx,16
- jae @f
- mov ebx,dword [SHA1_Buff+eax]
- mov dword [SHA1_W+eax],ebx
- jmp .@1
- @@:
- ; rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1)
- mov eax,ecx
- sub eax,3
- shl eax,2
- mov ebx,dword [SHA1_W+eax]
- mov eax,ecx
- sub eax,8
- shl eax,2
- xor ebx,dword [SHA1_W+eax]
- mov eax,ecx
- sub eax,14
- shl eax,2
- xor ebx,dword [SHA1_W+eax]
- mov eax,ecx
- sub eax,16
- shl eax,2
- xor ebx,dword [SHA1_W+eax]
- rol ebx,1
- mov eax,ecx
- shl eax,2
- mov dword [SHA1_W+eax],ebx
- .@1:
- mov edx,[SHA1_a]
- rol edx,5
- ; Расчет t и k
- cmp ecx,20
- jae @f
- ; (b & c) | ((~b) & d)
- mov eax,[SHA1_b]
- and eax,[SHA1_c]
- mov ebx,[SHA1_b]
- not ebx
- and ebx,[SHA1_d]
- or eax,ebx
- add edx,0x5A827999
- jmp .@2
- @@:
- cmp ecx,40
- jae @f
- ; b ^ c ^ d
- mov eax,[SHA1_b]
- xor eax,[SHA1_c]
- xor eax,[SHA1_d]
- add edx,0x6ED9EBA1
- jmp .@2
- @@:
- cmp ecx,60
- jae @f
- ; (b & c) | (b & d) | (c & d)
- mov eax,[SHA1_b]
- and eax,[SHA1_c]
- mov ebx,[SHA1_b]
- and ebx,[SHA1_d]
- or eax,ebx
- mov ebx,[SHA1_c]
- and ebx,[SHA1_d]
- or eax,ebx
- add edx,0x8F1BBCDC
- jmp .@2
- @@:
- mov eax,[SHA1_b]
- xor eax,[SHA1_c]
- xor eax,[SHA1_d]
- add edx,0xCA62C1D6
- .@2:
- add edx,eax
- add edx,[SHA1_e]
- mov eax,ecx
- shl eax,2
- add edx,[SHA1_W+eax]
- mov eax,[SHA1_d] ; e = d
- mov [SHA1_e],eax
- mov eax,[SHA1_c] ; d = c
- mov [SHA1_d],eax
- mov eax,[SHA1_b] ; c = rol(b,30)
- rol eax,30
- mov [SHA1_c],eax
- mov eax,[SHA1_a] ; b = a
- mov [SHA1_b],eax
- mov [SHA1_a],edx ; a = t
- inc ecx
- cmp ecx,80
- jne .cycle_loop
- mov eax,[SHA1_a]
- add [SHA1_h0],eax
- mov eax,[SHA1_b]
- add [SHA1_h1],eax
- mov eax,[SHA1_c]
- add [SHA1_h2],eax
- mov eax,[SHA1_d]
- add [SHA1_h3],eax
- mov eax,[SHA1_e]
- add [SHA1_h4],eax
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ; Сегмент данных
- section '.data' data readable writeable
- ...
- somedata db 'Yeah! I like Flat Assembler!',0 ; Исходные данные для хеширования
- mask db '%.8x%.8x%.8x%.8x%.8x',0 ; Маска для вывода строки
- ; Сегмент кода
- section '.code' code readable executable
- ...
- ; Расчет длины строки. Для бинарных данных lstrlen лучше не использовать
- invoke lstrlen,somedata
- ; Расчет SHA1
- stdcall SHA1,somedata,eax
- ; Перевод в строку, буквы строчные
- invoke wsprintf, buff, mask, [SHA1_h0], [SHA1_h1], [SHA1_h2],\
- [SHA1_h3], [SHA1_h4]
- ...
В приложении программа с исходным кодом, подсчитывающая хеш SHA1 от введенной строки.
Просмотров: 6724 | Комментариев: 13

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

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
; На выходе: заполненные переменные 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
И смотрю на твой код, ты используешь короткие команды: 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".
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 реализациями многих хешей и крипто поделёнными каждый по отдельным файлам на асме ;)
да ладно! у тебя наверняка даже в компе лежит либа cryptohash c реализациями многих хешей и крипто поделёнными каждый по отдельным файлам на асме ;)

Добавить комментарий
Заполните форму для добавления комментария

На всякий случай
На сайте поправлено, в исходнике осталось- ; На выходе: заполненные переменные SHA1_h0 - SHA1_h0