4
0
mirror of https://github.com/QuasarApp/LIEF.git synced 2025-05-10 18:59:33 +00:00

Merge branch 'feature/build_version'

This commit is contained in:
Romain Thomas 2018-12-13 15:39:56 +01:00
commit f2b93bf796
28 changed files with 741 additions and 5 deletions

@ -33,6 +33,7 @@ set(LIEF_PYTHON_MACHO_SRC
"${CMAKE_CURRENT_LIST_DIR}/objects/pySubFramework.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyDyldEnvironment.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyEncryptionInfo.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyBuildVersion.cpp"
"${CMAKE_CURRENT_LIST_DIR}/pyEnums.cpp"
)

@ -300,6 +300,16 @@ void create<Binary>(py::module& m) {
"Return binary's " RST_CLASS_REF(lief.MachO.EncryptionInfo) " if any.",
py::return_value_policy::reference)
.def_property_readonly("has_build_version",
&Binary::has_build_version,
"``True`` if the binary has a " RST_CLASS_REF(lief.MachO.BuildVersion) " command",
py::return_value_policy::reference_internal)
.def_property_readonly("build_version",
static_cast<no_const_getter<BuildVersion&>>(&Binary::build_version),
"Return binary's " RST_CLASS_REF(lief.MachO.BuildVersion) " if any.",
py::return_value_policy::reference)
.def("virtual_address_to_offset",
&Binary::virtual_address_to_offset,

@ -0,0 +1,128 @@
/* 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/BuildVersion.hpp"
#include "LIEF/MachO/EnumToString.hpp"
#include "enums_wrapper.hpp"
#include "pyMachO.hpp"
#define PY_ENUM(x) LIEF::MachO::to_string(x), x
namespace LIEF {
namespace MachO {
template<class T>
using getter_t = T (BuildVersion::*)(void) const;
template<class T>
using setter_t = void (BuildVersion::*)(T);
template<>
void create<BuildVersion>(py::module& m) {
py::class_<BuildVersion, LoadCommand> cls(m, "BuildVersion");
py::class_<BuildToolVersion, LIEF::Object> tool_version_cls(m, "BuildToolVersion");
// Build Tool Version
// ==================
tool_version_cls
.def_property_readonly("tool",
&BuildToolVersion::tool,
"" RST_CLASS_REF(.BuildeVersion.TOOLS) " type")
.def_property_readonly("version",
&BuildToolVersion::version,
"Version of the tool")
.def("__eq__", &BuildToolVersion::operator==)
.def("__ne__", &BuildToolVersion::operator!=)
.def("__hash__",
[] (const BuildToolVersion& version) {
return Hash::hash(version);
})
.def("__str__",
[] (const BuildToolVersion& version)
{
std::ostringstream stream;
stream << version;
return stream.str();
});
LIEF::enum_<BuildToolVersion::TOOLS>(tool_version_cls, "TOOLS")
.value(PY_ENUM(BuildToolVersion::TOOLS::UNKNOWN))
.value(PY_ENUM(BuildToolVersion::TOOLS::CLANG))
.value(PY_ENUM(BuildToolVersion::TOOLS::SWIFT))
.value(PY_ENUM(BuildToolVersion::TOOLS::LD));
cls
.def_property("platform",
static_cast<getter_t<BuildVersion::PLATFORMS>>(&BuildVersion::platform),
static_cast<setter_t<BuildVersion::PLATFORMS>>(&BuildVersion::platform),
"Target " RST_CLASS_REF(.BuildVersion.PLATFORMS) "")
.def_property("minos",
static_cast<getter_t<BuildVersion::version_t>>(&BuildVersion::minos),
static_cast<setter_t<BuildVersion::version_t>>(&BuildVersion::minos),
"Minimal OS version on which this binary was built to run")
.def_property("sdk",
static_cast<getter_t<BuildVersion::version_t>>(&BuildVersion::minos),
static_cast<setter_t<BuildVersion::version_t>>(&BuildVersion::minos),
"SDK Version")
.def_property_readonly("tools",
static_cast<getter_t<BuildVersion::tools_list_t>>(&BuildVersion::tools),
"List of " RST_CLASS_REF(BuildToolVersion) " used when while this binary")
.def("__eq__", &BuildVersion::operator==)
.def("__ne__", &BuildVersion::operator!=)
.def("__hash__",
[] (const BuildVersion& version) {
return Hash::hash(version);
})
.def("__str__",
[] (const BuildVersion& version)
{
std::ostringstream stream;
stream << version;
return stream.str();
});
LIEF::enum_<BuildVersion::PLATFORMS>(cls, "PLATFORMS")
.value(PY_ENUM(BuildVersion::PLATFORMS::UNKNOWN))
.value(PY_ENUM(BuildVersion::PLATFORMS::MACOS))
.value(PY_ENUM(BuildVersion::PLATFORMS::IOS))
.value(PY_ENUM(BuildVersion::PLATFORMS::TVOS))
.value(PY_ENUM(BuildVersion::PLATFORMS::WATCHOS));
}
}
}

@ -138,7 +138,9 @@ void init_enums(py::module& m) {
.value(PY_ENUM(LIEF::MachO::LOAD_COMMAND_TYPES::LC_LINKER_OPTION))
.value(PY_ENUM(LIEF::MachO::LOAD_COMMAND_TYPES::LC_LINKER_OPTIMIZATION_HINT))
.value(PY_ENUM(LIEF::MachO::LOAD_COMMAND_TYPES::LC_VERSION_MIN_TVOS))
.value(PY_ENUM(LIEF::MachO::LOAD_COMMAND_TYPES::LC_VERSION_MIN_WATCHOS));
.value(PY_ENUM(LIEF::MachO::LOAD_COMMAND_TYPES::LC_VERSION_MIN_WATCHOS))
.value(PY_ENUM(LIEF::MachO::LOAD_COMMAND_TYPES::LC_NOTE))
.value(PY_ENUM(LIEF::MachO::LOAD_COMMAND_TYPES::LC_BUILD_VERSION));
LIEF::enum_<LIEF::MachO::MACHO_SECTION_TYPES>(m, "SECTION_TYPES")

@ -64,6 +64,7 @@ void init_objects(py::module& m) {
CREATE(SubFramework, m);
CREATE(DyldEnvironment, m);
CREATE(EncryptionInfo, m);
CREATE(BuildVersion, m);
}
}

@ -77,6 +77,7 @@ SPECIALIZE_CREATE(SegmentSplitInfo);
SPECIALIZE_CREATE(SubFramework);
SPECIALIZE_CREATE(DyldEnvironment);
SPECIALIZE_CREATE(EncryptionInfo);
SPECIALIZE_CREATE(BuildVersion);
}
}

@ -285,6 +285,24 @@ Encryption Info
----------
Build Version
*************
.. doxygenclass:: LIEF::MachO::BuildVersion
:project: lief
----------
Build Tool Version
******************
.. doxygenclass:: LIEF::MachO::BuildToolVersion
:project: lief
----------
Utilities
*********

@ -353,6 +353,27 @@ Encryption Info
----------
Build Version
*************
.. autoclass:: lief.MachO.BuildVersion
:members:
:inherited-members:
:undoc-members:
----------
Build Tool Version
******************
.. autoclass:: lief.MachO.BuildToolVersion
:members:
:inherited-members:
:undoc-members:
----------
Enums

@ -672,7 +672,22 @@ def print_functions(binary):
for idx, f in enumerate(binary.functions):
print(" [{:d}] {}: 0x{:x}".format(idx, f.name, f.address))
@exceptions_handler(Exception)
def print_build_version(binary):
print("== Build Version ==\n")
build_version = binary.build_version
print("Platform: {}".format(str(build_version.platform).split(".")[-1]))
print("Min OS: {:d}.{:d}.{:d}".format(*build_version.minos))
print("SDK: {:d}.{:d}.{:d}".format(*build_version.sdk))
tools = build_version.tools
if len(tools) > 0:
print("~~ Tools ({}) ~~".format(len(tools)))
for tool in tools:
tool_str = str(tool.tool).split(".")[-1]
print(" {} - {}.{}.{}".format(tool_str, *tool.version))
def main():
parser = argparse.ArgumentParser(usage='%(prog)s [options] <macho-file>')
@ -808,6 +823,10 @@ def main():
action='store_true', dest='show_functions',
help='All functions found in the binary')
parser.add_argument('--build-version',
action='store_true', dest='show_build_version',
help='Show build version')
parser.add_argument("binary",
metavar="<macho-file>",
help='Target Mach-O File')
@ -924,6 +943,9 @@ def main():
if args.show_functions or args.show_all:
print_functions(binary)
if (args.show_build_version or args.show_all) and binary.has_build_version:
print_build_version(binary)
sys.exit(EXIT_STATUS)

@ -47,6 +47,7 @@
#include "LIEF/MachO/SubFramework.hpp"
#include "LIEF/MachO/DyldEnvironment.hpp"
#include "LIEF/MachO/EncryptionInfo.hpp"
#include "LIEF/MachO/BuildVersion.hpp"
namespace LIEF {
namespace MachO {
@ -403,6 +404,13 @@ class LIEF_API Binary : public LIEF::Binary {
DyldEnvironment& dyld_environment(void);
const DyldEnvironment& dyld_environment(void) const;
//! ``true`` if the binary has Build Version command.
bool has_build_version(void) const;
//! Return the MachO::BuildVersion
BuildVersion& build_version(void);
const BuildVersion& build_version(void) const;
template<class T>
LIEF_LOCAL bool has_command(void) const;

@ -0,0 +1,125 @@
/* 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_BUILD_VERSION_COMMAND_H_
#define LIEF_MACHO_BUILD_VERSION_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 BuildToolVersion : public LIEF::Object {
public:
//! @brief Version is an array of **3** integers
using version_t = std::array<uint32_t, 3>;
public:
enum class TOOLS {
UNKNOWN = 0,
CLANG = 1,
SWIFT = 2,
LD = 3,
};
public:
BuildToolVersion(void);
BuildToolVersion(const build_tool_version& tool);
//! TOOL used
TOOLS tool(void) const;
//! Version associated with the tool
version_t version(void) const;
virtual ~BuildToolVersion(void);
bool operator==(const BuildToolVersion& rhs) const;
bool operator!=(const BuildToolVersion& rhs) const;
virtual void accept(Visitor& visitor) const override;
LIEF_API friend std::ostream& operator<<(std::ostream& os, const BuildToolVersion& tool);
private:
TOOLS tool_{TOOLS::UNKNOWN};
version_t version_;
};
class LIEF_API BuildVersion : public LoadCommand {
friend class BinaryParser;
public:
//! @brief Version is an array of **3** integers
using version_t = std::array<uint32_t, 3>;
using tools_list_t = std::vector<BuildToolVersion>;
public:
enum class PLATFORMS {
UNKNOWN = 0,
MACOS = 1,
IOS = 2,
TVOS = 3,
WATCHOS = 4,
};
public:
BuildVersion(void);
BuildVersion(const build_version_command *version_cmd);
BuildVersion& operator=(const BuildVersion& copy);
BuildVersion(const BuildVersion& copy);
virtual BuildVersion* clone(void) const override;
version_t minos(void) const;
void minos(version_t version);
version_t sdk(void) const;
void sdk(version_t version);
PLATFORMS platform(void) const;
void platform(PLATFORMS plat);
tools_list_t tools(void) const;
virtual ~BuildVersion(void);
bool operator==(const BuildVersion& rhs) const;
bool operator!=(const BuildVersion& rhs) const;
virtual void accept(Visitor& visitor) const override;
virtual std::ostream& print(std::ostream& os) const override;
private:
PLATFORMS platform_{PLATFORMS::UNKNOWN};
version_t minos_;
version_t sdk_;
tools_list_t tools_;
};
}
}
#endif

@ -106,8 +106,12 @@ class LIEF_API Builder {
template <typename T>
void build_segments(void);
template<class T>
void build(BuildVersion* bv);
void build_uuid(void);
template <typename T>
void build_symbols(void);

@ -19,6 +19,7 @@
#include "LIEF/MachO/Structures.hpp"
#include "LIEF/MachO/DataCodeEntry.hpp"
#include "LIEF/MachO/BuildVersion.hpp"
namespace LIEF {
namespace MachO {
@ -51,6 +52,8 @@ LIEF_API const char* to_string(VM_PROTECTIONS e);
LIEF_API const char* to_string(SYMBOL_ORIGINS e);
LIEF_API const char* to_string(EXPORT_SYMBOL_FLAGS e);
LIEF_API const char* to_string(DataCodeEntry::TYPES e);
LIEF_API const char* to_string(BuildVersion::PLATFORMS e);
LIEF_API const char* to_string(BuildToolVersion::TOOLS e);
} // namespace MachO

@ -105,6 +105,8 @@ enum _LIEF_EN(LOAD_COMMAND_TYPES) {
_LIEF_EI(LC_LINKER_OPTIMIZATION_HINT) = 0x0000002Eu,
_LIEF_EI(LC_VERSION_MIN_TVOS) = 0x0000002Fu,
_LIEF_EI(LC_VERSION_MIN_WATCHOS) = 0x00000030u,
_LIEF_EI(LC_NOTE) = 0x00000031u,
_LIEF_EI(LC_BUILD_VERSION) = 0x00000032u,
};
@ -586,4 +588,3 @@ enum _LIEF_EN(CPU_SUBTYPES_POWERPC) {
};

@ -61,6 +61,8 @@ class LIEF_API Hash : public LIEF::Hash {
virtual void visit(const SubFramework& sf) override;
virtual void visit(const DyldEnvironment& sf) override;
virtual void visit(const EncryptionInfo& e) override;
virtual void visit(const BuildVersion& e) override;
virtual void visit(const BuildToolVersion& e) override;
virtual ~Hash(void);
};

@ -65,6 +65,8 @@ class LIEF_API JsonVisitor : public LIEF::JsonVisitor {
virtual void visit(const SubFramework& sf) override;
virtual void visit(const DyldEnvironment& sf) override;
virtual void visit(const EncryptionInfo& e) override;
virtual void visit(const BuildVersion& e) override;
virtual void visit(const BuildToolVersion& e) override;
};
}

@ -318,6 +318,22 @@ struct version_min_command {
uint32_t sdk; // X.Y.Z is encoded in nibbles xxxx.yy.zz
};
struct build_version_command {
uint32_t cmd;
uint32_t cmdsize;
uint32_t platform;
uint32_t minos;
uint32_t sdk;
uint32_t ntools;
};
struct build_tool_version {
uint32_t tool;
uint32_t version;
};
struct dyld_info_command {
uint32_t cmd;
uint32_t cmdsize;
@ -592,3 +608,4 @@ struct unwind_info_compressed_second_level_page_header {

@ -149,6 +149,8 @@ LIEF_MACHO_FORWARD(SegmentSplitInfo)
LIEF_MACHO_FORWARD(SubFramework)
LIEF_MACHO_FORWARD(DyldEnvironment)
LIEF_MACHO_FORWARD(EncryptionInfo)
LIEF_MACHO_FORWARD(BuildVersion)
LIEF_MACHO_FORWARD(BuildToolVersion)
// OAT
// ===============================
@ -469,6 +471,12 @@ class LIEF_API Visitor {
//! @brief Method to visit a LIEF::MachO::DyldEnvironment
LIEF_MACHO_VISITABLE(EncryptionInfo)
//! @brief Method to visit a LIEF::MachO:BuildVersion:
LIEF_MACHO_VISITABLE(BuildVersion)
//! @brief Method to visit a LIEF::MachO:BuildToolVersion:
LIEF_MACHO_VISITABLE(BuildToolVersion)
// OAT part
// ========

@ -1808,6 +1808,21 @@ const EncryptionInfo& Binary::encryption_info(void) const {
}
// BuildVersion command
// ++++++++++++++++++++
bool Binary::has_build_version(void) const {
return this->has_command<BuildVersion>();
}
BuildVersion& Binary::build_version(void) {
return this->command<BuildVersion>();
}
const BuildVersion& Binary::build_version(void) const {
return this->command<BuildVersion>();
}
LoadCommand& Binary::operator[](LOAD_COMMAND_TYPES type) {
return this->get(type);
}

@ -388,6 +388,29 @@ void BinaryParser::parse_load_commands(void) {
break;
}
case LOAD_COMMAND_TYPES::LC_BUILD_VERSION:
{
VLOG(VDEBUG) << "[+] Parsing " << to_string(static_cast<LOAD_COMMAND_TYPES>(command.cmd));
const auto cmd = &this->stream_->peek<build_version_command>(loadcommands_offset);
//VLOG(VDEBUG) << "Version: " << std::hex << cmd->version;
//VLOG(VDEBUG) << "SDK: " << std::hex << cmd->sdk;
load_command = std::unique_ptr<BuildVersion>{new BuildVersion{cmd}};
BuildVersion* build_version = load_command->as<BuildVersion>();
for (size_t i = 0; i < cmd->ntools; ++i) {
const uint64_t cmd_offset = loadcommands_offset + sizeof(build_version_command) + i * sizeof(build_tool_version);
if (not this->stream_->can_read<build_tool_version>(cmd_offset)) {
break;
}
auto&& tool_struct = this->stream_->peek<build_tool_version>(cmd_offset);
build_version->tools_.emplace_back(tool_struct);
}
break;
}
// =================
// Code Signature
// =================

173
src/MachO/BuildVersion.cpp Normal file

@ -0,0 +1,173 @@
/* 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/BuildVersion.hpp"
namespace LIEF {
namespace MachO {
BuildToolVersion::BuildToolVersion(void) = default;
BuildToolVersion::BuildToolVersion(const build_tool_version& tool) :
tool_{static_cast<BuildToolVersion::TOOLS>(tool.tool)},
version_{{
static_cast<uint32_t>((tool.version >> 16) & 0xFFFF),
static_cast<uint32_t>((tool.version >> 8) & 0xFF),
static_cast<uint32_t>((tool.version >> 0) & 0xFF)
}}
{}
BuildToolVersion::TOOLS BuildToolVersion::tool(void) const {
return this->tool_;
}
BuildToolVersion::version_t BuildToolVersion::version(void) const {
return this->version_;
}
BuildToolVersion::~BuildToolVersion(void) = default;
bool BuildToolVersion::operator==(const BuildToolVersion& rhs) const {
size_t hash_lhs = Hash::hash(*this);
size_t hash_rhs = Hash::hash(rhs);
return hash_lhs == hash_rhs;
}
bool BuildToolVersion::operator!=(const BuildToolVersion& rhs) const {
return not (*this == rhs);
}
void BuildToolVersion::accept(Visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& operator<<(std::ostream& os, const BuildToolVersion& tool) {
BuildToolVersion::version_t version = tool.version();
os << to_string(tool.tool()) << " - ";
os << std::dec
<< version[0] << "."
<< version[1] << "."
<< version[2] << std::endl;
return os;
}
// Build Version
// =============
BuildVersion::BuildVersion(void) = default;
BuildVersion& BuildVersion::operator=(const BuildVersion&) = default;
BuildVersion::BuildVersion(const BuildVersion&) = default;
BuildVersion::~BuildVersion(void) = default;
BuildVersion::BuildVersion(const build_version_command *version_cmd) :
LoadCommand::LoadCommand{static_cast<LOAD_COMMAND_TYPES>(version_cmd->cmd), version_cmd->cmdsize},
platform_{static_cast<BuildVersion::PLATFORMS>(version_cmd->platform)},
minos_{{
static_cast<uint32_t>((version_cmd->minos >> 16) & 0xFFFF),
static_cast<uint32_t>((version_cmd->minos >> 8) & 0xFF),
static_cast<uint32_t>((version_cmd->minos >> 0) & 0xFF)
}},
sdk_{{
static_cast<uint32_t>((version_cmd->sdk >> 16) & 0xFFFF),
static_cast<uint32_t>((version_cmd->sdk >> 8) & 0xFF),
static_cast<uint32_t>((version_cmd->sdk >> 0) & 0xFF)
}}
{
}
BuildVersion* BuildVersion::clone(void) const {
return new BuildVersion(*this);
}
BuildVersion::version_t BuildVersion::minos(void) const {
return this->minos_;
}
void BuildVersion::minos(BuildVersion::version_t version) {
this->minos_ = version;
}
BuildVersion::version_t BuildVersion::sdk(void) const {
return this->sdk_;
}
void BuildVersion::sdk(BuildVersion::version_t version) {
this->sdk_ = version;
}
BuildVersion::PLATFORMS BuildVersion::platform(void) const {
return this->platform_;
}
void BuildVersion::platform(BuildVersion::PLATFORMS plat) {
this->platform_ = plat;
}
BuildVersion::tools_list_t BuildVersion::tools(void) const {
return this->tools_;
}
void BuildVersion::accept(Visitor& visitor) const {
visitor.visit(*this);
}
bool BuildVersion::operator==(const BuildVersion& rhs) const {
size_t hash_lhs = Hash::hash(*this);
size_t hash_rhs = Hash::hash(rhs);
return hash_lhs == hash_rhs;
}
bool BuildVersion::operator!=(const BuildVersion& rhs) const {
return not (*this == rhs);
}
std::ostream& BuildVersion::print(std::ostream& os) const {
LoadCommand::print(os);
BuildVersion::version_t minos = this->minos();
BuildVersion::version_t sdk = this->sdk();
os << std::setw(10) << "Platform: " << to_string(this->platform()) << std::endl;
os << std::setw(10) << "Min OS: " << std::dec
<< minos[0] << "."
<< minos[1] << "."
<< minos[2] << std::endl;
os << std::setw(10) << "SDK: " << std::dec
<< sdk[0] << "."
<< sdk[1] << "."
<< sdk[2] << std::endl;
for (const BuildToolVersion& tool_version : this->tools()) {
os << " " << tool_version << std::endl;
}
return os;
}
}
}

@ -152,6 +152,11 @@ void Builder::build(void) {
this->build<T>(cmd->as<DyldEnvironment>());
continue;
}
if (cmd->is<BuildVersion>()) {
this->build<T>(cmd->as<BuildVersion>());
continue;
}
}
this->build_segments<T>();
@ -311,7 +316,6 @@ void Builder::build_uuid(void) {
uuid_cmd->originalData_.data());
}
const std::vector<uint8_t>& Builder::get_build(void) {
return this->raw_.raw();
}

@ -928,6 +928,63 @@ void Builder::build(DyldEnvironment* de) {
}
template<class T>
void Builder::build(BuildVersion* bv) {
build_version_command raw_cmd;
const BuildVersion::tools_list_t& tools = bv->tools();
const uint32_t raw_size = sizeof(build_version_command) + tools.size() * sizeof(build_tool_version);
const uint32_t size_needed = align(raw_size, sizeof(typename T::uint));
const uint32_t padding = size_needed - raw_size;
if (bv->originalData_.size() != size_needed or
bv->size() != size_needed) {
//LOG(WARNING) << "Not enough spaces to rebuild " << bv->value() << ": Skip!";
//LOG(WARNING) << std::hex << bv->originalData_.size() << " vs " << size_needed;
}
const BuildVersion::version_t& minos = bv->minos();
const BuildVersion::version_t& sdk = bv->sdk();
std::fill(
reinterpret_cast<uint8_t*>(&raw_cmd),
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(build_version_command),
0);
raw_cmd.cmd = static_cast<uint32_t>(bv->command());
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
raw_cmd.minos = static_cast<uint32_t>(minos[0] << 16 | minos[1] << 8 | minos[2]);
raw_cmd.sdk = static_cast<uint32_t>(sdk[0] << 16 | sdk[1] << 8 | sdk[2]);
raw_cmd.platform = static_cast<uint32_t>(bv->platform());
raw_cmd.ntools = tools.size();
//raw_cmd.name = static_cast<uint32_t>(sizeof(build_version_command));
std::vector<uint8_t> raw_tools(raw_cmd.ntools * sizeof(build_tool_version), 0);
auto tools_array = reinterpret_cast<build_tool_version*>(raw_tools.data());
for (size_t i = 0; i < tools.size(); ++i) {
BuildToolVersion::version_t version = tools[i].version();
tools_array[i].tool = static_cast<uint32_t>(tools[i].tool());
tools_array[i].version = static_cast<uint32_t>(version[0] << 16 | version[1] << 8 | version[2]);
}
bv->size_ = size_needed;
bv->originalData_.clear();
// Write Header
std::move(
reinterpret_cast<uint8_t*>(&raw_cmd),
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
std::back_inserter(bv->originalData_));
std::move(
std::begin(raw_tools),
std::end(raw_tools),
std::back_inserter(bv->originalData_));
bv->originalData_.insert(std::end(bv->originalData_), padding, 0);
}
}
}

@ -56,6 +56,7 @@ set(LIEF_MACHO_SRC
"${CMAKE_CURRENT_LIST_DIR}/DyldEnvironment.cpp"
"${CMAKE_CURRENT_LIST_DIR}/EncryptionInfo.cpp"
"${CMAKE_CURRENT_LIST_DIR}/TrieNode.cpp"
"${CMAKE_CURRENT_LIST_DIR}/BuildVersion.cpp"
)
set(LIEF_MACHO_INCLUDE_FILES
@ -99,6 +100,7 @@ set(LIEF_MACHO_INCLUDE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/SubFramework.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/DyldEnvironment.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/EncryptionInfo.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/BuildVersion.hpp"
"${CMAKE_CURRENT_LIST_DIR}/TrieNode.hpp"
)

@ -54,7 +54,7 @@ const char* to_string(FILE_TYPES e) {
}
const char* to_string(LOAD_COMMAND_TYPES e) {
CONST_MAP(LOAD_COMMAND_TYPES, const char*, 49) enumStrings {
CONST_MAP(LOAD_COMMAND_TYPES, const char*, 51) enumStrings {
{ LOAD_COMMAND_TYPES::LC_SEGMENT, "SEGMENT"},
{ LOAD_COMMAND_TYPES::LC_SYMTAB, "SYMTAB"},
{ LOAD_COMMAND_TYPES::LC_SYMSEG, "SYMSEG"},
@ -103,7 +103,9 @@ const char* to_string(LOAD_COMMAND_TYPES e) {
{ LOAD_COMMAND_TYPES::LC_LINKER_OPTION, "LINKER_OPTION"},
{ LOAD_COMMAND_TYPES::LC_LINKER_OPTIMIZATION_HINT, "LINKER_OPTIMIZATION_HINT"},
{ LOAD_COMMAND_TYPES::LC_VERSION_MIN_TVOS, "VERSION_MIN_TVOS"},
{ LOAD_COMMAND_TYPES::LC_VERSION_MIN_WATCHOS, "VERSION_MIN_WATCHOS"}
{ LOAD_COMMAND_TYPES::LC_VERSION_MIN_WATCHOS, "VERSION_MIN_WATCHOS"},
{ LOAD_COMMAND_TYPES::LC_NOTE, "NOTE"},
{ LOAD_COMMAND_TYPES::LC_BUILD_VERSION, "BUILD_VERSION"}
};
auto it = enumStrings.find(e);
return it == enumStrings.end() ? "Out of range" : it->second;
@ -494,5 +496,29 @@ const char* to_string(DataCodeEntry::TYPES e) {
return it == enumStrings.end() ? "UNKNOWN" : it->second;
}
const char* to_string(BuildVersion::PLATFORMS e) {
CONST_MAP(BuildVersion::PLATFORMS, const char*, 5) enumStrings {
{ BuildVersion::PLATFORMS::UNKNOWN, "UNKNOWN" },
{ BuildVersion::PLATFORMS::MACOS, "MACOS" },
{ BuildVersion::PLATFORMS::IOS, "IOS" },
{ BuildVersion::PLATFORMS::TVOS, "TVOS" },
{ BuildVersion::PLATFORMS::WATCHOS, "WATCHOS" },
};
auto it = enumStrings.find(e);
return it == enumStrings.end() ? "UNKNOWN" : it->second;
}
const char* to_string(BuildToolVersion::TOOLS e) {
CONST_MAP(BuildToolVersion::TOOLS, const char*, 4) enumStrings {
{ BuildToolVersion::TOOLS::UNKNOWN, "UNKNOWN" },
{ BuildToolVersion::TOOLS::SWIFT, "SWIFT" },
{ BuildToolVersion::TOOLS::CLANG, "CLANG" },
{ BuildToolVersion::TOOLS::LD, "LD" },
};
auto it = enumStrings.find(e);
return it == enumStrings.end() ? "UNKNOWN" : it->second;
}
}
}

@ -295,6 +295,21 @@ void Hash::visit(const EncryptionInfo& e) {
this->process(e.crypt_id());
}
void Hash::visit(const BuildVersion& e) {
BuildVersion::tools_list_t tools = e.tools();
this->visit(*e.as<LoadCommand>());
this->process(e.platform());
this->process(e.minos());
this->process(e.sdk());
this->process(std::begin(tools), std::end(tools));
}
void Hash::visit(const BuildToolVersion& e) {
this->process(e.tool());
this->process(e.version());
}

@ -173,6 +173,12 @@ void JsonVisitor::visit(const Binary& binary) {
v(binary.encryption_info());
this->node_["encryption_info"] = v.get();
}
if (binary.has_build_version()) {
JsonVisitor v;
v(binary.build_version());
this->node_["build_version"] = v.get();
}
}
@ -472,6 +478,30 @@ void JsonVisitor::visit(const EncryptionInfo& e) {
}
void JsonVisitor::visit(const BuildVersion& e) {
this->visit(*e.as<LoadCommand>());
this->node_["platform"] = to_string(e.platform());
this->node_["minos"] = e.minos();
this->node_["sdk"] = e.sdk();
std::vector<json> tools;
for (const BuildToolVersion& toolv : e.tools()) {
JsonVisitor v;
v(toolv);
tools.emplace_back(std::move(v.get()));
}
this->node_["tools"] = tools;
}
void JsonVisitor::visit(const BuildToolVersion& e) {
this->node_["tool"] = to_string(e.tool());
this->node_["version"] = e.version();
}
} // namespace MachO

@ -179,6 +179,23 @@ class TestMachO(TestCase):
self.assertEqual(functions[-1].name, "ctor_0")
def test_build_version(self):
binary = lief.MachO.parse(get_sample('MachO/FAT_MachO_arm-arm64-binary-helloworld.bin'))
target = binary[1]
self.assertTrue(target.has_build_version)
build_version = target.build_version
self.assertEqual(build_version.minos, [12, 1, 0])
self.assertEqual(build_version.sdk, [12, 1, 0])
self.assertEqual(build_version.platform, lief.MachO.BuildVersion.PLATFORMS.IOS)
tools = build_version.tools
self.assertEqual(len(tools), 1)
self.assertEqual(tools[0].version, [409, 12, 0])
self.assertEqual(tools[0].tool, lief.MachO.BuildToolVersion.TOOLS.LD)
if __name__ == '__main__':