/* 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);
}