自定义 PE 可执行文件 - Windows 10 无法执行运行给定文件

问题描述 投票:0回答:1

我正在尝试为 win10 64 位编写一个自定义 PE 文件导出器,在添加对齐后我遇到了一些麻烦。我创建了一个基本测试用例:一个包含“ret”指令的 .exe 文件:

std::vector<unsigned char> text_bytecode = {
    0xC3 // 'ret'
};

pe_builder builder;
builder.add_section(
    ".text",
    text_bytecode,
    windows::section_header_characteristics::mem_execute | 
    windows::section_header_characteristics::mem_read |
    windows::section_header_characteristics::cnt_code |
    windows::section_header_characteristics::align_16_bytes
);

builder.emit_to_file(path);

这是PE构建器的后端:

u64 align(u64 value, u64 alignment) {
    return (value + alignment - 1) & ~(alignment - 1);
}

pe_builder::pe_builder() {
    // initialize the dos header
    m_dos_header.magic = 0x5A4D;
    m_dos_header.cparhdr = 4;
    m_dos_header.lfanew = sizeof(windows::dos_header); // place the NT headers directly behind the DOS header

    // NT headers
    // technically, the PE signature goes here

    // initialize the file header
    m_file_header.machine = windows::get_machine_type();
    m_file_header.section_count = 0;
    m_file_header.optional_header_size = sizeof(windows::optional_header_64_bit);
    m_file_header.characteristics = windows::file_header_characteristics::executable;

    // initialize the optional header
    m_optional_header.magic = 0x020B; // PE32+
    m_optional_header.section_alignment = 4096;
    m_optional_header.file_alignment = 512;
    m_optional_header.major_linker_version = 14;
    m_optional_header.minor_linker_version = 0;
    m_optional_header.major_os_version = 6; // win10 compatibility
    m_optional_header.minor_os_version = 0;
    m_optional_header.major_image_version = 1;
    m_optional_header.major_subsystem_version = 6; // win10 compatibility
    m_optional_header.image_base = 0x140000000;
    m_optional_header.win32_version_value = 0;
    m_optional_header.subsystem = 3; // windows console subsystem
    m_optional_header.DLL_characteristics = 0x8160; // NX-Compatible and Terminal Server Aware
    m_optional_header.stack_reserve_size = 0x100000;
    m_optional_header.stack_commit_size = 0x1000;
    m_optional_header.heap_reserve_size = 0x100000;
    m_optional_header.heap_commit_size = 0x1000;
    m_optional_header.loader_flags = 0;
    m_optional_header.RVA_and_size_count = 0;

    u64 header_size =
        sizeof(windows::dos_header) +
        sizeof(g_pe_signature) +
        sizeof(windows::file_header) +
        sizeof(windows::optional_header_64_bit);

    m_optional_header.entry_point_address = align(header_size, m_optional_header.section_alignment);
    m_optional_header.code_base = m_optional_header.entry_point_address;
}

void pe_builder::add_section(
    const std::string& name,
    std::vector<unsigned char> data,
    windows::section_header_characteristics characteristics
) {
    m_file_header.section_count++;

    // If it's a code section, update the SizeOfCode in the optional header
    if ((characteristics & windows::section_header_characteristics::cnt_code) != 0) {
        m_optional_header.code_size += data.size();
    }

    windows::section_header new_section = {};
    memcpy(new_section.name, name.c_str(), std::min<size_t>(name.size(), 8));

    new_section.virtual_size = data.size();
    new_section.raw_data_size = align(data.size(), m_optional_header.file_alignment);

    if (m_section_data.empty()) {
        new_section.raw_data_pointer = align(
            sizeof(windows::dos_header) +
            sizeof(g_pe_signature) +
            sizeof(windows::file_header) +
            sizeof(windows::optional_header_64_bit) +
            sizeof(windows::section_header) * m_file_header.section_count,
            m_optional_header.file_alignment
        );
        new_section.virtual_address = m_optional_header.entry_point_address;
    }
    else {
        const auto& last_section_header = m_sections.back();
        new_section.raw_data_pointer = last_section_header.raw_data_pointer +
            align(m_section_data.back().size(), m_optional_header.file_alignment);
        new_section.virtual_address = last_section_header.virtual_address +
            align(m_section_data.back().size(), m_optional_header.section_alignment);
    }

    new_section.characteristics = characteristics;
    m_sections.push_back(new_section);
    m_section_data.push_back(data);
}

