#include "tls.h" #include #include #include #include namespace raven { static uint64_t integer(NativeProcess &process, uint64_t address, size_t width) { const auto bytes = process.read_memory(address, width); uint64_t result = 0; for (size_t index = 0; index < width; ++index) result |= uint64_t(bytes[index]) << (index * 8); return result; } static void put(NativeProcess &process, uint64_t address, uint64_t value, size_t width) { std::array bytes{}; for (size_t index = 0; index < width; ++index) bytes[index] = static_cast(value >> (index * 8)); process.write_memory(address, std::span(bytes).first(width)); } std::optional NativeTLS::parse(const PEModule *module, size_t total) const { std::vector functions; const auto directory = module->image.directories[9]; if (!directory.address && !directory.size) return std::nullopt; const uint64_t base = module->loaded.base(); const auto within = [&](uint64_t address, uint64_t size) { if (address < base || address - base > module->image.image_size || size > module->image.image_size - (address - base)) throw std::runtime_error("static TLS address is outside its module: " + module->name); }; if (!module->image.is_64 || directory.size < 40) throw std::runtime_error("invalid PE64 TLS directory: " + module->name); within(base + directory.address, directory.size); const auto raw = process.read_memory(base + directory.address, 40); const auto field = [&](size_t offset, size_t width = 8) { uint64_t result = 0; for (size_t index = 0; index < width; ++index) result |= uint64_t(raw[offset + index]) << (index * 8); return result; }; const uint64_t start = field(0), end = field(8), index = field(16); uint64_t array = field(24); const uint64_t zero_fill = field(32, 4), characteristics = field(36, 4); const uint64_t alignment_code = (characteristics >> 20) & 15; if (end < start || (characteristics & ~uint64_t(0xf00000)) || alignment_code == 15) throw std::runtime_error("invalid static TLS size or alignment: " + module->name); if (end - start > 64 * 1024 * 1024) throw std::runtime_error("static TLS template exceeds allocation limit"); const uint64_t size = end - start + zero_fill; if (size > 64 * 1024 * 1024 || total > 64 * 1024 * 1024 - size) throw std::runtime_error("static TLS exceeds initial thread allocation limit"); within(index, 4); // Validate all template and index permissions before publishing any slot. process.write_memory(index, process.read_memory(index, 4)); std::vector bytes; if (start || end) { within(start, end - start); bytes = process.read_memory(start, static_cast(end - start)); } if (array) { for (size_t position = 0;; ++position) { if (position == 1024) throw std::runtime_error("unterminated static TLS callback array"); within(array, 8); const auto function = integer(process, array, 8); if (!function) break; within(function, 1); const auto mappings = module->loaded.mappings(); if (!std::any_of(mappings.begin(), mappings.end(), [&](const auto &mapping) { return function >= mapping.address && function - mapping.address < mapping.size && (mapping.protection & PROT_EXEC); })) throw std::runtime_error("static TLS callback is not executable"); functions.push_back(function); array += 8; } } const size_t alignment = alignment_code ? size_t(1) << (alignment_code - 1) : 16; return Template{index, static_cast(size), alignment, std::move(bytes), std::move(functions)}; } NativeTLS::NativeTLS(NativeProcess &process, const NativeEnvironment &environment, std::span order) : process(process), environment(environment) { main_teb = environment.teb(); if (process.active() || process.context().registers.gs_base != environment.teb() || order.size() > 10000) throw std::runtime_error("invalid static TLS initialization state"); if (integer(process, environment.teb() + 0x58, 8)) throw std::runtime_error("initial thread static TLS already exists"); std::map> prepared; size_t allocated = 0; for (const auto *module : order) { if (!module || prepared.contains(module)) throw std::runtime_error("invalid static TLS module order"); auto item = parse(module, allocated); if (item) allocated += item->size; prepared.emplace(module, std::move(item)); } for (const auto *module : order) if (prepared.at(module)) { process.write_memory(environment.teb() + 0x58, process.read_memory(environment.teb() + 0x58, 8)); break; } for (const auto *module : order) install(*module, prepared.at(module)); if (vector) put(process, environment.teb() + 0x58, vector, 8); } void NativeTLS::validate() const { if (integer(process, main_teb + 0x58, 8) != vector) throw std::runtime_error("native loader TLS vector identity was modified"); for (const auto &[module, entry] : entries) if (entry.slot != UINT32_MAX && (integer(process, entry.index, 4) != entry.slot || integer(process, vector + uint64_t(entry.slot) * 8, 8) != entry.block)) throw std::runtime_error("native loader TLS slot identity was modified"); } void NativeTLS::install(const PEModule &module, const std::optional