Распаковка данных в формате LZSA1 и LZSA2 на Ассемблере
Распаковка данных в формате LZSA1 и LZSA2 на Ассемблере
Алгоритм LZSA - представитель "высшей лиги" среди алгоритмов упаковки данных. Разработан в 2018 году признанным гуру упаковки Emmanuel Marty. Целью создания алгоритма LZSA была большая скорость упаковки данных и очень высокая степень компрессии для использования, главным образом, на 8-битных платформах. Существует две разновидности алгоритма LZSA, которые отличаются скоростью работы и, соответственно, степенью сжатия и размером модуля распаковки.
LZSA позволяет сжимать файлы любого объема, но сами данные обрабатываются блоками по 64 килобайта. Из-за этой особенности ассемблерные функции распаковки поддерживают данные, исходный размер которых также не превышает 64 килобайт, а паковать файлы прилагаемыми утилитами надо обязательно с ключом "-r", то есть "raw block format". Кстати, в интернетах выложена только 64-битная версия упаковщика LZSA, 32-битный вариант мне пришлось собирать самостоятельно из авторских исходников. Оба варианта есть в прилагаемом архиве.
Я адаптировал ассемблерную функцию распаковки данных в формате LZSA1 на синтаксис FASM. Оптимизировать тут нечего, код и так практически идеальный.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Распаковка данных в формате LZSA1
- ; Copyright (C) 2019 Emmanuel Marty
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; lpOut - указатель на буфер для распакованных данных
- ; На выходе:
- ; EAX = размер распакованных данных
- ;------------------------------------------------------------
- proc lzsa1_unpack lpCompressed:DWORD,lpOut:DWORD
- pusha
- mov esi,[lpCompressed]
- mov edi,[lpOut]
- .decode_token:
- xor eax,eax
- lodsb
- movzx edx,al
- and al,70h
- shr al,4
- cmp al,07h
- jne .got_literals
- lodsb
- add al,07h
- jnc .got_literals
- jne .mid_literals
- lodsw
- jmp .got_literals
- .mid_literals:
- lodsb
- inc ah
- .got_literals:
- xchg ecx,eax
- rep movsb
- test dl,dl
- js .get_long_offset
- dec ecx
- xchg eax,ecx
- lodsb
- jmp .get_match_length
- .get_long_offset:
- lodsw
- .get_match_length:
- xchg eax,edx
- and al,0Fh
- add al,3
- cmp al,12h
- jne .got_matchlen
- lodsb
- add al,12h
- jnc .got_matchlen
- jne .mid_matchlen
- lodsw
- test eax,eax
- je .done_decompressing
- jmp .got_matchlen
- .mid_matchlen:
- lodsb
- inc ah
- .got_matchlen:
- xchg ecx,eax
- xchg esi,eax
- mov esi,edi
- movsx edx,dx
- add esi,edx
- rep movsb
- xchg esi,eax
- jmp .decode_token
- .done_decompressing:
- sub edi,[lpOut]
- mov [esp+28],edi
- popa
- ret
- endp
Для резервирования памяти под распакованные данные надо сперва узнать их размер. В принципе, можно использовать блок памяти фиксированного размера 64 килобайта, но лучше все-таки делать это более точно. Я модифицировал функцию распаковки, чтобы она просто возвращала количество байт, которые будут занимать распакованные данные.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Получение размера распакованных данных
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; На выходе:
- ; EAX = размер распакованных данных
- ;------------------------------------------------------------
- proc lzsa1_size lpCompressed:DWORD
- pusha
- mov esi,[lpCompressed]
- xor edi,edi
- .decode_token:
- xor eax,eax
- lodsb
- movzx edx,al
- and al,70h
- shr al,4
- cmp al,07h
- jne .got_literals
- lodsb
- add al,07h
- jnc .got_literals
- jne .mid_literals
- lodsw
- jmp .got_literals
- .mid_literals:
- lodsb
- inc ah
- .got_literals:
- xchg ecx,eax
- add edi,ecx
- rep lodsb
- test dl,dl
- js .get_long_offset
- dec ecx
- xchg eax,ecx
- lodsb
- jmp .get_match_length
- .get_long_offset:
- lodsw
- .get_match_length:
- xchg eax,edx
- and al,0Fh
- add al,3
- cmp al,12h
- jne .got_matchlen
- lodsb
- add al,12h
- jnc .got_matchlen
- jne .mid_matchlen
- lodsw
- test eax,eax
- je .done_decompressing
- jmp .got_matchlen
- .mid_matchlen:
- lodsb
- inc ah
- .got_matchlen:
- add edi,eax
- jmp .decode_token
- .done_decompressing:
- mov [esp+28],edi
- popa
- ret
- endp
Формат данных, упакованных по алгоритму LZSA2, отличается от LZSA1, функция для их распаковки, соответственно, тоже будет другой.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Распаковка данных в формате LZSA2
- ; Copyright (C) 2019 Emmanuel Marty
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; lpOut - указатель на буфер для распакованных данных
- ; На выходе:
- ; EAX = размер распакованных данных
- ;------------------------------------------------------------
- proc lzsa2_unpack lpCompressed:DWORD,lpOut:DWORD
- pusha
- push ebp
- mov esi,[lpCompressed]
- mov edi,[lpOut]
- xor ebx,ebx
- inc bh
- xor ebp,ebp
- .decode_token:
- xor eax,eax
- lodsb
- movzx edx,al
- and al,18h
- shr al,3
- cmp al,03h
- jne .got_literals
- stdcall .get_nibble
- add al,cl
- cmp al,12h
- jne .got_literals
- lodsb
- add al,12h
- jnc .got_literals
- lodsw
- .got_literals:
- xchg ecx, eax
- rep movsb
- test dl,0C0h
- js .rep_match_or_large_offset
- xchg ecx,eax
- jne .offset_9_bit
- cmp dl,20h
- stdcall .get_nibble_x
- jmp .dec_offset_top
- .offset_9_bit:
- lodsb
- dec ah
- test dl,20h
- je .get_match_length
- .dec_offset_top:
- dec ah
- jmp .get_match_length
- .rep_match_or_large_offset:
- jpe .rep_match_or_16_bit
- cmp dl,0A0h
- xchg ah,al
- stdcall .get_nibble_x
- sub al,2
- jmp .get_match_length_1
- .rep_match_or_16_bit:
- test dl,20h
- jne .repeat_match
- lodsb
- .get_match_length_1:
- xchg ah,al
- lodsb
- .get_match_length:
- xchg ebp,eax
- .repeat_match:
- xchg eax, edx
- and al,07h
- add al,2
- cmp al,09h
- jne .got_matchlen
- stdcall .get_nibble
- add al,cl
- cmp al,18h
- jne .got_matchlen
- lodsb
- add al,18h
- jnc .got_matchlen
- je .done_decompressing
- lodsw
- .got_matchlen:
- xchg ecx,eax
- xchg esi,eax
- movsx ebp,bp
- lea esi,[ebp+edi]
- rep movsb
- xchg esi,eax
- jmp .decode_token
- .get_nibble_x:
- cmc
- rcr al,1
- stdcall .get_nibble
- or al,cl
- rol al,1
- xor al,0E1h
- retn
- .get_nibble:
- neg bh
- jns .has_nibble
- xchg ebx,eax
- lodsb
- xchg ebx,eax
- .has_nibble:
- mov cl,4
- ror bl,cl
- mov cl,0Fh
- and cl,bl
- retn
- .done_decompressing:
- pop ebp
- sub edi,[lpOut]
- mov [esp+28],edi
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Получение размера распакованных данных
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; На выходе:
- ; EAX = размер распакованных данных
- ;------------------------------------------------------------
- proc lzsa2_size lpCompressed:DWORD
- pusha
- push ebp
- mov esi,[lpCompressed]
- xor edi,edi
- xor ebx,ebx
- inc bh
- xor ebp,ebp
- .decode_token:
- xor eax,eax
- lodsb
- movzx edx,al
- and al,18h
- shr al,3
- cmp al,03h
- jne .got_literals
- stdcall .get_nibble
- add al,cl
- cmp al,12h
- jne .got_literals
- lodsb
- add al,12h
- jnc .got_literals
- lodsw
- .got_literals:
- xchg ecx, eax
- add edi,ecx
- rep lodsb
- test dl,0C0h
- js .rep_match_or_large_offset
- xchg ecx,eax
- jne .offset_9_bit
- cmp dl,20h
- stdcall .get_nibble_x
- jmp .dec_offset_top
- .offset_9_bit:
- lodsb
- dec ah
- test dl,20h
- je .get_match_length
- .dec_offset_top:
- dec ah
- jmp .get_match_length
- .rep_match_or_large_offset:
- jpe .rep_match_or_16_bit
- cmp dl,0A0h
- xchg ah,al
- stdcall .get_nibble_x
- sub al,2
- jmp .get_match_length_1
- .rep_match_or_16_bit:
- test dl,20h
- jne .repeat_match
- lodsb
- .get_match_length_1:
- xchg ah,al
- lodsb
- .get_match_length:
- xchg ebp,eax
- .repeat_match:
- xchg eax, edx
- and al,07h
- add al,2
- cmp al,09h
- jne .got_matchlen
- stdcall .get_nibble
- add al,cl
- cmp al,18h
- jne .got_matchlen
- lodsb
- add al,18h
- jnc .got_matchlen
- je .done_decompressing
- lodsw
- .got_matchlen:
- add edi,eax
- jmp .decode_token
- .get_nibble_x:
- cmc
- rcr al,1
- stdcall .get_nibble
- or al,cl
- rol al,1
- xor al,0E1h
- retn
- .get_nibble:
- neg bh
- jns .has_nibble
- xchg ebx,eax
- lodsb
- xchg ebx,eax
- .has_nibble:
- mov cl,4
- ror bl,cl
- mov cl,0Fh
- and cl,bl
- retn
- .done_decompressing:
- pop ebp
- mov [esp+28],edi
- popa
- ret
- endp
В приложении примеры программы с исходными текстами, которые извлекают из памяти иконки, упакованные по алгоритму LZSA1 и LZSA2, а затем выводят их на форму. Там же в архиве оригинальный упаковщик LZSA от Emmanuel Marty для 32-битных и 64-битных систем.
Просмотров: 1796 | Комментариев: 2
Метки: Assembler, распаковка
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(29.04.2021 в 11:31):
Самое время задуматься о смене антивируса.
NeshAliNehrin
(29.04.2021 в 11:29):
Моё почтение.
Касперский в очередной раз отправил архив в топку. Ругается на HEUR:Trojan.Win32.Genpak.vho в lzsa64.exe
Печально, что не удалось ознакомиться с исходниками в полном объёме, но это не критично. Объем и подача учебного материала по прежнему на высоком уровне.
Благодарю за культурный досуг.
Касперский в очередной раз отправил архив в топку. Ругается на HEUR:Trojan.Win32.Genpak.vho в lzsa64.exe
Печально, что не удалось ознакомиться с исходниками в полном объёме, но это не критично. Объем и подача учебного материала по прежнему на высоком уровне.
Благодарю за культурный досуг.
Добавить комментарий
Заполните форму для добавления комментария