Последние записи
- Преобразовать массив байт в вещественное число (single)
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
- Определить текущую ОС
- Автоматическая смена языка (раскладки клавиатуры)
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
27th
Фев
Шифровка с помощью пароля. Улучшаем алгоритм
Posted by Chas under Журнал, Статьи
Доброго времени суток. Если не все читали мою предыдущую статью «Шифруем файл с помощью пароля» [1], то рекомендую прочитать перед прочтением данного материала. В ней я описал методику шифрования файлов с помощью пароля. Это конечно довольно-таки простая методика. В этой же статье, я расскажу про более сложную методику шифрования с помощью пароля. Итак, приступим…
Руслан Аблязов
by rpy3uH http://www.programmersforum.ru
В чем уязвимость старого алгоритма? Допустим, в файле будет некоторая последовательность одинаковых байт. После шифрования будет видно повторение одной и той же последовательности, которая имеет длину равную длине пароля. Будет видно, на сколько отличаются символы в пароле (например, второй символ на 3 «больше», чем первый, третий на 20 «меньше», чем второй и т.д.). После этого, нам только останется перебрать все 256 вариантов пароля, и, в итоге, файл будет расшифрован в считанные минуты.
Отсюда вывод: не надо обрабатывать данные в файле «данными» пароля, то есть при шифровании не надо использовать пароль в чистом виде. Один из множества выходов заключается в том, что бы использовать при шифровании некую контрольную сумму пароля. В качестве контрольной суммы будем использовать CRC32 пароля. CRC32 – это урезанный вариант «хеша», который имеет размер не 16 байт, а 4 байта. Следовательно, шаг у нас будет на 1 байт как в прошлом случае, а 4 байта. Вместо прибавления/убавления, по-моему, использовать взаимно обратную логическую операцию XOR (это только мое мнение, возможно, она мне понравилась из-за того, что для нее не надо писать дешифратор, поскольку шифратор также является дешифратором). Использовать одну и ту CRC32 слишком просто, потому что можно подобрать такое слово, у которого CRC32 будет такой же что и у пароля. Поэтому надо будет видоизменять пароль после очередной операции XOR. У видоизмененного пароля CRC32 будет уже совсем другим. Правило изменения пароля будет таким:
- текущее смещение в файле делится 4, и делится на длину пароля – 1, остаток от деления будет числом K;
- символ в пароле, который находится на позиции K, перемещается в конец пароля.
Довольно-таки незамысловатое правило. Но, если следовать этому правилу, мы получим исходный пароль после N*(N-1) модификаций. Если использовать пароль длиной 8 символов (средняя длина пароля), то количество модификаций будет равно 56-ти. Подобрать слово такой же длины, у которого все 56 модификаций имеют такую же CRC32, почти невозможно. Если длина файла не кратна четырем, то остаток (1, 2 или 3 байта), «ксорится» побайтово с CRC32 текущей модификации пароля.
Практические занятия
Теория закончилась, приступим к практике. Сначала напишем функцию, которая будет шифровать некоторую область данных в памяти с помощью пароля. Я реализую ее на ассемблере и включу в DLL-ку (будет использован компилятор FASM). Тем, кому это не интересно, могут пропустить эту часть. Функция получения CRC32 некоторого блока:
; IN>
; ESI = block offset
; EDI = block size
; OUT
; EAX = CRC32
push esi
push edi
push ecx
push ebx
push edx
cld
xor ecx,ecx
dec ecx
mov edx,ecx
.NextByteCRC:
xor eax,eax
xor ebx,ebx
lodsb
xor al,cl
mov cl,ch
mov ch,dl
mov dl,dh
mov dh,8
.NextBitCRC:
shr bx,1
rcr ax,1
jnc .NoCRC
xor ax,08320h
xor bx,0EDB8h
.NoCRC:
dec dh
jnz .NextBitCRC
xor ecx,eax
xor edx,ebx
dec edi
jnz .NextByteCRC
not edx
not ecx
mov eax,edx
rol eax,16
mov ax,cx
pop edx
pop ebx
pop ecx
pop edi
pop esi
ret
Теперь функция модификации пароля NextPasswordMod:
;IN
; ESI offset ZS of password
; EAX ordinal number ((текущее смещение в файле)/4)
; EDI password length
pushad
dec edi
xor edx, edx
div edi
add esi, edx
mov bh, [esi]
mov ecx, edi
sub ecx, edx
.next:
mov bl, [esi+1]
mov [esi], bl
inc esi
loop .next
mov [esi], bh
popad
ret
Идем далее. Теперь сама функция:
proc CSCA1 DataAddress, DataSize, Password
; DataAddress – data offset
; DataSize – data size
; Password – offset ZS of password
local PasswordCp:DWORD
local PasswordLength:DWORD
pushad
mov edi, [Password]
call GetZSLength
mov [PasswordLength], eax
stdcall [VirtualAlloc],0,eax, MEM_COMMIT+MEM_RESERVE,
PAGE_READWRITE
mov edi, eax ; eax = password offset
mov esi, [Password]
mov ecx, [PasswordLength]
rep movsb
mov [PasswordCp], eax
Мы получили длину пароля. Выделили память и скопировали туда пароль, чтобы можно было без всякого «страха» модифицировать пароль:
xor ecx, ecx
.next: ; ecx
mov esi, [PasswordCp]
mov edi, [PasswordLength]
mov eax, ecx
call NextPasswordMod
mov esi, [PasswordCp]
mov edi, [PasswordLength]
call GET_CRC32 ;eax = CRC32 of current password mod
mov edi, [DataAddress]
imul edx, ecx,4 ;edx = current position
add edi, edx ; edi = current offset
mov ebx, [DataSize]
sub ebx, edx
cmp ebx, 0
jz .end
cmp ebx, 4
jl .not_full_xor
Здесь мы модифицируем пароль, получаем его контрольную сумму CRC32, и смотрим в конце-ли мы, если в конце, прыгаем в конец, если осталось меньше чем четыре байта, прыгаем в завершающую стадию шифрования:
full_xor:
xor [edi], eax
inc ecx
jmp .next
.not_full_xor:
mov ecx, ebx
.rep:
dec ecx
add edi, ecx
mov dl, [edi]
xor dl, al
mov [edi], dl
sub edi, ecx
inc ecx
shr eax, 8
loop .rep
.end:
stdcall [VirtualFree],[PasswordCp],0, MEM_RELEASE
popad
ret
endp
Полностью исходник DLL-ки и сама DLL есть в архиве с примером [1]. Как эту DLL можно использовать? Приведу пример на Delphi:
procedure CSCA1(DataAddress:pointer; DataSize:DWORD; Password:PChar); stdcall; external ‘CSCA1.DLL ‘;
procedure TForm1.Button1Click(Sender: TObject);
var
FH,FMH:THandle;
DataAddr:pointer;
FSize:DWORD;
begin
if not OpenDialog1.Execute then exit;
FH:=CreateFile(pchar(OpenDialog1.FileName), GENERIC_ALL, FILE_SHARE_READ,0, OPEN_EXISTING, 0, 0);
FSize:=GetFileSize(FH,nil);
FMH:=CreateFileMapping(FH,0,PAGE_READWRITE,0,FSize,”);
DataAddr:=MapViewOfFile(FMH,FILE_MAP_WRITE,0,0,FSize);
CSCA1(DataAddr,FSize,pchar(Edit1.Text));
UnmapViewOfFile(DataAddr);
CloseHandle(FMH);
CloseHandle(FH);
end;
Запустим проект на компиляцию (см. рисунок 1):
Рис. 1. Окно тестового проекта
При первом нажатии на кнопку файл шифруется, а при втором – файл дешифруется (см. рисунок 2 и 3):
Рис. 2. Диалог выбора файла
Рис. 3. Содержимое файла. Шифровка и дешифровка
Вот, наверное, и все. Да, чуть не забыл. Почему функция так называется? Я назвал алгоритм так – Control Sum Crypt Algorithm v1.0, т.е. CSCA1. Качаем и пользуемся. В архиве [2] исходник DLL (FASM), сама DLL и пример ее использования.
Модернизируем алгоритм
В первом варианте описан довольно эффективный (по-моему) алгоритм шифрования данных с помощью пароля. Но у него есть недостаток: если пароль состоит из одинаковых символов, то все модификации пароля сходят на нет (как ни модифицируй, все равно та же строка)! А теперь я расскажу, как этого избежать.
Единственное, что приходит в голову – это добавление к паролю дополнительных символов, чтобы при модифицировании пароля получалась другая CRC32. Увеличение пароля, одновременно увеличивает защищенность алгоритма, так как количество модификаций увеличивается, а следовательно увеличивается и количество CRC32 с помощью которых шифруются данные.
Итак, мы решили, что надо увеличивать пароль. Но как? Вариант, с добавлением в конец одной и той же последовательности символов не подходит, потому что алгоритм становится предсказуемым. Следовательно, надо добавлять последовательность символов в зависимости от самого пароля. Неплохим будет такой способ: код каждого символа пароля уменьшается на число равное его позиции в пароле, после чего получившаяся строка добавляется к искомому паролю. В результате уменьшения кода символа может получиться значение 0, а это обозначает конец строки, чтобы не было нуля мы заменяем его на код $7F. В результате этого преобразования длина пароля увеличивается в два раза и уже можно быть на 100% уверенным, что в нем будет как минимум 2 разных символа. По моим расчетам, в результате такого преобразования, защищенность алгоритма возрастает как минимум в 4 раза.
Теперь, остается только реализовать все это. Основной код алгоритма не изменится, изменится только начальный код функции шифровки и добавится функция для расширения пароля. Код функции расширения:
;ESI - pointer to string
pushad
mov ebx, esi
mov edi, esi
call GetZSLength
add edi, eax
mov ecx, eax
rep movsb
mov byte [edi], 0
add ebx, eax
mov ecx, 1
.rep:
sub byte [ebx], cl
cmp byte [ebx], 0
jnz @f
mov byte [ebx], 7Fh
@@:
inc ecx
inc ebx
cmp byte [ebx],0
jz .endrep
jmp .rep
.endrep:
popad
ret
Сначала мы сразу создаем копию пароля сразу за ним, после чего уменьшаем код каждого символа в копии пароля на число равное позиции символа. Как видно, производится беззнаковое уменьшение, то есть, например, уменьшение кода символа 11 на 15 даст нам значение кода 252. Следует отметить, что максимальное уменьшение равно 255, так как уменьшение более чем на 255 нецелесообразно. Таким образом, даже если искомая длина пароля больше чем 255, то 256 символ пароля останется неизменным, а 257 символ уменьшится на 1.
Теперь осталось только изменить сам код шифрующей функции. Начальный код функции шифровки теперь будет таким:
proc CSCA1 DataAddress, DataSize, Password
local PasswordCp:DWORD
local PasswordLength:DWORD
pushad
mov edi, [Password]
call GetZSLength
mov ebx, eax
shl eax, 1
mov [PasswordLength], eax
inc eax
stdcall [VirtualAlloc],0,eax, MEM_COMMIT+MEM_RESERVE, PAGE_READWRITE
mov edi, eax ; eax = password offset
mov esi, [Password]
mov ecx, ebx
rep movsb
mov [PasswordCp], eax
mov esi, eax
call ExpandString
xor ecx, ecx
.next: ; ecx
mov esi, [PasswordCp]
mov edi, [PasswordLength]
mov eax, ecx
call NextPasswordMod
Заключение
В первом варианте функцию шифровки я вынес в отдельную DLL-ку. Аргументирую свое решение тем, что этот алгоритм можно будет использовать в программах написанных как на С/С++, так и на Delphi. Наверное это и вправду неудобно… В архиве с исходниками [3] есть также реализация этого алгоритма на Delphi с использованием встроенного ассемблера.
Все упомянутые в статье исходные коды и ресурсы приложены в виде архива непосредственно к журналу.
Ресурсы
- Р.Аблязов. Шифруем файл с помощью пароля. – Блог программистов http://programmersclub.ru/gruzin-shifr-fail
- Исходник алгоритма шифрования CSCA1 и пример использования http://pblog.ru/wp-content/uploads/csca1.zip
- CSCA v1.1 исходники на Assembler и Delphi http://pblog.ru/wp-content/uploads/csca-11.zip
Статья из десятого выпуска журнала «ПРОграммист».
Похожие статьи
Купить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)