Blog. Just Blog

Извлечение главной иконки из исполняемого файла

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Извлечение главной иконки из исполняемого файла
Извлечение главной иконки из исполняемого файла

В комментариях к недавней статье про замену главной иконки исполняемого файла задали очень правильный вопрос: как извлечь иконки из ресурсов исполняемого файла? Сегодня я постараюсь подробно рассказать вам, как это делается.

Начнем с описания структур, необходимых для работы с иконками. Они уже были в прошлых статьях. Так как мы будем использовать ресурсы и файлы иконок, нам понадобятся они все.
  1. ; Структуры для работы с иконками в группе
  2. struct ICONDIR
  3.     idReserved dw ? ; Зарезервировано (должно быть 0)
  4.     idType     dw ? ; Тип ресурса (1 для иконок)
  5.     idCount    dw ? ; Количество изображений в иконке
  6. ends
  7.  
  8. struct GRPICONDIRENTRY
  9.     bWidth        db ? ; Ширина изображения в пикселах
  10.     bHeight       db ? ; Высота изображения в пикселах
  11.     bColorCount   db ? ; Количество цветов
  12.     bReserved     db ? ; Зарезервировано
  13.     wPlanes       dw ? ; Зарезервировано
  14.     wBitCount     dw ? ; Зарезервировано
  15.     dwBytesInRes  dd ? ; Размер данных изображения
  16.     nID           dw ? ; ID иконки в ресурсах
  17. ends
  18.  
  19. struct ICONDIRENTRY
  20.     bWidth        db ? ; Ширина изображения в пикселах
  21.     bHeight       db ? ; Высота изображения в пикселах
  22.     bColorCount   db ? ; Количество цветов
  23.     bReserved     db ? ; Зарезервировано
  24.     wPlanes       dw ? ; Зарезервировано
  25.     wBitCount     dw ? ; Зарезервировано
  26.     dwBytesInRes  dd ? ; Размер данных изображения
  27.     dwImageOffset dd ? ; Смещение данных относительно начала файла
  28. ends
Начальные действия похожи на действия при замене иконок. Сперва загружаем исходный исполняемый файл в память перебираем в нем все ресурсы с типом RT_GROUP_ICON, то есть группы иконок. После обработки файл из памяти выгружается. Тут проблем быть не должно.
  1.         ; Загрузить исходный файл в память
  2.         invoke  LoadLibrary,szSource
  3.         or      eax,eax
  4.         jz      loc_exit
  5.  
  6.         ; Хэндл загруженного исходного файла
  7.         mov     [hSource],eax
  8.  
  9.         ; Найти и обработать первую группу иконок в ресурсах
  10.         invoke  EnumResourceNames,[hSource],RT_GROUP_ICON,EnumResNameProc,NULL
  11.  
  12.         ; Выгрузить файл из памяти
  13.         invoke  FreeLibrary,[hSource]
  14.  
  15. loc_exit:
Для каждой найденной группы иконок надо обработать доступные языки. Как я уже писал в предыдущей статье, для каждого языка в ресурсах может быть своя группа иконок. Поэтому надо перебрать их поочередно, но извлекать иконки будем все равно только из первого доступного.
  1. proc EnumResNameProc hModule:DWORD, lpszType:DWORD, lpszName:DWORD, lParam:DWORD
  2.         pusha
  3.         invoke  EnumResourceLanguages,[hModule],[lpszType],[lpszName],\
  4.                 EnumResLangProc,NULL
  5.         popa
  6.         ; Обрабатываем только первую группу иконок
  7.         mov     eax,FALSE
  8.         ret
  9. endp
Когда мы получим доступ к группе иконок в ресурсах, у нас будет два варианта их извлечения: записать каждую иконку в отдельный файл или сформировать единый файл мультииконки, куда будут записаны все иконки из ресурсов. Начнем с самого простого - извлечение иконок в отдельные файлы. Для этого нам в сегменте данных понадобятся следующие переменные:
  1. ; Шаблон имени файла для отдельных иконок
  2. mask      db 'extract_single_%ux%u_%ubit.ico',0
  3. szFile    rb MAX_PATH
  4.  
  5. dGIconNum dd ? ; Количество иконок в группе
  6. hResource dd ? ; Хэндл иконки
  7.  
  8. iconX     dd ? ; Размер иконки X
  9. iconY     dd ? ; Размер иконки Y
  10. iconB     dd ? ; Глубина цвета иконки
  11.  
  12. dSize     dd ? ; Размер данных изображения иконки
  13. lpRes     dd ? ; Указатель на данные изображения иконки
  14.  
  15. ; Заголовок файла иконки
  16. res_head ICONDIR
  17. res_data ICONDIRENTRY
  18. res_len  = $-res_head
