Hacking Icon Resources

Introduction

In this tutorial, I will show you how to replace an application's icon with a given .ICO file. To keep things simple, you will use an .ICO file containing a single 32x32 image (meaning, I will give the complete source code to accomplish this), but I will provide the general theory to enable you to extend the example to support all types of icons.

There are a lot of icon editors around, so use your favorite one to create a 32x32, 256-color icon.

First Things First: the Structure of an .ICO File on Disk

Icon files have the following structure:

ICON HEADER:
   WORD Reserved             - reserved
   WORD Type                 - 1 means icon, 2 means cursor
   WORD ImageCount           - number of images stored inside the file

   ICON DIRECTORY ENTRY      - there are ImageCount directory entries
      BYTE Width             - width of image
      BYTE Height            - height of image
      BYTE Colors            - 0 means 256 or more
      BYTE Reserved          - reserved
      WORD Planes            - number of planes
      WORD BitsPerPixel      - bits per pixel
      DWORD ImageSize        - size of image data
      DWORD ImageOffset      - offset of image data inside file
   ICON DIRECTORY ENTRY
   ...
   ICON DIRECTORY ENTRY
       
   ICON IMAGE                - the actual image data (which has the
                               structure of ICON IMAGE a bitmap) found
                               at the location specified by ImageOffset
   ...
   ICON IMAGE

As you can see, a .ICO file can contain more than one image.

The Secret

It isn't enough to read a .ICO file into memory and insert it as a RC_ICON resource because the resulting resource will be corrupted. In resources, icons are split into several RC_ICON resources and a RC_GROUP_ICON. A RC_ICON entry is basically the above ICON IMAGE. RC_GROUP_ICON contains the ICON HEADER and DIRECTORY ENTRIES with a single modification: The DWORD ImageOffset becomes WORD ResourceID. That is because the actual image data is no longer following its header, so it can be identified by its resource ID.

An application can have several icons contained inside its resources. Usually, the displayed icon has the resource ID equal to 1, but this is not necessary. Windows chooses which icon to display based on the RC_GROUP_ICON resource named 'MAINICON'. You can use a tool such as PE Explorer to take a look at the resources of compiled executable modules (.EXE and .DLL files).

What You Will Do

You will load the ICON IMAGE of an .ICO file into a buffer, create a structure containing RC_GROUP_ICON resource data, and inject both of these into a given executable file.

Here are some structure declarations:

// Make sure structures are packed the same way as Windows packs them
#pragma pack(push, 2)

typedef struct    // This is the Directory Entry stored in resources
{
   BYTE Width;
   BYTE Height;
   BYTE Colors;
   BYTE Reserved;
   WORD Planes;
   WORD BitsPerPixel;
   DWORD ImageSize;
   WORD ResourceID
} IconDirResEntry, *PIconDirResEntry;

typedef struct    // This is the actual RT_GROUP_ICON structure
{
   WORD Reserved;
   WORD ResourceType;
   WORD ImageCount;
   PIconDirResEntry Enries;    // The number of entries is ImageCount
} GroupIcon;

// Restore default packing
#pragma pack(pop)

For the example, you can declare a simpler structure because you will use an .ICO file containing only one image. You will combine the header and the directory entry into a single structure:

#pragma pack(push, 2)

typedef struct {
  WORD Reserved1;       // reserved, must be 0
  WORD ResourceType;    // type is 1 for icons
  WORD ImageCount;      // number of icons in structure (1)
  BYTE Width;           // icon width (32)
  BYTE Height;          // icon height (32)
  BYTE Colors;          // colors (0 means more than 8 bits per pixel)
  BYTE Reserved2;       // reserved, must be 0
  WORD Planes;          // color planes
  WORD BitsPerPixel;    // bit depth
  DWORD ImageSize;      // size of structure
  WORD ResourceID;      // resource ID
} GROUPICON;

#pragma pack(pop)

The last structure is the RC_GROUP_ICON resource you will inject into the executable.

Using WinAPI

You will update the resources using WinAPI's BeginUpdateResource, UpdateResource, and EndUpdateResource. Declare a InjectMainIcon method that will receive the filename of an application and the filename of an icon.

