Extraction d’icônes sous Windows CE et Windows Mobile

20 avril 2009 04:29 par Jean-Michel Guemguem

Windows CE (et Windows Mobile) n’expose pas toutes les API de manipulation des ressources d’un fichier exécutable ou d’une dll. Pour pouvoir en extraire les icônes, il faut réaliser l’ensemble du travail d’extraction “à la main”.

Les fichiers exécutables sous Windows CE suivent le format de fichier Microsoft Portable Exécutable (PE). Les spécifications de ce format sont disponibles ici. Une description du format de fichier Ico est disponible ici.

Comme l’indique les spécifications, les icônes sont stockées dans une section dédiée aux ressources de l’exécutable. Les ressources sont stockées dans une arborescence, avec deux types de branche pour la gestion des icônes:

  • RT_GROUP_ICON: Contient les informations sur les icônes (Nb d’images, Taille, …)
  • RT_ICON: Contient les images des icônes

Pour naviguer à travers cette arborescence et simplifier l’utilisation des adresses virtuelles relatives, le plus efficace est de mapper en mémoire le fichier exécutable:

   1: // Ouverture du fichier en lecture
   2: m_hFile = CreateFile(m_wsFileNameToExtract.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);                    
   3: if (INVALID_HANDLE_VALUE == m_hFile)
   4: {
   5:     m_wsMessage = L"Ouverture impossible du fichier";
   6:     return false;
   7: }
   8:  
   9: // Fichier en mémoire partagée
  10: m_hFileMapping = CreateFileMapping(m_hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  11: if (0 == m_hFileMapping)
  12: {
  13:     CloseHandle(m_hFile);
  14:     m_wsMessage = L"Mappage impossible du fichier";
  15:     return false;
  16: }    
  17:  
  18: // Mappe le fichier dans le process
  19: m_pMapViewOfFile = (PBYTE)MapViewOfFile(m_hFileMapping, FILE_MAP_READ, 0, 0, 0);
  20: if (0 == m_pMapViewOfFile)
  21: {
  22:     CloseHandle(m_hFileMapping);
  23:     CloseHandle(m_hFile);
  24:     m_wsMessage = L"Mappage impossible du fichier";
  25:     return false;
  26: }

Ensuite il faut rechercher la section dédiée aux ressources

   1: // Entête DOS
   2: PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)m_pMapViewOfFile;
   3: // Pas une signature DOS ?
   4: if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic)
   5: {
   6:     m_wsMessage = L"Le fichier n'est pas un fichier executable ou une librairie";
   7:     return false;
   8: }
   9:  
  10: // Entete NT du fichier PE 
  11: PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR) m_pMapViewOfFile + (DWORD_PTR) pDosHeader->e_lfanew);
  12:  
  13: // Adresse virtuelle des ressources
  14: m_dVirtualAddressResource = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
  15: if (!m_dVirtualAddressResource)
  16: {
  17:     m_wsMessage = L"Le fichier ne contient pas de ressource";
  18:     return false;
  19: }
  20:  
  21: // Recherche la section gérant les ressources
  22: PIMAGE_SECTION_HEADER pResourceSectionHeader = 0;
  23: PIMAGE_SECTION_HEADER pFirstSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
  24:  
  25: for (WORD numSection = 0; numSection < pNtHeader->FileHeader.NumberOfSections; numSection++)
  26: {
  27:     // Adresse virtuelle de la section ?
  28:     if (m_dVirtualAddressResource == (pFirstSectionHeader + numSection)->VirtualAddress)
  29:     {
  30:         pResourceSectionHeader = pFirstSectionHeader + numSection;
  31:         break;
  32:     }
  33: }
  34:  
  35: // Pas de section gérant les ressources ?
  36: if (0 == pResourceSectionHeader)
  37: {
  38:     m_wsMessage = L"Le fichier ne contient pas de ressource";
  39:     return false;
  40: }
  41:  
  42: // Emplacement de l'arborescence des ressources
  43: m_pResourceDirectory = m_pMapViewOfFile + m_dVirtualAddressResource - pResourceSectionHeader->VirtualAddress + pResourceSectionHeader->PointerToRawData;
  44: if (!m_pResourceDirectory)
  45: {
  46:     m_wsMessage = L"Le fichier ne contient pas de ressource";
  47:     return false;
  48: }