Для большей наглядности каждая отдельная иконка будет сохраняться в файл, в имени которого содержится размер иконки и глубина цвета. Осталось все это воплотить в коде:
  1. proc EnumResLangProc hModule:DWORD, lpszType:DWORD, lpszName:DWORD,\
  2.         wIDLanguage:DWORD, lParam:DWORD
  3.         pusha
  4.  
  5.         ; Найти группу иконок в ресурсах
  6.         invoke  FindResourceEx,[hModule],[lpszType],[lpszName],[wIDLanguage]
  7.         or      eax,eax
  8.         ; Группа не найдена
  9.         jz      .loc_next
  10.  
  11.         ; Загрузить группу иконок
  12.         invoke  LoadResource,[hModule],eax
  13.         or      eax,eax
  14.         ; Загрузить не удалось
  15.         jz      .loc_next
  16.  
  17.         ; Получить указатель на начало данных в памяти
  18.         invoke  LockResource,eax
  19.  
  20.         ; Проверить корректность группы
  21.         mov     esi,eax
  22.         cmp     word [esi+ICONDIR.idType],1
  23.         jne     .loc_next
  24.  
  25.         ; Получить количество иконок в группе
  26.         movzx   eax,word [esi+ICONDIR.idCount]
  27.         or      eax,eax
  28.         ; Количество иконок нулевое
  29.         jz      .loc_next
  30.  
  31.         mov     [dGIconNum],eax
  32.         xor     ebx,ebx
  33.  
  34.         ; Обработать отдельные иконки из группы
  35.         add     esi,sizeof.ICONDIR
  36. .loc_process_icon:
  37.         ; Очистить память для новой иконки
  38.         invoke  RtlZeroMemory,res_head,res_len
  39.  
  40.         ; Идентификатор иконки в ресурсах
  41.         movzx   eax,word[esi+GRPICONDIRENTRY.nID]
  42.  
  43.         ; Найти ресурс иконки
  44.         invoke  FindResourceEx,[hModule],RT_ICON,eax,[wIDLanguage]
  45.         or      eax,eax
  46.         jz      .loc_next_icon
  47.         mov     [hResource],eax
  48.  
  49.         ; Получить размер данных
  50.         invoke  SizeofResource,[hModule],eax
  51.         or      eax,eax
  52.         jz      .loc_next_icon
  53.         mov     [dSize],eax
  54.  
  55.         ; Загрузить ресурс
  56.         invoke  LoadResource,[hModule],[hResource]
  57.         or      eax,eax
  58.         jz      .loc_next_icon
  59.  
  60.         ; Получить указатель на начало данных в памяти
  61.         invoke  LockResource,eax
  62.         mov     [lpRes],eax
  63.  
  64.         ; Сформировать заголовок файла для одиночной иконки
  65.         mov     [res_head.idReserved],0
  66.         mov     [res_head.idType],1
  67.         mov     [res_head.idCount],1
  68.  
  69.         ; Сформировать запись ICONDIRENTRY для одиночной иконки
  70.         movzx   eax,byte [esi+GRPICONDIRENTRY.bWidth]
  71.         mov     [iconX],eax
  72.         mov     [res_data.bWidth],al
  73.         movzx   eax,byte [esi+GRPICONDIRENTRY.bHeight]
  74.         mov     [iconY],eax
  75.         mov     [res_data.bHeight],al
  76.         movzx   eax,byte [esi+GRPICONDIRENTRY.bColorCount]
  77.         mov     [res_data.bColorCount],al
  78.         mov     [res_data.bReserved],0
  79.         mov     ax,[esi+GRPICONDIRENTRY.wPlanes]
  80.         mov     [res_data.wPlanes],ax
  81.         movzx   eax,word [esi+GRPICONDIRENTRY.wBitCount]
  82.         mov     [iconB],eax
  83.         mov     [res_data.wBitCount],ax
  84.         ; Размер данных изображения
  85.         mov     eax,[dSize]
  86.         mov     [res_data.dwBytesInRes],eax
  87.         ; Смещение данных сразу после заголовка файла
  88.         mov     [res_data.dwImageOffset],sizeof.ICONDIR+sizeof.ICONDIRENTRY
  89.  
  90.         ; Для иконки в PNG-формате размеры X и Y будут нулевыми
  91.         cmp     [iconX],0
  92.         jne     @f
  93.  
  94.         mov     eax,[lpRes]
  95.         ; Это точно PNG?
  96.         cmp     dword [eax],474E5089h
  97.         jne     .loc_next_icon
  98.         cmp     dword [eax+0Ch],'IHDR'
  99.         jne     .loc_next_icon
  100.         ; Размер и глубина цвета из заголовка PNG
  101.         mov     edx,[eax+10h]
  102.         bswap   edx
  103.         mov     [iconX],edx
  104.         mov     edx,[eax+14h]
  105.         bswap   edx
  106.         mov     [iconY],edx
  107.         movzx   edx,byte [eax+18h]
  108.         mov     [iconB],edx
  109. @@:
  110.         ; Сформировать имя файла
  111.         invoke  wsprintf,szFile,mask,[iconX],[iconY],[iconB]
  112.         add     esp,20
  113.  
  114. .loc_write_file:
  115.         ; Записать иконку в файл
  116.         push    ebx
  117.         invoke  _lcreat,szFile,0
  118.         mov     ebx,eax
  119.         ; Записать заголовок иконки
  120.         invoke  _lwrite,ebx,res_head,sizeof.ICONDIR+sizeof.ICONDIRENTRY
  121.         ; Записать данные из ресурсов
  122.         invoke  _lwrite,ebx,[lpRes],[dSize]
  123.         invoke  _lclose,ebx
  124.         pop     ebx
  125.  
  126. .loc_next_icon:
  127.         ; Следующая иконка в группе
  128.         add     esi,sizeof.GRPICONDIRENTRY
  129.         inc     ebx
  130.         cmp     ebx,[dGIconNum]
  131.         jb      .loc_process_icon
  132.  
  133. .loc_next:
  134.         popa
  135.         mov     eax,FALSE
  136.         ret
  137. endp
