lief-project / LIEF

LIEF - Library to Instrument Executable Formats (C++, Python, Rust)
https://lief.re
Apache License 2.0
4.47k stars 622 forks source link

Create an ELF from scratch #213

Open Isaac0616 opened 6 years ago

Isaac0616 commented 6 years ago

After reading the tutorial "02 - Create a PE from scratch", I am wondering is it possible to create an ELF from scratch. I tried

from lief import ELF
binary = ELF.Binary('test', ELF.ELF_CLASS.CLASS64)

However, if I try to add a section, it shows

not_implemented: Adding segment for NONE is not implemented

And if I try to binary.write('test') or exit the ipython, it will show "abort (core dumped)".

It seems that the binary created in this way will not set any headers, not even the magic number and the class specified in the constructor.

>>> print binary.header
Magic:                           0 0 0 0
Class                            NONE
Endianness:                      NONE
Version:                         NONE
OS/ABI:                          SYSTEMV
Machine type:                    None
File type:                       NONE
Object file version:             NONE
Entry Point:                     0x0
Program header offset:           0x0
Section header offset:           0
Processor Flag                   0
Header size:                     0
Size of program header :         0
Number of program header:        0
Size of section header:          0
Number of section headers:       0
Section Name Table idx:          0

Is this the expected behavior?

romainthomas commented 6 years ago

Hi! Yes currently it's not supported

FFengIll commented 6 years ago

@romainthomas How do you think about the Necessity of this? ( I mean I am trying to solve it if I could)

While review, there is not a good way to create a ELF if not exist (Because we need to known the type and keep the API). BUT, it is possible to init an empty ELF with some default setting (OR another api to complete a init work while not exist).

  switch(this->header().file_type()) {
    case E_TYPE::ET_EXEC:
      {
        return this->add_segment<E_TYPE::ET_EXEC>(segment, new_base);
        break;
      }

    case E_TYPE::ET_DYN:
      {
        return this->add_segment<E_TYPE::ET_DYN>(segment, new_base);
        break;
      }

    default:
      {
        throw not_implemented(std::string("Adding segment for ") + to_string(this->header().file_type()) + " is not implemented");
      }
  }
romainthomas commented 6 years ago

While review, there is not a good way to create a ELF if not exist (Because we need to known the type and keep the API). I agree, I'm thinking about an API to do it.

They idea would to create an empty ELF and specifying arch and type (e.g ELF64 - x86_64 - EXEC) and then to have an api to add segment and section.

FFengIll commented 6 years ago

They idea would to create an empty ELF and specifying arch and type (e.g ELF64 - x86_64 - EXEC) and then to have an api to add segment and section.

I think it could be a solution. While construct the Binary, build the elf header and some possible segment and section (base on the flag) (empty segment maybe a challenge). (I am under trying but not too soon).

Furthermore, a problem is how to process it when the file existed.

3droj7 commented 4 years ago

Will this issue be solved/implemented?

romainthomas commented 2 years ago

Posted in #659 and copied to track the use-case:


Hello,

Thanks for your reply. I think there are two issues, which could be discussed separately:

  1. A Python program using LIEF can segfault (when calling elf.add(segment) on a crafted elf object). In my humble opinion, it would be more developer-friendly to report an error (for example by raising an exception) instead of segfaulting.
  2. LIEF currently does not support building ELF files from scratch. This is a "feature request", as I understand LIEF has been built with the idea of modifying existing ELF files.

For the second issue, it can be helpful that I describe my use-case more precisely. In my work, I am often encountering firmware in exotic formats. For example a few years ago, I encountered the firmware used by HP iLO 4 Baseboard Management Controller. This firmware defined for each process their sections, named in a way similar as in an ELF file (.text, .data, etc.). At the time, I wrote a tool which extracted the memory loaded for each process as an ELF file, crafting the sections by hand (cf. https://github.com/fishilico/ilo4_toolbox/blob/eea2d2e853b6292b927c028b0768eb555bb5f5ab/scripts/iLO4/ioonag_unpacker/unpack_firmware.py#L1363-L1390 for the ELF header, and https://github.com/fishilico/ilo4_toolbox/blob/eea2d2e853b6292b927c028b0768eb555bb5f5ab/scripts/iLO4/ioonag_unpacker/unpack_firmware.py#L1213-L1239 for each entry of program and section headers). It was quite painful to add features such as adding symbols for known functions, when extracting a firmware. So I wanted to use LIEF for it and failed.

Back to the present, I saw your blog post titled "New ELF Builder" and I thought that LIEF now enabled building ELF from scratch. However it requires "a minimum layout" which does not seem to be documented (and the fact that LIEF supports creating PE files from scratch also gave me false hope, https://lief-project.github.io/doc/latest/tutorials/02_pe_from_scratch.html).

What do you think of a feature which would enable building an ELF file from scratch, using defined sections which are for example extracted from a firmware? If I understand correctly, the approach of building a bare ELF and adding the sections one by one is hard to support, because the ELF program header could need to be moved around. Nevertheless if LIEF provided a way to create an ELF directly with given sections (and if it was then possible to define symbols), it would be great :)

Originally posted by @niooss-ledger in https://github.com/lief-project/LIEF/issues/659#issuecomment-1024980927

romainthomas commented 2 years ago
  1. A Python program using LIEF can segfault (when calling elf.add(segment) on a crafted elf object). In my humble opinion, it would be more developer-friendly to report an error (for example by raising an exception) instead of segfaulting.

Yes I completely agree and I removed the ELF Binary constructor as it actually does not construct a consistent object layout.

It was quite painful to add features such as adding symbols for known functions, when extracting a firmware. So I wanted to use LIEF for it and failed.

It was a similar request feature that @wisk had. He worked on it a while ago and his experimentation are here. They are based on the old ELF builder but they are worth reading.

Back to the present, I saw your blog post titled "New ELF Builder" and I thought that LIEF now enabled building ELF from scratch.

Yes the title is a bit confusing but it was more about the performances.

What do you think of a feature which would enable building an ELF file from scratch, using defined sections which are for example extracted from a firmware?

I thought about that for a while and my approach would be to initialize a Binary from a predefined set of section (or segments). It would be something like:

import lief

section1 = lief.ELF.Section(".test1")
...
section2 = lief.ELF.Section(".test2")
crafted = lief.ELF.Binary.create("test", lief.ELF.ELF_CLASS.CLASS64
                                 [section1, section2], symbol_table=True)
if crafted is None:
  print("Crafting failed")
  return

# ... continue the modification

I'll try to make a new PoC to get a better picture of this feature with the new ELF builder :blush:

Currently I'm focus on cleaning the code base of LIEF to prepare the next release

Originally posted by @romainthomas in https://github.com/lief-project/LIEF/issues/659#issuecomment-1025213921

fzakaria commented 1 year ago

Curious if any changes happened here.