void pe_builder::emit_to_file(
    const filepath& path
) {
    // Open the output file in binary mode
    std::ofstream file(path, std::ios::binary);

    if (!file.is_open()) {
        // return false; // Failed to open the file
    }

    file.write(reinterpret_cast<char*>(&m_dos_header), sizeof(m_dos_header));
    file.write(reinterpret_cast<const char*>(&g_pe_signature), sizeof(g_pe_signature));
    file.write(reinterpret_cast<char*>(&m_file_header), sizeof(m_file_header));
    file.write(reinterpret_cast<char*>(&m_optional_header), sizeof(m_optional_header));

    for (size_t i = 0; i < m_sections.size(); ++i) {
        // Write the section header
        file.write(reinterpret_cast<const char*>(&m_sections[i]), sizeof(m_sections[i]));

        // Assuming m_section_data is a vector of vectors (or similar container) storing data for each section
        const auto& section_data = m_section_data[i];

        // Write the section data
        file.write(reinterpret_cast<const char*>(section_data.data()), section_data.size());
    }

    // Close the file
    file.close();

    m_dos_header.print();
    m_file_header.print();
    m_optional_header.print();
    for(const auto& section : m_sections) {
        section.print();
    }
}

对于任何想知道我已经打印了最终值的人(请注意,它们被打印为各自的 int 对应项,因此缺少十六进制格式,尽管我还应该注意,十六进制值和标志应该都是正确的,因为我创建了一个与他们一起工作的 MVP)。

dos header:
  magic:    23117
  cblp:     0
  cp:       0
  crlc:     0
  cparhdr:  4
  minalloc: 0
  maxalloc: 0
  ss:       0
  sp:       0
  csum:     0
  ip:       0
  cs:       0
  lfarlc:   0
  ovno:     0
  res:      0 0 0 0
  oemid:    0
  oeminfo:  0
  res2:     0 0 0 0 0 0 0 0 0 0
  lfanew:   64
file header:
  machine:                 34404
  section_count:           1
  time_date_stamp:         0
  pointer_to_symbol_table: 0
  symbol_count:            0
  optional_header_size:    240
  characteristics:         2
optional header (64 bit):
  magic:                   523
  major_linker_version:    14
  minor_linker_version:    0
  code_size:               1
  initialized_data_size:   0
  uninitialized_data_size: 0
  entry_point_address:     4096
  code_base:               4096
  image_base:              5368709120
  section_alignment:       4096
  file_alignment:          512
  major_os_version:        6
  minor_os_version:        0
  major_image_version:     1
  minor_image_version:     0
  major_subsystem_version: 6
  minor_subsystem_version: 0
  win32_version_value:     0
  image_size:              0
  header_size:             0
  check_sum:               0
  subsystem:               3
  DLL_characteristics:     33120
  stack_reserve_size:      1048576
  stack_commit_size:       4096
  heap_reserve_size:       1048576
  heap_commit_size:        4096
  loader_flags:            0
  RVA_and_size_count:      0
  data_directories:
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
    virtual address: 0, size: 0
section header:
  name:                .text
  virtual_size:        1
  virtual_address:     4096
  raw_data_size:       512
  raw_data_pointer:    512
  relocation_pointer:  0
  line_number_pointer: 0
  relocation_count:    0
  line_number_count:   0
  characteristics:     32
c++ windows executable low-level
1个回答
0
投票

我还编写过一次 exe 导出器,以下是我观察到的输出中的一些异常情况:

  1. 将你的pe头特征设置为35而不是2。你只指定了IMAGE_FILE_EXECUTABLE_IMAGE,但你没有重定位表,所以你需要IMAGE_FILE_RELOCS_STRIPPED,你仍然需要IMAGE_FILE_EXECUTABLE_IMAGE,我会推荐IMAGE_FILE_LARGE_ADDRESS_AWARE用于64位应用程序。

  2. 在可选的 pe 标头中,将 SizeOfCode 设置为代码部分的对齐大小。如果代码部分只有一条指令也没关系。将 SizeOfCode 设置为节的总大小(它是SectionAlign 的倍数)。 *实际的指令数量由 .text 部分的 VirtualSize 反映,对于单个 ret 指令,VirtualSize 应保持为 1。

  3. 在可选的 pe 标头中,ImageSize 和 HeaderSize 不应为零。对于简单的测试,我会推荐静态值!对于 HeaderSize,我通常使用 1024 字节,因为所有标头都适合该空间,并且它是 FileAlign 的倍数。 ImageSize 可以是 SectionAlign * Numbers of Sections + HeaderSize,这样每个部分最多可达SectionAlign 字节 + 所有标题的一些空间。 请注意,ImageSize 的静态值仅用于测试,不应在生产环境中使用!

  4. 将 NumberOfRvaAndSizes 设置为 16,即使它们只是零,它们仍然存在。

  5. 将 .text 部分的特征设置为 1610612768 而不是 32。您需要 IMAGE_SCN_CNT_CODE、IMAGE_SCN_MEM_EXECUTE 和 IMAGE_SCN_MEM_READ。

希望您能找到问题所在!如果有其他意见,我很乐意更新我的答案。如果可能的话,您可以将导出的 exe 作为二进制文件附加,我将检查结构。

© www.soinside.com 2019 - 2024. All rights reserved.