From a16e1c4d13c7071fabe6a5a46b6d6c0fd9565b72 Mon Sep 17 00:00:00 2001 From: Romain Thomas <romainthomasc@gmail.com> Date: Mon, 2 Apr 2018 08:20:15 +0200 Subject: [PATCH] Expose DataInCode --- api/python/MachO/CMakeLists.txt | 2 + api/python/MachO/objects/pyBinary.cpp | 12 ++- api/python/MachO/objects/pyDataCodeEntry.cpp | 86 +++++++++++++++++ api/python/MachO/objects/pyDataInCode.cpp | 76 +++++++++++++++ api/python/MachO/pyMachO.cpp | 3 + api/python/MachO/pyMachO.hpp | 2 + api/python/enums_wrapper.hpp | 2 + doc/sphinx/api/cpp/macho.rst | 19 ++++ doc/sphinx/api/python/macho.rst | 29 ++++++ examples/python/macho_reader.py | 26 ++++++ include/LIEF/MachO/Binary.hpp | 8 ++ include/LIEF/MachO/DataCodeEntry.hpp | 82 +++++++++++++++++ include/LIEF/MachO/DataInCode.hpp | 77 ++++++++++++++++ include/LIEF/MachO/EnumToString.hpp | 2 + include/LIEF/MachO/hash.hpp | 2 + include/LIEF/Visitor.hpp | 8 ++ src/MachO/Binary.cpp | 15 +++ src/MachO/BinaryParser.tcc | 17 ++++ src/MachO/CMakeLists.txt | 4 + src/MachO/DataCodeEntry.cpp | 96 +++++++++++++++++++ src/MachO/DataInCode.cpp | 97 ++++++++++++++++++++ src/MachO/EnumToString.cpp | 14 +++ src/MachO/hash.cpp | 13 +++ tests/macho/macho_tests.py | 27 ++++++ 24 files changed, 718 insertions(+), 1 deletion(-) create mode 100644 api/python/MachO/objects/pyDataCodeEntry.cpp create mode 100644 api/python/MachO/objects/pyDataInCode.cpp create mode 100644 include/LIEF/MachO/DataCodeEntry.hpp create mode 100644 include/LIEF/MachO/DataInCode.hpp create mode 100644 src/MachO/DataCodeEntry.cpp create mode 100644 src/MachO/DataInCode.cpp diff --git a/api/python/MachO/CMakeLists.txt b/api/python/MachO/CMakeLists.txt index 5da291c..0c0b3ae 100644 --- a/api/python/MachO/CMakeLists.txt +++ b/api/python/MachO/CMakeLists.txt @@ -27,6 +27,8 @@ set(LIEF_PYTHON_MACHO_SRC "${CMAKE_CURRENT_LIST_DIR}/objects/pyParserConfig.cpp" "${CMAKE_CURRENT_LIST_DIR}/objects/pyDynamicSymbolCommand.cpp" "${CMAKE_CURRENT_LIST_DIR}/objects/pyCodeSignature.cpp" + "${CMAKE_CURRENT_LIST_DIR}/objects/pyDataInCode.cpp" + "${CMAKE_CURRENT_LIST_DIR}/objects/pyDataCodeEntry.cpp" "${CMAKE_CURRENT_LIST_DIR}/pyMachOStructures.cpp" ) diff --git a/api/python/MachO/objects/pyBinary.cpp b/api/python/MachO/objects/pyBinary.cpp index 4869f3e..71f32bc 100644 --- a/api/python/MachO/objects/pyBinary.cpp +++ b/api/python/MachO/objects/pyBinary.cpp @@ -236,7 +236,7 @@ void init_MachO_Binary_class(py::module& m) { py::return_value_policy::reference) .def_property_readonly("has_code_signature", - &Binary::has_dynamic_symbol_command, + &Binary::has_code_signature, "``True`` if the binary is signed (i.e. has a " RST_CLASS_REF(lief.MachO.CodeSignature) " command)", py::return_value_policy::reference_internal) @@ -245,6 +245,16 @@ void init_MachO_Binary_class(py::module& m) { "Return binary's " RST_CLASS_REF(lief.MachO.CodeSignature) " if any.", py::return_value_policy::reference) + .def_property_readonly("has_data_in_code", + &Binary::has_data_in_code, + "``True`` if the binary has a " RST_CLASS_REF(lief.MachO.DataInCode) " command", + py::return_value_policy::reference_internal) + + .def_property_readonly("data_in_code", + static_cast<no_const_getter<DataInCode&>>(&Binary::data_in_code), + "Return binary's " RST_CLASS_REF(lief.MachO.DataInCode) " if any.", + py::return_value_policy::reference) + .def("virtual_address_to_offset", &Binary::virtual_address_to_offset, diff --git a/api/python/MachO/objects/pyDataCodeEntry.cpp b/api/python/MachO/objects/pyDataCodeEntry.cpp new file mode 100644 index 0000000..a42bc9c --- /dev/null +++ b/api/python/MachO/objects/pyDataCodeEntry.cpp @@ -0,0 +1,86 @@ +/* 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/MachO/hash.hpp" +#include "LIEF/MachO/DataCodeEntry.hpp" +#include "LIEF/MachO/EnumToString.hpp" + +#include "enums_wrapper.hpp" + +#include "pyMachO.hpp" + +#define PY_ENUM(x) LIEF::MachO::to_string(x), x + +template<class T> +using getter_t = T (DataCodeEntry::*)(void) const; + +template<class T> +using setter_t = void (DataCodeEntry::*)(T); + + +void init_MachO_DataCodeEntry_class(py::module& m) { + + + py::class_<DataCodeEntry, LIEF::Object> cls(m, "DataCodeEntry"); + + cls + .def_property("offset", + static_cast<getter_t<uint32_t>>(&DataCodeEntry::offset), + static_cast<setter_t<uint32_t>>(&DataCodeEntry::offset), + "Offset of the data") + + .def_property("length", + static_cast<getter_t<uint16_t>>(&DataCodeEntry::length), + static_cast<setter_t<uint16_t>>(&DataCodeEntry::length), + "Length of the data") + + .def_property("type", + static_cast<getter_t<DataCodeEntry::TYPES>>(&DataCodeEntry::type), + static_cast<setter_t<DataCodeEntry::TYPES>>(&DataCodeEntry::type), + "Type of the entry (" RST_CLASS_REF(lief.MachO.DataCodeEntry.TYPES) "") + + + .def("__eq__", &DataCodeEntry::operator==) + .def("__ne__", &DataCodeEntry::operator!=) + .def("__hash__", + [] (const DataCodeEntry& func) { + return Hash::hash(func); + }) + + + .def("__str__", + [] (const DataCodeEntry& func) + { + std::ostringstream stream; + stream << func; + std::string str = stream.str(); + return str; + }); + + + LIEF::enum_<DataCodeEntry::TYPES>(cls, "TYPES") + .value(PY_ENUM(DataCodeEntry::TYPES::UNKNOWN)) + .value(PY_ENUM(DataCodeEntry::TYPES::DATA)) + .value(PY_ENUM(DataCodeEntry::TYPES::JUMP_TABLE_8)) + .value(PY_ENUM(DataCodeEntry::TYPES::JUMP_TABLE_16)) + .value(PY_ENUM(DataCodeEntry::TYPES::JUMP_TABLE_32)) + .value(PY_ENUM(DataCodeEntry::TYPES::ABS_JUMP_TABLE_32)); + +} diff --git a/api/python/MachO/objects/pyDataInCode.cpp b/api/python/MachO/objects/pyDataInCode.cpp new file mode 100644 index 0000000..bdeb3a6 --- /dev/null +++ b/api/python/MachO/objects/pyDataInCode.cpp @@ -0,0 +1,76 @@ +/* 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/MachO/hash.hpp" +#include "LIEF/MachO/DataInCode.hpp" + +#include "pyMachO.hpp" + +template<class T> +using getter_t = T (DataInCode::*)(void) const; + +template<class T> +using setter_t = void (DataInCode::*)(T); + + +void init_MachO_DataInCode_class(py::module& m) { + + // Init Iterator + init_ref_iterator<DataInCode::it_entries>(m); + + py::class_<DataInCode, LoadCommand>(m, "DataInCode") + .def_property("data_offset", + static_cast<getter_t<uint32_t>>(&DataInCode::data_offset), + static_cast<setter_t<uint32_t>>(&DataInCode::data_offset), + "Offset in the binary where signature starts") + + .def_property("data_size", + static_cast<getter_t<uint32_t>>(&DataInCode::data_size), + static_cast<setter_t<uint32_t>>(&DataInCode::data_size), + "Size of the raw signature") + + .def_property_readonly("entries", + static_cast<DataInCode::it_entries (DataInCode::*)(void)>(&DataInCode::entries), + "Iterator over " RST_CLASS_REF(lief.MachO.DataCodeEntry) "", + py::return_value_policy::reference_internal) + + .def("add", + &DataInCode::add, + "Add an new " RST_CLASS_REF(lief.MachO.DataCodeEntry) "", + "entry"_a) + + .def("__eq__", &DataInCode::operator==) + .def("__ne__", &DataInCode::operator!=) + .def("__hash__", + [] (const DataInCode& func) { + return Hash::hash(func); + }) + + + .def("__str__", + [] (const DataInCode& func) + { + std::ostringstream stream; + stream << func; + std::string str = stream.str(); + return str; + }); + +} diff --git a/api/python/MachO/pyMachO.cpp b/api/python/MachO/pyMachO.cpp index 875bcf7..eeabddd 100644 --- a/api/python/MachO/pyMachO.cpp +++ b/api/python/MachO/pyMachO.cpp @@ -52,6 +52,9 @@ void init_MachO_module(py::module& m) { init_MachO_DynamicSymbolCommand_class(LIEF_MachO_module); init_MachO_CodeSignature_class(LIEF_MachO_module); + init_MachO_DataInCode_class(LIEF_MachO_module); + init_MachO_DataCodeEntry_class(LIEF_MachO_module); + // Enums init_MachO_Structures_enum(LIEF_MachO_module); } diff --git a/api/python/MachO/pyMachO.hpp b/api/python/MachO/pyMachO.hpp index 1be1feb..2b22ce4 100644 --- a/api/python/MachO/pyMachO.hpp +++ b/api/python/MachO/pyMachO.hpp @@ -53,6 +53,8 @@ void init_MachO_ThreadCommand_class(py::module&); void init_MachO_RPathCommand_class(py::module&); void init_MachO_DynamicSymbolCommand_class(py::module&); void init_MachO_CodeSignature_class(py::module&); +void init_MachO_DataInCode_class(py::module&); +void init_MachO_DataCodeEntry_class(py::module&); // Enums void init_MachO_Structures_enum(py::module&); diff --git a/api/python/enums_wrapper.hpp b/api/python/enums_wrapper.hpp index c4df545..357cc8c 100644 --- a/api/python/enums_wrapper.hpp +++ b/api/python/enums_wrapper.hpp @@ -20,6 +20,8 @@ #include "LIEF/visibility.h" +namespace py = pybind11; + namespace LIEF { template <class Type> diff --git a/doc/sphinx/api/cpp/macho.rst b/doc/sphinx/api/cpp/macho.rst index 3b01178..58c9ea6 100644 --- a/doc/sphinx/api/cpp/macho.rst +++ b/doc/sphinx/api/cpp/macho.rst @@ -234,6 +234,25 @@ Code Signature ---------- +Data In Code +************ + +.. doxygenclass:: LIEF::MachO::DataInCode + :project: lief + +---------- + +Data Code Entry +**************** + +.. doxygenclass:: LIEF::MachO::DataCodeEntry + :project: lief + +.. doxygenenum:: LIEF::MachO::DataCodeEntry::TYPES + :project: lief + +---------- + Utilities ********* diff --git a/doc/sphinx/api/python/macho.rst b/doc/sphinx/api/python/macho.rst index c70b432..27859b8 100644 --- a/doc/sphinx/api/python/macho.rst +++ b/doc/sphinx/api/python/macho.rst @@ -293,6 +293,35 @@ Code Signature ---------- +Data In Code +************ + +.. autoclass:: lief.MachO.DataInCode + :members: + :inherited-members: + :undoc-members: + +---------- + +Data Code Entry +*************** + +.. autoclass:: lief.MachO.DataCodeEntry + :members: + :inherited-members: + :undoc-members: + + +TYPES +~~~~~ + +.. autoclass:: lief.MachO.DataCodeEntry.TYPES + :members: + :inherited-members: + :undoc-members: + +---------- + Enums ***** diff --git a/examples/python/macho_reader.py b/examples/python/macho_reader.py index e80d242..4e589db 100644 --- a/examples/python/macho_reader.py +++ b/examples/python/macho_reader.py @@ -370,6 +370,25 @@ def print_function_starts(binary): print("") +@exceptions_handler(Exception) +def print_data_in_code(binary): + format_str = "{:<13} {:<30}" + format_hex = "{:<13} 0x{:<28x}" + format_dec = "{:<13} {:<30d}" + + print("== Data In Code ==") + + datacode = binary.data_in_code + + print(format_hex.format("Offset:", datacode.data_offset)) + print(format_hex.format("Size:", datacode.data_size)) + print("") + for entry in datacode.entries: + type_str = str(entry.type).split(".")[-1] + print("- {:<14}: 0x{:x} ({:d} bytes)".format(type_str, entry.offset, entry.length)) + print("") + + @exceptions_handler(Exception) @@ -636,6 +655,10 @@ def main(): action='store_true', dest='show_dynamic_symbol_command', help="Display the 'Symbol Command' command") + parser.add_argument('--data-in-code', + action='store_true', dest='show_data_in_code', + help="Display the 'Data In Code' command") + parser.add_argument('--bind-opcodes', action='store_true', dest='show_bind_opcodes', help='Display the "Bind" opcodes') @@ -727,6 +750,9 @@ def main(): if (args.show_dynamic_symbol_command or args.show_all) and binary.has_dynamic_symbol_command: print_dynamic_symbol_command(binary) + if (args.show_data_in_code or args.show_all) and binary.has_data_in_code: + print_data_in_code(binary) + if (args.show_rpath_command or args.show_all) and binary.has_rpath: print_rpath_command(binary) diff --git a/include/LIEF/MachO/Binary.hpp b/include/LIEF/MachO/Binary.hpp index bacb4a2..59d9127 100644 --- a/include/LIEF/MachO/Binary.hpp +++ b/include/LIEF/MachO/Binary.hpp @@ -42,6 +42,7 @@ #include "LIEF/MachO/ThreadCommand.hpp" #include "LIEF/MachO/RPathCommand.hpp" #include "LIEF/MachO/CodeSignature.hpp" +#include "LIEF/MachO/DataInCode.hpp" namespace LIEF { namespace MachO { @@ -298,6 +299,13 @@ class LIEF_API Binary : public LIEF::Binary { CodeSignature& code_signature(void); const CodeSignature& code_signature(void) const; + //! @brief ``true`` if the binaryhas a MachO::DataInCode command. + bool has_data_in_code(void) const; + + //! @brief Return the MachO::Signature + DataInCode& data_in_code(void); + const DataInCode& data_in_code(void) const; + template<class T> LIEF_LOCAL bool has_command(void) const; diff --git a/include/LIEF/MachO/DataCodeEntry.hpp b/include/LIEF/MachO/DataCodeEntry.hpp new file mode 100644 index 0000000..bff5d77 --- /dev/null +++ b/include/LIEF/MachO/DataCodeEntry.hpp @@ -0,0 +1,82 @@ + +/* 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_DATA_CODE_ENTRY_H_ +#define LIEF_MACHO_DATA_CODE_ENTRY_H_ +#include <string> +#include <vector> +#include <iostream> + +#include "LIEF/visibility.h" +#include "LIEF/types.hpp" + +#include "LIEF/Object.hpp" + +namespace LIEF { +namespace MachO { + +//! Interface of an entry in DataInCode +class DataCodeEntry : public LIEF::Object { + public: + enum class TYPES { + UNKNOWN = 0, + DATA = 1, + JUMP_TABLE_8 = 2, + JUMP_TABLE_16 = 3, + JUMP_TABLE_32 = 4, + ABS_JUMP_TABLE_32 = 5, + }; + + public: + DataCodeEntry(void); + DataCodeEntry(uint32_t off, uint16_t length, TYPES type); + DataCodeEntry(const data_in_code_entry* entry); + + DataCodeEntry& operator=(const DataCodeEntry&); + DataCodeEntry(const DataCodeEntry&); + + //! Offset of the data + uint32_t offset(void) const; + + //! Length of the data + uint16_t length(void) const; + + // Type of the data + TYPES type(void) const; + + void offset(uint32_t off); + void length(uint16_t length); + void type(TYPES type); + + virtual ~DataCodeEntry(void); + + bool operator==(const DataCodeEntry& rhs) const; + bool operator!=(const DataCodeEntry& rhs) const; + + virtual void accept(Visitor& visitor) const override; + + LIEF_API friend std::ostream& operator<<(std::ostream& os, const DataCodeEntry& entry); + + private: + uint32_t offset_; + uint16_t length_; + TYPES type_; +}; + +} +} + +#endif diff --git a/include/LIEF/MachO/DataInCode.hpp b/include/LIEF/MachO/DataInCode.hpp new file mode 100644 index 0000000..4e98bca --- /dev/null +++ b/include/LIEF/MachO/DataInCode.hpp @@ -0,0 +1,77 @@ +/* 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_DATA_IN_CODE_COMMAND_H_ +#define LIEF_MACHO_DATA_IN_CODE_COMMAND_H_ +#include <string> +#include <vector> +#include <iostream> + +#include "LIEF/visibility.h" +#include "LIEF/types.hpp" + +#include "LIEF/MachO/LoadCommand.hpp" +#include "LIEF/MachO/DataCodeEntry.hpp" + +namespace LIEF { +namespace MachO { + +class BinaryParser; + +//! Interface of the LC_DATA_IN_CODE command +class LIEF_API DataInCode : public LoadCommand { + friend class BinaryParser; + public: + using entries_t = std::vector<DataCodeEntry>; + using it_const_entries = const_ref_iterator<const entries_t&>; + using it_entries = ref_iterator<entries_t&>; + + public: + DataInCode(void); + DataInCode(const linkedit_data_command *cmd); + + DataInCode& operator=(const DataInCode&); + DataInCode(const DataInCode&); + + uint32_t data_offset(void) const; + uint32_t data_size(void) const; + + void data_offset(uint32_t offset); + void data_size(uint32_t size); + + DataInCode& add(const DataCodeEntry& entry); + + it_const_entries entries(void) const; + it_entries entries(void); + + virtual ~DataInCode(void); + + bool operator==(const DataInCode& rhs) const; + bool operator!=(const DataInCode& 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_; + entries_t entries_; + +}; + +} +} +#endif diff --git a/include/LIEF/MachO/EnumToString.hpp b/include/LIEF/MachO/EnumToString.hpp index 3cfd0a2..e6f1009 100644 --- a/include/LIEF/MachO/EnumToString.hpp +++ b/include/LIEF/MachO/EnumToString.hpp @@ -18,6 +18,7 @@ #include "LIEF/visibility.h" #include "LIEF/MachO/Structures.hpp" +#include "LIEF/MachO/DataCodeEntry.hpp" namespace LIEF { namespace MachO { @@ -48,6 +49,7 @@ LIEF_API const char* to_string(BIND_OPCODES e); LIEF_API const char* to_string(EXPORT_SYMBOL_KINDS e); LIEF_API const char* to_string(VM_PROTECTIONS e); LIEF_API const char* to_string(SYMBOL_ORIGINS e); +LIEF_API const char* to_string(DataCodeEntry::TYPES e); } // namespace MachO } // namespace LIEF diff --git a/include/LIEF/MachO/hash.hpp b/include/LIEF/MachO/hash.hpp index 0e31b52..9329189 100644 --- a/include/LIEF/MachO/hash.hpp +++ b/include/LIEF/MachO/hash.hpp @@ -53,6 +53,8 @@ class LIEF_API Hash : public LIEF::Hash { virtual void visit(const ExportInfo& einfo) override; virtual void visit(const FunctionStarts& fs) override; virtual void visit(const CodeSignature& cs) override; + virtual void visit(const DataInCode& dic) override; + virtual void visit(const DataCodeEntry& dce) override; virtual ~Hash(void); }; diff --git a/include/LIEF/Visitor.hpp b/include/LIEF/Visitor.hpp index e6c7b18..26b8a54 100644 --- a/include/LIEF/Visitor.hpp +++ b/include/LIEF/Visitor.hpp @@ -140,6 +140,8 @@ LIEF_MACHO_FORWARD(BindingInfo) LIEF_MACHO_FORWARD(ExportInfo) LIEF_MACHO_FORWARD(FunctionStarts) LIEF_MACHO_FORWARD(CodeSignature) +LIEF_MACHO_FORWARD(DataInCode) +LIEF_MACHO_FORWARD(DataCodeEntry) class LIEF_API Visitor { @@ -403,6 +405,12 @@ class LIEF_API Visitor { //! @brief Method to visit a LIEF::MachO::CodeSignature LIEF_MACHO_VISITABLE(CodeSignature) + //! @brief Method to visit a LIEF::MachO::DataInCode + LIEF_MACHO_VISITABLE(DataInCode) + + //! @brief Method to visit a LIEF::MachO::DataCodeEntry + LIEF_MACHO_VISITABLE(DataCodeEntry) + template<class T> void dispatch(const T& obj); diff --git a/src/MachO/Binary.cpp b/src/MachO/Binary.cpp index ac0f765..925dee7 100644 --- a/src/MachO/Binary.cpp +++ b/src/MachO/Binary.cpp @@ -814,6 +814,21 @@ const CodeSignature& Binary::code_signature(void) const { } +// DataInCode command +// +++++++++++++++++++++ +bool Binary::has_data_in_code(void) const { + return this->has_command<DataInCode>(); +} + +DataInCode& Binary::data_in_code(void) { + return this->command<DataInCode>(); +} + +const DataInCode& Binary::data_in_code(void) const { + return this->command<DataInCode>(); +} + + void Binary::accept(LIEF::Visitor& visitor) const { diff --git a/src/MachO/BinaryParser.tcc b/src/MachO/BinaryParser.tcc index da7098f..f6a08f2 100644 --- a/src/MachO/BinaryParser.tcc +++ b/src/MachO/BinaryParser.tcc @@ -411,6 +411,23 @@ void BinaryParser::parse_load_commands(void) { break; } + case LOAD_COMMAND_TYPES::LC_DATA_IN_CODE: + { + + const linkedit_data_command* cmd = + reinterpret_cast<const linkedit_data_command*>( + this->stream_->read(loadcommands_offset, sizeof(linkedit_data_command))); + load_command = std::unique_ptr<DataInCode>{new DataInCode{cmd}}; + DataInCode* datacode = load_command.get()->as<DataInCode>(); + + const data_in_code_entry* entries = reinterpret_cast<const data_in_code_entry*>(this->stream_->read(datacode->data_offset(), datacode->data_size())); + const size_t nb_entries = datacode->data_size() / sizeof(data_in_code_entry); + for (size_t i = 0; i < nb_entries; ++i) { + datacode->add(&entries[i]); + } + break; + } + diff --git a/src/MachO/CMakeLists.txt b/src/MachO/CMakeLists.txt index 155f4c4..08160bd 100644 --- a/src/MachO/CMakeLists.txt +++ b/src/MachO/CMakeLists.txt @@ -49,6 +49,8 @@ set(LIEF_MACHO_SRC "${CMAKE_CURRENT_LIST_DIR}/ParserConfig.cpp" "${CMAKE_CURRENT_LIST_DIR}/hash.cpp" "${CMAKE_CURRENT_LIST_DIR}/CodeSignature.cpp" + "${CMAKE_CURRENT_LIST_DIR}/DataInCode.cpp" + "${CMAKE_CURRENT_LIST_DIR}/DataCodeEntry.cpp" ) set(LIEF_MACHO_INCLUDE_FILES @@ -86,6 +88,8 @@ set(LIEF_MACHO_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/ParserConfig.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/hash.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/CodeSignature.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/DataInCode.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/DataCodeEntry.hpp" ) source_group("Header Files\\MachO" FILES ${LIEF_MACHO_INCLUDE_FILES}) diff --git a/src/MachO/DataCodeEntry.cpp b/src/MachO/DataCodeEntry.cpp new file mode 100644 index 0000000..f50d7c3 --- /dev/null +++ b/src/MachO/DataCodeEntry.cpp @@ -0,0 +1,96 @@ +/* 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 <numeric> +#include <iomanip> + +#include "LIEF/MachO/hash.hpp" + +#include "LIEF/MachO/DataCodeEntry.hpp" + +namespace LIEF { +namespace MachO { + +DataCodeEntry& DataCodeEntry::operator=(const DataCodeEntry&) = default; +DataCodeEntry::DataCodeEntry(const DataCodeEntry&) = default; +DataCodeEntry::~DataCodeEntry(void) = default; + +DataCodeEntry::DataCodeEntry(void) : + offset_{0}, + length_{0}, + type_{TYPES::UNKNOWN} +{} + +DataCodeEntry::DataCodeEntry(uint32_t off, uint16_t length, TYPES type) : + offset_{off}, + length_{length}, + type_{type} +{} + +DataCodeEntry::DataCodeEntry(const data_in_code_entry* entry) : + offset_{entry->offset}, + length_{entry->length}, + type_{static_cast<TYPES>(entry->kind)} +{} + + +uint32_t DataCodeEntry::offset(void) const { + return this->offset_; +} + +uint16_t DataCodeEntry::length(void) const { + return this->length_; +} + +DataCodeEntry::TYPES DataCodeEntry::type(void) const { + return this->type_; +} + +void DataCodeEntry::offset(uint32_t off) { + this->offset_ = off; +} + +void DataCodeEntry::length(uint16_t length) { + this->length_ = length; +} + +void DataCodeEntry::type(TYPES type) { + this->type_ = type; +} + +void DataCodeEntry::accept(Visitor& visitor) const { + visitor.visit(*this); +} + + +bool DataCodeEntry::operator==(const DataCodeEntry& rhs) const { + size_t hash_lhs = Hash::hash(*this); + size_t hash_rhs = Hash::hash(rhs); + return hash_lhs == hash_rhs; +} + +bool DataCodeEntry::operator!=(const DataCodeEntry& rhs) const { + return not (*this == rhs); +} + +std::ostream& operator<<(std::ostream& os, const DataCodeEntry& entry) { + return os; +} + + + + +} +} diff --git a/src/MachO/DataInCode.cpp b/src/MachO/DataInCode.cpp new file mode 100644 index 0000000..48131b3 --- /dev/null +++ b/src/MachO/DataInCode.cpp @@ -0,0 +1,97 @@ +/* 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 <numeric> +#include <iomanip> + +#include "LIEF/MachO/hash.hpp" + +#include "LIEF/MachO/DataInCode.hpp" + +namespace LIEF { +namespace MachO { + +DataInCode::DataInCode(void) = default; +DataInCode& DataInCode::operator=(const DataInCode&) = default; +DataInCode::DataInCode(const DataInCode&) = default; +DataInCode::~DataInCode(void) = default; + +DataInCode::DataInCode(const linkedit_data_command *cmd) : + LoadCommand::LoadCommand{static_cast<LOAD_COMMAND_TYPES>(cmd->cmd), cmd->cmdsize}, + data_offset_{cmd->dataoff}, + data_size_{cmd->datasize} +{} + +uint32_t DataInCode::data_offset(void) const { + return this->data_offset_; +} + +uint32_t DataInCode::data_size(void) const { + return this->data_size_; +} + +void DataInCode::data_offset(uint32_t offset) { + this->data_offset_ = offset; +} + +void DataInCode::data_size(uint32_t size) { + this->data_size_ = size; +} + + +DataInCode& DataInCode::add(const DataCodeEntry& entry) { + this->entries_.push_back(entry); + return *this; +} + + +DataInCode::it_const_entries DataInCode::entries(void) const { + return this->entries_; +} + +DataInCode::it_entries DataInCode::entries(void) { + return this->entries_; +} + + +void DataInCode::accept(Visitor& visitor) const { + visitor.visit(*this); +} + + +bool DataInCode::operator==(const DataInCode& rhs) const { + size_t hash_lhs = Hash::hash(*this); + size_t hash_rhs = Hash::hash(rhs); + return hash_lhs == hash_rhs; +} + +bool DataInCode::operator!=(const DataInCode& rhs) const { + return not (*this == rhs); +} + + +std::ostream& DataInCode::print(std::ostream& os) const { + LoadCommand::print(os); + os << std::left; + os << std::endl; + os << "Data location:" << std::endl; + os << std::setw(8) << "Offset" << ": 0x" << this->data_offset() << std::endl; + os << std::setw(8) << "Size" << ": 0x" << this->data_size() << std::endl; + return os; +} + + +} +} diff --git a/src/MachO/EnumToString.cpp b/src/MachO/EnumToString.cpp index a512106..eeeabb1 100644 --- a/src/MachO/EnumToString.cpp +++ b/src/MachO/EnumToString.cpp @@ -470,5 +470,19 @@ const char* to_string(SYMBOL_ORIGINS e) { return it == enumStrings.end() ? "Out of range" : it->second; } + +const char* to_string(DataCodeEntry::TYPES e) { + CONST_MAP(DataCodeEntry::TYPES, const char*, 6) enumStrings { + { DataCodeEntry::TYPES::UNKNOWN, "UNKNOWN" }, + { DataCodeEntry::TYPES::DATA, "DATA" }, + { DataCodeEntry::TYPES::JUMP_TABLE_8, "JUMP_TABLE_8" }, + { DataCodeEntry::TYPES::JUMP_TABLE_16, "JUMP_TABLE_16" }, + { DataCodeEntry::TYPES::JUMP_TABLE_32, "JUMP_TABLE_32" }, + { DataCodeEntry::TYPES::ABS_JUMP_TABLE_32, "ABS_JUMP_TABLE_32" }, + }; + auto it = enumStrings.find(e); + return it == enumStrings.end() ? "UNKNOWN" : it->second; +} + } } diff --git a/src/MachO/hash.cpp b/src/MachO/hash.cpp index 002ab55..87995da 100644 --- a/src/MachO/hash.cpp +++ b/src/MachO/hash.cpp @@ -245,6 +245,19 @@ void Hash::visit(const CodeSignature& cs) { this->process(cs.data_size()); } +void Hash::visit(const DataInCode& dic) { + this->process(dic.data_offset()); + this->process(dic.data_size()); + this->process(std::begin(dic.entries()), std::end(dic.entries())); +} + +void Hash::visit(const DataCodeEntry& dce) { + this->process(dce.offset()); + this->process(dce.length()); + this->process(dce.type()); +} + + diff --git a/tests/macho/macho_tests.py b/tests/macho/macho_tests.py index f46786c..6aa9459 100644 --- a/tests/macho/macho_tests.py +++ b/tests/macho/macho_tests.py @@ -117,6 +117,33 @@ class TestMachO(TestCase): self.assertEqual(relocations[0].has_section, True) self.assertEqual(relocations[0].section.name, "__cstring") + def test_data_in_code(self): + binary = lief.parse(get_sample('MachO/MachO32_ARM_binary_data-in-code-LLVM.bin')) + + self.assertTrue(binary.has_data_in_code) + dcode = binary.data_in_code + + self.assertEqual(dcode.data_offset, 0x11c) + self.assertEqual(dcode.data_size, 0x20) + + self.assertEqual(len(dcode.entries), 4) + + self.assertEqual(dcode.entries[0].type, lief.MachO.DataCodeEntry.TYPES.DATA) + self.assertEqual(dcode.entries[0].offset, 0) + self.assertEqual(dcode.entries[0].length, 4) + + self.assertEqual(dcode.entries[1].type, lief.MachO.DataCodeEntry.TYPES.JUMP_TABLE_32) + self.assertEqual(dcode.entries[1].offset, 4) + self.assertEqual(dcode.entries[1].length, 4) + + self.assertEqual(dcode.entries[2].type, lief.MachO.DataCodeEntry.TYPES.JUMP_TABLE_16) + self.assertEqual(dcode.entries[2].offset, 8) + self.assertEqual(dcode.entries[2].length, 2) + + self.assertEqual(dcode.entries[3].type, lief.MachO.DataCodeEntry.TYPES.JUMP_TABLE_8) + self.assertEqual(dcode.entries[3].offset, 10) + self.assertEqual(dcode.entries[3].length, 1) + if __name__ == '__main__':