Skip to content

Commit

Permalink
add a diagnostic for an invalid, early release address bin
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan-rsm-McKenzie committed May 3, 2024
1 parent 85fa600 commit da3e995
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
19 changes: 19 additions & 0 deletions CommonLibF4/include/REL/Relocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ namespace REL

return func(std::forward<First>(a_first), std::addressof(result), std::forward<Rest>(a_rest)...);
}

std::optional<std::string> sha512(std::span<const std::byte> a_data);
}

inline constexpr std::uint8_t NOP = 0x90;
Expand Down Expand Up @@ -626,6 +628,23 @@ namespace REL
if (!_mmap.open(path)) {
stl::report_and_fail(fmt::format("failed to open: {}", path));
}
if (version == REL::Version(1, 10, 980)) {
const auto sha = detail::sha512({ _mmap });
if (!sha) {
stl::report_and_fail(fmt::format("failed to hash: {}", path));
}
// Address bins are expected to be pre-sorted. This bin was released without being sorted, and will cause lookups to randomly fail.
if (*sha == "2AD60B95388F1B6E77A6F86F17BEB51D043CF95A341E91ECB2E911A393E45FE8156D585D2562F7B14434483D6E6652E2373B91589013507CABAE596C26A343F1"sv) {
stl::report_and_fail(fmt::format(
"The address bin you are using ({}) is corrupted. "
"Please go to the Nexus page for Address Library and redownload the file corresponding to version {}.{}.{}.{}",
path,
version[0],
version[1],
version[2],
version[3]));
}
}
_id2offset = std::span{
reinterpret_cast<const mapping_t*>(_mmap.data() + sizeof(std::uint64_t)),
*reinterpret_cast<const std::uint64_t*>(_mmap.data())
Expand Down
85 changes: 85 additions & 0 deletions CommonLibF4/src/REL/Relocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,93 @@

#include <Windows.h>

#include <bcrypt.h>

#define NT_SUCCESS(a_status) (a_status >= 0)

#include "F4SE/Logger.h"

namespace REL
{
namespace log = F4SE::log;

namespace detail
{
std::optional<std::string> sha512(std::span<const std::byte> a_data)
{
::BCRYPT_ALG_HANDLE algorithm;
if (!NT_SUCCESS(::BCryptOpenAlgorithmProvider(
&algorithm,
BCRYPT_SHA512_ALGORITHM,
nullptr,
0))) {
log::error("failed to open algorithm provider");
return std::nullopt;
}
const stl::scope_exit delAlgorithm([&]() {
[[maybe_unused]] const auto success = NT_SUCCESS(::BCryptCloseAlgorithmProvider(algorithm, 0));
assert(success);
});

::BCRYPT_HASH_HANDLE hash;
if (!NT_SUCCESS(::BCryptCreateHash(
algorithm,
&hash,
nullptr,
0,
nullptr,
0,
0))) {
log::error("failed to create hash");
return std::nullopt;
}
const stl::scope_exit delHash([&]() {
[[maybe_unused]] const auto success = NT_SUCCESS(::BCryptDestroyHash(hash));
assert(success);
});

if (!NT_SUCCESS(::BCryptHashData(
hash,
reinterpret_cast<::PUCHAR>(const_cast<std::byte*>(a_data.data())), // does not modify contents of buffer
static_cast<::ULONG>(a_data.size()),
0))) {
log::error("failed to hash data");
return std::nullopt;
}

::DWORD hashLen = 0;
::ULONG discard = 0;
if (!NT_SUCCESS(::BCryptGetProperty(
hash,
BCRYPT_HASH_LENGTH,
reinterpret_cast<::PUCHAR>(&hashLen),
sizeof(hashLen),
&discard,
0))) {
log::error("failed to get property");
return std::nullopt;
}
std::vector<::UCHAR> buffer(static_cast<std::size_t>(hashLen));

if (!NT_SUCCESS(::BCryptFinishHash(
hash,
buffer.data(),
static_cast<::ULONG>(buffer.size()),
0))) {
log::error("failed to finish hash");
return std::nullopt;
}

std::string result;
result.reserve(buffer.size() * 2);
for (const auto byte : buffer) {
result += fmt::format("{:02X}", byte);
}

return { std::move(result) };
}
}

void Module::load_segments()
{
auto dosHeader = reinterpret_cast<const ::IMAGE_DOS_HEADER*>(_base);
Expand Down

0 comments on commit da3e995

Please sign in to comment.