Skip to content

Commit

Permalink
Make sure ELF, PE, and Mach-O parser can consume bytes.
Browse files Browse the repository at this point in the history
Related to #1167
  • Loading branch information
romainthomas committed Feb 4, 2025
1 parent e2ef1ea commit 8cae042
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 10 deletions.
3 changes: 3 additions & 0 deletions api/python/lief/ELF/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5512,6 +5512,9 @@ class X86ISA(NoteGnuProperty.Property):

AVX512_BF16 = 32

@overload
def parse(buffer: bytes, config: ParserConfig = ...) -> Optional[Binary]: ...

@overload
def parse(filename: str, config: ParserConfig = ...) -> Optional[Binary]: ...

Expand Down
3 changes: 3 additions & 0 deletions api/python/lief/MachO/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2720,6 +2720,9 @@ class it_data_in_code_entries:

def __next__(self) -> DataCodeEntry: ...

@overload
def parse(buffer: bytes, config: ParserConfig = ...) -> Optional[FatBinary]: ...

@overload
def parse(filename: str, config: ParserConfig = ...) -> Optional[FatBinary]: ...

Expand Down
3 changes: 3 additions & 0 deletions api/python/lief/PE/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3925,6 +3925,9 @@ def get_type(raw: Sequence[int]) -> Union[PE_TYPE, lief.lief_errors]: ...

def oid_to_string(arg: str, /) -> str: ...

@overload
def parse(buffer: bytes, config: ParserConfig = ...) -> Optional[Binary]: ...

@overload
def parse(filename: str, config: ParserConfig = ...) -> Optional[Binary]: ...

Expand Down
14 changes: 5 additions & 9 deletions api/python/src/Abstract/pyParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "LIEF/Abstract/Parser.hpp"
#include "LIEF/Abstract/Binary.hpp"
#include "LIEF/logging.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"

namespace LIEF::py {

Expand All @@ -33,12 +34,9 @@ void create<Parser>(nb::module_& m) {

m.def("parse",
[] (nb::bytes bytes) {
std::string raw_str(bytes.c_str(), bytes.size());
const std::vector<uint8_t> raw = {
std::make_move_iterator(std::begin(raw_str)),
std::make_move_iterator(std::end(raw_str))
};
return Parser::parse(raw);
auto strm = std::make_unique<SpanStream>(
reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
return Parser::parse(std::move(strm));
},
R"delim(
Parse a binary supported by LIEF from the given bytes and return either:
Expand All @@ -48,9 +46,7 @@ void create<Parser>(nb::module_& m) {
- :class:`lief.MachO.Binary`
depending on the given binary format.
)delim"_doc,
"raw"_a,
nb::rv_policy::take_ownership);
)delim"_doc, "raw"_a, nb::rv_policy::take_ownership);

m.def("parse", nb::overload_cast<const std::string&>(&Parser::parse),
R"delim(
Expand Down
16 changes: 16 additions & 0 deletions api/python/src/ELF/objects/pyParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,29 @@

#include "LIEF/ELF/Parser.hpp"
#include "LIEF/ELF/Binary.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"

namespace LIEF::ELF::py {

template<>
void create<Parser>(nb::module_& m) {
using namespace LIEF::py;

m.def("parse",
[] (nb::bytes bytes, const ParserConfig& config) {
auto strm = std::make_unique<SpanStream>(
reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
return Parser::parse(std::move(strm), config);
},
R"delim(
Parse the ELF binary from the given bytes and return a :class:`lief.ELF.Binary` object.
The second argument is an optional configuration (:class:`~lief.ELF.ParserConfig`)
that can be used to define which part(s) of the ELF should be parsed or skipped.
)delim"_doc, "buffer"_a, "config"_a = ParserConfig::all(),
nb::rv_policy::take_ownership);

m.def("parse",
nb::overload_cast<const std::string&, const ParserConfig&>(&Parser::parse),
R"delim(
Expand Down
14 changes: 14 additions & 0 deletions api/python/src/MachO/objects/pyParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "LIEF/MachO/Parser.hpp"
#include "LIEF/MachO/FatBinary.hpp"
#include "LIEF/logging.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"

#include "MachO/pyMachO.hpp"

Expand All @@ -35,6 +36,19 @@ template<>
void create<Parser>(nb::module_& m) {
using namespace LIEF::py;

m.def("parse",
[] (nb::bytes bytes, const ParserConfig& config) {
auto strm = std::make_unique<SpanStream>(
reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
return Parser::parse(std::move(strm), config);
},
R"delim(
Parse the given binary and return a :class:`~lief.MachO.FatBinary` object
One can configure the parsing with the ``config`` parameter. See :class:`~lief.MachO.ParserConfig`,
)delim"_doc, "buffer"_a, "config"_a = ParserConfig::deep(),
nb::rv_policy::take_ownership);

m.def("parse",
nb::overload_cast<const std::string&, const ParserConfig&>(&LIEF::MachO::Parser::parse),
R"delim(
Expand Down
17 changes: 17 additions & 0 deletions api/python/src/PE/objects/pyParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,29 @@
#include "LIEF/PE/Parser.hpp"
#include "LIEF/PE/Binary.hpp"

#include "LIEF/BinaryStream/SpanStream.hpp"

namespace LIEF::PE::py {

template<>
void create<Parser>(nb::module_& m) {
using namespace LIEF::py;

m.def("parse",
[] (nb::bytes bytes, const ParserConfig& config) {
auto strm = std::make_unique<SpanStream>(
reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
return Parser::parse(std::move(strm), config);
},
R"delim(
Parse the PE binary from the given bytes and return a :class:`lief.PE.Binary` object.
The second argument is an optional configuration (:class:`~lief.PE.ParserConfig`)
that can be used to define which part(s) of the PE should be parsed or skipped.
)delim"_doc, "buffer"_a, "config"_a = ParserConfig::all(),
nb::rv_policy::take_ownership);

m.def("parse",
static_cast<std::unique_ptr<Binary>(*)(const std::string&, const ParserConfig&)>(&Parser::parse),
"Parse the PE binary from the given **file path** and return a " RST_CLASS_REF(lief.PE.Binary) " object"_doc,
Expand Down
15 changes: 14 additions & 1 deletion tests/api/test_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def test_iterator():
next(items)

with pytest.raises(IndexError) as e_info:
items[100]
items[100]


def test_abstract_concrete():
Expand All @@ -161,3 +161,16 @@ def test_abstract_concrete():
abstract = pe.abstract
assert type(abstract) == lief.Binary
assert type(abstract.concrete) == lief.PE.Binary

def test_from_bytes():
input_path = Path(get_sample('PE/PE64_x86-64_binary_HelloWorld.exe'))
pe = lief.PE.parse(input_path.read_bytes())
assert pe is not None

input_path = Path(get_sample('ELF/python3.12d'))
elf = lief.ELF.parse(input_path.read_bytes())
assert elf is not None

input_path = Path(get_sample('MachO/MachO64_AArch64_weak-sym-fc.bin'))
macho = lief.MachO.parse(input_path.read_bytes())
assert macho is not None

0 comments on commit 8cae042

Please sign in to comment.