本节我们将通过两个示例程序,演示对PE文件内图标资源的置换与提取。
本节必须掌握的知识点:
更改图标
提取图标资源
7.4.1 更改图标
让我们来做一个实验,替换PE文件中现有的图标。如果手工替换,一定是先找到资源表,然后分别替换图标资源和图标组资源就可以了。当然这个过程稍微有点繁复,可能会涉及到其他资源的重定位。还好,Windows操作系统为开发者提供了一组更新PE文件中资源的API函数:BeginUpdateResource、UpdateResource、EndUpdateResource。用来枚举 PE 文件中资源的函数有:EnumResourceTypes、EnumResourceNames、EnumResourceLanguages。具体的使用方法可以参见MSDN,下面我们将使用这些函数实现图标资源的替换。
实验五十:更改图标
●模块1:resource.h(略)
●模块2:peinfo.rc(略)
●模块3:info.h
#pragma once
#ifndef INFO_H_
#define INFO_H_
#include <windows.h>
#include <richedit.h> //CHARFORMAT富文本结构定义
#include <commctrl.h> //通用控件
#pragma comment(lib,"comctl32.lib")
#include <strsafe.h> //StringCchCopy
#include <stdlib.h>
// 文件中的ICO头部
typedef struct
{
byte bWidth; //宽度
byte bHeight; //高度
byte bColorCount; //颜色数
byte bReserved; //保留字,必须为0
WORD wPlanes; //调色板
WORD wBitCount; //每个像素的位数
DWORD dwBytesInRes; //资源长度
DWORD dwImageOffset; //资源在文件偏移
}ICON_DIR_ENTRY;
typedef struct
{
WORD idReserved; //保留字,必须为0
WORD idType; //资源类别,如果是1表示为ICO文件
WORD idCount; //图标数量
//ICON_DIR_ENTRY idEntries; //图标项,一个图标一项
}ICON_DIR;
//PE中ICO头部
typedef struct
{
byte bWidth; //宽度
byte bHeight; //高度
byte bColorCount; //颜色数
byte bReserved; //保留字,必须为0
WORD wPlanes; //调色板
WORD wBitCount; //每个像素的位数
DWORD dwBytesInRes; //资源长度
WORD nID; //资源在文件序号
}PE_ICON_DIR_ENTRY;
typedef struct
{
WORD idReserved; //保留字,必须为0
WORD idType; //资源类别,如果是1表示为ICO文件
WORD idCount; //图标数量
PE_ICON_DIR_ENTRY idEntries; //图标项,一个图标一项
}PE_ICON_DIR;
//函数声明
BOOL CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Exception(void);
void init(); //初始化
void _OpenFile();//打开PE文件并处理
int CALLBACK _Handler(EXCEPTION_POINTERS * lpExceptionPoint);
void ShowErrMsg();
void _AppendInfo(const TCHAR * _lpsz);//往文本框中追加文本
//将boy.ico图标替换指定PE程序的图标
BOOL _doUpdate(TCHAR* lpszFile, TCHAR* lpszExeFile);
#endif
●模块4:PEUpdateIcon.c
/*------------------------------------------------------------------------
FileName: PEUpdateIcon.c
实验50:更改程序图标实例(支持32位和64位PE文件)
(c) bcdaren, 2024
-----------------------------------------------------------------------*/
#include <strsafe.h> //StringCchCopy
#include "resource.h"
#include "info.h"
HANDLE hInstance;
HWND hWinMain, hWinEdit;
HMODULE hRichEdit;
TCHAR szFileName[MAX_PATH];
HANDLE hFileDump;
HANDLE hFile;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
TCHAR szDllEdit[] = TEXT("RichEd20.dll");
TCHAR szClassEdit[] = TEXT("RichEdit20W");//peinfo.rc中定义
hRichEdit = LoadLibrary((LPCWSTR)&szDllEdit);
hInstance = GetModuleHandle(NULL);
DialogBoxParam(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL,
(DLGPROC)DlgProc, (LPARAM)0);
FreeLibrary(hRichEdit);
return 0;
}
//初始化窗口函数
void init()
{
CHARFORMAT stCf;
TCHAR szClassEdit[] = TEXT("RichEdit20A");
TCHAR szFont[] = TEXT("宋体");
HINSTANCE hInstance;
hWinEdit = GetDlgItem(hWinMain, IDC_INFO);
//为窗口程序设置图标
hInstance = GetModuleHandle(NULL);
HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ICO_MAIN));
//HICON hIcon = LoadIcon(hInstance, TEXT("#111"));
SendMessage(hWinMain, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
//设置编辑控件
SendMessage(hWinEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
RtlZeroMemory(&stCf, sizeof(stCf));
stCf.cbSize = sizeof(stCf);
stCf.yHeight = 9 * 20;
stCf.dwMask = CFM_FACE | CFM_SIZE | CFM_BOLD;
StringCchCopy((LPTSTR)&stCf.szFaceName, lstrlen(szFont) + 1, (LPCTSTR)&szFont);
SendMessage(hWinEdit, EM_SETCHARFORMAT, 0, (LPARAM)&stCf);
SendMessage(hWinEdit, EM_EXLIMITTEXT, 0, -1);//设为-1表示无限制
}
//富文本窗口回调函数
BOOL CALLBACK DlgProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
const TCHAR szErr[] = TEXT("文件格式错误!");
const TCHAR szErrFormat[] = TEXT("这个文件不是PE格式的文件!");
switch (wMsg)
{
case WM_CLOSE:
EndDialog(hWnd, 0);
return TRUE;
case WM_INITDIALOG:
hWinMain = hWnd;
init(); //初始化
return TRUE;
case WM_COMMAND:
switch (wParam)
{
case IDM_EXIT:
EndDialog(hWnd, 0);
return TRUE;
case IDM_OPEN:
_OpenFile();
return TRUE;
case IDM_1:
MessageBox(NULL, szErrFormat, szErr, MB_ICONWARNING);
return TRUE;
case IDM_2:
MessageBox(NULL, szErrFormat, szErr, MB_ICONWARNING);
return TRUE;
case IDM_3:
MessageBox(NULL, szErrFormat, szErr, MB_ICONWARNING);
return TRUE;
}
}
return FALSE;
}
//执行比对PE文件
void _OpenFile()
{
OPENFILENAME stOF;
HANDLE hFile = NULL;
HANDLE hMapFile = NULL;
PBYTE lpMemory = NULL; //PE文件内存映射文件地址
int dwFileSize;
const TCHAR szDefaultExt[] = TEXT("exe");
const TCHAR szFilter[] = TEXT("PE Files (*.exe)\0*.exe\0")\
TEXT("DLL Files(*.dll)\0*.dll\0")\
TEXT("SCR Files(*.scr)\0*.scr\0")\
TEXT("FON Files(*.fon)\0*.fon\0")\
TEXT("DRV Files(*.drv)\0*.drv\0")\
TEXT("All Files(*.*)\0*.*\0\0");
const TCHAR szErr[] = TEXT("文件格式错误!");
const TCHAR szErrFormat[] = TEXT("操作文件时出现错误!");
const TCHAR szOut1[] = TEXT("----------------------------------------------------------------\r\n")
TEXT("待处理的PE文件:%s\r\n");
static TCHAR lpszBoyIcon[] =
TEXT("D:\\code\\winpe\\ch07\\PEUpdateIcon\\boy.ico");
const TCHAR szFailure[] = TEXT("执行图标修改失败。");
const TCHAR szSuccess[] = TEXT("恭喜你,图标修改成功成功的。");
IMAGE_DOS_HEADER *lpstDOS; //DOS块地址
IMAGE_NT_HEADERS *lpstNT; //PE文件头地址
//显示打开文件对话框
RtlZeroMemory(&st