You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅用Windows内置工具生成输出Hello World的.exe并自制简易编译器

针对你的简易编译器开发问题的解决方案

一、将词法分析结果转换为机器码

你的核心需求是把print("Hello world!")这类高级指令转成x86_64机器码,且不依赖第三方汇编器,以下是具体步骤:

1. 映射高级指令到Win32 API调用

print本质是向控制台输出字符串,对应Win32的WriteConsoleA API,需要先通过GetStdHandle获取标准输出句柄,所以指令序列大致是:

1. 获取标准输出句柄:GetStdHandle(STD_OUTPUT_HANDLE)
2. 调用WriteConsoleA输出字符串
3. 退出程序

2. 手动编码x86_64机器码

x86_64指令由操作码、ModR/M字节、立即数等组成,需参考Intel x86_64指令集手册编码。比如:

  • mov rcx, 0xFFFFFFF5(设置GetStdHandle的参数,STD_OUTPUT_HANDLE的值)对应的机器码是48 C7 C9 F5 FF FF FF
  • call qword ptr [rip+0x0](调用导入表中的GetStdHandle)对应的机器码是FF 15 00 00 00 00

3. 处理动态链接(关键)

Windows API地址会随系统版本/ASLR随机化变化,不能硬编码地址,必须通过PE文件的导入表让系统加载时自动解析API地址:

  • 定义导入描述符指向kernel32.dll
  • 写入导入名称表(INT)和导入地址表(IAT),分别存储API名称和系统填充的实际地址

二、生成可执行文件并运行机器码

Windows仅识别PE(Portable Executable)格式的可执行文件,你需要用C++手动构造PE结构并写入磁盘:

1. 构造PE文件结构

PE文件包含以下核心部分,你需要用二进制字节数组逐个构建:

  • DOS头:以MZ开头,指定PE头的偏移位置
  • PE头:标记文件为x86_64架构,指定节数量等信息
  • 可选头:定义入口点、镜像基址、节对齐方式等
  • 节表:定义代码段(.text)、数据段(.data)的位置和属性
  • 节数据:存放机器码、字符串、导入表等实际内容

2. 用C++生成PE文件示例

以下是极简的C++代码,直接生成可打印Hello world!的PE文件:

#include <fstream>
#include <vector>

int main() {
    std::vector<unsigned char> pe_data;

    // 1. DOS头
    pe_data.insert(pe_data.end(), {
        0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00,
        0x04, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xB8, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    });

    // 2. PE头+可选头(简化版)
    pe_data.insert(pe_data.end(), {
        0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x02, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xE0, 0x00, 0x02, 0x20, 0x0B, 0x02, 0x10, 0x00,
        0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00,
        0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
        0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00,
        0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    });

    // 3. 节表
    pe_data.insert(pe_data.end(), {
        '.', 't', 'e', 'x', 't', 0, 0, 0, 0x20, 0x00, 0x00, 0x00,
        0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA0, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x60, 0x00, 0x00, 0x00, '.', 'd', 'a', 't', 'a',
        0, 0, 0, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
        0x10, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC0, 0x00, 0x00, 0x00
    });

    // 4. .text节机器码
    pe_data.insert(pe_data.end(), {
        0x48, 0xC7, 0xC9, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x00,
        0x48, 0x89, 0xC1, 0x48, 0xC7, 0xC2, 0x30, 0x00, 0x00, 0x00, 0x49, 0xC7, 0xC0,
        0x0D, 0x00, 0x00, 0x00, 0x49, 0xC7, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x00,
        0xFF, 0x15, 0x08, 0x00, 0x00, 0x00, 0x48, 0x31, 0xC0, 0xC3
    });
    while (pe_data.size() < 0xA0 + 0x20) pe_data.push_back(0x90);

    // 5. .data节数据(字符串+导入表)
    pe_data.insert(pe_data.end(), {
        'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', 0, 0, 0, 0,
        0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x50, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'k', 'e', 'r', 'n', 'e',
        'l', '3', '2', '.', 'd', 'l', 'l', 0, 0, 0, 0, 0x00, 0x00, 'G', 'e', 't',
        'S', 't', 'd', 'H', 'a', 'n', 'd', 'l', 'e', 0, 0x00, 0x00, 'W', 'r', 'i',
        't', 'e', 'C', 'o', 'n', 's', 'o', 'l', 'e', 'A', 0
    });

    // 写入exe文件
    std::ofstream out("hello.exe", std::ios::binary);
    out.write(reinterpret_cast<const char*>(pe_data.data()), pe_data.size());
    out.close();

    return 0;
}

3. 编译并运行

  • 用Windows自带的cl.exe(需打开Developer Command Prompt)编译上述代码:cl /EHsc pe_generator.cpp
  • 运行生成的pe_generator.exe,会输出hello.exe
  • 直接双击或在cmd中运行hello.exe即可看到输出

4. 调试验证

  • 用Windows自带的dumpbin.exe检查PE结构:dumpbin /headers hello.exe
  • 若程序崩溃,可通过事件查看器的“Windows日志->应用程序”查看错误信息

内容的提问来源于stack exchange,提问作者GoogleCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 23:04:53