Skip to content

Commit 5e7f778

Browse files
committed
feat: blacklist invalid address library bins
1 parent 15252a9 commit 5e7f778

5 files changed

Lines changed: 90 additions & 0 deletions

File tree

include/REL/IDDB.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ namespace REL
4848
void load_v2(STREAM& a_stream);
4949
void load_v5(STREAM& a_stream);
5050
void unpack_file(STREAM& a_stream, const HEADER_V2& a_header);
51+
void validate_file();
5152

5253
protected:
5354
friend class Offset2ID;

include/REX/REX.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "REX/REX/CONVERT.h"
66
#include "REX/REX/Enum.h"
77
#include "REX/REX/EnumSet.h"
8+
#include "REX/REX/HASH.h"
89
#include "REX/REX/INI.h"
910
#include "REX/REX/JSON.h"
1011
#include "REX/REX/LOG.h"

include/REX/REX/HASH.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#pragma once
2+
3+
#include "REX/BASE.h"
4+
#include "REX/REX/ScopeExit.h"
5+
#include "REX/W32/BCRYPT.h"
6+
7+
namespace REX
8+
{
9+
inline std::optional<std::string> SHA512(std::span<const std::byte> a_data)
10+
{
11+
REX::W32::BCRYPT_ALG_HANDLE algorithm;
12+
if (!REX::W32::NT_SUCCESS(REX::W32::BCryptOpenAlgorithmProvider(&algorithm, REX::W32::BCRYPT_SHA512_ALGORITHM)))
13+
return std::nullopt;
14+
15+
const TScopeExit cleanup_algo([&]() {
16+
[[maybe_unused]] const auto success = REX::W32::NT_SUCCESS(REX::W32::BCryptCloseAlgorithmProvider(algorithm));
17+
assert(success);
18+
});
19+
20+
REX::W32::BCRYPT_HASH_HANDLE hash;
21+
if (!REX::W32::NT_SUCCESS(REX::W32::BCryptCreateHash(algorithm, &hash)))
22+
return std::nullopt;
23+
24+
const TScopeExit cleanup_hash([&]() {
25+
[[maybe_unused]] const auto success = REX::W32::NT_SUCCESS(REX::W32::BCryptDestroyHash(hash));
26+
assert(success);
27+
});
28+
29+
if (!REX::W32::NT_SUCCESS(REX::W32::BCryptHashData(hash, reinterpret_cast<std::uint8_t*>(const_cast<std::byte*>(a_data.data())), static_cast<std::uint32_t>(a_data.size()))))
30+
return std::nullopt;
31+
32+
std::uint32_t length{ 0 };
33+
std::uint32_t output{ 0 };
34+
if (!REX::W32::NT_SUCCESS(REX::W32::BCryptGetProperty(hash, REX::W32::BCRYPT_HASH_LENGTH, reinterpret_cast<std::uint8_t*>(&length), sizeof(length), &output)))
35+
return std::nullopt;
36+
37+
std::vector<std::uint8_t> buffer(static_cast<std::size_t>(length));
38+
if (!REX::W32::NT_SUCCESS(REX::W32::BCryptFinishHash(hash, buffer.data(), static_cast<std::uint32_t>(buffer.size()))))
39+
return std::nullopt;
40+
41+
std::string result;
42+
result.reserve(buffer.size() * 2);
43+
for (const auto byte : buffer) {
44+
result += std::format("{:02X}", byte);
45+
}
46+
47+
return { std::move(result) };
48+
}
49+
}

include/REX/REX/MemoryMap.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ namespace REX
3939
return static_cast<std::byte*>(m_mapView);
4040
}
4141

42+
std::size_t size() const noexcept
43+
{
44+
return m_size;
45+
}
46+
4247
bool is_open() const
4348
{
4449
return m_mapView;

src/REL/IDDB.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "REL/Module.h"
33
#include "REL/Version.h"
44

5+
#include "REX/REX/HASH.h"
56
#include "REX/REX/LOG.h"
67
#include "REX/W32/KERNEL32.h"
78

@@ -196,6 +197,8 @@ namespace REL
196197
if (!m_mmap.create(false, m_path, mapName))
197198
REX::FAIL(L"Failed to create Address Library MemoryMap!\nError: {}\nPath: {}", REX::W32::GetLastError(), m_path.wstring());
198199

200+
validate_file();
201+
199202
m_v0 = {
200203
reinterpret_cast<MAPPING*>(m_mmap.data() + sizeof(std::uint64_t)),
201204
*reinterpret_cast<std::uint64_t*>(m_mmap.data())
@@ -221,6 +224,8 @@ namespace REL
221224
if (!m_mmap.create(true, mapName, byteSize))
222225
REX::FAIL("Failed to create Address Library MemoryMap!\nError: {}", REX::W32::GetLastError());
223226

227+
validate_file();
228+
224229
m_v0 = { reinterpret_cast<MAPPING*>(m_mmap.data()), header.address_count() };
225230

226231
if (m_mmap.is_owner()) {
@@ -255,6 +260,8 @@ namespace REL
255260
if (!m_mmap.create(false, m_path, mapName))
256261
REX::FAIL(L"Failed to create Address Library MemoryMap!\nError: {}\nPath: {}", REX::W32::GetLastError(), m_path.wstring());
257262

263+
validate_file();
264+
258265
m_v5 = { reinterpret_cast<std::uint32_t*>(m_mmap.data() + sizeof(HEADER_V5)), header.offset_count() };
259266

260267
} catch (const std::system_error&) {
@@ -345,6 +352,33 @@ namespace REL
345352
}
346353
}
347354

355+
void IDDB::validate_file()
356+
{
357+
// clang-format off
358+
std::unordered_map<IDDB::Loader, std::vector<std::pair<REL::Version, std::string_view>>> g_blacklistMap{
359+
{ IDDB::Loader::SKSE, {} },
360+
{ IDDB::Loader::F4SE, {
361+
{ REL::Version{ 1, 10, 980 }, "2AD60B95388F1B6E77A6F86F17BEB51D043CF95A341E91ECB2E911A393E45FE8156D585D2562F7B14434483D6E6652E2373B91589013507CABAE596C26A343F1"sv },
362+
{ REL::Version{ 1, 11, 159 }, "686D40387F638ED75AD43BB76CA14170576F1A30E91144F280987D13A3012B1CA6A4E04E6BE7A5B99E46C50332C49BE40C3D9448038E17D3D31C40E72A90AE26"sv }
363+
} },
364+
{ IDDB::Loader::SFSE, {} },
365+
{ IDDB::Loader::OBSE, {} },
366+
};
367+
// clang-format on
368+
369+
const auto mod = Module::GetSingleton();
370+
const auto version = mod->version();
371+
for (auto& check : g_blacklistMap[m_loader]) {
372+
if (version == check.first) {
373+
auto sha = REX::SHA512({ m_mmap.data(), m_mmap.size() });
374+
if (!sha)
375+
REX::FAIL("Failed to hash Address Library file!\nPath: {}", m_path.string());
376+
if (*sha == check.second)
377+
REX::FAIL("Invalid Address Library loaded!\n\nRedownload Address Library for your game version.\nGame Version: {}", version.string());
378+
}
379+
}
380+
}
381+
348382
std::uint64_t IDDB::offset(std::uint64_t a_id) const
349383
{
350384
const auto mod = Module::GetSingleton();

0 commit comments

Comments
 (0)