首页  编辑  

如何修改可执行文件的图标

Tags: /超级猛料/Resource.资源和使用/   Date Created:

如何修改可执行文件的图标

发布者: wzfish(at: 21cn.com) · 发布日期: 2001-12-17 · 来源: xici.net/main.asp

本帖版权归原作者,其它媒体或网站转载请与e龙西祠胡同[http://www.xici.net]或原作者联系,并注明出处。  

作者: antghazi 发表日期: 2001-12-14 17:48:53 返回《黑客也是侠》 快速返回

如何修改可执行文件的图标

作者:AntGhazi/2001.12.14 主页:antghazi.yeah.net

在网上有很多关于PE文件格式的说明,讲得最多莫过于IMAGE_DOS_HEADER、IMAGE_NT_HEADERS、IMAGE_SECTION_HEADER、等。而对于节的介绍最多的,也莫过于函数引入引出节。而关于资源节.rsrc的介绍则少之又少。好了,废话少说。

PE文件格式如下:

对于PE的详细介绍在MSDN中也有,邹丹(www.zaodan.com)与罗大侠(asm.yeah.net)的主页上也有细详的介绍。这里我在修改ICON中的一种做法。讲解中所用到的语句并不全面,重要的是这个思路。最后面我会给出一个直修改资源的函数。

首先,我们需要两个可执行文件,并且已知这两个exe文件都有图标资源。

1、 peSource.exe (从此文件中提取图标)

2、 peDesc.exe (将图标写入此文件)

第二部,分别打开这两个文件,hFileSource设为只读,hFileDesc设为可写。

HANDLE hFileSource;

HANDLE hFileDesc;

打开后,大家最常用的莫过于文件映射,这里为方便与直观,我们直接把文件读到一个内存块中。

//先得到长度

DWORD dwSourceSize =::GetFileSize(hFileSource);

DWORD dwDescSize =::GetFileSize(hFileDesc);

DWORD byte_write=0;

//读取

char *pFileSource =new char[dwSourceSize];

char *pFileDesc =new char[dwDescSize];

::ReadFile(hFileSource,pFileSource,dwSourceSize,&byte_write,0);

::ReadFile(hFileDesc,pFileDesc,dwDescSize,&byte_write,0);

好了,现在我们已经分别将两个文件读入内存中。让我们先将pFileSource指到资源节的头部。Section的结构说明如下:

typedef struct _IMAGE_SECTION_HEADER {

BYTE Name[IMAGE_SIZEOF_SHORT_NAME];

union {

DWORD PhysicalAddress;

DWORD VirtualSize;

} Misc;

DWORD VirtualAddress;

DWORD SizeOfRawData;

DWORD PointerToRawData;

DWORD PointerToRelocations;

DWORD PointerToLinenumbers;

WORD NumberOfRelocations;

WORD NumberOfLinenumbers;

DWORD Characteristics;

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

通常情况,资源节的名称一般都为:.rsrc。目前我们只考虑这种情况。

IMAGE_DOS_HEADER *dosHeadA=(IMAGE_DOS_HEADER *)pFileSource; //DOS头

IMAGE_NT_HEADERS *ntHeadA=(IMAGE_NT_HEADERS *) (pFileSource + dosHeadA->e_lfanew); //NT头

IMAGE_SECTION_HEADER *secHeadA=(IMAGE_SECTION_HEADER *)((char *)ntHeadA+ sizeof(IMAGE_NT_HEADERS)); //第一个节的首地址

//循环找出.rsrc节

for(int i=0;i<ntHeadA->FileHeader .NumberOfSections ;i++,secHeadA++){

if(strcmp((char *)secHeadA->Name,".rsrc")==0){ //找到.rsrc节

break;

}

}

好了,现在我们已经找到.rsrc节表。根据节表,我们就可以找到资源的入口地址。

IMAGE_RESOURCE_DIRECTORY *dirResourceA=(IMAGE_RESOURCE_DIRECTORY *)((char *)pFileSource + secHeadA->PointerToRawData); //得到资源入口地址

到这里,我才开始讲到我们今天的目的----资源结构,下面有几个需要用到的结构与相关的解释:

// Resource Format.

//

//

// Resource directory consists of two counts, following by a variable length

// array of directory entries. The first count is the number of entries at

// beginning of the array that have actual names associated with each entry.

// The entries are in ascending order, case insensitive strings. The second

// count is the number of entries that immediately follow the named entries.

// This second count identifies the number of entries that have 16-bit integer

// Ids as their name. These entries are also sorted in ascending order.

//

// This structure allows fast lookup by either name or number, but for any

// given resource entry only one form of lookup is supported, not both.

// This is consistant with the syntax of the .RC file and the .RES file.

//

typedef struct _IMAGE_RESOURCE_DIRECTORY { //资源树结构

DWORD Characteristics; //标识此资源的类型

DWORD TimeDateStamp;

WORD MajorVersion;

WORD MinorVersion;

WORD NumberOfNamedEntries;

WORD NumberOfIdEntries; //此结构下还包函有的资源结构树,即:还有几个子树。

// IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; //请注意这里,下面还会讲到。

} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

此结构的其他解释请见VC的头文件winnt.h.

整个资源的结构就好像一棵树型,不同资源如:menu,icon,dialog,cursor等。都如同每根树枝,树枝的Characteristics会标识不同的资源类型,而每根树枝又会有子树枝。这样一直循环,直到IMAGE_RESOURCE_DIRECTORY的NumberOfIdEntries为零时才结束。通常情况,子树都为为三层。每一个子树的类型由IMAGE_RESOURCE_DIRECTORY中的Characteristics来标识。如:当第一层的Characteristics==3时,则说明此结构为ICON资源。Characteristics类型定义如下(可在winuser.h中找到):

/*

* Predefined Resource Types

*/

#define RT_CURSOR MAKEINTRESOURCE(1)

#define RT_BITMAP MAKEINTRESOURCE(2)

#define RT_ICON MAKEINTRESOURCE(3)

#define RT_MENU MAKEINTRESOURCE(4)

#define RT_DIALOG MAKEINTRESOURCE(5)

#define RT_STRING MAKEINTRESOURCE(6)

#define RT_FONTDIR MAKEINTRESOURCE(7)

#define RT_FONT MAKEINTRESOURCE(8)

#define RT_ACCELERATOR MAKEINTRESOURCE(9)

#define RT_RCDATA MAKEINTRESOURCE(10)

#define RT_MESSAGETABLE MAKEINTRESOURCE(11)

总结构如下(偷懒,copy而来):

好了,整个资源的结构已经弄清楚了。现在我们要做的就是得到每个子资源的入口地址。这里要用到的一个结构是:

// Each directory contains the 32-bit Name of the entry and an offset,

// relative to the beginning of the resource directory of the data associated

// with this directory entry. If the name of the entry is an actual text

// string instead of an integer Id, then the high order bit of the name field

// is set to one and the low order 31-bits are an offset, relative to the

// beginning of the resource directory of the string, which is of type

// IMAGE_RESOURCE_DIRECTORY_STRING. Otherwise the high bit is clear and the

// low-order 16-bits are the integer Id that identify this resource directory

// entry. If the directory entry is yet another resource directory (i.e. a

// subdirectory), then the high order bit of the offset field will be

// set to indicate this. Otherwise the high bit is clear and the offset

// field points to a resource data entry.

//

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {

union {

struct {

DWORD NameOffset:31;

DWORD NameIsString:1;

};

DWORD Name;

WORD Id;

};

union {

DWORD OffsetToData; //指向资源的入口址

struct {

DWORD OffsetToDirectory:31;

DWORD DataIsDirectory:1; //指向下一级目录的相对地址

};

};

}IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

上面对IMAGE_RESOURCE_DIRECTORY_ENTRY的解释也已经是非常清楚了。

结构中有两个成员:OffsetToData,DataIsDirectroy,当DiataIsDirectroy大于0时,则说明此结构还有下一级目录,否则,OffsetToData肯定不为0。那OffsetToData的值就是我们所得到的资源入口的RVA了。

那么,IMAGE_RESOURCE_DIRECTORY_ENTRY结构应该怎么得到呢?让我们再看一下,IMAGE_RESOURCE_DIRECTORY的结构说明吧。

typedef struct _IMAGE_RESOURCE_DIRECTORY { //资源树结构

DWORD Characteristics; //标识此资源的类型

DWORD TimeDateStamp;

WORD MajorVersion;

WORD MinorVersion;

WORD NumberOfNamedEntries;

WORD NumberOfIdEntries; // 、、//IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; //紧跟在后面的就是 IMAGE_RESOURCE_DIRECTORY_ENTRY结构数组,DirectoryEntries数组的个数实际上也就是NumberOfIdEntries.你也可以理解为

IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[NumberOfIdEntries];,

} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

那这样一看来,IMAGE_RESOURCE_DIRECTORY_ENTRY的第一个地址等于父树地址加上IMAGE_RESOURCE_DIRECTORY结构的大小即可。

如:IMAGE_RESOURCE_DIRECTORY *dirTempB=(IMAGE_RESOURCE_DIRECTORY *)((char *)dirResourceB+entryResourceB->OffsetToDirectory);

最后一个是IMAGE_RESOURCE_DATA_ENTRY结构,比较简单,大家看一下就知道了。

// Each resource data entry describes a leaf node in the resource directory

// tree. It contains an offset, relative to the beginning of the resource

// directory of the data for the resource, a size field that gives the number

// of bytes of data at that offset, a CodePage that should be used when

// decoding code point values within the resource data. Typically for new

// applications the code page would be the unicode code page.

//

typedef struct _IMAGE_RESOURCE_DATA_ENTRY {

DWORD OffsetToData;

DWORD Size;

DWORD CodePage;

DWORD Reserved;

} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;

好了,讲了这么多,现在我们可以开始计算了,(我们以读取第三层第一个ICON为例<通常资源都为三层>):

前面我们已经得到根资源的地址:dirResourceA

IMAGE_RESOURCE_DIRECTORY *dirResourceA=(IMAGE_RESOURCE_DIRECTORY *)((char *)pFileA + secHeadA->PointerToRawData); //根

IMAGE_RESOURCE_DIRECTORY_ENTRY *entryResourceA=(IMAGE_RESOURCE_DIRECTORY_ENTRY *)((DWORD)dirResourceA + sizeof (IMAGE_RESOURCE_DIRECTORY));

IMAGE_RESOURCE_DIRECTORY *dirTemp; //第二层

IMAGE_RESOURCE_DIRECTORY_ENTRY *entryTemp;

IMAGE_RESOURCE_DIRECTORY *dirTempICON; //第三层

IMAGE_RESOURCE_DIRECTORY_ENTRY *entryTempICON;

IMAGE_RESOURCE_DATA_ENTRY *entryData; //资源入口结构

for(i=0;i<(dirResourceA->NumberOfIdEntries+dirResourceA->NumberOfNamedEntries);i++,entryResourceA++){ //所有资源

if(entryResourceA->Name==3){ //ICON

dirTemp=(IMAGE_RESOURCE_DIRECTORY *)((char *)dirResourceA+entryResourceA->OffsetToDirectory);

entryTemp=(IMAGE_RESOURCE_DIRECTORY_ENTRY *)((char *)dirTemp+sizeof(IMAGE_RESOURCE_DIRECTORY));

for(int k=0;k<(dirTemp->NumberOfIdEntries+dirTemp->NumberOfNamedEntries);k++,entryTemp++){ //子目录

if(entryTemp->DataIsDirectory >0){ //还有子目录

dirTempICON=(IMAGE_RESOURCE_DIRECTORY *)((char *)dirResourceA + entryTemp->OffsetToDirectory );

entryTempICON=(IMAGE_RESOURCE_DIRECTORY_ENTRY *)((char *)dirTempICON + sizeof(IMAGE_RESOURCE_DIRECTORY));

entryData=(IMAGE_RESOURCE_DATA_ENTRY *)((char *)dirResourceA + entryTempICON->OffsetToData ); //资源入口结构

break; //得到后跳出

}

}

}

}

最后,读入内存中:

DWORD dwIconSize=entryDataA->Size;

char *pSrcIcon=entryDataA->OffsetToData - secHeadA->VirtualAddress + (char *)dirResourceA;

char *pSourceIcon= new char[dwIconSize+1];

memcpy(pSourceIcon,pSrcIcon,dwIconSize);

最后得到的数据就在pSourceIcon中了。

同理,得到另一个文件中的ICON入口地址,用pSourceIcon覆盖之即可。

函数地址:http://go3.163.com/antghazi/main3.htm

写得比较零乱,如有不明或有错误的地方,欢迎指正。

mailto:antghazi@163.net

http://antghazi.yeah.net

AntGhazi/2001.12.14