Skip to content

Commit

Permalink
Parse MachO LC_FUNCTION_STARTS
Browse files Browse the repository at this point in the history
  • Loading branch information
romainthomas committed Jul 7, 2017
1 parent f7cc518 commit 18d8919
Show file tree
Hide file tree
Showing 20 changed files with 474 additions and 11 deletions.
1 change: 1 addition & 0 deletions api/python/MachO/CMakeLists.txt
Expand Up @@ -12,6 +12,7 @@ set(LIEF_PYTHON_MACHO_SRC
"${CMAKE_CURRENT_LIST_DIR}/objects/pyMainCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyDylinker.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyDyldInfo.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyFunctionStarts.cpp"
"${CMAKE_CURRENT_LIST_DIR}/pyMachOStructures.cpp"
)

Expand Down
11 changes: 11 additions & 0 deletions api/python/MachO/objects/pyBinary.cpp
Expand Up @@ -134,6 +134,17 @@ void init_MachO_Binary_class(py::module& m) {
"Return binary's " RST_CLASS_REF(lief.MachO.DyldInfo) " if any.",
py::return_value_policy::reference)

.def_property_readonly("has_function_starts",
&Binary::has_function_starts,
"``True`` if the binary has a " RST_CLASS_REF(lief.MachO.FunctionStarts) " command.",
py::return_value_policy::reference_internal)

.def_property_readonly("function_starts",
static_cast<no_const_getter<FunctionStarts&>>(&Binary::function_starts),
"Return binary's " RST_CLASS_REF(lief.MachO.FunctionStarts) " if any.",
py::return_value_policy::reference)



