Извлечение главной иконки из исполняемого файла
Извлечение главной иконки из исполняемого файла
В комментариях к недавней статье про замену главной иконки исполняемого файла задали очень правильный вопрос: как извлечь иконки из ресурсов исполняемого файла? Сегодня я постараюсь подробно рассказать вам, как это делается.
Начнем с описания структур, необходимых для работы с иконками. Они уже были в прошлых статьях. Так как мы будем использовать ресурсы и файлы иконок, нам понадобятся они все.
Code (Assembler) : Убрать нумерацию
- ; Структуры для работы с иконками в группе
- struct ICONDIR
- idReserved dw ? ; Зарезервировано (должно быть 0)
- idType dw ? ; Тип ресурса (1 для иконок)
- idCount dw ? ; Количество изображений в иконке
- ends
- struct GRPICONDIRENTRY
- bWidth db ? ; Ширина изображения в пикселах
- bHeight db ? ; Высота изображения в пикселах
- bColorCount db ? ; Количество цветов
- bReserved db ? ; Зарезервировано
- wPlanes dw ? ; Зарезервировано
- wBitCount dw ? ; Зарезервировано
- dwBytesInRes dd ? ; Размер данных изображения
- nID dw ? ; ID иконки в ресурсах
- ends
- struct ICONDIRENTRY
- bWidth db ? ; Ширина изображения в пикселах
- bHeight db ? ; Высота изображения в пикселах
- bColorCount db ? ; Количество цветов
- bReserved db ? ; Зарезервировано
- wPlanes dw ? ; Зарезервировано
- wBitCount dw ? ; Зарезервировано
- dwBytesInRes dd ? ; Размер данных изображения
- dwImageOffset dd ? ; Смещение данных относительно начала файла
- ends
Code (Assembler) : Убрать нумерацию
- ; Загрузить исходный файл в память
- invoke LoadLibrary,szSource
- or eax,eax
- jz loc_exit
- ; Хэндл загруженного исходного файла
- mov [hSource],eax
- ; Найти и обработать первую группу иконок в ресурсах
- invoke EnumResourceNames,[hSource],RT_GROUP_ICON,EnumResNameProc,NULL
- ; Выгрузить файл из памяти
- invoke FreeLibrary,[hSource]
- loc_exit:
Code (Assembler) : Убрать нумерацию
- proc EnumResNameProc hModule:DWORD, lpszType:DWORD, lpszName:DWORD, lParam:DWORD
- pusha
- invoke EnumResourceLanguages,[hModule],[lpszType],[lpszName],\
- EnumResLangProc,NULL
- popa
- ; Обрабатываем только первую группу иконок
- mov eax,FALSE
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ; Шаблон имени файла для отдельных иконок
- mask db 'extract_single_%ux%u_%ubit.ico',0
- szFile rb MAX_PATH
- dGIconNum dd ? ; Количество иконок в группе
- hResource dd ? ; Хэндл иконки
- iconX dd ? ; Размер иконки X
- iconY dd ? ; Размер иконки Y
- iconB dd ? ; Глубина цвета иконки
- dSize dd ? ; Размер данных изображения иконки
- lpRes dd ? ; Указатель на данные изображения иконки
- ; Заголовок файла иконки
- res_head ICONDIR
- res_data ICONDIRENTRY
- res_len = $-res_head
Code (Assembler) : Убрать нумерацию
- proc EnumResLangProc hModule:DWORD, lpszType:DWORD, lpszName:DWORD,\
- wIDLanguage:DWORD, lParam:DWORD
- pusha
- ; Найти группу иконок в ресурсах
- invoke FindResourceEx,[hModule],[lpszType],[lpszName],[wIDLanguage]
- or eax,eax
- ; Группа не найдена
- jz .loc_next
- ; Загрузить группу иконок
- invoke LoadResource,[hModule],eax
- or eax,eax
- ; Загрузить не удалось
- jz .loc_next
- ; Получить указатель на начало данных в памяти
- invoke LockResource,eax
- ; Проверить корректность группы
- mov esi,eax
- cmp word [esi+ICONDIR.idType],1
- jne .loc_next
- ; Получить количество иконок в группе
- movzx eax,word [esi+ICONDIR.idCount]
- or eax,eax
- ; Количество иконок нулевое
- jz .loc_next
- mov [dGIconNum],eax
- xor ebx,ebx
- ; Обработать отдельные иконки из группы
- add esi,sizeof.ICONDIR
- .loc_process_icon:
- ; Очистить память для новой иконки
- invoke RtlZeroMemory,res_head,res_len
- ; Идентификатор иконки в ресурсах
- movzx eax,word[esi+GRPICONDIRENTRY.nID]
- ; Найти ресурс иконки
- invoke FindResourceEx,[hModule],RT_ICON,eax,[wIDLanguage]
- or eax,eax
- jz .loc_next_icon
- mov [hResource],eax
- ; Получить размер данных
- invoke SizeofResource,[hModule],eax
- or eax,eax
- jz .loc_next_icon
- mov [dSize],eax
- ; Загрузить ресурс
- invoke LoadResource,[hModule],[hResource]
- or eax,eax
- jz .loc_next_icon
- ; Получить указатель на начало данных в памяти
- invoke LockResource,eax
- mov [lpRes],eax
- ; Сформировать заголовок файла для одиночной иконки
- mov [res_head.idReserved],0
- mov [res_head.idType],1
- mov [res_head.idCount],1
- ; Сформировать запись ICONDIRENTRY для одиночной иконки
- movzx eax,byte [esi+GRPICONDIRENTRY.bWidth]
- mov [iconX],eax
- mov [res_data.bWidth],al
- movzx eax,byte [esi+GRPICONDIRENTRY.bHeight]
- mov [iconY],eax
- mov [res_data.bHeight],al
- movzx eax,byte [esi+GRPICONDIRENTRY.bColorCount]
- mov [res_data.bColorCount],al
- mov [res_data.bReserved],0
- mov ax,[esi+GRPICONDIRENTRY.wPlanes]
- mov [res_data.wPlanes],ax
- movzx eax,word [esi+GRPICONDIRENTRY.wBitCount]
- mov [iconB],eax
- mov [res_data.wBitCount],ax
- ; Размер данных изображения
- mov eax,[dSize]
- mov [res_data.dwBytesInRes],eax
- ; Смещение данных сразу после заголовка файла
- mov [res_data.dwImageOffset],sizeof.ICONDIR+sizeof.ICONDIRENTRY
- ; Для иконки в PNG-формате размеры X и Y будут нулевыми
- cmp [iconX],0
- jne @f
- mov eax,[lpRes]
- ; Это точно PNG?
- cmp dword [eax],474E5089h
- jne .loc_next_icon
- cmp dword [eax+0Ch],'IHDR'
- jne .loc_next_icon
- ; Размер и глубина цвета из заголовка PNG
- mov edx,[eax+10h]
- bswap edx
- mov [iconX],edx
- mov edx,[eax+14h]
- bswap edx
- mov [iconY],edx
- movzx edx,byte [eax+18h]
- mov [iconB],edx
- @@:
- ; Сформировать имя файла
- invoke wsprintf,szFile,mask,[iconX],[iconY],[iconB]
- add esp,20
- .loc_write_file:
- ; Записать иконку в файл
- push ebx
- invoke _lcreat,szFile,0
- mov ebx,eax
- ; Записать заголовок иконки
- invoke _lwrite,ebx,res_head,sizeof.ICONDIR+sizeof.ICONDIRENTRY
- ; Записать данные из ресурсов
- invoke _lwrite,ebx,[lpRes],[dSize]
- invoke _lclose,ebx
- pop ebx
- .loc_next_icon:
- ; Следующая иконка в группе
- add esi,sizeof.GRPICONDIRENTRY
- inc ebx
- cmp ebx,[dGIconNum]
- jb .loc_process_icon
- .loc_next:
- popa
- mov eax,FALSE
- ret
- endp
Второй, более сложный случай - извлечение всех доступных иконок из главной группы иконок исполняемого файла в единый файл мультииконки. В сегменте данных понадобятся следующие переменные:
Code (Assembler) : Убрать нумерацию
- ; Максимально допустимое количество иконок в группе
- MAX_ICONS = 128
- ; Файл с иконками
- szFile db 'extract_multi.ico',0
- hFile dd ?
- dGIconNum dd ? ; Количество иконок в группе
- hResource dd ? ; Хэндл иконки
- pGroup dd ? ; Указатель на группу иконок в памяти
- dOffset dd ? ; Смещение данных от начала файла иконки
- dSize dd ? ; Размер данных изображения иконки
- lpRes dd ? ; Указатель на данные изображения иконки
- ; Заголовок файла иконок
- res_head ICONDIR
- res_data rb (sizeof.ICONDIRENTRY*MAX_ICONS)
- res_len = $-res_head
Code (Assembler) : Убрать нумерацию
- proc EnumResLangProc hModule:DWORD, lpszType:DWORD, lpszName:DWORD,\
- wIDLanguage:DWORD, lParam:DWORD
- pusha
- ; Найти группу иконок в ресурсах
- invoke FindResourceEx,[hModule],[lpszType],[lpszName],[wIDLanguage]
- or eax,eax
- ; Группа не найдена
- jz .loc_next
- ; Загрузить группу иконок
- invoke LoadResource,[hModule],eax
- or eax,eax
- ; Загрузить не удалось
- jz .loc_next
- ; Получить указатель на начало данных в памяти
- invoke LockResource,eax
- mov esi,eax
- mov [pGroup],eax
- ; Проверить корректность группы
- cmp word [esi+ICONDIR.idType],1
- jne .loc_next
- ; Получить количество иконок в группе
- movzx eax,word [esi+ICONDIR.idCount]
- or eax,eax
- ; Количество иконок нулевое
- jz .loc_next
- cmp eax,MAX_ICONS
- ; Количество иконок превышает максимально допустимое
- ja .loc_next
- mov [dGIconNum],eax
- ; Обработать отдельные иконки из группы
- add esi,sizeof.ICONDIR
- ; Очистить память для иконки
- invoke RtlZeroMemory,res_head,res_len
- ; Сформировать заголовок файла иконки
- mov [res_head.idReserved],0
- mov [res_head.idType],1
- mov [res_head.idCount],0
- mov edi,res_data
- .loc_process_icon:
- ; Сформировать запись ICONDIRENTRY для одной иконки
- movzx eax,word[esi+GRPICONDIRENTRY.nID]
- ; Найти ресурс иконки
- invoke FindResourceEx,[hModule],RT_ICON,eax,[wIDLanguage]
- or eax,eax
- jz .loc_next_icon
- mov [hResource],eax
- ; Получить размер данных
- invoke SizeofResource,[hModule],eax
- or eax,eax
- jz .loc_next_icon
- mov [dSize],eax
- ; Загрузить ресурс
- invoke LoadResource,[hModule],[hResource]
- or eax,eax
- jz .loc_next_icon
- ; Сформировать запись ICONDIRENTRY для одной иконки
- mov al,[esi+GRPICONDIRENTRY.bWidth]
- mov [edi+ICONDIRENTRY.bWidth],al
- mov al,[esi+GRPICONDIRENTRY.bHeight]
- mov [edi+ICONDIRENTRY.bHeight],al
- mov al,[esi+GRPICONDIRENTRY.bColorCount]
- mov [edi+ICONDIRENTRY.bColorCount],al
- mov ax,[esi+GRPICONDIRENTRY.wBitCount]
- mov [edi+ICONDIRENTRY.wBitCount],ax
- mov [edi+ICONDIRENTRY.bReserved],0
- mov ax,[esi+GRPICONDIRENTRY.wPlanes]
- mov [edi+ICONDIRENTRY.wPlanes],ax
- mov eax,[dOffset]
- mov [edi+ICONDIRENTRY.dwImageOffset],eax
- mov eax,[dSize]
- mov [edi+ICONDIRENTRY.dwBytesInRes],eax
- add [dOffset],eax
- ; Увеличить количество иконок в заголовке файла
- inc [res_head.idCount]
- ; Следующая структура ICONDIRENTRY
- add edi,sizeof.ICONDIRENTRY
- .loc_next_icon:
- ; Следующая иконка в группе
- add esi,sizeof.GRPICONDIRENTRY
- inc ebx
- cmp ebx,[dGIconNum]
- jb .loc_process_icon
- ; Хоть одна иконка нашлась?
- cmp [res_head.idCount],0
- je .loc_next
- ; Создать файл иконки
- invoke _lcreat,szFile,0
- mov [hFile],eax
- ; Вычислить размер получившегося заголовка для мульти-иконки
- movzx eax,word [res_head.idCount]
- xor edx,edx
- mov ecx,sizeof.ICONDIRENTRY
- mul ecx
- add eax,sizeof.ICONDIR
- ; Скорректировать смещение данных с учетом размера заголовка
- xor ebx,ebx
- movzx ecx,word [res_head.idCount]
- mov edi,res_data
- @@:
- add [edi+ICONDIRENTRY.dwImageOffset],eax
- add edi,sizeof.ICONDIRENTRY
- inc ebx
- cmp ebx,ecx
- jne @b
- ; Записать в файл заголовок иконки
- invoke _lwrite,[hFile],res_head,eax
- ; Записать в файл отдельные иконки из группы
- mov esi,[pGroup]
- add esi,sizeof.ICONDIR
- xor ebx,ebx
- .loc_save_icon:
- movzx eax,word[esi+GRPICONDIRENTRY.nID]
- ; Найти ресурс иконки
- invoke FindResourceEx,[hModule],RT_ICON,eax,[wIDLanguage]
- or eax,eax
- jz .loc_next_save
- mov [hResource],eax
- ; Получить размер данных
- invoke SizeofResource,[hModule],eax
- or eax,eax
- jz .loc_next_save
- mov [dSize],eax
- ; Загрузить ресурс
- invoke LoadResource,[hModule],[hResource]
- or eax,eax
- jz .loc_next_save
- ; Получить указатель на начало данных в памяти
- invoke LockResource,eax
- mov [lpRes],eax
- ; Записать данные из ресурсов
- invoke _lwrite,[hFile],[lpRes],[dSize]
- .loc_next_save:
- add esi,sizeof.GRPICONDIRENTRY
- inc ebx
- cmp ebx,[dGIconNum]
- jb .loc_save_icon
- invoke _lclose,[hFile]
- .loc_next:
- popa
- mov eax,FALSE
- ret
- endp
При необходимости все приведенные выше функции можно легко модифицировать, чтобы извлекать не только первую иконку, но и все иконки из файла. А взяв за основу этот код, также можно сделать собственный экстрактор ресурсов типа Resource Ripper. На этом тему работы с иконками исполняемых файлов считаю полностью раскрытой.
В приложении примеры программ с исходными текстами. Одна из них извлекает иконки из ресурсов исполняемого файла в отдельные файлы, а вторая создает файл с мультииконкой.
Просмотров: 2534 | Комментариев: 4
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(19.05.2018 в 15:23):
Сергей Озеров, рад был помочь!
Сергей Озеров
(18.05.2018 в 16:28):
Это я спрашивал :) Больщущее спасибо !!!!
ManHunter
(18.05.2018 в 15:40):
Вопрос заключается не в том, ЧЕМ извлечь иконки, а КАК. Программ по извлечению я и сам могу пару десятков накидать, но речь тут вовсе не о них.
by matrixa
(18.05.2018 в 15:28):
А не проще ли через тот же UniExtractor?
Добавить комментарий
Заполните форму для добавления комментария