Теперь поясню словами, что тут происходит. Сперва группа иконок, извлеченная из ресурсов, проверяется на корректность. То есть idType в структуре ICONDIR должен равняться 1, а количество иконок idCount должно быть не меньше 1. Максимальное количество иконок в группе не проверяется, так как никакой памяти под них заранее не резервируется. Дальше поочередно из группы читается структура GRPICONDIRENTRY для каждой иконки, определяется размер графических данных иконки и их местонахождение в памяти. Все недоступные или поврежденные ресурсы игнорируются. Затем формируется заголовок файла иконки ICONDIR, информация из GRPICONDIRENTRY дублируется в ICONDIRENTRY и полный заголовок записывается в файл иконки, после чего к нему дописываются графические данные. Поскольку мы формируем имя файла иконки с учетом размеров и глубины цвета иконки, надо учитывать симтуации, когда в качестве иконки используется изображение в формате PNG размером 256х256 пикселов. В этом случае поля структур bWidth и bHeight будут нулевыми. Выполняется дополнительная проверка, и для PNG-файлов информация о размере изображения извлекается напрямую из файла. Если интересно, откуда что берется, подробности о внутреннем формате PNG вы можете почитать в официальной документации.

Второй, более сложный случай - извлечение всех доступных иконок из главной группы иконок исполняемого файла в единый файл мультииконки. В сегменте данных понадобятся следующие переменные:
  1. ; Максимально допустимое количество иконок в группе
  2. MAX_ICONS = 128
  3.  
  4. ; Файл с иконками
  5. szFile    db 'extract_multi.ico',0
  6. hFile     dd ?
  7.  
  8. dGIconNum dd ? ; Количество иконок в группе
  9. hResource dd ? ; Хэндл иконки
  10. pGroup    dd ? ; Указатель на группу иконок в памяти
  11.  
  12. dOffset   dd ? ; Смещение данных от начала файла иконки
  13. dSize     dd ? ; Размер данных изображения иконки
  14. lpRes     dd ? ; Указатель на данные изображения иконки
  15.  
  16. ; Заголовок файла иконок
  17. res_head ICONDIR
  18. res_data rb (sizeof.ICONDIRENTRY*MAX_ICONS)
  19. res_len  = $-res_head
