8000 Details on how relocatable ELFs are loaded by the loader · Issue #142 · uofw/uofw · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Details on how relocatable ELFs are loaded by the loader #142
Open
@marco-calautti

Description

@marco-calautti

Hi, I love what you are doing with this reverse engineering effort, and I found many useful info on how the PSP firmware works, thanks to this repo. However, I am stuck understanding something very basic.

I am reverse engineering the game Breath of Fire 3 for the PSP, with the goal of modifying parts of the code of the BOOT.BIN, which is a relocatable ELF.

In order to do that, my idea was to add a new segment to the program header of BOOT.BIN. I tried multiple attempts, but I always get a 0x80020148 error when running on the PSP. When running instead from the PPSSPP emulator everything works as expected and my new segment is loaded at the desired address, and my code runs as I want (since the ELF is relocatable, I installed a hook on the main entry of the .text section that jumps to the new segment, which takes care of patching .text at runtime).

I was hoping you could help me shed some light on what is going on on real hardware.

  • The simplest way I tried to add a segment, without having to modifying all the offsets in the rest of the ELF, is to simply change the offset of the program header which is stored in the elf header, so to move the program header at the bottom of the ELF. There I write the original program header + a new segment entry. I realized this does not work on real hardware probably because the loader verifies the ELF by only loading the first 512 bytes of the file, and thus the new program header is not available. This is at least according to
    /* The first 512 bytes of the file of the module to be loaded have been read into pBlock. */
    Is this correct?
  • The second attempt was to use a proper ELF manipulation library: I am using ELFIO with C++, and I add a new segment, together with even a new section pointing to it, and the program header stays at the beginning of the file. Ghidra, PRXTool, readelf, and even PPSSPP are able to open the file, and the game works fine in PPSSPP. You can find the code I am using to add the segment below.

Can you see any obvious mistakes? Or even better, are you aware of a better/proper way to extend a PRX/ELF with a new segment or simply add space for custom code?

Just to clarify, I am making my new segment RWX and I am also changing the first code segment as well as the section .text to RWX as well, since I am changing code at runtime.

#include <elfio/elfio.hpp>
#include <iostream>
#include <cstring>
#include <cstdint>

#define ALIGN 0x40
#define SEGMENT_SIZE 0x800

static ELFIO::Elf32_Addr align(ELFIO::Elf32_Addr value, size_t align){
    return (value + (align-1))/align*align;
}

int main() {
    ELFIO::elfio reader;

    // Load existing 32-bit MIPS ELF file
    if (!reader.load("input_mips.elf")) {
        std::cerr << "Failed to load ELF file\n";
        return 1;
    }

    // Check it's ELF32 and MIPS
    if (reader.get_class() != ELFIO::ELFCLASS32 || reader.get_machine() != ELFIO::EM_MIPS) {
        std::cerr << "Not a 32-bit MIPS ELF file\n";
        return 1;
    }

    auto flags = reader.segments[0]->get_flags();
    reader.segments[0]->set_flags(flags | ELFIO::PF_W);

    auto last_segment = reader.segments[reader.segments.size()-1];
    ELFIO::Elf32_Addr new_segment_virtual_addr = align(
                    last_segment->get_virtual_address() + last_segment->get_memory_size(),ALIGN);

    // Add a PT_LOAD segment
    ELFIO::segment* new_seg = reader.segments.add();
    new_seg->set_type(ELFIO::PT_LOAD);
    new_seg->set_flags(ELFIO::PF_R | ELFIO::PF_W | ELFIO::PF_X);
    new_seg->set_align(ALIGN);
    new_seg->set_virtual_address(new_segment_virtual_addr);
    new_seg->set_file_size(SEGMENT_SIZE);
    new_seg->set_memory_size(SEGMENT_SIZE);

    // Create new section
    ELFIO::section* new_sec = reader.sections.add(".mydata");
    new_sec->set_type(ELFIO::SHT_PROGBITS);
    new_sec->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_WRITE | ELFIO::SHF_EXECINSTR);
    new_sec->set_addr_align(ALIGN);
    new_sec->set_address(new_segment_virtual_addr);

    std::vector<char> data(SEGMENT_SIZE);
    new_sec->set_data(data.data(), data.size());

    // Connect section to segment
    new_seg->add_section_index(new_sec->get_index(), new_sec->get_addr_align());

    // Save to new file
    if (!reader.save("output_mips.elf")) {
        std::cerr << "Failed to save ELF\n";
        return 1;
    }

    std::cout << "Segment and section added to MIPS ELF32\n";
    return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0