【文章标题】易程序格式分析
【文章作者】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结束!