-
-
易程序格式分析[原创]
-
发表于: 2006-7-29 19:31 8368
-
【文章标题】易程序格式分析
【文章作者】nohacks(非安全,hacker0058)
【作者主页】hacker0058.ys168.com
【文章出处】看雪论坛(bbs.pediy.com),易论坛
先看下吴涛给我们的易程序格式说明:
==========================================
--/*
!!! 版权声明:
本文件及其中所有实例的版权均为易语言作者吴涛所有,仅授权给第三方用作了
解易语言相关技术,禁止用于其他任何场合。
*/
一、易语言3.X可执行数据格式技术资料:
为了支持跨平台,易语言定义有自己的可执行文件格式,编译器会把易程序编译
为此种格式的数据,当需要在特定操作系统上运行时,连接器会使用该操作系统的本
地格式对易格式可执行数据进行封装(在Windows系统中将封装到EXE文件的.ecode代
码段中),成为可以在本地执行的可执行文件。
具体格式定义如下:
// 编译后的易格式可执行数据头信息
typedef struct
{
#define NEW_E_APP_MARK 0x454E5457 // 'WTNE'
DWORD m_dwMark; // 程序标记,应该为 NEW_E_APP_MARK 。
#define E_MARK_TEXT " / MADE BY E COMPILER - WUTAO "
char m_chMark [32]; // 用作放置易语言的说明文本E_MARK_TEXT。
INT m_nHeaderSize; // 本头信息的尺寸,为 sizeof (APP_HEADER_INFO) + 附加数据尺寸
INT m_nVersion; // 程序版本,从1开始。
INT m_nType; // 程序类型,为 PT_DEBUG_RUN_VER 或 PT_RELEASE_RUN_VER 。
DWORD m_dwState; // 程序的状态标志。
DWORD m_Reserved; // 保留
INT m_nDllCmdCount; // 易程序中定义的DllCmd数目。
// 程序启动入口点的机器代码偏移(相对于本头信息首)。
INT m_nStartCodeOffset;
/////////////////////////////////// 段信息
// 下面宏指定标准段的段名。
#define SN_CONST _T("@const")
#define SN_FORM _T("@form")
#define SN_HELPFUNC _T("@hlpfn")
#define SN_CODE _T("@code")
#define SN_VAR _T("@var")
// 记录用作快速定位的段信息位置,所有偏移位置均相对相对于本头信息首,
// 如果该段不存在,则为 -1 。
INT m_nConstSectionOffset; // 常量数据段位置的偏移量
INT m_nWinFormSectionOffset; // 窗口模板数据段位置的偏移量
/* 接口数据段位置的偏移量,接口数据段用作易程序获取来自支持库的支持 */
INT m_nHelpFuncSectionOffset;
INT m_nCodeSectionOffset; // 代码数据段位置的偏移量
INT m_nVarSectionOffset; // 未初始化全局变量数据段位置的偏移量
// 记录所有段信息的链首。
// 本成员提供首段的 SECTION_INFO 信息相对于本头信息首的偏移量,如无任何段,为-1。
INT m_nBeginSectionOffset;
// 1、INT m_nDllFileNameConstOffset [m_nDllCmdCount]; // 为在常量段中的偏移。
// 2、INT m_nDllCmdNameConstOffset [m_nDllCmdCount]; // 为在常量段中的偏移。
// 3、顺序存放程序中所有被使用支持库的支持库指定串,以空文本串结束。
}
APP_HEADER_INFO, *PAPP_HEADER_INFO;
// 用作记录段数据中的重定位信息项。
typedef struct
{
// 重定位信息基址类别(宏值不可再改变)。
#define RT_HELP_FUNC 0 // 相对于接口数据段段数据基址重定位
#define RT_CONST 1 // 相对于常量数据段段数据基址重定位
#define RT_GLOBAL_VAR 2 // 相对于全局变量数据段段数据基址重定位
#define RT_CODE 3 // 相对于代码数据段段数据基址重定位
unsigned m_btType : 3;
// 指定相对某段段数据首的一个偏移INT的位置,该INT内的值在重定位时必须加上由m_btType说明的基址。
unsigned m_dwOffset: 29;
}
RELOCATION_INF, *PRELOCATION_INF;
// 程序数据段信息。
typedef struct
{
INT m_nSectionSize; // 本段信息的尺寸,为 sizeof (SECTION_INFO) + 所有附加数据尺寸。
// 记录下一数据段相对程序头信息首的偏移量,如本段为最后一段,此成员应为-1。
INT m_nNextSectionOffset;
#define SCN_READ (1 << 0) // 本段数据是可读的。
#define SCN_WRITE (1 << 1) // 本段数据是可写的。
#define SCN_EXECUTE (1 << 2) // 本段数据包含可执行代码。
#define SCN_DISCARDABLE (1 << 3) // 本段数据在程序载入完毕后即可被抛弃。
#define SCN_EXTEND (1 << 4) // 本段数据载入后尺寸将被扩充(扩充算法为简单
// 地附加被初始化为0的数据空间)。
DWORD m_dwState; // 记录本段的状态标志,见SCN宏。
#define MAX_SECTION_NAME_LEN 20
char m_szName [MAX_SECTION_NAME_LEN + 4]; // 段名。
/* 段数据的载入后尺寸;
如段具有SCN_EXTEND标志,此成员记录段数据将被扩充到的尺寸。
否则等同于m_nRecordSize; */
INT m_nLoadedSize;
/* 段数据的记录尺寸(实际记录在文件中的数据尺寸),有可能为0。
如段具有SCN_EXTEND标志,此成员记录段数据被扩充前的尺寸。
否则等同于段数据的载入后尺寸。 */
INT m_nRecordSize;
// 段数据的偏移位置(相对于程序头信息首),如果没有记录段数据(即m_nRecordSize为0),则为-1。
INT m_nRecordOffset;
//////////////////////////
INT m_nReLocationItemCount; // 段数据内所有需要重定位的偏移INT的数目。
INT m_nExportSymbolCount; // 本段输出的符号数目。
/* 后面顺序为:
// 记录具体的所有重定位项。
RELOCATION_INF m_aryReLocationItem [m_nReLocationItemCount];
// 所输出符号的对应数据基于段数据首的位置偏移。
INT m_arySymbolDataOffset [m_nExportSymbolCount];
// 顺序存放所有输出符号名称基于段数据首的位置偏移,与m_arySymbolDataOffset相对应。
INT m_szarySymbolNameOffset [m_nExportSymbolCount];
*/
}
SECTION_INFO, *PSECTION_INFO;
/**********************************************************************************/
飞扬软件工作室 吴涛
2003年7月11日
====================================================================
根据上面这段说明,我们知道在Windows系统中,易编译器会把可执行数据封装到EXE文件的.ecode代
码段中),成为可以在本地执行的可执行文件。也就是说易数据的就是从.ecode开始的!
根据说明,文件头(APP_HEADER_INFO)应该是这样的:
================================================
.数据类型 APP_HEADER_INFO, , SIZE:14*4+32*1=88
.成员 m_dwMark, 整数型, , , 程序标记,应该为 NEW_E_APP_MARK
.成员 m_chMark, 字节型, , "32", 用作放置易语言的说明文本E_MARK_TEXT
.成员 m_nHeaderSize, 整数型, , , 本头信息的尺寸,为 sizeof (APP_HEADER_INFO) + 附加数据尺寸
.成员 m_nVersion, 整数型, , , 程序版本,从1开始
.成员 m_nType, 整数型, , , 程序类型,为 PT_DEBUG_RUN_VER 或 PT_RELEASE_RUN_VER
.成员 m_dwState, 整数型, , , 程序的状态标志
.成员 m_Reserved, 整数型, , , 保留
.成员 m_nDllCmdCount, 整数型, , , 易程序中定义的DllCmd数目
.成员 m_nStartCodeOffset, 整数型, , , 程序启动入口点的机器代码偏移(相对于本头信息首)
.成员 m_nConstSectionOffset, 整数型, , , 常量数据段位置的偏移量
.成员 m_nWinFormSectionOffset, 整数型, , , 窗口模板数据段位置的偏移量
.成员 m_nHelpFuncSectionOffset, 整数型, , , 接口数据段位置的偏移量,接口数据段用作易程序获取来自支持库的支持
.成员 m_nCodeSectionOffset, 整数型, , , 代码数据段位置的偏移量
.成员 m_nVarSectionOffset, 整数型, , , 未初始化全局变量数据段位置的偏移量
.成员 m_nBeginSectionOffset, 整数型, , , 本成员提供首段的 SECTION_INFO 信息相对于本头信息首的偏移量,如无任何段,为-1。
.数据类型 APP_HEADER_INFO续
.成员 m_nDllFileNameConstOffset, 短整数型, , "1", m_nDllFileNameConstOffset [m_nDllCmdCount],DLL名为在常量段中的偏移,成员不固定!
.成员 m_nDllCmdNameConstOffset, 短整数型, , "1", m_nDllCmdNameConstOffset [m_nDllCmdCount],DLL命令名在常量段中的偏移,成员不固定!
.成员 支持库指定串, 文本型, , , 顺序存放程序中所有被使用支持库的支持库指定串,以空文本串结束
=======================================================
节数据应该是这样:
=======================================================
.数据类型 SECTION_INFO, , SIZE:8*4+24*1=56
.成员 m_nSectionSize, 整数型, , , 本段信息的尺寸,为 sizeof (SECTION_INFO) + 所有附加数据尺寸
.成员 m_nNextSectionOffset, 整数型, , , 记录下一数据段相对程序头信息首的偏移量,如本段为最后一段,此成员应为-1
.成员 m_dwState, 整数型, , , 记录本段的状态标志
.成员 m_szName, 字节型, , "24", 段名
.成员 m_nLoadedSize, 整数型, , , 段数据的载入后尺寸
.成员 m_nRecordSize, 整数型, , , 段数据的记录尺寸
.成员 m_nRecordOffset, 整数型, , , 段数据的偏移位置(相对于程序头信息首),如果没有记录段数据(即m_nRecordSize为0),则为-1
.成员 m_nReLocationItemCount, 整数型, , , 段数据内所有需要重定位的偏移INT的数目
.成员 m_nExportSymbolCount, 整数型, , , 本段输出的符号数目。
.数据类型 SECTION_INFO续
.成员 m_aryReLocationItem, RELOCATION_INF, , "1", 记录具体的所有重定位项,m_aryReLocationItem [m_nReLocationItemCount],成员不固定!
.成员 m_arySymbolDataOffset, 短整数型, , "1", m_arySymbolDataOffset [m_nExportSymbolCount],所输出符号的对应数据基于段数据首的位置偏移,成员不固定!
.成员 m_szarySymbolNameOffset, 短整数型, , "1", m_szarySymbolNameOffset [m_nExportSymbolCount],顺序存放所有输出符号名称基于段数据首的位置偏移,与m_arySymbolDataOffset相对应,成员不固定!
============================================================
上面2个数据类型中,我把不固定大小的成员分开了,只是为了方便程序调用,实际上是连在一起的!
.DLL命令 复制E头, , , "RtlMoveMemory"
.参数 目标区块, APP_HEADER_INFO
.参数 源区块, 整数型
.参数 尺寸, 整数型, , SIZE=88
.DLL命令 复制E节表, , , "RtlMoveMemory"
.参数 目标区块, SECTION_INFO
.参数 源区块, 整数型
.参数 尺寸, 整数型, , SIZE=56
我们以后可以在程序中调用像这样的命令来,非常方便!
================================
复制E头 (APP_HEADER_INFO, ecode段开始地址, 88)
加入文本 (“ 程序标记 :” + 到文本 (APP_HEADER_INFO.m_dwMark))
加入文本 (“ 说明文本 :” + 到文本 (APP_HEADER_INFO.m_chMark))
加入文本 (“ 文件头尺寸 :” + 到文本 (APP_HEADER_INFO.m_nHeaderSize))
........
=============================================
到这里也没什么难点,就是这里要注意一下:
// 1、INT m_nDllFileNameConstOffset [m_nDllCmdCount]; // 为在常量段中的偏移。
// 2、INT m_nDllCmdNameConstOffset [m_nDllCmdCount]; // 为在常量段中的偏移。
// 3、顺序存放程序中所有被使用支持库的支持库指定串,以空文本串结束。
上面说的有点含糊不清,实际上这里的常量段中的偏移是指常量段中附加数据段的偏移,也就是:
偏移=常量段中的偏移(m_nConstSectionOffset)+常量段的长度(m_nSectionSize)
还有就是支持库指定串应该是这样的结构:(注:字节13也就是回车符)
文件名+{13}+数字签名 +{13}+主版本号+{13}+副版本号+{13}+中文说明+{0)
最后以全0结束!
好了,难点都说了,下面是易程序源码:
(执行文件体积太大,我只放易源码)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!