LoadPE 被加载PE文件代码分析(ASM汇编版本)>>03

📅 2026/6/20 10:28:03
LoadPE  被加载PE文件代码分析(ASM汇编版本)>>03
目录一、整体加载流程概览二、详细实现步骤汇编思路阶段0: OEP环境设置引用等确定OEP加载程序需要加载的程序大小编译器链接器解决基地址(到时候在看看)申请LoadPE所需要的空间加载的过程包引用环境变量等查看基地址是否在400000地址加载定义全局变量三、阶段分析阶段 1打开目标文件阶段 2 3文件映射到虚拟内存阶段 4解析 PE 结构核心阶段阶段 5跳到目标程序运行四、汇编实现的核心特点五、总结一、整体加载流程概览LoadPE 的加载过程主要分为5 个大阶段共 10 余个具体步骤打开文件映射虚拟内存获取虚拟内存解析 PE 结构最核心、最复杂的一步跳到目标程序运行二、详细实现步骤汇编思路阶段0: OEP环境设置引用等确定OEP加载程序首先要确定加载的程序的入口点也就是OEP但是加载到目标主机 可能0x40000被占用所以我们loadPE需要解决这个问题需要加载的程序大小可选头里面有个sizeofImage 394000也就是这个大小编译器链接器解决基地址(到时候在看看)工程选项 设置链接器申请LoadPE所需要的空间也就是在程序入口点code的位置后面申请394000H 字节的空间给我的 LoadPE函数加载的程序IMAGE_SIZE EQU 394000H .code ORG IMAGE_SIZE ; 也就是LoadPE函数的其实位置是394000H 1H LoadPE PROC ; Function ret LoadPE endp加载的过程包引用环境变量等使用的模式.386 .model flat,c option casemap:none引用的宏静态库.const ; constant IMAGE_SIZE EQU 394000H .data ; data g_szfile db PlantsVsZombies.exe ; file Name .code ORG IMAGE_SIZE LoadPE PROC ; Function ret LoadPE endp start: ; main INVOKE LoadPE ; call function end start查看基地址是否在400000地址加载这也就说明成功加载到了0x00400000的位置400000以后的位置都预留好了 --- 394000定义全局变量LOCAL hFILE:HANDLE LOCAL hFileMap:HANDLE LOCAL szPeBuffer:LPVOID LOCAL pDosHeader:PTR IMAGE_DOS_HEADER LOCAL pNtHeader:PTR IMAGE NT HEADERS LOCAL pSecetionHeader:PTR IMAGE_SECTION_HEADER LOCAL dwNumberSec:DWORD LOCAL dwSizeOfHeader:DWORD LOCAL dwEP:DWORD LOCAL plmagelmpHeader:PTR IMAGE_IMPORT_DESCRIPTOR LOCAL ZeroHeadImp: IMAGE IMPORT DESCRIPTOR LOCAL dwImageBase:DWORD LOCAL dwMod:HANDLE三、阶段分析阶段 1打开目标文件使用CreateFileAPI 打开目标程序文件例如PlantsVsZombies.exe。以只读方式打开获取文件句柄hFILE。阶段 2 3文件映射到虚拟内存调用CreateFileMapping创建一个文件映射对象。调用MapViewOfFile将文件内容映射到进程的虚拟内存空间得到内存指针szPeBuffer。此时目标 PE 文件的内容以只读的形式存在于内存中供后续解析使用。阶段 4解析 PE 结构核心阶段这是整个 LoadPE 中最重要、最体现汇编功底的部分需要一步步手动解析 PE 文件格式4.1 获取 DOS 头将映射的内存首地址赋值给pDosHeader。通过e_lfanew字段定位到 PE 头的位置。4.2 获取 NT 头根据 DOS 头的e_lfanew偏移找到IMAGE_NT_HEADERS。4.3 获取文件头大小SizeOfHeaders从 NT 头的 OptionalHeader 中读取SizeOfHeaders用于后续拷贝头部。4.4 获取节数量NumberOfSections从 FileHeader 中读取节的数量后续用于遍历所有节。4.5 获取程序入口点AddressOfEntryPoint读取 OptionalHeader 中的AddressOfEntryPointRVA并加上 ImageBase得到最终入口点地址。4.6 获取导入表Import Directory从 DataDirectory 中找到导入表的位置IMAGE_DIRECTORY_ENTRY_IMPORT。由于植物大战僵尸是需要api函数由于我们是脱了操作系统那就没人帮我获取请api需要自己手动解析导入表4.7 获取名称表和 IAT 表遍历IMAGE_IMPORT_DESCRIPTOR结构分别获取 DLL 名称 RVA 和 FirstThunkIAT、OriginalFirstThunkINT。4.8 加载所需的 DLL对于每个需要导入的 DLL调用 LoadLibrary 将其加载到当前进程。填充IAT表4.9 修复导入地址表IAT遍历 IAT 中的每个函数如果是序号导入则直接使用序号。如果是名称导入则通过 GetProcAddress 获取函数地址。将获取到的真实函数地址填充回目标程序的 IAT 表中使其能正常调用系统 API。阶段 5跳到目标程序运行对目标加载地址通常为00400000进行VirtualProtect修改内存保护属性为PAGE_EXECUTE_READWRITE。将解析好的 PE 头部和所有节数据拷贝到目标地址空间。完成所有准备工作后使用JMP指令直接跳转到目标程序的入口点dwEP。此时控制权完全交给目标程序LoadPE 的使命完成。四、汇编实现的核心特点全程手动操作几乎所有 PE 结构字段都需要通过寄存器偏移手动读取和计算。寄存器密集由于汇编无法直接进行内存到内存的操作大量使用MOV、ADD、LEA等指令以及ASSUME伪指令来模拟结构体。内存精确控制通过VirtualProtectcrt_memcpy实现对内存的精准拷贝和权限管理。地址固定技巧使用ORG IMAGE_SIZE将自身代码推后为目标程序的 ImageBase00400000预留足够空间。五、总结用汇编实现 LoadPE 的本质是自己当一次 Windows Loader—— 手动打开文件、映射内存、解析 PE 头、拷贝数据、修复导入表最后把执行权交给目标程序。整个过程虽然代码量较大、逻辑繁琐但赋予了开发者对加载流程的完全控制权为后续的免杀优化例如手动导出表解析、减少敏感 API 调用、直接 syscall 等提供了坚实的基础。这就是为什么许多高隐蔽 Loader 都倾向于使用汇编来实现核心加载逻辑。