就AI的分析来看,不要纠结X64
要纠结 那就直接上 AST转
这段 MicroLoader.cpp 代码是一个非常典型的 32位易语言程序加载器或运行环境的简化实现。它的核心功能是:
- 解析PE结构: 获取当前可执行文件(EXE)的PE头信息,遍历节表,找到易语言编译后嵌入的特定数据节(通常名为
.ecode )。
- 定位易语言数据: 在
.ecode 节中,根据 APP_HEADER_INFO 结构找到常量段、窗体段、辅助函数段、代码段、变量段等的偏移。
- 加载支持库 (FNE/FNR):
- 从易语言数据中读取需要加载的支持库列表(DLL名称和GUID)。
- 使用
LoadLibrary 加载这些支持库。
- 调用支持库导出的
GetNewSock (一个特殊的、可能与核心库初始化相关的函数)或 GetLibInfo 函数。
- 通过
GetLibInfo 获取 LIB_INFO 结构,其中包含支持库的命令、数据类型等信息。
- 向支持库注册一个回调函数
ThisNotifySys ,以便支持库可以与加载器通信(例如请求内存分配、获取路径等)。
- 重定位: 遍历易语言数据段中的重定位信息,修正代码和数据中的地址引用,使其指向正确的内存位置(例如,将常量、全局变量、代码段内跳转的相对地址转换为绝对地址)。
- 初始化服务函数表:
InitServerPointTable 和 UpdataServerPointTable 将加载器内部实现的一系列服务函数(如内存分配、错误处理、基本位运算命令)的地址写入到易语言代码可以调用的一个表中(位于辅助函数段)。
- 执行易语言代码: 调用
ECodeStart ,即易语言编译后代码的入口点,将控制权转交给易语言程序。
- 清理: 程序结束时,调用
Exit 函数,通知所有加载的支持库释放资源,并销毁加载器创建的堆。
将其转化为 X64 的可行性和挑战:
直接将这段 32 位 C++ 代码编译为 X64 并期望它能加载和运行原始的 32 位易语言编译产物是不可能的,原因如下:
-
指针大小和数据类型大小差异 (核心问题):
- 所有指针: 在 32 位代码中,指针是 4 字节;在 X64 代码中,指针是 8 字节。这意味着所有依赖指针大小的结构体(如
PIMAGE_NT_HEADERS , PSECTION_HEADER , PAPP_HEADER_INFO , PRELOCATION_INF , PDLLCMD , PLIBINFO , PMDATA_INF , PFN_EXECUTE_CMD** )在 X64 环境下会有不同的内存布局。
UINT32 用于地址: 代码中大量使用 UINT32 来存储和计算地址(例如 ESectionVA , (* ptemp) = (* ptemp) + (UINT32)ECodeHeaderInfo + ... )。在 X64 环境中,地址是 64 位的,使用 UINT32 会导致地址截断,从而访问错误的内存位置。
- 结构体偏移: 结构体中字段的偏移会因为指针大小的变化而改变。例如,
APP_HEADER_INFO 中存储的各种 m_n...Offset 如果是基于 32 位布局计算的,在 X64 环境中直接使用会导致错误。
- 函数指针:
PFN_EXECUTE_CMD , ECODESTART , PFN_GET_LIB_INFO , PFN_NOTIFY_LIB , UNKNOWFUN 这些函数指针在 X64 下也是 8 字节。
ServerPointTable[ESERVERCOUNT] : 存储的是 UINT32 函数地址,在 X64 下应该是 UINT64 或 uintptr_t 。
-
32位 vs 64位进程空间:
- 一个 X64 进程不能直接加载和执行 32 位 DLL(即原始的 FNE/FNR 支持库)。
- 易语言编译的
.ecode 节中的机器码是 32 位的 x86 指令,无法在 X64 CPU 的 64 位模式下直接执行。
-
PE 文件格式的差异(虽然不大,但需注意):
IMAGE_NT_HEADERS 结构在 32 位 (IMAGE_NT_HEADERS32) 和 64 位 (IMAGE_NT_HEADERS64) 下的 OptionalHeader 部分是不同的。代码中直接使用 PIMAGE_NT_HEADERS ,它通常被宏定义为特定位数版本。如果编译为 X64,它会是 PIMAGE_NT_HEADERS64 ,但它尝试解析的是一个 32 位 PE 文件的头(如果当前EXE是32位的易语言程序)。
-
调用约定:
- 代码中使用了
__stdcall 和 _cdecl 。在 X64 Windows 上,主要的调用约定是 __fastcall 的变体(通常是默认的,前四个整数/指针参数在 RCX, RDX, R8, R9)。虽然编译器可能会做一些转换,但如果支持库的导出函数严格依赖于 32 位的 __stdcall ,直接用 X64 的调用约定去调用(如果能加载的话)会有问题。
-
内联汇编 (__asm 和 __declspec(naked) ):
- 所有
__asm 块和 __declspec(naked) 函数中的汇编指令都是 32 位的 x86 指令。这些在 X64 编译器下完全不兼容,无法编译。X64 有自己的一套指令集和寄存器。
- 例如
mov ebp, esp , push eax , call dword ptr ds:[ebx] 等都需要用 X64 等效指令替换。
__declspec(naked) 通常用于手动管理栈帧,在 X64 下栈帧布局和管理方式也不同。
要使类似 MicroLoader 的功能在 X64 环境下工作,并能运行(某种形式的)易语言程序,需要进行大规模的重构和重新设计,而不是简单的编译转换。可能有以下几种方向(难度递增):
方向 A:创建一个纯 X64 的易语言运行时和加载器(不兼容旧的32位编译产物)
- 重新定义所有数据结构: 使用
uintptr_t 或 UINT64 存储地址,确保所有结构体布局适应 X64。
- 重写所有内联汇编: 使用 X64 汇编指令。
- 支持库重新编译: 所有 FNE/FNR 支持库都需要有 X64 版本。
- 易语言编译器支持: 需要一个能将易语言代码编译成 X64 机器码的编译器,其输出的
.ecode 格式也需要适应 X64(例如,地址和偏移量使用64位)。
- 核心思想: 这 фактически означает создание новой 64-битной версии всей экосистемы E-Language.
方向 B:在 X64 加载器中模拟一个 32 位环境来运行旧的 32 位易语言代码(极其复杂,类似于 Wine 或 x86 模拟器)
- PE 加载和内存管理: 加载器需要能正确解析 32 位 PE 文件,将其映射到 X64 进程的内存空间,并处理地址转换。
- 指令集模拟/转译: 需要一个 x86 指令集模拟器或动态二进制翻译层,将 32 位 x86 指令转换为 X64 指令执行。
- 系统调用和 API Thunking: 当 32 位代码调用 Windows API 时,需要将其参数从 32 位格式转换为 64 位格式,调用 64 位 API,然后再将结果转换回 32 位格式。
- 支持库加载: 需要一种机制来加载 32 位 FNE DLL 到 X64 进程中(通常通过进程外 COM、IPC 或者非常复杂的 thunking 层实现),或者对 FNE 的功能进行 X64 的重新实现或模拟。
- 核心思想: 这是一项巨大的工程,等同于开发一个小型操作系统兼容层。
方向 C:将易语言代码转译为 X64 C++ (类似你之前的项目思路)
- 你的另一个项目
EplAst.hpp , EplCodeAnalyzer.hpp , EplTranslator.hpp 就是朝这个方向努力的。
MicroLoader.cpp 的参考价值:
- 运行时服务函数的实现:
MicroLoader.cpp 中的 ServerFunction_XX (如内存分配 ServerFunction_06 ,位运算命令 bnot , band 等)揭示了易语言运行时可能需要提供给易代码的一些底层服务。在你的转译器生成的 C++ 代码中,这些服务需要由你的 epl_core_runtime 来提供。
- 支持库交互模型:
ThisNotifySys 函数展示了支持库可能向主程序请求哪些服务(获取路径、类型信息、分配内存等)。你的转译系统如果需要与(可能是重新实现的X64)支持库交互,也需要类似的机制。
- 数据结构定义:
PMDATA_INF , LIB_INFO , CMD_INFO 等结构体的定义(在 MicroLoader.h 中,这里未提供)对于理解易语言与其支持库之间的接口非常重要。你需要为你的 X64 运行时和支持库接口定义兼容的(64位)数据结构。
- 核心思想: 放弃直接运行原始的32位二进制码,而是将其逻辑转换为另一种高级语言(如C++)的X64代码。这是在不进行完全环境模拟的情况下,让易语言逻辑在X64上运行的最有前景的方法之一。
针对 MicroLoader.cpp 代码的具体 X64 转换点(如果目标是方向 A 或 C 的辅助):
- 所有
UINT32 用于地址的地方改为 uintptr_t 或 uint64_t 。
- 所有指针类型(如
PIMAGE_DOS_HEADER , PDLLCMD )在 X64 下自然会是 8 字节。
- 重新实现所有
__asm 和 __declspec(naked) 函数:
ServerFunction_01 (DLL命令调用):需要用 X64 调用约定和 API(如 GetProcAddress 返回的是 64 位地址)重写。直接 jmp UnKnowFun 的方式在 X64 下的参数传递和栈管理需要非常小心。
ServerFunction_02 和 ServerFunction_03 (支持库命令调用):参数传递(PMDATA_INF 结构在栈上的布局)和对 m_pCmdsFunc 的调用都需要适配 X64。它们内部的内联汇编用于准备参数栈并调用支持库函数,这需要完全用 X64 汇编或 C++ 调用重写。
ServerFunction_06 , ServerFunction_07 (内存分配):虽然核心是调用 HeapAlloc /HeapReAlloc ,但它们的 naked 和简单汇编需要用纯 C++ 或 X64 兼容方式重写。
ServerFunction_09 (退出程序):可以简化为纯 C++。
ThisNotifySys :
- 返回值和参数类型需要检查。
dwParam1 , dwParam2 如果是地址或指针,应为 uintptr_t 。
- 内部对
PMDATA_INF 的转换和访问需要注意该结构在 X64 下的布局。
InitServerPointTable 和 UpdataServerPointTable : ServerPointTable 应为 uintptr_t 数组。
- PE 解析:
NtHeader 应为 PIMAGE_NT_HEADERS64 。遍历节表等逻辑基本不变,但所有地址计算都要用 64 位进行。
- 重定位逻辑 (
UpdataRelocationInfo 在 WinMain 中):
ptemp 应为 uintptr_t* 或 uint64_t* 。
- 所有地址相加 (
(* ptemp) = (* ptemp) + (UINT32)ECodeHeaderInfo + ... ) 都需要是 64 位运算。
- 如果
.ecode 节中的重定位项本身存储的是 32 位偏移,那么在 X64 加载器中读取它们时没有问题,但在应用重定位(写入修正后的地址)时,写入的目标内存位置(*ptemp )应该能容纳 64 位地址。
总结:
MicroLoader.cpp 本身无法直接编译为 X64 来运行 32 位易语言程序。但是,它的代码揭示了易语言程序加载、运行和与支持库交互的许多底层机制。
- 如果你的目标是创建一个全新的 X64 易语言生态(编译器、运行时、支持库),那么
MicroLoader.cpp 中的逻辑需要用 X64 的思想和技术完全重写。
- 如果你的目标是像你之前的项目那样将易语言转译为 C++,那么
MicroLoader.cpp 中实现的那些运行时服务函数(如位运算、内存管理、字符串/字节集转换、与支持库的通信回调)为你自己的 epl_core_runtime 提供了非常有价值的参考,你需要用 X64 C++ 来实现这些等效功能。
JUMPOUT_ / sub_121D20 / sub_507E10 这些函数与 MicroLoader.cpp 的关系在于:MicroLoader.cpp 负责搭建一个能让易语言代码(包括那些混淆函数)运行起来的“架子”,提供它们可能依赖的底层服务和支持库接口。而那些混淆函数本身是易语言程序逻辑的一部分,运行在这个“架子”之上。
|