.def("__str__",
[] (const Binary& binary)
Expand Down
80 changes: 80 additions & 0 deletions api/python/MachO/objects/pyFunctionStarts.cpp
@@ -0,0 +1,80 @@
/* Copyright 2017 R. Thomas
* Copyright 2017 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>

#include <string>
#include <sstream>

#include "LIEF/visitors/Hash.hpp"
#include "LIEF/MachO/FunctionStarts.hpp"

#include "pyMachO.hpp"

template<class T>
using getter_t = T (FunctionStarts::*)(void) const;

template<class T>
using setter_t = void (FunctionStarts::*)(T);


void init_MachO_FunctionStarts_class(py::module& m) {

py::class_<FunctionStarts, LoadCommand>(m, "FunctionStarts")

.def_property("data_offset",
static_cast<getter_t<uint32_t>>(&FunctionStarts::data_offset),
static_cast<setter_t<uint32_t>>(&FunctionStarts::data_offset),
"Offset in the binary where *start functions* are located")

.def_property("data_size",
static_cast<getter_t<uint32_t>>(&FunctionStarts::data_size),
static_cast<setter_t<uint32_t>>(&FunctionStarts::data_size),
"Size of the functions list in the binary")

.def_property("functions",
static_cast<getter_t<const std::vector<uint64_t>&>>(&FunctionStarts::functions),
static_cast<setter_t<const std::vector<uint64_t>&>>(&FunctionStarts::functions),
"Addresses of every function entry point in the executable\n\n"

"This allows for functions to exist that have no entries in the symbol table.\n\n"

".. warning::\n\n"
"\tThe address is relative to the ``__TEXT`` segment\n\n",
py::return_value_policy::reference_internal)

.def("add_function",
&FunctionStarts::add_function,
"Add a new function",
"address"_a)

.def("__eq__", &FunctionStarts::operator==)
.def("__ne__", &FunctionStarts::operator!=)
.def("__hash__",
[] (const FunctionStarts& func) {
return LIEF::Hash::hash(func);
})


.def("__str__",
[] (const FunctionStarts& func)
{
std::ostringstream stream;
stream << func;
std::string str = stream.str();
return str;
});

}
1 change: 1 addition & 0 deletions api/python/MachO/pyMachO.cpp
Expand Up @@ -38,6 +38,7 @@ void init_MachO_module(py::module& m) {
init_MachO_MainCommand_class(LIEF_MachO_module);
init_MachO_DylinkerCommand_class(LIEF_MachO_module);
init_MachO_DyldInfo_class(LIEF_MachO_module);
init_MachO_FunctionStarts_class(LIEF_MachO_module);


// Enums
Expand Down
1 change: 1 addition & 0 deletions api/python/MachO/pyMachO.hpp
Expand Up @@ -38,6 +38,7 @@ void init_MachO_UUIDCommand_class(py::module&);
void init_MachO_MainCommand_class(py::module&);
void init_MachO_DylinkerCommand_class(py::module&);
void init_MachO_DyldInfo_class(py::module&);
void init_MachO_FunctionStarts_class(py::module&);

// Enums
void init_MachO_Structures_enum(py::module&);
Expand Down
9 changes: 9 additions & 0 deletions doc/sphinx/api/python/macho.rst
Expand Up @@ -131,6 +131,15 @@ Dyld Info

----------

Function starts
***************

.. autoclass:: lief.MachO.FunctionStarts
:members:
:inherited-members:
:undoc-members:

----------


Enum
Expand Down
32 changes: 31 additions & 1 deletion examples/python/macho_reader.py
Expand Up @@ -156,6 +156,8 @@ def print_uuid(binary):
uuid_str = " ".join(map(lambda e : "{:02x}".format(e), cmd.uuid))
print("UUID: {}".format(uuid_str))

print("")


def print_main_command(binary):

Expand All @@ -169,11 +171,33 @@ def print_main_command(binary):
print(format_hex.format("Entry point:", cmd.entrypoint))
print(format_hex.format("Stack size:", cmd.stack_size))

print("")


def print_dylinker(binary):
print("== Dylinker ==")
print("Path: {}".format(binary.dylinker.name))

print("")

def print_function_starts(binary):
format_str = "{:<13} {:<30}"
format_hex = "{:<13} 0x{:<28x}"
format_dec = "{:<13} {:<30d}"

print("== Function Starts ==")

fstarts = binary.function_starts

print(format_hex.format("Offset:", fstarts.data_offset))
print(format_hex.format("Size:", fstarts.data_size))
print("Functions: ({:d})".format(len(fstarts.functions)))
for idx, address in enumerate(fstarts.functions):
print(" [{:d}] __TEXT + 0x{:x}".format(idx, address))

print("")



def print_dyld_info(binary):
print("== Dyld Info ==")
Expand All @@ -189,7 +213,6 @@ def print_dyld_info(binary):
print(f_value.format("Lazy Bind", dyld_info.lazy_bind[0], dyld_info.lazy_bind[1]))
print(f_value.format("Export", dyld_info.export_info[0], dyld_info.export_info[1]))


print("")

def main():
Expand Down Expand Up @@ -238,6 +261,10 @@ def main():
action='store_true', dest='show_dyldinfo',
help='Display the DyldInfo command')

parser.add_argument('--function-starts',
action='store_true', dest='show_function_starts',
help='Display the FunctionStarts command')

parser.add_argument("binary",
metavar="<macho-file>",
help='Target Mach-O File')
Expand Down Expand Up @@ -285,6 +312,9 @@ def main():
if (args.show_dyldinfo or args.show_all) and binary.has_dyld_info:
print_dyld_info(binary)

if (args.show_function_starts or args.show_all) and binary.has_function_starts:
print_function_starts(binary)


if __name__ == "__main__":
main()
10 changes: 9 additions & 1 deletion include/LIEF/MachO/Binary.hpp
Expand Up @@ -36,6 +36,7 @@
#include "LIEF/MachO/MainCommand.hpp"
#include "LIEF/MachO/DynamicSymbolCommand.hpp"
#include "LIEF/MachO/DyldInfo.hpp"
#include "LIEF/MachO/FunctionStarts.hpp"

namespace LIEF {
namespace MachO {
Expand Down Expand Up @@ -190,10 +191,17 @@ class DLL_PUBLIC Binary : public LIEF::Binary {
//! @brief ``true`` if the binary has a MachO::DyldInfo command.
bool has_dyld_info(void) const;

//! @brief Return the MachO::Dyl
//! @brief Return the MachO::Dyld command
DyldInfo& dyld_info(void);
const DyldInfo& dyld_info(void) const;

//! @brief ``true`` if the binary has a MachO::FunctionStarts command.
bool has_function_starts(void) const;

//! @brief Return the MachO::FunctionStarts command
FunctionStarts& function_starts(void);
const FunctionStarts& function_starts(void) const;

template<class T>
bool has_command(void) const;

Expand Down
2 changes: 2 additions & 0 deletions include/LIEF/MachO/BinaryParser.hpp
Expand Up @@ -54,6 +54,8 @@ class DLL_PUBLIC BinaryParser : public LIEF::Parser {
Binary* get_binary(void);

private:
static std::pair<uint64_t, uint64_t> decode_uleb128(const VectorStream& stream, uint64_t offset);

BinaryParser(std::unique_ptr<VectorStream>&& stream);

void parse(void);
Expand Down
79 changes: 79 additions & 0 deletions include/LIEF/MachO/FunctionStarts.hpp
@@ -0,0 +1,79 @@
/* Copyright 2017 R. Thomas
* Copyright 2017 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIEF_MACHO_FUNCTION_STARTS_COMMAND_H_
#define LIEF_MACHO_FUNCTION_STARTS_COMMAND_H_
#include <string>
#include <vector>
#include <iostream>
#include <array>

#include "LIEF/visibility.h"
#include "LIEF/types.hpp"

#include "LIEF/MachO/LoadCommand.hpp"

namespace LIEF {
namespace MachO {

class DLL_PUBLIC FunctionStarts : public LoadCommand {
public:
FunctionStarts(void);
FunctionStarts(const linkedit_data_command *cmd);

FunctionStarts& operator=(const FunctionStarts& copy);
FunctionStarts(const FunctionStarts& copy);

//! @brief Offset in the binary where *start functions* are located
uint32_t data_offset(void) const;

//! @brief Size of the functions list in the binary
uint32_t data_size(void) const;

//! @brief Addresses of every function entry point in the executable.
//!
//! This allows for functions to exist that have no entries in the symbol table.
//!
//! @warning The address is relative to the ``__TEXT`` segment
const std::vector<uint64_t>& functions(void) const;

std::vector<uint64_t>& functions(void);

//! @brief Add a new function
void add_function(uint64_t address);

void data_offset(uint32_t offset);
void data_size(uint32_t size);
void functions(const std::vector<uint64_t>& funcs);

virtual ~FunctionStarts(void);

bool operator==(const FunctionStarts& rhs) const;
bool operator!=(const FunctionStarts& rhs) const;

virtual void accept(Visitor& visitor) const override;

virtual std::ostream& print(std::ostream& os) const override;

private:
uint32_t data_offset_;
uint32_t data_size_;
std::vector<uint64_t> functions_;

};

}
}
#endif
1 change: 1 addition & 0 deletions include/LIEF/MachO/utils.hpp
Expand Up @@ -26,6 +26,7 @@ namespace MachO {
DLL_PUBLIC bool is_macho(const std::string& file);
DLL_PUBLIC bool is_fat(const std::string& file);
DLL_PUBLIC bool is_64(const std::string& file);
DLL_PUBLIC uint64_t decode_uleb128(const std::string& file);
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/MachO/Binary.cpp
Expand Up @@ -540,6 +540,20 @@ const DyldInfo& Binary::dyld_info(void) const {
return this->get_command<DyldInfo>();
}

// Function Starts
// +++++++++++++++
bool Binary::has_function_starts(void) const {
return this->has_command<FunctionStarts>();
}

FunctionStarts& Binary::function_starts(void) {
return this->get_command<FunctionStarts>();
}

const FunctionStarts& Binary::function_starts(void) const {
return this->get_command<FunctionStarts>();
}



void Binary::accept(LIEF::Visitor& visitor) const {
Expand Down
16 changes: 16 additions & 0 deletions src/MachO/BinaryParser.cpp
Expand Up @@ -124,5 +124,21 @@ Binary* BinaryParser::get_binary(void) {
}


std::pair<uint64_t, uint64_t> BinaryParser::decode_uleb128(const VectorStream& stream, uint64_t offset) {
uint64_t value = 0;
unsigned shift = 0;
uint64_t current_offset = offset - sizeof(uint8_t);
do {
current_offset += sizeof(uint8_t);
value += static_cast<uint64_t>(stream.read_integer<uint8_t>(current_offset) & 0x7f) << shift;
shift += 7;
} while (stream.read_integer<uint8_t>(current_offset) >= 128);

uint64_t delta = current_offset - offset;
delta++;
return {value, delta};
}


} // namespace MachO
} // namespace LIEF

0 comments on commit 18d8919

Please sign in to comment.