如何修改可执行文件的图标
生活随笔
收集整理的這篇文章主要介紹了
如何修改可执行文件的图标
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
如何修改可執行文件的圖標
創建時間:2001-12-17
文章屬性:轉載
文章來源: www.xici.net/main.asp
文章提交: wzfish (wzfish_at_21cn.com)
本帖版權歸原作者,其它媒體或網站轉載請與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 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的如何修改可执行文件的图标的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle 11g 从rman全备中恢
- 下一篇: oracle的redo和undo,Ora