关于exe或dll文件中的字符串资源

转载 2015年11月18日 13:59:48

资源在WindowsGUI的程序中随处可见,这也是微软为了方便把界面和逻辑分开的优秀策略。所谓的资源就是一些用于描述界面或其他特征的说明性字符,它们有固定的格式。这点与HTML有相似之处。而微软提供了处理这些资源描述字符的函数,并上升到Win32API的高度。资源有两种存在形式,一是编译之前,此时的资源被存放到.rc文件(此文件为纯文本文件)中,在程序编译的时候,资源编译器rc.exe把.rc文件编译为.res二进制文件,然后链接器把.res文件和.obj文件链接在一起,并作为资源节存在于生成的.exe文件中。

 

对于.rc文件,相信很多程序员都很熟悉了,可以直接用文本编辑器编辑。为了可视化设计,微软和其他第三方软件厂商还提供了可视化编辑工具,称之为资源编辑器。VS带的资源编辑器,相信大家都不会陌生。

 

对于.res文件或者是.exe文件中的资源节的格式,大多数程序员就望而却步了。详细介绍.exe文件结构的资料也不多,能够说清楚资源节格式的资料就更少了。微软提供了FindResource(), LoadResource(), LockResource(), UpdateResource()等函数供用户查看修改二进制资源节里的资源。但是具体的用法解释的并不清楚,网上的例子也很少。但是这方便有做的功能很晚上的软件,如eXeScope,不仅可以查看二进制资源,还可以修改之,可惜没有公开源代码。由于项目需要,这几天一直找相关的例子,发现象Dialog,ICON等资源的处理有很多成功的例子,但是对于看似简单的字符串资源的处理却几乎无一成功的!!!就连老外也一样!!!于是乎,疯狂找资料,终于找到一位明白人写的文章,还有源代码,只是有两点可惜:一是作者是老外,国内程序员不一定都喜欢英语;二是程序源码采用VB编写,这让C/C++的程序源很不舒服。于是乎,对这篇文章关于字符串资源的处理的部分进行研究并重新用C/C++实现的想法涌上心头。由于我对于VB也不是很熟悉,转换语言的过程也不太顺利,不过总算是成功转成了C/C++。技术不敢独享,愿为大家献上,不当之处还请见谅!

 

1 字符串资源的存放格式与处理单位

不想其他资源,字符串是不能单独处理的,像UpdateResource()等函数要求的最小处理单位是“串组”,这是由字符串资源的存放方式决定的。每16个字符串为一个串组,字符串的排序以字符串的资源ID号为依据,0-15为第一串组,16-31位第二串组,32-47位第三串组,...。串组ID号从1开始,字符串ID号从0开始。由此可以得到如下公式:

串组ID = (取整)(字符串ID/16) + 1

同一串组的字符串是相邻存放的,我们以7号串组为例说明。如下图所示:

 

2      你好

5      你好abc

0  

1      你

中间省略

 

 

 


上图列出了几个代表的串,其中最开头的串的ID为96,一个串有两部分组成,前一部分占2个字节,用于描述本串的长度,以UNICODE字符为单位(也就是2个字节),注意字符串资源里的字符都是采用UNICODE16位编码的。后一部分是串的具体内容,注意没有结尾标志。从上图可以看出,ID号为96的串的长度为2个字符,内容为“你好”。下一个串也就是ID为97的串的长度为0,所以没有内容;接着98号串长度为5,内容为“你好abc"(请注意,在UNICODE编码方式下,所有字符都占据2个字节,英文字母也一样)。最后一个字符串,也就是111号,长度为1,内容为“你”。

2 处理方法

理解了字符串资源的存放方式与处理单位,对于其处理方法也就不难了。基本思路是,首先根据字符串的ID号,找到所处的串组号,然后找到整个串组的内容,随后定位到需要处理的串,修改其长度标示和内容。修改不会影响到前面的串,但却会影响到后面的串,所以首先要把后面的所有串先备份,然后添加到修改后的串的尾部。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


如上图所示,需要修改的串显示为红色,串组以此串为分割点分成前后两部分,这两部分原样复制,把需要修改的串修改后重新将其与前后两部分衔接起来。

顺着这个思路问题迎刃而解,具体代码如下:

BOOL StringResourceModify(CString strResExePath, int ID, CString strNew, long LangID)

