@ 261107.7
current
/* Copyright 2017 - 2026 R. Thomas
 * Copyright 2017 - 2026 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 <sstream>
#include "LIEF/PE/Parser.hpp"
#include "LIEF/PE/Binary.hpp"
#include "LIEF/PE/LoadConfigurations/EnclaveConfiguration.hpp"
#include "LIEF/PE/LoadConfigurations/EnclaveImport.hpp"
#include "LIEF/BinaryStream/BinaryStream.hpp"

#include "PE/Structures.hpp"

#include "logging.hpp"
#include "internal_utils.hpp"

namespace LIEF::PE {

std::string EnclaveConfiguration::to_string() const {
  static constexpr auto WIDTH = 39;
  std::ostringstream os;
  os << fmt::format("{:>{}}: {:#010x}\n", "Size", WIDTH, size())
     << fmt::format("{:>{}}: {:#010x}\n", "Minimum Required Config Size", WIDTH,
                    min_required_config_size())
     << fmt::format("{:>{}}: {:#010x} (debuggable={})\n", "Policy Flags", WIDTH,
                    policy_flags(), is_debuggable())
     << fmt::format("{:>{}}: {}\n", "Number of Enclave Import Descriptors", WIDTH,
                    nb_imports())
     << fmt::format("{:>{}}: {:#010x}\n", "RVA to enclave imports", WIDTH,
                    import_list_rva())
     << fmt::format("{:>{}}: {:#06x}\n", "Size of enclave import", WIDTH,
                    import_entry_size())
     << fmt::format("{:>{}}: {}\n", "Image version", WIDTH, image_version())
     << fmt::format("{:>{}}: {}\n", "Security version", WIDTH, security_version())
     << fmt::format("{:>{}}: {:#018x}\n", "Enclave Size", WIDTH, enclave_size())
     << fmt::format("{:>{}}: {}\n", "Number of Threads", WIDTH, nb_threads())
     << fmt::format("{:>{}}: {:#010x}\n", "Enclave flags", WIDTH, enclave_flags());

  os << fmt::format("{:>{}}: {}\n", "Image ID", WIDTH,
                    hex_dump(image_id(), /*sep=*/" "));
  os << fmt::format("{:>{}}: {}", "Family ID", WIDTH,
                    hex_dump(family_id(), /*sep=*/" "));

  if (!imports_.empty()) {
    os << '\n';
  }

  for (const EnclaveImport& imp : imports()) {
    os << indent(imp.to_string(), 4) << '\n';
  }

  return os.str();
}

template<class PE_T>
std::unique_ptr<EnclaveConfiguration>
    EnclaveConfiguration::parse(Parser& ctx, BinaryStream& stream) {
  using ptr_t = typename PE_T::uint;

  static constexpr uint32_t ENCLAVE_IMPORT_SIZE = 0x50;
  static constexpr uint32_t ENCLAVE_IMPORT_MAX_SIZE = 0x1000;

  auto config = std::make_unique<EnclaveConfiguration>();
  auto Size = stream.read<uint32_t>();
  if (!Size) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  if (*Size < MIN_SIZE) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto MinimumRequiredConfigSize = stream.read<uint32_t>();
  if (!MinimumRequiredConfigSize) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto PolicyFlags = stream.read<uint32_t>();
  if (!PolicyFlags) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto NumberOfImports = stream.read<uint32_t>();
  if (!NumberOfImports) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto ImportList = stream.read<uint32_t>();
  if (!ImportList) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }
  auto ImportEntrySize = stream.read<uint32_t>();
  if (!ImportEntrySize) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  if (!stream.read_array(config->family_id_)) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  if (!stream.read_array(config->image_id_)) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto ImageVersion = stream.read<uint32_t>();
  if (!ImageVersion) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto SecurityVersion = stream.read<uint32_t>();
  if (!SecurityVersion) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto EnclaveSize = stream.read<ptr_t>();
  if (!EnclaveSize) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto NumberOfThreads = stream.read<uint32_t>();
  if (!NumberOfThreads) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto EnclaveFlags = stream.read<uint32_t>();
  if (!EnclaveFlags) {
    LIEF_DEBUG("{}:{}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  (*config)
      .size(*Size)
      .min_required_config_size(*MinimumRequiredConfigSize)
      .policy_flags(*PolicyFlags)
      .import_list_rva(*ImportList)
      .import_entry_size(*ImportEntrySize)
      .image_version(*ImageVersion)
      .security_version(*SecurityVersion)
      .enclave_size(*EnclaveSize)
      .nb_threads(*NumberOfThreads)
      .enclave_flags(*EnclaveFlags);

  if (*NumberOfImports == 0) {
    return config;
  }

  if (*ImportEntrySize < ENCLAVE_IMPORT_SIZE ||
      *ImportEntrySize > ENCLAVE_IMPORT_MAX_SIZE)
  {
    return config;
  }

  const uint64_t base_offset = ctx.bin().rva_to_offset(*ImportList);
  const uint64_t stream_size = ctx.stream().size();

  if (base_offset >= stream_size) {
    return config;
  }

  const uint64_t max_imports = (stream_size - base_offset) / *ImportEntrySize;
  const uint64_t nb_imports = std::min<uint64_t>(*NumberOfImports, max_imports);

  for (size_t i = 0; i < nb_imports; ++i) {
    const uint64_t offset = base_offset + i * (uint64_t)*ImportEntrySize;
    ScopedStream scope(ctx.stream(), offset);
    auto import = EnclaveImport::parse(ctx, *scope);
    if (!import) {
      LIEF_DEBUG("Error: {}:{}", __FUNCTION__, __LINE__);
      return config;
    }

    config->imports_.push_back(std::move(*import));
  }

  return config;
}

template std::unique_ptr<EnclaveConfiguration>
    EnclaveConfiguration::parse<details::PE32>(Parser& ctx, BinaryStream& stream);

template std::unique_ptr<EnclaveConfiguration>
    EnclaveConfiguration::parse<details::PE64>(Parser& ctx, BinaryStream& stream);

}