Код получился более объемный, но это сделано для повышения его отказоустойчивости при обработке поврежденных или недоступных ресурсов.
  1. proc EnumResLangProc hModule:DWORD, lpszType:DWORD, lpszName:DWORD,\
  2.         wIDLanguage:DWORD, lParam:DWORD
  3.  
  4.         pusha
  5.  
  6.         ; Найти группу иконок в ресурсах
  7.         invoke  FindResourceEx,[hModule],[lpszType],[lpszName],[wIDLanguage]
  8.         or      eax,eax
  9.         ; Группа не найдена
  10.         jz      .loc_next
  11.  
  12.         ; Загрузить группу иконок
  13.         invoke  LoadResource,[hModule],eax
  14.         or      eax,eax
  15.         ; Загрузить не удалось
  16.         jz      .loc_next
  17.  
  18.         ; Получить указатель на начало данных в памяти
  19.         invoke  LockResource,eax
  20.         mov     esi,eax
  21.         mov     [pGroup],eax
  22.  
  23.         ; Проверить корректность группы
  24.         cmp     word [esi+ICONDIR.idType],1
  25.         jne     .loc_next
  26.  
  27.         ; Получить количество иконок в группе
  28.         movzx   eax,word [esi+ICONDIR.idCount]
  29.         or      eax,eax
  30.         ; Количество иконок нулевое
  31.         jz      .loc_next
  32.         cmp     eax,MAX_ICONS
  33.         ; Количество иконок превышает максимально допустимое
  34.         ja      .loc_next
  35.  
  36.         mov     [dGIconNum],eax
  37.  
  38.         ; Обработать отдельные иконки из группы
  39.         add     esi,sizeof.ICONDIR
  40.  
  41.         ; Очистить память для иконки
  42.         invoke  RtlZeroMemory,res_head,res_len
  43.  
  44.         ; Сформировать заголовок файла иконки
  45.         mov     [res_head.idReserved],0
  46.         mov     [res_head.idType],1
  47.         mov     [res_head.idCount],0
  48.  
  49.         mov     edi,res_data
  50.  
  51. .loc_process_icon:
  52.         ; Сформировать запись ICONDIRENTRY для одной иконки
  53.         movzx   eax,word[esi+GRPICONDIRENTRY.nID]
  54.  
  55.         ; Найти ресурс иконки
  56.         invoke  FindResourceEx,[hModule],RT_ICON,eax,[wIDLanguage]
  57.         or      eax,eax
  58.         jz      .loc_next_icon
  59.         mov     [hResource],eax
  60.  
  61.         ; Получить размер данных
  62.         invoke  SizeofResource,[hModule],eax
  63.         or      eax,eax
  64.         jz      .loc_next_icon
  65.         mov     [dSize],eax
  66.  
  67.         ; Загрузить ресурс
  68.         invoke  LoadResource,[hModule],[hResource]
  69.         or      eax,eax
  70.         jz      .loc_next_icon
  71.  
  72.         ; Сформировать запись ICONDIRENTRY для одной иконки
  73.         mov     al,[esi+GRPICONDIRENTRY.bWidth]
  74.         mov     [edi+ICONDIRENTRY.bWidth],al
  75.         mov     al,[esi+GRPICONDIRENTRY.bHeight]
  76.         mov     [edi+ICONDIRENTRY.bHeight],al
  77.         mov     al,[esi+GRPICONDIRENTRY.bColorCount]
  78.         mov     [edi+ICONDIRENTRY.bColorCount],al
  79.         mov     ax,[esi+GRPICONDIRENTRY.wBitCount]
  80.         mov     [edi+ICONDIRENTRY.wBitCount],ax
  81.         mov     [edi+ICONDIRENTRY.bReserved],0
  82.         mov     ax,[esi+GRPICONDIRENTRY.wPlanes]
  83.         mov     [edi+ICONDIRENTRY.wPlanes],ax
  84.         mov     eax,[dOffset]
  85.         mov     [edi+ICONDIRENTRY.dwImageOffset],eax
  86.         mov     eax,[dSize]
  87.         mov     [edi+ICONDIRENTRY.dwBytesInRes],eax
  88.         add     [dOffset],eax
  89.  
  90.         ; Увеличить количество иконок в заголовке файла
  91.         inc     [res_head.idCount]
  92.  
  93.         ; Следующая структура ICONDIRENTRY
  94.         add     edi,sizeof.ICONDIRENTRY
  95. .loc_next_icon:
  96.         ; Следующая иконка в группе
  97.         add     esi,sizeof.GRPICONDIRENTRY
  98.         inc     ebx
  99.         cmp     ebx,[dGIconNum]
  100.         jb      .loc_process_icon
  101.  
  102.         ; Хоть одна иконка нашлась?
  103.         cmp     [res_head.idCount],0
  104.         je      .loc_next
  105.  
  106.         ; Создать файл иконки
  107.         invoke  _lcreat,szFile,0
  108.         mov     [hFile],eax
  109.  
  110.         ; Вычислить размер получившегося заголовка для мульти-иконки
  111.         movzx   eax,word [res_head.idCount]
  112.         xor     edx,edx
  113.         mov     ecx,sizeof.ICONDIRENTRY
  114.         mul     ecx
  115.         add     eax,sizeof.ICONDIR
  116.  
  117.         ; Скорректировать смещение данных с учетом размера заголовка
  118.         xor     ebx,ebx
  119.         movzx   ecx,word [res_head.idCount]
  120.         mov     edi,res_data
  121. @@:
  122.         add     [edi+ICONDIRENTRY.dwImageOffset],eax
  123.         add     edi,sizeof.ICONDIRENTRY
  124.         inc     ebx
  125.         cmp     ebx,ecx
  126.         jne     @b
  127.  
  128.         ; Записать в файл заголовок иконки
  129.         invoke  _lwrite,[hFile],res_head,eax
  130.  
  131.         ; Записать в файл отдельные иконки из группы
  132.         mov     esi,[pGroup]
  133.         add     esi,sizeof.ICONDIR
  134.  
  135.         xor     ebx,ebx
  136. .loc_save_icon:
  137.         movzx   eax,word[esi+GRPICONDIRENTRY.nID]
  138.  
  139.         ; Найти ресурс иконки
  140.         invoke  FindResourceEx,[hModule],RT_ICON,eax,[wIDLanguage]
  141.         or      eax,eax
  142.         jz      .loc_next_save
  143.         mov     [hResource],eax
  144.  
  145.         ; Получить размер данных
  146.         invoke  SizeofResource,[hModule],eax
  147.         or      eax,eax
  148.         jz      .loc_next_save
  149.         mov     [dSize],eax
  150.  
  151.         ; Загрузить ресурс
  152.         invoke  LoadResource,[hModule],[hResource]
  153.         or      eax,eax
  154.         jz      .loc_next_save
  155.  
  156.         ; Получить указатель на начало данных в памяти
  157.         invoke  LockResource,eax
  158.         mov     [lpRes],eax
  159.  
  160.         ; Записать данные из ресурсов
  161.         invoke  _lwrite,[hFile],[lpRes],[dSize]
  162.  
  163. .loc_next_save:
  164.         add     esi,sizeof.GRPICONDIRENTRY
  165.         inc     ebx
  166.         cmp     ebx,[dGIconNum]
  167.         jb      .loc_save_icon
  168.  
  169.         invoke  _lclose,[hFile]
  170.  
  171. .loc_next:
  172.         popa
  173.         mov     eax,FALSE
  174.         ret
  175. endp