A partir de la section, il faut parcourir l’arborescence en quête des branches de groupes d’icônes

   1: // Racine de l'arborescence des ressources
   2: PIMAGE_RESOURCE_DIRECTORY pResourceDirectoryRacine = (PIMAGE_RESOURCE_DIRECTORY)m_pResourceDirectory;
   3:  
   4: // Première entrée pour la recherche d'icône
   5: PIMAGE_RESOURCE_DIRECTORY_ENTRY pResourceDirectoryEntryRacine = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResourceDirectoryRacine+1);
   6:  
   7: // Parcours les branches du premier niveau à la recherche des icônes
   8: for (WORD numEntry = 0; numEntry < (pResourceDirectoryRacine->NumberOfNamedEntries + pResourceDirectoryRacine->NumberOfIdEntries); numEntry++, pResourceDirectoryEntryRacine++)
   9: {
  10:     // Branche contenant des icones ?
  11:     if ((DWORD)RT_GROUP_ICON == pResourceDirectoryEntryRacine->Name)
  12:     {
  13:         // Extrait toutes les icônes de la branche
  14:         int iNbIcone = ExtractAllIcones((PIMAGE_RESOURCE_DIRECTORY)((pResourceDirectoryEntryRacine->OffsetToData & 0x7FFFFFFF) + m_pResourceDirectory));
  15:  
  16:         // Erreur ?
  17:         if (-1 == iNbIcone)
  18:         {
  19:             iNbTotalIcones = -1;
  20:             break;
  21:         }
  22:         iNbTotalIcones += iNbIcone;
  23:     }
  24: }

 

 

 

