Sometimes, it is useful to modify (add / remove / delete) resources in an EXE and/or DLL file at run time. This is not the limit, we can also modify the icon and version information on the fly and give a new look to our EXE/DLL files and load them with new features.
What are the ways to do this? Obviously, writing a separate module that will modify the resources. Let's go through the details, but before that, we need to understand the background details of the key terms.
A DLL (Dynamic Link Library) is a file that includes the source code or callable functions that provide a service to the application currently being executed.
EXE is a stand-alone application that could be executed on any platform. EXE is an independent application while a DLL is dependent on an application's call.
A window resource (I am talking about Win32 resource) instance consists of the following attributes:
- Type: cursors, dialogs, bitmaps etc.
- Name: a Unicode string or a 2-byte numeric identifier.
- Language: a 2-byte numeric value indicating the language of the resource instance; e.g., English (U.S.) = 1033, Chinese (Traditional) = 1028.
- Data: the actual data of the resource (e.g., a bitmap image).
Here, I am introducing the logic and code that replaces a
BITMAP resource in an exe file by an externally provided bitmap image.
To modify an exe file, we need to do the following steps:
- get the handle of the exe file.
- find the resources (get handle) that replace the existing resources of the exe file.
- load the resource (which replaces the existing resource of the exe file) into global memory.
- lock the resource into global memory.
- update the resource in the exe file.
- end the resource update.
- close the handles.
Code snippets and explanations
HANDLE hFile = CreateFile(bmpPath, GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD FileSize = GetFileSize(hFile, NULL);
In the above lines of code, I am taking the handle of my
BITMAP file and calculating its size. Here,
bmpPath(char array) stores the full path of my .bmp file,
GENERIC_READ is the requested access to the file, the third parameter
0 specifies that if
CreateFile succeeds, the file cannot be shared and cannot be opened again until the handle to the file is closed,
OPEN_EXISTING specifies to open the file only if it exists, and
FILE_ATTRIBUTE_NORMAL has the file attributes and flags,
FILE_ATTRIBUTE_NORMAL being the most common default value for files. After execution of the above two lines,
hFile contains a .bmp image handle, and
FileSize contains its size in bytes.
BYTE *pBuffer = new BYTE[FileSize];
ReadFile(hFile, pBuffer, FileSize, &dwBytesRead, NULL);
The second step (in the above two lines) is to read data in to memory. Here,
dwBytesRead is the integer type variable that receives the number of bytes read when using a synchronous
ReadFile sets this value to zero before doing any work.
HANDLE hExeFile = BeginUpdateResource(exePath, FALSE);
After execution of the above line,
hExeFile contains the handle that can be used by the
UpdateResource function to modify (add, delete, or replace) resources in a binary module.
exePath(char array) stores the full path of the exe file.
The next step is more important and needs extra care. The
BITMAP resource in the exe file has an ID, and first, we need to retrieve it. We can get it easily using an API function. For the time being, I am using a hardcoded ID directly. The
BITMAP image which replaces the exe's
BITMAP resource must be in binary form and must not contain header information when it is passed as an argument to the
UpdateResource function. The main reason behind this is, the exe file just replaces the raw
BITMAP data, not its header information, which means, after the execution of the
UpdateResource function, the new image contains the same header information as the old one, but new
BITMAP data. To implement this, we have to explicitly skip the header information in the
BITMAP file that replaces the exe resource.
BYTE *newBuffer = pBuffer + sizeof(BITMAPFILEHEADER);
DWORD NewFileSize = FileSize - sizeof(BITMAPFILEHEADER);
Now, after doing all the necessary work,
UpdateResource will be executed.
UpdateResource(hExeFile, RT_BITMAP, MAKEINTRESOURCE(130),
In the above line,
hExeFile is a module (exe file) handle returned by the
BeginUpdateResource function, referencing the file to be updated. Also, we need to pass the resource's type, name, and language ID information. Here,
RT_BITMAP is the resource type,
MAKEINTRESOURCE(130) is its name (130 is the ID of the exe's
BITMAP resource), and
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) is its language ID.
newBuffer is the binary data of the .bmp file that does not have header information.
After execution of the
UpdateResource function, it is necessary to call the
EndUpdateResource function because it commits or discards the changes made prior to a call to