Распаковка данных в формате NRV на Ассемблере
Распаковка данных в формате NRV на Ассемблере
Алгоритм компрессии NRV (Not Really Vanished) разработан автором знаменитого упаковщика UPX Markus F.X.J. Oberhumer. Алгоритм используется в этом упаковщике, а также в нескольких вариантах входит в состав библиотеки с открытым кодом UCL. Высокая скорость обработки данных сочетается с очень хорошей степенью компрессии данных.
Но, как говорится, "гладко было на бумаге, да забыли про овраги". При всем уважении к Маркусу, его стиль программирования, основанный на большом количестве макросов и инклудов, мне не нравится. Сперва пришлось угрохать кучу времени, чтобы из исходников скомпилировать работоспособную утилиту для упаковки данных. Затем из набора чудовищных ассемблерных макроконструкций и заведомо неработоспособных бинарных данных надо было собрать три варианта работающих распаковщиков. После этого внезапно выяснилось, что алгоритм блочный и данные распаковываются по каждому блоку отдельно, из-за этого пришлось снова модифицировать все три алгоритма. В результате на выходе мы имеем оригинальную утилиту для упаковки данных по трем разновидностям алгоритма NRV, ассемблерную функцию для подсчета размера данных и три полноценных ассемблерных распаковщика для каждого варианта алгоритма NRV.
Упакованные данные начинаются с "магического" 8-байтного заголовка, затем идет DWORD c различными флагами, затем байт с указанием использованного алгоритма упаковки, байт выбранной степени компрессии, DWORD с выбранным размером блока (обычно 40000h байт), DWORD с размером исходных данных, DWORD с размером упакованных данных. Затем идут уже сами упакованные данные. Если их исходный размер оказался больше размера одного блока, то дальше идет один или несколько следующих блоков с кратким заголовком, который состоит только из DWORD с размером исходных данных и DWORD с размером упакованных данных. Затем снова упакованные данные и так далее. Завершает всю эту конструкцию DWORD нулевым значением, а затем опционально DWORD с контрольной суммой исходных данных, если в параметрах упаковки было указано использование CRC.
Чтобы выяснить размер памяти, которую требуется выделить для приема распакованных данных, надо просто просуммировать размер всех исходных блоков из каждого заголовка. При обработке надо не забывать применять к размерам команду BSWAP. Для любого из трех разновидностей алгоритма эта функция будет одинаковой.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------------
- ; Получение размера распакованных данных
- ;---------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; На выходе:
- ; EAX = размер распакованных данных
- ;---------------------------------------------------
- proc nrv_size lpCompressed:DWORD
- pusha
- mov esi,[lpCompressed]
- add esi,18
- xor ebx,ebx
- .loc_scan:
- lodsd
- bswap eax
- or eax,eax
- jz .loc_done
- add ebx,eax
- lodsd
- bswap eax
- add esi,eax
- jmp .loc_scan
- .loc_done:
- mov [esp+28],ebx
- popa
- ret
- endp
Алгоритмы упаковки показывают различные результаты в зависимости от типа и размера исходных данных. На небольших объемах данных степень компрессии сильно варьируется, на больших файлах преимущество каждой более старшей версии алгоритма становится заметнее. Вот три варианта распаковщиков данных для nrv2b, nrv2d и nrv2e.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Распаковка данных в формате UCL (nrv2b)
- ; Copyright (C) Markus F.X.J Oberhumer
- ; Модификация: ManHunter / PCL
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; lpOut - указатель на буфер для распакованных данных
- ; На выходе:
- ; EAX = размер распакованных данных
- ;------------------------------------------------------------
- proc nrv2b_unpack lpCompressed:DWORD,lpOut:DWORD
- pusha
- mov esi,[lpCompressed]
- add esi,26
- mov edi,[lpOut]
- .decompr_block:
- xor ecx,ecx
- mul ecx
- dec edx
- jmp .decompr_start
- .decompr_literal:
- movsb
- .decompr_start:
- stdcall .get_bit
- jc .decompr_literal
- push 1
- pop ebx
- .decompr_match:
- stdcall .get_bit
- adc ebx,ebx
- stdcall .get_bit
- jnc .decompr_match
- sub ebx,3
- jb .decompr_same_off
- shl ebx,8
- mov bl,[esi]
- inc esi
- xor ebx,0FFFFFFFFh
- jnz .decompr_continue
- lodsd
- or eax,eax
- jz .decompr_end
- lodsd
- jmp .decompr_block
- .decompr_continue:
- xchg edx,ebx
- .decompr_same_off:
- stdcall .get_bit
- adc ecx, ecx
- stdcall .get_bit
- adc ecx,ecx
- jnz .decompr_literals
- inc ecx
- .decode_check_len:
- stdcall .get_bit
- adc ecx,ecx
- stdcall .get_bit
- jnc .decode_check_len
- inc ecx
- inc ecx
- .decompr_literals:
- cmp edx,0FFFFF300h
- adc ecx,1
- push esi
- lea esi,[edi+edx]
- rep movsb
- pop esi
- jmp .decompr_start
- .get_bit:
- add al,al
- jnz .get_bit_exit
- lodsb
- stc
- adc al,al
- .get_bit_exit:
- retn
- .decompr_end:
- sub edi,[lpOut]
- mov [esp+28],edi
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Распаковка данных в формате UCL (nrv2d)
- ; Copyright (C) Markus F.X.J Oberhumer
- ; Модификация: ManHunter / PCL
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; lpOut - указатель на буфер для распакованных данных
- ; На выходе:
- ; ebx = размер распакованных данных
- ;------------------------------------------------------------
- proc nrv2d_unpack lpCompressed:DWORD,lpOut:DWORD
- pusha
- mov esi,[lpCompressed]
- add esi,26
- mov edi,[lpOut]
- .decompr_block:
- xor ecx,ecx
- mul ecx
- dec edx
- jmp .decompr_start
- .decompr_literal:
- movsb
- .decompr_loop:
- add al,al
- jnz .decompr_gotbit
- .decompr_start:
- lodsb
- stc
- adc al,al
- .decompr_gotbit:
- jb .decompr_literal
- xor ebx,ebx
- inc ebx
- .decompr_match:
- stdcall .get_bit
- adc ebx,ebx
- stdcall .get_bit
- jb .decode_check_off
- dec ebx
- stdcall .get_bit
- adc ebx,ebx
- jmp .decompr_match
- .decode_check_off:
- sub ebx,3
- jb .decompr_same_off
- shl ebx,8
- mov bl,[esi]
- inc esi
- xor ebx,0FFFFFFFFh
- jnz .decompr_continue
- lodsd
- or eax,eax
- jz .decompr_end
- lodsd
- jmp .decompr_block
- .decompr_continue:
- sar ebx,1
- mov edx,ebx
- jmp .decompr_got_off
- .decompr_same_off:
- stdcall .get_bit
- .decompr_got_off:
- adc ecx,ecx
- stdcall .get_bit
- adc ecx,ecx
- jnz .decompr_got_len
- inc ecx
- .decode_check_len:
- stdcall .get_bit
- adc ecx,ecx
- stdcall .get_bit
- jnb .decode_check_len
- add ecx,2
- .decompr_got_len:
- cmp edx,0FFFFFB00h
- adc ecx,1
- push esi
- lea esi,[edi+edx]
- rep movsb
- pop esi
- jmp .decompr_loop
- .get_bit:
- add al,al
- jnz .get_bit_exit
- lodsb
- stc
- adc al,al
- .get_bit_exit:
- retn
- .decompr_end:
- sub edi,[lpOut]
- mov [esp+28],edi
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Распаковка данных в формате UCL (nrv2e)
- ; Copyright (C) Markus F.X.J Oberhumer
- ; Модификация: ManHunter / PCL
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; lpOut - указатель на буфер для распакованных данных
- ; На выходе:
- ; ebx = размер распакованных данных
- ;------------------------------------------------------------
- proc nrv2e_unpack lpCompressed:DWORD,lpOut:DWORD
- pusha
- mov esi,[lpCompressed]
- add esi,26
- mov edi,[lpOut]
- .decompr_block:
- xor ecx,ecx
- mul ecx
- dec edx
- jmp .decompr_start
- .decompr_literal:
- movsb
- .decompr_loop:
- add al,al
- jnz .decompr_gotbit
- .decompr_start:
- lodsb
- stc
- adc al,al
- .decompr_gotbit:
- jb .decompr_literal
- xor ebx,ebx
- inc ebx
- .decompr_match:
- stdcall .get_bit
- adc ebx,ebx
- stdcall .get_bit
- jb .decode_check_off
- dec ebx
- stdcall .get_bit
- adc ebx,ebx
- jmp .decompr_match
- .decode_check_off:
- sub ebx,3
- jb .decompr_same_off
- shl ebx,8
- mov bl,[esi]
- inc esi
- xor ebx,0FFFFFFFFh
- jnz .decompr_continue
- lodsd
- or eax,eax
- jz .decompr_end
- lodsd
- jmp .decompr_block
- .decompr_continue:
- sar ebx,1
- mov edx,ebx
- jnb .decompr_got_off
- .decompr_mlen1:
- stdcall .get_bit
- adc ecx,ecx
- jmp .decompr_got_len
- .decompr_same_off:
- stdcall .get_bit
- jc .decompr_mlen1
- .decompr_got_off:
- inc ecx
- stdcall .get_bit
- jc .decompr_mlen1
- .decode_check_len:
- stdcall .get_bit
- adc ecx, ecx
- stdcall .get_bit
- jnb .decode_check_len
- add ecx,2
- .decompr_got_len:
- cmp edx,0FFFFFB00h
- adc ecx,2
- push esi
- lea esi,[edi+edx]
- rep movsb
- pop esi
- jmp .decompr_loop
- .get_bit:
- add al,al
- jnz .get_bit_exit
- lodsb
- stc
- adc al,al
- .get_bit_exit:
- retn
- .decompr_end:
- sub edi,[lpOut]
- mov [esp+28],edi
- popad
- ret
- endp
В приложении примеры программ с исходными текстами, которые извлекают из памяти иконки, упакованные по трем разновидностям алгоритма NRV, а затем выводят их на форму. Там же в архиве оригинальная утилита для упаковки и распаковки данных от Markus F.X.J. Oberhumer.
Просмотров: 1270 | Комментариев: 0
Метки: Assembler, распаковка
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
Комментариeв нет
Добавить комментарий
Заполните форму для добавления комментария