Как и в предыдущем случае, подробно поясню, как и что тут делается. Проверка корректности группы выполняется почти так же, как и в случае с одиночными иконками, за исключением дополнительной проверки на максимально возможное количество иконок. А вот дальнейшая обработка выполняется в несколько этапов: сперва формируется полный заголовок мультииконки, затем к ней дописывается графическая информация всех доступных иконок. При формировании заголовка мультииконки, поля dwImageOffset в структурах ICONDIRENTRY первоначально формируются только с учетом размеров графических данных иконок, так как мы не знаем, какой размер заголовка получится в итоге. После формирования заголовка к этим значениям прибавляется размер полученного заголовка. Память под заголовок приходится резервировать заранее, поэтому и добавлена проверка на максимально возможное количество иконок, в моем случае это 128. Зато не надо выполнять проверку на PNG, они обрабатываются точно так же, как и обычные иконки. Почему нельзя просто скопировать всю информацию из GRPICONDIRENTRY в ICONDIRENTRY? Даже если мы узнаем количество иконок из поля idCount заголовка группы, это ни в коем случае не гарантирует, что все они доступны и могут быть загружены из ресурсов. Это утверждение справедливо, например, для упакованных файлов. Именно поэтому сперва выполняется проверка доступности всех ресурсов, и в мультииконку попадают только те, которые фактически присутствуют в исходном файле.

При необходимости все приведенные выше функции можно легко модифицировать, чтобы извлекать не только первую иконку, но и все иконки из файла. А взяв за основу этот код, также можно сделать собственный экстрактор ресурсов типа Resource Ripper. На этом тему работы с иконками исполняемых файлов считаю полностью раскрытой.

В приложении примеры программ с исходными текстами. Одна из них извлекает иконки из ресурсов исполняемого файла в отдельные файлы, а вторая создает файл с мультииконкой.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

Extract.Icons.Demo.zip (33,638 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 2534 | Комментариев: 4

Метки: Assembler, иконки
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
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?

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0079 сек.) / Память: 4.5 Mb
Наверх