Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a new section & segment to .ELF without disrupting file #719

Open
NotNite opened this issue Aug 10, 2024 · 5 comments
Open

Adding a new section & segment to .ELF without disrupting file #719

NotNite opened this issue Aug 10, 2024 · 5 comments

Comments

@NotNite
Copy link

NotNite commented Aug 10, 2024

Hi, I was wondering if it's possible to add a custom section/segment to an ELF file. For context, I'm currently working on a tool to patch PlayStation 3 ELFs to allow custom code. I need to add a bit of shellcode into the executable, so I wanted to make a custom section to store my code in and then modify the entrypoint. My code to modify the executable is a little complex, so I made this simple example:

fn main() {
    let input = std::fs::read("hbtest.elf").unwrap();
    let mut builder = object::build::elf::Builder::read(input.as_slice()).unwrap();

    // 32 NOPs (0x60000000 in PowerPC)
    let mut custom_data: Vec<u8> = Vec::new();
    for _ in 0..32 {
        custom_data.push(0x60);
        custom_data.push(0x00);
        custom_data.push(0x00);
        custom_data.push(0x00);
    }

    let section_id = {
        let section = builder.sections.add();
        section.name = ".sprxpatcher".into();
        section.sh_type = object::elf::SHT_PROGBITS;
        section.sh_flags = (object::elf::SHF_ALLOC | object::elf::SHF_EXECINSTR) as u64;
        section.sh_addralign = 16;
        section.sh_size = custom_data.len() as u64;
        section.data = object::build::elf::SectionData::Data(custom_data.into());
        section.id()
    };

    let section_addr = {
        let segment = builder
            .segments
            .add_load_segment(object::elf::PF_R | object::elf::PF_X, builder.load_align);
        let section = builder.sections.get_mut(section_id);
        segment.append_section(section);
        section.sh_addr
    };

    println!("Section address: 0x{:x}", section_addr);

    let output = std::fs::File::create("hbtest.modified.elf").unwrap();
    let mut buffer = object::write::StreamingBuffer::new(output);
    builder.write(&mut buffer).unwrap();
}

However, when running this, it panics:

Section address: 0x267b10
thread 'main' panicked at src/main.rs:38:32:
called `Result::unwrap()` on an `Err` value: Error("Unsupported sh_offset value 0x200 for section '.init', expected at least 0x238")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\sprxpatcher.exe` (exit code: 101)

I assume this is because the addition of the new section/segment into the program header table has shifted the file offsets by 0x38 (the size of the new entry). Is it possible to repair these offsets such that the modified ELF writes successfully? I tried just shifting every sh_offset by 0x38, but that didn't seem to work. I suspect there is more work to be done here (or it's just flat out impossible).

@philipc
Copy link
Contributor

philipc commented Aug 11, 2024

There's code in the object-rewrite crate to do this, but there's no public API for it. It's possible we could move this code to object::write::elf, but I'm uncertain what a public API for this would look like. The main issue is that it relies on heuristics, so I'd prefer if a public API allowed those heuristics to be changed.

Another option would be to extend the object-rewrite API to allow adding a section.

@NotNite
Copy link
Author

NotNite commented Aug 11, 2024

I tried using object-rewrite, but it seems to not want to move to adjust for the new program header entry:

[2024-08-11T01:09:20Z INFO  object_rewrite::elf] Immovable program headers (end address 10238) overlaps immovable .init (start address 10200)

Commenting out the block addition for the program headers (just for fun, probably would break things anyways) leads to the same error as before:

[2024-08-11T01:08:53Z INFO  object_rewrite::elf] Moving 1 sections, adding 1 PT_LOAD segments, splitting 0 segments
[2024-08-11T01:08:53Z INFO  object_rewrite::elf] Moved .sprxpatcher to offset 247b10, addr 277b10
[2024-08-11T01:08:53Z INFO  object_rewrite::elf] Added PT_LOAD segment with p_flags 5, offset 247b10, addr 277b10, size 8e
Error: Unsupported sh_offset value 0x200 for section '.init', expected at least 0x270

I'm unsure if resizing the program headers is even something that's possible... 🤔

@philipc
Copy link
Contributor

philipc commented Aug 11, 2024

Ah okay, in my tests so far the section following the program headers has been safe to move.

I'm not sure, but it's likely that the .init section isn't safe to move without patching any relative addressing instructions within it. That's not something this crate can do, since it requires disassembling/assembling instructions.

The alternative is to try moving the elf header and program headers down a page to make room. I think this is theoretically possible, but not something I've tried.

@s5bug
Copy link

s5bug commented Aug 11, 2024

Could you not keep the ELF header in the same spot and move the program header table to the end of the file? Program header entries if I recall don't use any sort of relative positioning / can be placed anywhere in the ELF.

@philipc
Copy link
Contributor

philipc commented Aug 11, 2024

That is a possibility. There was a linux bug that meant it didn't support program headers that aren't in the first PT_LOAD segment, but that seems to be fixed: torvalds/linux@0da1d50

object::build::elf::Builder will need support for moving it too (currently a TODO).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants