From 8316dacf0bac85d755ded7f899e033f689ee579b Mon Sep 17 00:00:00 2001 From: Romain Thomas Date: Sat, 25 May 2024 06:45:08 +0200 Subject: [PATCH] Add Mach-O `UnknownCommand`. This command is a placeholder when LIEF does not a Mach-O load command --- api/python/lief/MachO.pyi | 6 ++ api/python/src/MachO/init.cpp | 2 + api/python/src/MachO/objects/CMakeLists.txt | 73 ++++++++++--------- .../src/MachO/objects/pyLoadCommand.cpp | 1 + .../src/MachO/objects/pyUnknownCommand.cpp | 39 ++++++++++ api/rust/autocxx_ffi.rs | 2 + api/rust/cargo/lief/src/macho/commands.rs | 10 +++ .../cargo/lief/src/macho/commands/unknown.rs | 40 ++++++++++ api/rust/cargo/lief/tests/macho_tests.rs | 6 ++ api/rust/include/LIEF/rust/MachO.hpp | 1 + .../LIEF/rust/MachO/UnknownCommand.hpp | 36 +++++++++ doc/sphinx/api/cpp/macho.rst | 8 ++ doc/sphinx/api/python/macho.rst | 11 +++ doc/sphinx/changelog.rst | 2 + include/LIEF/MachO.hpp | 3 +- include/LIEF/MachO/LoadCommand.hpp | 2 + include/LIEF/MachO/UnknownCommand.hpp | 71 ++++++++++++++++++ include/LIEF/MachO/hash.hpp | 2 + include/LIEF/Visitor.hpp | 4 + src/MachO/BinaryParser.tcc | 3 +- src/MachO/CMakeLists.txt | 1 + src/MachO/LoadCommand.cpp | 1 + src/MachO/UnknownCommand.cpp | 32 ++++++++ src/MachO/hash.cpp | 5 ++ src/MachO/json.cpp | 6 ++ src/MachO/json_internal.hpp | 2 + tests/macho/test_generic.py | 8 ++ 27 files changed, 339 insertions(+), 38 deletions(-) create mode 100644 api/python/src/MachO/objects/pyUnknownCommand.cpp create mode 100644 api/rust/cargo/lief/src/macho/commands/unknown.rs create mode 100644 api/rust/include/LIEF/rust/MachO/UnknownCommand.hpp create mode 100644 include/LIEF/MachO/UnknownCommand.hpp create mode 100644 src/MachO/UnknownCommand.cpp diff --git a/api/python/lief/MachO.pyi b/api/python/lief/MachO.pyi index 5b3e38d8..f1e361c4 100644 --- a/api/python/lief/MachO.pyi +++ b/api/python/lief/MachO.pyi @@ -1110,6 +1110,7 @@ class LoadCommand(lief.Object): ID_DYLIB: ClassVar[LoadCommand.TYPE] = ... ID_DYLINKER: ClassVar[LoadCommand.TYPE] = ... LAZY_LOAD_DYLIB: ClassVar[LoadCommand.TYPE] = ... + LIEF_UNKNOWN: ClassVar[LoadCommand.TYPE] = ... LINKER_OPTIMIZATION_HINT: ClassVar[LoadCommand.TYPE] = ... LINKER_OPTION: ClassVar[LoadCommand.TYPE] = ... LOADFVMLIB: ClassVar[LoadCommand.TYPE] = ... @@ -1610,6 +1611,11 @@ class UUIDCommand(LoadCommand): uuid: list[int] def __init__(self, *args, **kwargs) -> None: ... +class UnknownCommand(LoadCommand): + def __init__(self, *args, **kwargs) -> None: ... + @property + def original_command(self) -> int: ... + class VersionMin(LoadCommand): sdk: list[int] version: list[int] diff --git a/api/python/src/MachO/init.cpp b/api/python/src/MachO/init.cpp index 0920217c..aa093479 100644 --- a/api/python/src/MachO/init.cpp +++ b/api/python/src/MachO/init.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,7 @@ void init_objects(nb::module_& m) { CREATE(ChainedBindingInfo, m); CREATE(TwoLevelHints, m); CREATE(LinkerOptHint, m); + CREATE(UnknownCommand, m); CREATE(Builder, m); } diff --git a/api/python/src/MachO/objects/CMakeLists.txt b/api/python/src/MachO/objects/CMakeLists.txt index 4748e3a3..942bd868 100644 --- a/api/python/src/MachO/objects/CMakeLists.txt +++ b/api/python/src/MachO/objects/CMakeLists.txt @@ -1,46 +1,47 @@ target_sources(pyLIEF PRIVATE - pyDylibCommand.cpp pyBinary.cpp + pyBindingInfo.cpp + pyBuildVersion.cpp + pyBuilder.cpp + pyChainedBindingInfo.cpp + pyCodeSignature.cpp + pyCodeSignatureDir.cpp + pyDataCodeEntry.cpp + pyDataInCode.cpp + pyDyldBindingInfo.cpp + pyDyldChainedFixups.cpp + pyDyldEnvironment.cpp + pyDyldExportsTrie.cpp + pyDyldInfo.cpp + pyDylibCommand.cpp + pyDylinker.cpp + pyDynamicSymbolCommand.cpp + pyEncryptionInfo.cpp + pyExportInfo.cpp pyFatBinary.cpp - pyLoadCommand.cpp - pySegmentCommand.cpp + pyFilesetCommand.cpp + pyFunctionStarts.cpp pyHeader.cpp - pySection.cpp + pyLinkerOptHint.cpp + pyLoadCommand.cpp + pyMainCommand.cpp pyParser.cpp + pyParserConfig.cpp + pyRPathCommand.cpp + pyRelocation.cpp + pyRelocationDyld.cpp + pyRelocationFixup.cpp + pyRelocationObject.cpp + pySection.cpp + pySegmentCommand.cpp + pySegmentSplitInfo.cpp + pySourceVersion.cpp + pySubFramework.cpp pySymbol.cpp pySymbolCommand.cpp - pyUUID.cpp - pySourceVersion.cpp - pyVersionMin.cpp - pyMainCommand.cpp - pyDylinker.cpp - pyDyldInfo.cpp - pyDyldChainedFixups.cpp - pyChainedBindingInfo.cpp - pyDyldBindingInfo.cpp - pyDyldExportsTrie.cpp - pyRelocationFixup.cpp - pyFunctionStarts.cpp - pyRelocation.cpp - pyRelocationObject.cpp - pyRelocationDyld.cpp - pyBindingInfo.cpp - pyExportInfo.cpp pyThreadCommand.cpp - pyRPathCommand.cpp - pyParserConfig.cpp - pyDynamicSymbolCommand.cpp - pyCodeSignature.cpp - pySegmentSplitInfo.cpp - pyDataInCode.cpp - pyDataCodeEntry.cpp - pySubFramework.cpp - pyDyldEnvironment.cpp - pyEncryptionInfo.cpp - pyBuildVersion.cpp - pyFilesetCommand.cpp - pyCodeSignatureDir.cpp pyTwoLevelHints.cpp - pyLinkerOptHint.cpp - pyBuilder.cpp + pyUUID.cpp + pyUnknownCommand.cpp + pyVersionMin.cpp ) diff --git a/api/python/src/MachO/objects/pyLoadCommand.cpp b/api/python/src/MachO/objects/pyLoadCommand.cpp index dccda923..6bb78873 100644 --- a/api/python/src/MachO/objects/pyLoadCommand.cpp +++ b/api/python/src/MachO/objects/pyLoadCommand.cpp @@ -116,6 +116,7 @@ void create(nb::module_& m) { .value(PY_ENUM(LoadCommand::TYPE::DYLD_EXPORTS_TRIE)) .value(PY_ENUM(LoadCommand::TYPE::DYLD_CHAINED_FIXUPS)) .value(PY_ENUM(LoadCommand::TYPE::FILESET_ENTRY)) + .value(PY_ENUM(LoadCommand::TYPE::LIEF_UNKNOWN)) #undef PY_ENUM ; diff --git a/api/python/src/MachO/objects/pyUnknownCommand.cpp b/api/python/src/MachO/objects/pyUnknownCommand.cpp new file mode 100644 index 00000000..9768d0f1 --- /dev/null +++ b/api/python/src/MachO/objects/pyUnknownCommand.cpp @@ -0,0 +1,39 @@ +/* Copyright 2017 - 2024 R. Thomas + * Copyright 2017 - 2024 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 +#include +#include +#include + +#include "LIEF/MachO/UnknownCommand.hpp" + +#include "MachO/pyMachO.hpp" + +namespace LIEF::MachO::py { + +template<> +void create(nb::module_& m) { + + nb::class_(m, "UnknownCommand", + "Generic class when the command is not recognized by LIEF"_doc) + + .def_prop_ro("original_command", + nb::overload_cast<>(&UnknownCommand::original_command, nb::const_)) + + LIEF_DEFAULT_STR(UnknownCommand); +} + +} diff --git a/api/rust/autocxx_ffi.rs b/api/rust/autocxx_ffi.rs index 898d4786..a58db936 100644 --- a/api/rust/autocxx_ffi.rs +++ b/api/rust/autocxx_ffi.rs @@ -384,6 +384,8 @@ include_cpp! { block_constructors!("MachO_Utils") generate!("MachO_VersionMin") block_constructors!("MachO_VersionMin") + generate!("MachO_UnknownCommand") + block_constructors!("MachO_UnknownCommand") safety!(unsafe) } diff --git a/api/rust/cargo/lief/src/macho/commands.rs b/api/rust/cargo/lief/src/macho/commands.rs index 6dd6c373..7c491b72 100644 --- a/api/rust/cargo/lief/src/macho/commands.rs +++ b/api/rust/cargo/lief/src/macho/commands.rs @@ -26,6 +26,7 @@ pub mod thread_command; pub mod two_level_hints; pub mod uuid; pub mod version_min; +pub mod unknown; pub use build_version::BuildVersion; pub use code_signature::CodeSignature; @@ -52,6 +53,7 @@ pub use thread_command::ThreadCommand; pub use two_level_hints::TwoLevelHints; pub use uuid::UUID; pub use version_min::VersionMin; +pub use unknown::Unknown; use crate::common::FromFFI; use crate::{declare_iterator, to_slice}; @@ -261,6 +263,7 @@ pub enum Commands<'a> { TwoLevelHints(TwoLevelHints<'a>), UUID(UUID<'a>), VersionMin(VersionMin<'a>), + Unknown(Unknown<'a>), } impl<'a> Commands<'a> { @@ -443,6 +446,13 @@ impl<'a> Commands<'a> { std::mem::transmute::(ffi_entry) }; Commands::VersionMin(VersionMin::from_ffi(raw)) + } else if ffi::MachO_UnknownCommand::classof(cmd_ref) { + let raw = { + type From = cxx::UniquePtr; + type To = cxx::UniquePtr; + std::mem::transmute::(ffi_entry) + }; + Commands::Unknown(Unknown::from_ffi(raw)) } else { Commands::Generic(Generic::from_ffi(ffi_entry)) } diff --git a/api/rust/cargo/lief/src/macho/commands/unknown.rs b/api/rust/cargo/lief/src/macho/commands/unknown.rs new file mode 100644 index 00000000..7b742eac --- /dev/null +++ b/api/rust/cargo/lief/src/macho/commands/unknown.rs @@ -0,0 +1,40 @@ +use super::Command; +use lief_ffi as ffi; +use crate::common::FromFFI; + +use std::marker::PhantomData; + +pub struct Unknown<'a> { + ptr: cxx::UniquePtr, + _owner: PhantomData<&'a ffi::MachO_Binary> +} + + +impl Unknown<'_> { + pub fn original_command(&self) -> u64 { + self.ptr.original_command() + } +} + +impl std::fmt::Debug for Unknown<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let base = self as &dyn Command; + f.debug_struct("UnknownCommand") + .finish() + } +} + +impl FromFFI for Unknown<'_> { + fn from_ffi(cmd: cxx::UniquePtr) -> Self { + Self { + ptr: cmd, + _owner: PhantomData + } + } +} + +impl Command for Unknown<'_> { + fn get_base(&self) -> &ffi::MachO_Command { + self.ptr.as_ref().unwrap().as_ref() + } +} diff --git a/api/rust/cargo/lief/tests/macho_tests.rs b/api/rust/cargo/lief/tests/macho_tests.rs index d2c4a539..1668d759 100644 --- a/api/rust/cargo/lief/tests/macho_tests.rs +++ b/api/rust/cargo/lief/tests/macho_tests.rs @@ -1,4 +1,5 @@ mod utils; + use lief::generic::Binary as GenericBinary; use lief::macho::binding_info::{self, AsGeneric}; use lief::macho::commands::{Command, Commands}; @@ -109,6 +110,10 @@ fn explore_macho(_: &str, macho: &lief::macho::Binary) { format!("{:?}", export); } } + + Commands::Unknown(ukn) => { + println!("Original: {:?}", ukn.original_command()); + } _ => {} } } @@ -258,4 +263,5 @@ fn test_api() { test_with("json_api.cpp_1.o"); test_with("python3_issue_476.bin"); test_with("FAT_MachO_x86_x86-64_library_libc++abi.dylib"); + test_with("libadd_unknown_cmd.so"); } diff --git a/api/rust/include/LIEF/rust/MachO.hpp b/api/rust/include/LIEF/rust/MachO.hpp index 92f561f9..61ddd3eb 100644 --- a/api/rust/include/LIEF/rust/MachO.hpp +++ b/api/rust/include/LIEF/rust/MachO.hpp @@ -47,4 +47,5 @@ #include "LIEF/rust/MachO/TwoLevelHints.hpp" #include "LIEF/rust/MachO/UUIDCommand.hpp" #include "LIEF/rust/MachO/VersionMin.hpp" +#include "LIEF/rust/MachO/UnknownCommand.hpp" #include "LIEF/rust/MachO/utils.hpp" diff --git a/api/rust/include/LIEF/rust/MachO/UnknownCommand.hpp b/api/rust/include/LIEF/rust/MachO/UnknownCommand.hpp new file mode 100644 index 00000000..38775e35 --- /dev/null +++ b/api/rust/include/LIEF/rust/MachO/UnknownCommand.hpp @@ -0,0 +1,36 @@ +/* Copyright 2024 R. Thomas + * + * 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. + */ + +#pragma once + +#include "LIEF/MachO/UnknownCommand.hpp" +#include "LIEF/rust/MachO/LoadCommand.hpp" + +class MachO_UnknownCommand : public MachO_Command { + public: + using lief_t = LIEF::MachO::UnknownCommand; + MachO_UnknownCommand(const lief_t& base) : MachO_Command(base) {} + + auto original_command() const { + return impl().original_command(); + } + + static bool classof(const MachO_Command& cmd) { + return lief_t::classof(&cmd.get()); + } + + private: + const lief_t& impl() const { return as(this); } +}; diff --git a/doc/sphinx/api/cpp/macho.rst b/doc/sphinx/api/cpp/macho.rst index f5da093b..438a8421 100644 --- a/doc/sphinx/api/cpp/macho.rst +++ b/doc/sphinx/api/cpp/macho.rst @@ -374,6 +374,14 @@ Two Level Hints Command ---------- +Unknown Command +*************** + +.. doxygenclass:: LIEF::MachO::UnknownCommand + :project: lief + +---------- + Utilities ********* diff --git a/doc/sphinx/api/python/macho.rst b/doc/sphinx/api/python/macho.rst index 9b8e9ba0..444fed46 100644 --- a/doc/sphinx/api/python/macho.rst +++ b/doc/sphinx/api/python/macho.rst @@ -487,6 +487,17 @@ Linker Optimization Hint ---------- +UnknownCommand +************** + +.. lief-inheritance:: lief._lief.MachO.UnknownCommand + :top-classes: lief._lief.MachO.LoadCommand + :parts: 2 + +.. autoclass:: lief.MachO.UnknownCommand + +---------- + Builder ******* diff --git a/doc/sphinx/changelog.rst b/doc/sphinx/changelog.rst index 38b74f98..a57a812d 100644 --- a/doc/sphinx/changelog.rst +++ b/doc/sphinx/changelog.rst @@ -64,6 +64,8 @@ Changelog :MachO: + * Add generic :class:`lief.MachO.UnknownCommand` to support Apple private Load + commands not officially supported by LIEF. * Re-scope ``LOAD_COMMAND_TYPES`` into :class:`lief.MachO.LoadCommand.TYPE` * Re-scope ``FILE_TYPES`` into :class:`lief.MachO.Header.FILE_TYPE` * Re-scope ``HEADER_FLAGS`` into :class:`lief.MachO.Header.FLAGS` diff --git a/include/LIEF/MachO.hpp b/include/LIEF/MachO.hpp index 49911767..6790510f 100644 --- a/include/LIEF/MachO.hpp +++ b/include/LIEF/MachO.hpp @@ -23,8 +23,8 @@ #include "LIEF/MachO/Binary.hpp" #include "LIEF/MachO/BinaryParser.hpp" #include "LIEF/MachO/BindingInfo.hpp" -#include "LIEF/MachO/BuildVersion.hpp" #include "LIEF/MachO/BuildToolVersion.hpp" +#include "LIEF/MachO/BuildVersion.hpp" #include "LIEF/MachO/Builder.hpp" #include "LIEF/MachO/ChainedBindingInfo.hpp" #include "LIEF/MachO/CodeSignature.hpp" @@ -67,6 +67,7 @@ #include "LIEF/MachO/ThreadCommand.hpp" #include "LIEF/MachO/TwoLevelHints.hpp" #include "LIEF/MachO/UUIDCommand.hpp" +#include "LIEF/MachO/UnknownCommand.hpp" #include "LIEF/MachO/VersionMin.hpp" #include "LIEF/MachO/enums.hpp" #include "LIEF/MachO/hash.hpp" diff --git a/include/LIEF/MachO/LoadCommand.hpp b/include/LIEF/MachO/LoadCommand.hpp index ac6de735..e0bd6422 100644 --- a/include/LIEF/MachO/LoadCommand.hpp +++ b/include/LIEF/MachO/LoadCommand.hpp @@ -95,6 +95,8 @@ class LIEF_API LoadCommand : public Object { DYLD_EXPORTS_TRIE = 0x80000033u, DYLD_CHAINED_FIXUPS = 0x80000034u, FILESET_ENTRY = 0x80000035u, + + LIEF_UNKNOWN = 0xffee0001u }; public: diff --git a/include/LIEF/MachO/UnknownCommand.hpp b/include/LIEF/MachO/UnknownCommand.hpp new file mode 100644 index 00000000..8476601e --- /dev/null +++ b/include/LIEF/MachO/UnknownCommand.hpp @@ -0,0 +1,71 @@ +/* Copyright 2024 R. Thomas + * + * 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_UNKNOWN_COMMAND_H +#define LIEF_MACHO_UNKNOWN_COMMAND_H +#include + +#include "LIEF/visibility.h" + +#include "LIEF/MachO/LoadCommand.hpp" + +namespace LIEF { +namespace MachO { + +namespace details { +struct load_command; +} + + +//! Generic class when the command is not recognized by LIEF +class LIEF_API UnknownCommand : public LoadCommand { + + public: + UnknownCommand() = delete; + UnknownCommand(const details::load_command& command) : + LoadCommand(command), + original_command_(static_cast(command_)) + { + command_ = LoadCommand::TYPE::LIEF_UNKNOWN; + } + + UnknownCommand& operator=(const UnknownCommand& copy) = default; + UnknownCommand(const UnknownCommand& copy) = default; + + std::unique_ptr clone() const override { + return std::unique_ptr(new UnknownCommand(*this)); + } + + ~UnknownCommand() override = default; + + /// The original `LC_` int that is not supported by LIEF + uint64_t original_command() const { + return original_command_; + } + + void accept(Visitor& visitor) const override; + + std::ostream& print(std::ostream& os) const override; + + static bool classof(const LoadCommand* cmd) { + return cmd->command() == LoadCommand::TYPE::LIEF_UNKNOWN; + } + + private: + uint64_t original_command_ = 0; +}; + +} +} +#endif diff --git a/include/LIEF/MachO/hash.hpp b/include/LIEF/MachO/hash.hpp index f6cd6293..ca86e4b8 100644 --- a/include/LIEF/MachO/hash.hpp +++ b/include/LIEF/MachO/hash.hpp @@ -61,6 +61,7 @@ class ThreadCommand; class TwoLevelHints; class UUIDCommand; class VersionMin; +class UnknownCommand; //! Class which implements a visitor to compute //! a **deterministic** hash for LIEF MachO objects @@ -112,6 +113,7 @@ class LIEF_API Hash : public LIEF::Hash { void visit(const TwoLevelHints& e) override; void visit(const UUIDCommand& uuid) override; void visit(const VersionMin& vmin) override; + void visit(const UnknownCommand& ukn) override; ~Hash() override; }; diff --git a/include/LIEF/Visitor.hpp b/include/LIEF/Visitor.hpp index 7c69a1b8..d58e29aa 100644 --- a/include/LIEF/Visitor.hpp +++ b/include/LIEF/Visitor.hpp @@ -183,6 +183,7 @@ LIEF_MACHO_FORWARD(FilesetCommand) LIEF_MACHO_FORWARD(TwoLevelHints) LIEF_MACHO_FORWARD(CodeSignatureDir) LIEF_MACHO_FORWARD(LinkerOptHint) +LIEF_MACHO_FORWARD(UnknownCommand) // OAT // =============================== @@ -607,6 +608,9 @@ class LIEF_API Visitor { //! Method to visit a LIEF::MachO::LinkerOptHint LIEF_MACHO_VISITABLE(LinkerOptHint) + //! @brief Method to visit a LIEF::MachO::UnknownCommand + LIEF_MACHO_VISITABLE(UnknownCommand) + // OAT part // ======== diff --git a/src/MachO/BinaryParser.tcc b/src/MachO/BinaryParser.tcc index 013bb8da..260f256f 100644 --- a/src/MachO/BinaryParser.tcc +++ b/src/MachO/BinaryParser.tcc @@ -39,6 +39,7 @@ #include "LIEF/MachO/BuildVersion.hpp" #include "LIEF/MachO/EnumToString.hpp" #include "LIEF/MachO/FilesetCommand.hpp" +#include "LIEF/MachO/UnknownCommand.hpp" #include "LIEF/MachO/FunctionStarts.hpp" #include "LIEF/MachO/LinkEdit.hpp" #include "LIEF/MachO/LinkerOptHint.hpp" @@ -1047,7 +1048,7 @@ ok_error_t BinaryParser::parse_load_commands() { if (not_parsed.insert(cmd_type).second) { LIEF_WARN("Command '{}' not parsed!", to_string(cmd_type)); } - load_command = std::make_unique(*command); + load_command = std::make_unique(*command); } } diff --git a/src/MachO/CMakeLists.txt b/src/MachO/CMakeLists.txt index c2d073e8..9073238d 100644 --- a/src/MachO/CMakeLists.txt +++ b/src/MachO/CMakeLists.txt @@ -54,6 +54,7 @@ target_sources(LIB_LIEF PRIVATE TrieNode.cpp TwoLevelHints.cpp UUIDCommand.cpp + UnknownCommand.cpp VersionMin.cpp exports_trie.cpp hash.cpp diff --git a/src/MachO/LoadCommand.cpp b/src/MachO/LoadCommand.cpp index 4634a3d4..2740f9d9 100644 --- a/src/MachO/LoadCommand.cpp +++ b/src/MachO/LoadCommand.cpp @@ -132,6 +132,7 @@ const char* to_string(LoadCommand::TYPE e) { ENTRY(DYLD_EXPORTS_TRIE), ENTRY(DYLD_CHAINED_FIXUPS), ENTRY(FILESET_ENTRY), + ENTRY(LIEF_UNKNOWN), }; #undef ENTRY diff --git a/src/MachO/UnknownCommand.cpp b/src/MachO/UnknownCommand.cpp new file mode 100644 index 00000000..7438b239 --- /dev/null +++ b/src/MachO/UnknownCommand.cpp @@ -0,0 +1,32 @@ +/* Copyright 2024 R. Thomas + * + * 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 "LIEF/MachO/UnknownCommand.hpp" +#include "LIEF/Visitor.hpp" + +#include + +namespace LIEF::MachO { + +void UnknownCommand::accept(Visitor& visitor) const { + visitor.visit(*this); +} + +std::ostream& UnknownCommand::print(std::ostream& os) const { + LoadCommand::print(os); + os << fmt::format("Original Command: {}", original_command()) << '\n'; + return os; +} + +} diff --git a/src/MachO/hash.cpp b/src/MachO/hash.cpp index 7b7e300a..ece77f18 100644 --- a/src/MachO/hash.cpp +++ b/src/MachO/hash.cpp @@ -288,6 +288,11 @@ void Hash::visit(const VersionMin& vmin) { process(vmin.sdk()); } +void Hash::visit(const UnknownCommand& ukn) { + visit(*ukn.as()); + process(ukn.original_command()); +} + void Hash::visit(const SourceVersion& sv) { visit(*sv.as()); process(sv.version()); diff --git a/src/MachO/json.cpp b/src/MachO/json.cpp index 29e785c6..77d77a8b 100644 --- a/src/MachO/json.cpp +++ b/src/MachO/json.cpp @@ -394,6 +394,12 @@ void JsonVisitor::visit(const VersionMin& vmin) { node_["sdk"] = vmin.sdk(); } +void JsonVisitor::visit(const UnknownCommand& ukn) { + visit(*ukn.as()); + + node_["original_command"] = ukn.original_command(); +} + void JsonVisitor::visit(const SegmentSplitInfo& ssi) { visit(*ssi.as()); node_["data_offset"] = ssi.data_offset(); diff --git a/src/MachO/json_internal.hpp b/src/MachO/json_internal.hpp index b9b0c469..f84fd21e 100644 --- a/src/MachO/json_internal.hpp +++ b/src/MachO/json_internal.hpp @@ -62,6 +62,7 @@ class ThreadCommand; class TwoLevelHints; class UUIDCommand; class VersionMin; +class UnknownCommand; //! Class that implements the Visitor pattern to output //! a JSON representation of a MachO object @@ -109,6 +110,7 @@ class JsonVisitor : public LIEF::JsonVisitor { void visit(const TwoLevelHints& e) override; void visit(const UUIDCommand& uuid) override; void visit(const VersionMin& vmin) override; + void visit(const UnknownCommand& ukn) override; }; } diff --git a/tests/macho/test_generic.py b/tests/macho/test_generic.py index 91548d5d..6c76fef0 100644 --- a/tests/macho/test_generic.py +++ b/tests/macho/test_generic.py @@ -286,3 +286,11 @@ def test_issue_1055(): for section in sample.sections: size = len(section.content) assert size is not None + +def test_unknown_command(): + sample = lief.MachO.parse(get_sample("MachO/libadd_unknown_cmd.so")).at(0) + unknown_cmd = sample.commands[15] + assert isinstance(unknown_cmd, lief.MachO.UnknownCommand) + assert unknown_cmd.original_command == 0x3333 + print(hash(unknown_cmd)) + print(unknown_cmd)