Et enfin pour chaque icône, générer son fichier équivalent au format ICO

   1: PIMAGE_RESOURCE_DATA_ENTRY pResourceDataGroupIcon = (PIMAGE_RESOURCE_DATA_ENTRY)((DWORD_PTR)(m_pResourceDirectory) + (DWORD_PTR)(pResourceEntryGroupIcon->OffsetToData));
   2:  
   3: // Entete de l'icône
   4: LPGRPICONDIR pGrpIconDir = (LPGRPICONDIR)(m_pResourceDirectory + pResourceDataGroupIcon->OffsetToData - m_dVirtualAddressResource);
   5:  
   6: // Nom du fichier icône <= ID de la ressource + _ + CodePage
   7: std::wostringstream wossNameIcon;
   8: wossNameIcon << wId << L"_" << pResourceEntryGroupIcon->Id << L".ico";
   9:  
  10: // Début de la génération du fichier icône
  11: std::ofstream ofsIcon(wossNameIcon.str().c_str(), std::ios_base::binary);
  12: if (!ofsIcon)
  13: {
  14:     m_wsMessage = L"Création du fichier icône impossible";
  15:     return -1;
  16: }
  17:  
  18: try
  19: {
  20:     // Insere l'entete du fichier icône
  21:     ofsIcon.write((char*)&pGrpIconDir->idReserved, sizeof(pGrpIconDir->idReserved));
  22:     ofsIcon.write((char*)&pGrpIconDir->idType, sizeof(pGrpIconDir->idType));
  23:     ofsIcon.write((char*)&pGrpIconDir->idCount, sizeof(pGrpIconDir->idCount));
  24:  
  25:     // Emplacement de la première image 
  26:     DWORD dImageOffset = 6 + 16 * pGrpIconDir->idCount;
  27:  
  28:     // Insere les infos de chaque image de l'icône
  29:     LPGRPICONDIRENTRY pGrpIconDireEntry = (LPGRPICONDIRENTRY) (pGrpIconDir->idEntries);
  30:     for (WORD numImage = 0; numImage < pGrpIconDir->idCount; numImage++, pGrpIconDireEntry++)
  31:     {
  32:         ofsIcon.write((char*)&pGrpIconDireEntry->bWidth, sizeof(pGrpIconDireEntry->bWidth));
  33:         ofsIcon.write((char*)&pGrpIconDireEntry->bHeight, sizeof(pGrpIconDireEntry->bHeight));
  34:         ofsIcon.write((char*)&pGrpIconDireEntry->bColorCount, sizeof(pGrpIconDireEntry->bColorCount));
  35:         ofsIcon.write((char*)&pGrpIconDireEntry->bReserved, sizeof(pGrpIconDireEntry->bReserved));
  36:         ofsIcon.write((char*)&pGrpIconDireEntry->wPlanes, sizeof(pGrpIconDireEntry->wPlanes));
  37:         ofsIcon.write((char*)&pGrpIconDireEntry->wBitCount, sizeof(pGrpIconDireEntry->wBitCount));
  38:         ofsIcon.write((char*)&pGrpIconDireEntry->dwBytesInRes, sizeof(pGrpIconDireEntry->dwBytesInRes));
  39:         ofsIcon.write((char*)&dImageOffset, sizeof(DWORD));
  40:         
  41:         // Emplacement de la prochaine image
  42:         dImageOffset += pGrpIconDireEntry->dwBytesInRes;
  43:     }
  44:     
  45:     iRetour = 1;
  46:  
  47:     // Insere chaque image de l'icône
  48:     pGrpIconDireEntry = (LPGRPICONDIRENTRY) (pGrpIconDir->idEntries);
  49:     for (WORD numImage = 0; numImage < pGrpIconDir->idCount; numImage++, pGrpIconDireEntry++)
  50:     {
  51:         if (!WriteImageIcon(ofsIcon, pGrpIconDireEntry->nID))
  52:         {
  53:             m_wsMessage = L"Insertion d'image impossible";
  54:             iRetour = -1;
  55:         }
  56:     }
  57: }
  58: catch (...)
  59: {
  60:     m_wsMessage = L"Insertion dans le fichier icône impossible";
  61:     iRetour= -1;
  62: }
  63:  
  64: // Libère le fichier
  65: ofsIcon.close();
  66:  
  67: ....
  68:  
  69: bool CExtractResourceFromPEFile::WriteImageIcon(std::ofstream &ofsIcon, WORD dIdImage)
  70: {
  71:     // Racine de l'arborescence des ressources
  72:     PIMAGE_RESOURCE_DIRECTORY pResourceDirectoryRacine = (PIMAGE_RESOURCE_DIRECTORY)m_pResourceDirectory;
  73:  
  74:     // Premiere entrée pour la recherche des images de l'icône
  75:     PIMAGE_RESOURCE_DIRECTORY_ENTRY pResourceEntryRacineRechercheImageIcone = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResourceDirectoryRacine + 1);
  76:  
  77:     // Recherche la branche contenant les images des icônes (dans le premier niveau de l'arborescence des ressources)
  78:     for (WORD numEntry1 = 0; numEntry1 < (pResourceDirectoryRacine->NumberOfNamedEntries + pResourceDirectoryRacine->NumberOfIdEntries); numEntry1++, pResourceEntryRacineRechercheImageIcone++)
  79:     {
  80:         // Branche d'image d'icône ?
  81:         if ((DWORD)RT_ICON == pResourceEntryRacineRechercheImageIcone->Name)
  82:         {
  83:             // Branche des images d'icônes
  84:             PIMAGE_RESOURCE_DIRECTORY pResourceDirectoryImageIcons = (PIMAGE_RESOURCE_DIRECTORY)((pResourceEntryRacineRechercheImageIcone->OffsetToData & 0x7FFFFFFF) + m_pResourceDirectory);
  85:  
  86:             // Recherche l'image correspondant à l'icône
  87:             PIMAGE_RESOURCE_DIRECTORY_ENTRY pResourceEntryImageIcons = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResourceDirectoryImageIcons + 1);
  88:             for (WORD numImageIcon = 0; numImageIcon < (pResourceDirectoryImageIcons->NumberOfNamedEntries + pResourceDirectoryImageIcons->NumberOfIdEntries); numImageIcon++, pResourceEntryImageIcons++)
  89:             {
  90:                 // Image de l'icône ?
  91:                 if (dIdImage == pResourceEntryImageIcons->Id)
  92:                 {
  93:                     // Branche d'une image d'icône
  94:                     PIMAGE_RESOURCE_DIRECTORY pResourceDirectoryImageIcon = (PIMAGE_RESOURCE_DIRECTORY)((pResourceEntryImageIcons->OffsetToData & 0x7FFFFFFF) + m_pResourceDirectory);
  95:                     
  96:                     // Icône
  97:                     PIMAGE_RESOURCE_DIRECTORY_ENTRY pResourceEntryImageIcon = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResourceDirectoryImageIcon+1);
  98:                     PIMAGE_RESOURCE_DATA_ENTRY pResourceDataImageIcon = (PIMAGE_RESOURCE_DATA_ENTRY)((DWORD_PTR)(m_pResourceDirectory) + (DWORD_PTR)(pResourceEntryImageIcon->OffsetToData));
  99:                     
 100:                     // Ajoute l'image au fichier icône
 101:                     ofsIcon.write((char*)(m_pResourceDirectory + pResourceDataImageIcon->OffsetToData - m_dVirtualAddressResource), pResourceDataImageIcon->Size);
 102:                 }
 103:             }
 104:         }
 105:     }
 106:  
 107:     return true;
 108: }

La solution Visual Studio complète est disponible ici.