Распаковка ZIP-архивов на Ассемблере
Распаковка ZIP-архивов на Ассемблере
Если система выполняет какие-то действия "из коробки", то с очень большой вероятностью ваше приложение также сможет их использовать, надо только узнать как. Начиная с Windows XP, Проводник может работать с архивами в формате ZIP, как будто это обычные каталоги. Файлы и подкаталоги из них извлекаются обычным копированием. Давайте научимся делать то же самое.
Для начала нам потребуется солидная пачка GUID'ов, интерфейсов, структур и констант. Надеюсь, что подобный факт для вас уже давно не является чем-то сверхъестественным. Разработчик FASM, как я понимаю, принципиально остановил наполнение инклудов на уровне Windows 2000.
Code (Assembler) : Убрать нумерацию
- ; GUID {000214E6-0000-0000-C000-000000000046}
- IID_IShellFolder \
- dd 0000214E6h
- dw 00000h
- dw 00000h
- db 0C0h, 000h, 000h, 000h, 000h, 000h, 000h, 046h
- ; IID_IShellFolder Interface
- struct IShellFolder
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IShellFolder
- ParseDisplayName dd ? ; 00Ch
- EnumObjects dd ? ; 010h
- BindToObject dd ? ; 014h
- BindToStorage dd ? ; 018h
- CompareIDs dd ? ; 01Ch
- CreateViewObject dd ? ; 020h
- GetAttributesOf dd ? ; 024h
- GetUIObjectOf dd ? ; 028h
- GetDisplayNameOf dd ? ; 02Ch
- SetNameOf dd ? ; 030h
- ends
- ; GUID {0000000B-0000-0000-C000-000000000046}
- IID_IStorage \
- dd 00000000Bh
- dw 00000h
- dw 00000h
- db 0C0h, 000h, 000h, 000h, 000h, 000h, 000h, 046h
- ; IID_IStorage Interface
- struct IStorage
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IStorage
- CreateStream dd ? ; 00Ch
- OpenStream dd ? ; 010h
- CreateStorage dd ? ; 014h
- OpenStorage dd ? ; 018h
- CopyTo dd ? ; 01Ch
- MoveElementTo dd ? ; 020h
- Commit dd ? ; 024h
- Revert dd ? ; 028h
- EnumElements dd ? ; 02Ch
- DestroyElement dd ? ; 030h
- RenameElement dd ? ; 034h
- SetElementTimes dd ? ; 038h
- SetClass dd ? ; 03Ch
- SetStateBits dd ? ; 040h
- Stat dd ? ; 044h
- ends
- ; IEnumSTATSTG Interface
- struct IEnumSTATSTG
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IEnumSTATSTG
- Next dd ? ; 00Ch
- Skip dd ? ; 010h
- Reset dd ? ; 014h
- Clone dd ? ; 018h
- ends
- ; IID_IStream Interface
- struct IStream
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IStream
- Read dd ? ; 00Ch
- Write dd ? ; 010h
- Seek dd ? ; 014h
- SetSize dd ? ; 018h
- CopyTo dd ? ; 01Ch
- Commit dd ? ; 020h
- Revert dd ? ; 024h
- LockRegion dd ? ; 028h
- UnlockRegion dd ? ; 02Ch
- Stat dd ? ; 030h
- Clone dd ? ; 034h
- ends
- struct ULARGE_INTEGER
- LowPart dd ?
- HighPart dd ?
- ends
- struct STATSTG
- pwcsName dd ?
- type dd ?
- cbSize ULARGE_INTEGER
- mtime FILETIME
- ctime FILETIME
- atime FILETIME
- grfMode dd ?
- grfLocksSupported dd ?
- clsid rd 4
- grfStateBits dd ?
- reserved dd ?
- ends
- STGTY_STORAGE = 1
- STGTY_STREAM = 2
- STGM_READ = 0x00000000
- STGM_WRITE = 0x00000001
- STGM_CREATE = 0x00001000
- ERROR_ALREADY_EXISTS = 183
Code (Assembler) : Убрать нумерацию
- invoke CoInitialize,0
- ; Получить хэндл кучи
- invoke GetProcessHeap
- mov [hHeap],eax
- ; Получить полный путь к файлу архива
- invoke GetFullPathName,filename,MAX_PATH,szZipFileName,NULL
- ; Создать объект из файла
- invoke ILCreateFromPath,szZipFileName
- or eax,eax
- jz loc_exit
- mov [pidl],eax
- ; Связать дочерний объект с родительским
- invoke SHBindToParent,[pidl],IID_IShellFolder,psfParent,pidlChild
- or eax,eax
- jnz loc_clear1
- ; Связать объект с хранилищем
- mov eax,[psfParent]
- mov eax,[eax]
- stdcall dword [eax+IShellFolder.BindToObject],[psfParent],\
- [pidlChild],NULL,IID_IStorage,storage
- or eax,eax
- jnz loc_clear2
- ; Выделить память в куче для имени папки извлечения
- invoke HeapAlloc,[hHeap],HEAP_ZERO_MEMORY,MAX_PATH*2
- mov ebx,eax
- invoke lstrcpy,ebx,szZipFileName
- invoke PathRemoveExtension,ebx
- ; Извлечь архив в папку
- stdcall ZipExtract,[storage],ebx
- loc_clear2:
- ; Прибраться за собой
- mov eax,[psfParent]
- mov eax,[eax]
- stdcall dword [eax+IShellFolder.Release],[psfParent]
- loc_clear1:
- invoke ILFree,[pidl]
- loc_exit:
- invoke CoUninitialize
Code (Assembler) : Убрать нумерацию
- ; Рекурсивная функция для извлечения файлов из ZIP-архива
- proc ZipExtract dStorage:DWORD, lpFolder:DWORD
- locals
- enumerator dd ?
- subfolder dd ?
- stream dd ?
- fetched dd ?
- extracted dd ?
- endl
- ; Создать заданный (под)каталог для распаковки
- invoke CreateDirectory,[lpFolder],NULL
- or eax,eax
- jnz @f
- ; Каталог уже есть, все хорошо
- invoke GetLastError
- cmp eax,ERROR_ALREADY_EXISTS
- jne .loc_ret
- @@:
- ; Создать перечислитель
- lea eax,[enumerator]
- push eax
- mov eax,[dStorage]
- mov eax,[eax]
- stdcall dword [eax+IStorage.EnumElements],[dStorage],\
- 0,0,0
- or eax,eax
- jnz .loc_ret
- .loc_scan_zip:
- ; Перебрать файлы и подкаталоги
- lea eax,[fetched]
- push eax
- mov eax,[enumerator]
- mov eax,[eax]
- stdcall dword [eax+IEnumSTATSTG.Next],[enumerator],\
- 1,stat
- or eax,eax
- jnz .loc_done_scan
- ; Это подкаталог?
- cmp [stat.type],STGTY_STORAGE
- jne @f
- ; Создать новый путь с подкаталогом
- invoke HeapAlloc,[hHeap],HEAP_ZERO_MEMORY,MAX_PATH*2
- mov ebx,eax
- invoke wsprintf,ebx,mask,[lpFolder],[stat.pwcsName]
- add esp,16
- ; Открыть его как новое хранилище
- lea eax,[subfolder]
- push eax
- mov eax,[dStorage]
- mov eax,[eax]
- stdcall dword [eax+IStorage.OpenStorage],[dStorage],\
- [stat.pwcsName],0,STGM_READ,0,0
- ; Извлечь содержимое подкаталога
- stdcall ZipExtract,[subfolder],ebx
- ; Перейти к следующей записи
- jmp .loc_next
- @@:
- ; Это файл?
- cmp [stat.type],STGTY_STREAM
- jne .loc_next
- ; Создать новый путь с каталогом и именем файла
- invoke HeapAlloc,[hHeap],HEAP_ZERO_MEMORY,MAX_PATH*2
- mov ebx,eax
- invoke wsprintf,ebx,mask,[lpFolder],[stat.pwcsName]
- add esp,16
- ; Создать объект потока данных
- lea eax,[stream]
- push eax
- mov eax,[dStorage]
- mov eax,[eax]
- stdcall dword [eax+IStorage.OpenStream],[dStorage],\
- [stat.pwcsName],0,STGM_READ,0
- ; Связать объект потока с файлом
- lea eax,[extracted]
- invoke SHCreateStreamOnFile,ebx,STGM_WRITE+STGM_CREATE,eax
- ; Извлечь файл
- mov eax,[stream]
- mov eax,[eax]
- stdcall dword [eax+IStream.CopyTo],[stream],\
- [extracted],\
- [stat.cbSize.HighPart],[stat.cbSize.LowPart],\
- NULL,cbSize
- ; Прибраться за собой
- mov eax,[extracted]
- mov eax,[eax]
- stdcall dword [eax+IStream.Release],[extracted]
- mov eax,[stream]
- mov eax,[eax]
- stdcall dword [eax+IStream.Release],[stream]
- ; Освободить запись в куче
- invoke HeapFree,[hHeap],0,ebx
- .loc_next:
- ; Освободить память, выделенную под имя файла
- invoke CoTaskMemFree,[stat.pwcsName]
- jmp .loc_scan_zip
- .loc_done_scan:
- ; Освободить перечислитель
- mov eax,[enumerator]
- mov eax,[eax]
- stdcall dword [eax+IEnumSTATSTG.Release],[enumerator]
- .loc_ret:
- ; Освободить объект (под)каталога
- mov eax,[dStorage]
- mov eax,[eax]
- stdcall dword [eax+IStorage.Release],[dStorage]
- ; Освободить запись в куче
- invoke HeapFree,[hHeap],0,[lpFolder]
- ret
- endp
При обработке архива совершенно не обязательно извлекать из него все файлы. Например, вы можете найти какой-то конкретный файл, проверяя его имя. Или же вместо копирования файла в папку назначения можно просто прочитать его содержимое и использовать в своей программе. Это очень удобно в том случае, когда ваша программа хранит в архиве какие-то данные.
В приложении пример программы с исходным текстом, которая распаковывает из архива все файлы с вложенными подкаталогами.
Просмотров: 824 | Комментариев: 11
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
Иван Коссей
(19.03.2024 в 00:02):
Есть где-то пример распаковки ZIP с использованием микрософтовских функций CAB или ZLIB? Я сделал в ТАСМ32, и у меня не работает. Вылетает сразу в начале обработки потока с сообщением о неправильных данных. Работает прекрасно с вызовом UNZIP.
ManHunter
(20.07.2023 в 19:35):
morgot, интересный способ, надо попробовать. Спасибо за наводку!
morgot
(19.07.2023 в 17:51):
Интересно, почему негрософт не сделал нормальные документированные функции архивации? По факту, кроме убогих САВ, ничего нет. Заархивировать вот пробовали https://www.vbforums.com/showt...d-IDropTarge , но там изврат, по факту.
ManHunter
(12.07.2023 в 15:44):
Сперва надо поискать интерфейс по названию или по фрагменту названия в GUID Helper, если он не найдет, что маловероятно, то да, только ручками. Ну и опять же, если не найдет, то желательно сразу сообщить мне с указанием источников. Попробую распарсить и добавить. Это ж и в моих интересах тоже.
Grey
(12.07.2023 в 15:40):
Извини, не правильно вопрос поставил, он не конкретно к этой статье, статья - супер, а к СОМ-интерфейсам вообще.
Например есть интерфейс в dll или tlb библиотеке, как мне из него взять структуру для фасм, я полагаю только ручками переписывать из редактора midl или аналогичного.
Например есть интерфейс в dll или tlb библиотеке, как мне из него взять структуру для фасм, я полагаю только ручками переписывать из редактора midl или аналогичного.
ManHunter
(12.07.2023 в 15:32):
Все равно не понял. В исходнике же есть примеры вызовов методов по таким структурам. А так все распарсено в соответствии с порядком следования методов в заголовочных файлах и т.п.
Grey
(12.07.2023 в 15:28):
struct IShellFolder
; IUnknown
QueryInterface dd ? ; 000h
AddRef dd ? ; 004h
Release dd ? ; 008h
; IShellFolder
ParseDisplayName dd ? ; 00Ch
.... и т.д.
в от источниках дают С или IDL вручную на фасм переделываешь?
; IUnknown
QueryInterface dd ? ; 000h
AddRef dd ? ; 004h
Release dd ? ; 008h
; IShellFolder
ParseDisplayName dd ? ; 00Ch
.... и т.д.
в от источниках дают С или IDL вручную на фасм переделываешь?
ManHunter
(12.07.2023 в 15:24):
Не понял вопрос. Все взято из официальных источников
Grey
(12.07.2023 в 15:22):
Пропустил). А очередность и названия методов/функций?
ManHunter
(12.07.2023 в 14:58):
GUID Helper же
Grey
(12.07.2023 в 14:55):
Определение интерфейса из IDL в Fasm руками переводится или есть гдето на просторах программка для этих целей?
Добавить комментарий
Заполните форму для добавления комментария