{

         long ret;

         long x;

         HANDLE hResource;

         int iGroup;

         HRSRC hObject;

         HGLOBAL hResourceData;

         HGLOBAL lpResourceData;

         DWORD cbResource;

         HMODULE hModule;

         byte *lpData;

         byte *lpReadData;

         int lenOfData;

         int cbText;

        

         if(ID<1)                                 //不能修改第0个字符串

                   return FALSE;

         iGroup = ID/16 + 1;           //计算字符串所在的串组

         hModule = ::LoadLibrary(strResExePath);

          //找到这个串组

         hObject = ::FindResourceEx(hModule,RT_STRING,MAKEINTRESOURCE(iGroup),LangID );

         hResourceData = ::LoadResource(hModule, hObject);   //加载串组

         lpResourceData = ::LockResource(hResourceData);       //返回串组数据的地址

         cbResource = ::SizeofResource(hModule, hObject);        //获取串组数据长度(字节)

 

         if(cbResource <= 32 || hObject == 0)                                   //串组不存在,则增加一个串组

         {

                   cbResource = 32 + strNew.GetLength()*2;//串组至少2*16=32字节长度(全空)

                   lpData = new byte[cbResource];

                  

                   lenOfData = cbResource;

                   cbText = strNew.GetLength();                                      //串的字符数

 

                   //写入串长度,此时前面的串都是空串,各占据2个字节

                   ::CopyMemory((lpData + 2*(ID-(iGroup-1)*16)), &cbText, 2);

                   if(strNew.GetLength())

                   {

                            ::CopyMemory(lpData+2*(ID-(iGroup-1)*16)+2,

                                     strNew.GetBuffer(), strNew.GetLength()*2); //写入串内容

                   }

         }

         else //串组中有串非空,则修改

         {

                   lpReadData = new byte[cbResource];

                   lenOfData = cbResource;

                   ::CopyMemory(lpReadData, lpResourceData, cbResource);//复制串组原来的内容

                   long countX = 0;

                   int CHECKLNG = 0;

                   //遍历本串组第一个串到本串前一个串

                   for(x=0; x<(ID-(iGroup-1)*16); x++)

                   {

                            ::CopyMemory(&CHECKLNG, lpReadData+countX, 2); //读出每个串的长度

                            countX = countX + 2 + CHECKLNG*2;                //指向下一个字符串

                   }

                   //至此,countX代表了本串之前的所有串所占据的空间大小(字节)

 

                   //读出要修改串修改前的长度

                   ::CopyMemory(&CHECKLNG, lpReadData+countX, 2);

                   //至此,CHECKLNG代表修改前本串的长度

 

                   lpData = new byte[countX+2+strNew.GetLength()*2 + //从开头到本串(修改后)结束的长度

                            ( cbResource-(countX+2 + CHECKLNG*2)) ]; //从本串后一个串开始到结束的总长度

                   ::CopyMemory(lpData, lpReadData, countX); //读入开头到本串前一个串结束

                   cbText = strNew.GetLength();

                   ::CopyMemory(lpData+countX, &cbText, 2);  //写入修改后串长度

                  if(cbText)

                   {

                            //写入修改后串的内容

                            ::CopyMemory(lpData+countX+2, strNew.GetBuffer(), cbText*2);

                   }

                   if( (cbResource - (countX+2+CHECKLNG*2)) >0 )     //修改前,本串后面还有串

                   {

                            ::CopyMemory(lpData+countX+2+cbText*2,

                                                lpReadData+countX+2+CHECKLNG*2,                                                                                                                cbResource-(countX+2+CHECKLNG*2));//原来的内容保留

 

                   }

         }

 

         ::FreeLibrary(hModule);

         hResource = BeginUpdateResource(strResExePath, FALSE);

         ret = UpdateResource(hResource, RT_STRING, MAKEINTRESOURCE(iGroup),

                                                        LangID, lpData, lenOfData);

         ret = ::EndUpdateResource(hResource, FALSE);

 

         return ret;

}

相关文章推荐

改写dll字符串资源

建一个dll文件。打开vc,   一  File->New->Win32 Dynamic-link Library         Project name 输入dlltest ->ok->A sim...

获取exe和dll里面的资源

有时候需要仿照另一个程序实现一些对话框,比较笨的办法是打开那个程序,照着样子自己在VC里面画啊画。这样的效率实在有点低。现在有很多工具可以从exe和dll里面取出图片、图片、字符串、对话框等资源。比如...
  • wwwsq
  • wwwsq
  • 2014年02月12日 15:41
  • 4043

字符串资源的使用

添加字符串资源  保存  根据字符串ID 调用     LoadString, 得到字符串.

判断“资源字符串”是否可以构成“目标字符串”

题目:Ransom Note描述:Given an arbitrary ransom note string and another string containing letters from al...

MFC窗口程序显示命令行输出窗口的方法

MFC窗口程序显示命令行输出窗口的方法

【进程处理】4,刷新任务栏托盘区,清除异常关闭进程的残留图标

1,目的 之前介绍了枚举进程并关闭进程的方法,可是有一个问题,使用TerminateProcess关闭的进程如果生成过托盘图标,这个图标就会停留在右下角的托盘区(因为该进程来不及执行自己的图标清理...
  • dpsying
  • dpsying
  • 2014年02月28日 15:49
  • 6646

DLL和EXE加载包在自身内部的资源文件

1、插入资源的方法       insert-->resource-->import,在出现的对话框中选择一个要作为资源的文件,接着在出现的对话框中Resource type:下面的框中随便自己输入...

DLL和EXE如何读取包含在自身的资源文件

     1、插入资源的方法      insert-->resource-->import,在出现的对话框中选择一个要作为资源的文件,接着在出现的对话框中Resource type:下面的框中随便自...
  • kaylc
  • kaylc
  • 2011年02月23日 22:45
  • 3147

exeScope软件修改exe或dll文件资源-20150818

材料准备:exeScope软件 例程:用2.dll中的位图替换1.dll中的位图(或用同样格式的图片替换) 步骤一:先用exeScope软件先打开2.dll文件,选中要需要的位图          ...

获取.exe/.dll/.icon文件的Icon图标资源

获取.exe/.dll/.icon文件的Icon图标资源
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于exe或dll文件中的字符串资源
举报原因:
原因补充:

(最多只允许输入30个字)