A quick overview: BeginUpdateResource receives the name of an executable module and a boolean parameter which, if set to TRUE, will remove all resources found in that executable (you don't want that).

UpdateResource receives the handle of the module in which to do the update (the handle returned by BeginUpdateResource). It receives the type name of the resource and the name of the resource for which you can use MAKEINTRESOURCE. (For example, type is RC_ICON and name is MAKEINTRESOURCE(1). This inserts an icon resource giving it the ID 1.) It also receives the language identifier. You will use MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)to set the resource's language to default. Finally, UpdateResource receives a pointer to the buffer containing the resource and the length of that buffer.

The last method you will use is EndUpdateResource. It receives the handle provided by BeginUpdateResource and a boolean parameter that, if set to TRUE, discards all changes made to the resources.

Out of the Windows SDK Documentation:

   HANDLE BeginUpdateResource(
      LPCTSTR pFileName,    // pointer to file in which to update
                            // resources
      BOOL bDeleteExistingResources    // deletion option
     );

   BOOL UpdateResource(
      HANDLE hUpdate,    // update-file handle
      LPCTSTR lpType,    // address of resource type to update
      LPCTSTR lpName,    // address of resource name to update
      WORD wLanguage,    // language identifier of resource
      LPVOID lpData,     // address of resource data
      DWORD cbData       // length of resource data, in bytes
     );
       
   BOOL EndUpdateResource(
      HANDLE hUpdate,    // update-file handle
      BOOL fDiscard      // write flag
     );

Hacking Icon Resources

Putting It Together

How will your InjectMainIcon method look?

void InjectMainIcon(char *Where, char *What)
{
   HANDLE hWhere = BeginUpdateResource(Where, FALSE);
       
   char *buffer;       // buffer to store raw icon data
   long buffersize;    // length of buffer
   int hFile;          // file handle
       
   hFile = open(What, O_RDONLY | O_BINARY);
   if (hFile == -1)
      return;          // if file doesn't exist, can't be opened etc.
       
   // calculate buffer length and load file into buffer
   buffersize = filelength(hFile);
   buffer     = (char *)malloc(buffersize);
   read(hFile, buffer, buffersize);
   close(hFile);

   UpdateResource(
      hWhere,                         // Handle to executable
      RT_ICON,                        // Resource type - icon
      MAKEINTRESOURCE(1),             // Make the id 1
      MAKELANGID(LANG_ENGLISH,        // Default language
                 SUBLANG_DEFAULT),
      (buffer+22), 
      // skip the first 22 bytes because this is the  icon header
      // and directory entry (if the file  contains multiple
      // images, the directory entries will be larger than 22 bytes)
      buffersize-22    // length of buffer
     );

   // Again, we use this structure for educational purposes.
   // The icon header and directory entries can be read from the
   // file.
   GROUPICON grData;

   // This is the header
   grData.Reserved1 = 0;     // reserved, must be 0
   grData.ResourceType = 1;  // type is 1 for icons
   grData.ImageCount = 1;    // number of icons in structure (1)

   // This is the directory entry
   grData.Width        = 32;                 // icon width (32)
   grData.Height       = 32;                 // icon height (32)
   grData.Colors       = 0;                  // colors (256)
   grData.Reserved2    = 0;                  // reserved, must be 0
   grData.Planes       = 2;                  // color planes
   grData.BitsPerPixel = 32;                 // bit depth
   grData.ImageSize    = buffersize - 22;    // size of image
   grData.ResourceID   = 1;                  // resource ID is 1

   UpdateResource(
      hWhere,
      RT_GROUP_ICON,
      // RT_GROUP_ICON resources contain information about
      // stored icons
      "MAINICON",
      // MAINICON contains information about the application's
      // displayed icon
      MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
      &grData,
      // Pointer to this structure
      sizeof(GROUPICON)
     );
   

   free(buffer);    // free memory

   // Perform the update, don't discard changes
   EndUpdateResource(hWhere, FALSE);
}

Conclusion

The 16x16 icons that the system displays can be calculated automatically out of the 32x32 icon resource, so you are not obligated to add small icons. However, the code presented here can be modified to enable inserting all .ICO files by reading and splitting the header and dir entries from the file at runtime.



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds