user32: Fix icon extraction from non-native PE executables.

Hans Leidekker hans at codeweavers.com
Thu Jun 6 07:03:56 CDT 2013


---
 dlls/user32/exticon.c |  202 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 133 insertions(+), 69 deletions(-)

diff --git a/dlls/user32/exticon.c b/dlls/user32/exticon.c
index 5b9d681..e6e97d5 100644
--- a/dlls/user32/exticon.c
+++ b/dlls/user32/exticon.c
@@ -251,6 +251,127 @@ static BYTE * ICO_GetIconDirectory( LPBYTE peimage, LPicoICONDIR* lplpiID, ULONG
 	return 0;
 }
 
+/* caller must make sure we have a PE image */
+static BOOL get_nt_header( const BYTE *image, const IMAGE_NT_HEADERS32 **nt32, const IMAGE_NT_HEADERS64 **nt64 )
+{
+    const IMAGE_DOS_HEADER *dos = (const IMAGE_DOS_HEADER *)image;
+    const IMAGE_NT_HEADERS *nt = (const IMAGE_NT_HEADERS *)(image + dos->e_lfanew);
+
+    if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
+    {
+        *nt32 = (const IMAGE_NT_HEADERS32 *)nt;
+        *nt64 = NULL;
+        return TRUE;
+    }
+    else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
+    {
+        *nt64 = (const IMAGE_NT_HEADERS64 *)nt;
+        *nt32 = NULL;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static const IMAGE_SECTION_HEADER *get_next_section_header( const BYTE *image, WORD *index )
+{
+    const IMAGE_NT_HEADERS32 *hdr32;
+    const IMAGE_NT_HEADERS64 *hdr64;
+    const IMAGE_SECTION_HEADER *section = NULL;
+
+    if (!get_nt_header( image, &hdr32, &hdr64 )) return NULL;
+    if (hdr32)
+    {
+        if (*index >= hdr32->FileHeader.NumberOfSections) return NULL;
+        section = (const IMAGE_SECTION_HEADER *)((char *)hdr32 + sizeof(DWORD) +
+                  sizeof(IMAGE_FILE_HEADER) + hdr32->FileHeader.SizeOfOptionalHeader +
+                  *index * sizeof(IMAGE_SECTION_HEADER));
+    }
+    else
+    {
+        if (*index >= hdr64->FileHeader.NumberOfSections) return NULL;
+        section = (const IMAGE_SECTION_HEADER *)((char *)hdr64 + sizeof(DWORD) +
+                  sizeof(IMAGE_FILE_HEADER) + hdr64->FileHeader.SizeOfOptionalHeader +
+                  *index * sizeof(IMAGE_SECTION_HEADER));
+    }
+    if (section) (*index)++;
+    return section;
+}
+
+static DWORD get_resource_va( const BYTE *image )
+{
+    const IMAGE_NT_HEADERS32 *hdr32;
+    const IMAGE_NT_HEADERS64 *hdr64;
+
+    if (!get_nt_header( image, &hdr32, &hdr64 )) return 0;
+    if (hdr32) return hdr32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
+    return hdr64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
+}
+
+/* search for the root resource directory in a 32 or 64-bit PE image */
+static const IMAGE_RESOURCE_DIRECTORY *get_resource_root( const BYTE *image, DWORD size )
+{
+    const IMAGE_SECTION_HEADER *section;
+    const IMAGE_RESOURCE_DIRECTORY *root = NULL;
+    DWORD resource_va = get_resource_va( image );
+    WORD index = 0;
+
+    if (!resource_va) return NULL;
+
+    while ((section = get_next_section_header( image, &index )))
+    {
+        if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) continue;
+        if (section->PointerToRawData + section->SizeOfRawData > size)
+        {
+            WARN( "section size %u beyond image size %u)\n",
+                  section->PointerToRawData + section->SizeOfRawData, size );
+            return NULL;
+        }
+        /* FIXME: doesn't work when the resources are not in a separate section */
+        if (section->VirtualAddress == resource_va)
+        {
+            root = (IMAGE_RESOURCE_DIRECTORY *)(image + section->PointerToRawData);
+            break;
+        }
+    }
+    return root;
+}
+
+static BYTE *get_icon_group_data( BYTE *image, DWORD size, const IMAGE_RESOURCE_DATA_ENTRY *entry )
+{
+    const IMAGE_SECTION_HEADER *section;
+    WORD index = 0;
+
+    while ((section = get_next_section_header( image, &index )))
+    {
+        if (entry->OffsetToData < section->VirtualAddress ||
+            entry->OffsetToData + entry->Size > section->VirtualAddress + section->SizeOfRawData) continue;
+
+        if (entry->OffsetToData - section->VirtualAddress + section->PointerToRawData + entry->Size > size)
+        {
+            WARN( "overflow in PE lookup (size %u, have offset %u)\n", size,
+                 entry->OffsetToData - section->VirtualAddress + section->PointerToRawData + entry->Size );
+            return NULL;
+        }
+        return image + entry->OffsetToData - section->VirtualAddress + section->PointerToRawData;
+    }
+    return NULL;
+}
+
+static BYTE *get_icon_data( BYTE *image, DWORD size, const IMAGE_RESOURCE_DATA_ENTRY *entry )
+{
+    const IMAGE_SECTION_HEADER *section;
+    WORD index = 0;
+
+    while ((section = get_next_section_header( image, &index )))
+    {
+        if (entry->OffsetToData < section->VirtualAddress ||
+            entry->OffsetToData + entry->Size > section->VirtualAddress + section->SizeOfRawData) continue;
+
+        return image + entry->OffsetToData - section->VirtualAddress + section->PointerToRawData;
+    }
+    return NULL;
+}
+
 /*************************************************************************
  *	ICO_ExtractIconExW		[internal]
  *
@@ -420,47 +541,17 @@ static UINT ICO_ExtractIconExW(
 /* exe/dll */
 	else if( sig == IMAGE_NT_SIGNATURE )
 	{
-	  LPBYTE		idata,igdata;
-	  PIMAGE_DOS_HEADER	dheader;
-	  PIMAGE_NT_HEADERS	pe_header;
-	  PIMAGE_SECTION_HEADER	pe_sections;
-	  const IMAGE_RESOURCE_DIRECTORY *rootresdir,*iconresdir,*icongroupresdir;
-	  const IMAGE_RESOURCE_DATA_ENTRY *idataent,*igdataent;
-	  const IMAGE_RESOURCE_DIRECTORY_ENTRY *xresent;
-	  UINT	i, j;
-
-	  dheader = (PIMAGE_DOS_HEADER)peimage;
-	  pe_header = (PIMAGE_NT_HEADERS)(peimage+dheader->e_lfanew);	  /* it is a pe header, USER32_GetResourceTable checked that */
-	  pe_sections = (PIMAGE_SECTION_HEADER)(((char*)pe_header) + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER)
-	                                        + pe_header->FileHeader.SizeOfOptionalHeader);
-	  rootresdir = NULL;
-
-	  /* search for the root resource directory */
-	  for (i=0;i<pe_header->FileHeader.NumberOfSections;i++)
-	  {
-	    if (pe_sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
-	      continue;
-	    if (fsizel < pe_sections[i].PointerToRawData+pe_sections[i].SizeOfRawData) {
-              FIXME("File %s too short (section is at %d bytes, real size is %d)\n",
-		      debugstr_w(lpszExeFileName),
-		      pe_sections[i].PointerToRawData+pe_sections[i].SizeOfRawData,
-		      fsizel
-	      );
-	      goto end;
-	    }
-	    /* FIXME: doesn't work when the resources are not in a separate section */
-	    if (pe_sections[i].VirtualAddress == pe_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress)
-	    {
-	      rootresdir = (PIMAGE_RESOURCE_DIRECTORY)(peimage+pe_sections[i].PointerToRawData);
-	      break;
-	    }
-	  }
+        BYTE *idata, *igdata;
+        const IMAGE_RESOURCE_DIRECTORY *rootresdir, *iconresdir, *icongroupresdir;
+        const IMAGE_RESOURCE_DATA_ENTRY *idataent, *igdataent;
+        const IMAGE_RESOURCE_DIRECTORY_ENTRY *xresent;
+        UINT i;
 
-	  if (!rootresdir)
-	  {
-	    WARN("haven't found section for resource directory.\n");
-	    goto end;		/* failure */
-	  }
+        if (!(rootresdir = get_resource_root( peimage, fsizel )))
+        {
+	        WARN("haven't found section for resource directory.\n");
+            goto end;
+        }
 
 	  /* search for the group icon directory */
 	  if (!(icongroupresdir = find_entry_by_id(rootresdir, LOWORD(RT_GROUP_ICON), rootresdir)))
@@ -529,24 +620,7 @@ static UINT ICO_ExtractIconExW(
 	    igdataent = (const IMAGE_RESOURCE_DATA_ENTRY*)resdir;
 
 	    /* lookup address in mapped image for virtual address */
-	    igdata = NULL;
-
-	    for (j=0;j<pe_header->FileHeader.NumberOfSections;j++)
-	    {
-	      if (igdataent->OffsetToData < pe_sections[j].VirtualAddress)
-	        continue;
-	      if (igdataent->OffsetToData+igdataent->Size > pe_sections[j].VirtualAddress+pe_sections[j].SizeOfRawData)
-	        continue;
-
-	      if (igdataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData+igdataent->Size > fsizel) {
-                FIXME("overflow in PE lookup (%s has len %d, have offset %d), short file?\n", debugstr_w(lpszExeFileName), fsizel,
-	        	   igdataent->OffsetToData - pe_sections[j].VirtualAddress + pe_sections[j].PointerToRawData + igdataent->Size);
-	        goto end; /* failure */
-	      }
-	      igdata = peimage+(igdataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData);
-	    }
-
-	    if (!igdata)
+	    if (!(igdata = get_icon_group_data( peimage, fsizel, igdataent )))
 	    {
 	      FIXME("no matching real address for icongroup!\n");
 	      goto end;	/* failure */
@@ -573,18 +647,8 @@ static UINT ICO_ExtractIconExW(
             }
 	    xresdir = find_entry_default(xresdir, rootresdir);
 	    idataent = (const IMAGE_RESOURCE_DATA_ENTRY*)xresdir;
-	    idata = NULL;
 
-	    /* map virtual to address in image */
-	    for (j=0;j<pe_header->FileHeader.NumberOfSections;j++)
-	    {
-	      if (idataent->OffsetToData < pe_sections[j].VirtualAddress)
-	        continue;
-	      if (idataent->OffsetToData+idataent->Size > pe_sections[j].VirtualAddress+pe_sections[j].SizeOfRawData)
-	        continue;
-	      idata = peimage+(idataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData);
-	    }
-	    if (!idata)
+	    if (!(idata = get_icon_data( peimage, fsizel, idataent)))
 	    {
 	      WARN("no matching real address found for icondata!\n");
 	      RetPtr[i]=0;
-- 
1.7.10.4






More information about the wine-patches mailing list